1 /** @file
2 
3   Http2ClientSession.
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "Http2ClientSession.h"
25 #include "HttpDebugNames.h"
26 #include "tscore/ink_base64.h"
27 
28 #define REMEMBER(e, r)                          \
29   {                                             \
30     this->remember(MakeSourceLocation(), e, r); \
31   }
32 
33 #define STATE_ENTER(state_name, event)                                                       \
34   do {                                                                                       \
35     REMEMBER(event, this->recursion)                                                         \
36     SsnDebug(this, "http2_cs", "[%" PRId64 "] [%s, %s]", this->connection_id(), #state_name, \
37              HttpDebugNames::get_event_name(event));                                         \
38   } while (0)
39 
40 #define Http2SsnDebug(fmt, ...) SsnDebug(this, "http2_cs", "[%" PRId64 "] " fmt, this->connection_id(), ##__VA_ARGS__)
41 
42 #define HTTP2_SET_SESSION_HANDLER(handler) \
43   do {                                     \
44     REMEMBER(NO_EVENT, this->recursion);   \
45     this->session_handler = (handler);     \
46   } while (0)
47 
48 ClassAllocator<Http2ClientSession> http2ClientSessionAllocator("http2ClientSessionAllocator");
49 
50 // memcpy the requested bytes from the IOBufferReader, returning how many were
51 // actually copied.
52 static inline unsigned
copy_from_buffer_reader(void * dst,IOBufferReader * reader,unsigned nbytes)53 copy_from_buffer_reader(void *dst, IOBufferReader *reader, unsigned nbytes)
54 {
55   char *end;
56 
57   end = reader->memcpy(dst, nbytes, 0 /* offset */);
58   return end - static_cast<char *>(dst);
59 }
60 
61 static int
send_connection_event(Continuation * cont,int event,void * edata)62 send_connection_event(Continuation *cont, int event, void *edata)
63 {
64   SCOPED_MUTEX_LOCK(lock, cont->mutex, this_ethread());
65   return cont->handleEvent(event, edata);
66 }
67 
68 Http2ClientSession::Http2ClientSession() = default;
69 
70 void
destroy()71 Http2ClientSession::destroy()
72 {
73   if (!in_destroy) {
74     in_destroy = true;
75     REMEMBER(NO_EVENT, this->recursion)
76     Http2SsnDebug("session destroy");
77     // Let everyone know we are going down
78     do_api_callout(TS_HTTP_SSN_CLOSE_HOOK);
79   }
80 }
81 
82 void
free()83 Http2ClientSession::free()
84 {
85   if (this->_reenable_event) {
86     this->_reenable_event->cancel();
87     this->_reenable_event = nullptr;
88   }
89 
90   if (client_vc) {
91     client_vc->do_io_close();
92     client_vc = nullptr;
93   }
94 
95   // Make sure the we are at the bottom of the stack
96   if (connection_state.is_recursing() || this->recursion != 0) {
97     // Note that we are ready to be cleaned up
98     // One of the event handlers will catch it
99     kill_me = true;
100     return;
101   }
102 
103   REMEMBER(NO_EVENT, this->recursion)
104   Http2SsnDebug("session free");
105 
106   this->_milestones.mark(Http2SsnMilestone::CLOSE);
107   ink_hrtime total_time = this->_milestones.elapsed(Http2SsnMilestone::OPEN, Http2SsnMilestone::CLOSE);
108 
109   // Slow Log
110   if (Http2::con_slow_log_threshold != 0 && ink_hrtime_from_msec(Http2::con_slow_log_threshold) < total_time) {
111     Error("[%" PRIu64 "] Slow H2 Connection: open: %" PRIu64 " close: %.3f", this->con_id,
112           ink_hrtime_to_msec(this->_milestones[Http2SsnMilestone::OPEN]),
113           this->_milestones.difference_sec(Http2SsnMilestone::OPEN, Http2SsnMilestone::CLOSE));
114   }
115 
116   HTTP2_DECREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT, this->mutex->thread_holding);
117 
118   // Update stats on how we died.  May want to eliminate this.  Was useful for
119   // tracking down which cases we were having problems cleaning up.  But for general
120   // use probably not worth the effort
121   if (cause_of_death != Http2SessionCod::NOT_PROVIDED) {
122     switch (cause_of_death) {
123     case Http2SessionCod::HIGH_ERROR_RATE:
124       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_HIGH_ERROR_RATE, this_ethread());
125       break;
126     case Http2SessionCod::NOT_PROVIDED:
127       // Can't happen but this case is here to not have default case.
128       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_OTHER, this_ethread());
129       break;
130     }
131   } else {
132     switch (dying_event) {
133     case VC_EVENT_NONE:
134       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_DEFAULT, this_ethread());
135       break;
136     case VC_EVENT_ACTIVE_TIMEOUT:
137       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_ACTIVE, this_ethread());
138       break;
139     case VC_EVENT_INACTIVITY_TIMEOUT:
140       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_INACTIVE, this_ethread());
141       break;
142     case VC_EVENT_ERROR:
143       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_ERROR, this_ethread());
144       break;
145     case VC_EVENT_EOS:
146       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_EOS, this_ethread());
147       break;
148     default:
149       HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_OTHER, this_ethread());
150       break;
151     }
152   }
153 
154   ink_release_assert(this->client_vc == nullptr);
155 
156   this->connection_state.destroy();
157 
158   super::free();
159 
160   free_MIOBuffer(this->read_buffer);
161   free_MIOBuffer(this->write_buffer);
162   THREAD_FREE(this, http2ClientSessionAllocator, this_ethread());
163 }
164 
165 void
start()166 Http2ClientSession::start()
167 {
168   SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
169 
170   SET_HANDLER(&Http2ClientSession::main_event_handler);
171   HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_read_connection_preface);
172 
173   VIO *read_vio = this->do_io_read(this, INT64_MAX, this->read_buffer);
174   write_vio     = this->do_io_write(this, INT64_MAX, this->sm_writer);
175 
176   this->connection_state.init();
177   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_INIT, this);
178 
179   if (this->_reader->is_read_avail_more_than(0)) {
180     this->handleEvent(VC_EVENT_READ_READY, read_vio);
181   }
182 }
183 
184 void
new_connection(NetVConnection * new_vc,MIOBuffer * iobuf,IOBufferReader * reader)185 Http2ClientSession::new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, IOBufferReader *reader)
186 {
187   ink_assert(new_vc->mutex->thread_holding == this_ethread());
188   HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT, new_vc->mutex->thread_holding);
189   HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_CONNECTION_COUNT, new_vc->mutex->thread_holding);
190   this->_milestones.mark(Http2SsnMilestone::OPEN);
191 
192   // Unique client session identifier.
193   this->con_id    = ProxySession::next_connection_id();
194   this->client_vc = new_vc;
195   client_vc->set_inactivity_timeout(HRTIME_SECONDS(Http2::accept_no_activity_timeout));
196   this->schedule_event = nullptr;
197   this->mutex          = new_vc->mutex;
198   this->in_destroy     = false;
199 
200   this->connection_state.mutex = this->mutex;
201 
202   SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(new_vc);
203   if (ssl_vc != nullptr) {
204     this->read_from_early_data = ssl_vc->read_from_early_data;
205     Debug("ssl_early_data", "read_from_early_data = %" PRId64, this->read_from_early_data);
206   }
207 
208   Http2SsnDebug("session born, netvc %p", this->client_vc);
209 
210   this->client_vc->set_tcp_congestion_control(CLIENT_SIDE);
211 
212   this->read_buffer             = iobuf ? iobuf : new_MIOBuffer(HTTP2_HEADER_BUFFER_SIZE_INDEX);
213   this->read_buffer->water_mark = connection_state.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE);
214   this->_reader                 = reader ? reader : this->read_buffer->alloc_reader();
215 
216   // Set write buffer size to max size of TLS record (16KB)
217   this->write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_16K);
218   this->sm_writer    = this->write_buffer->alloc_reader();
219 
220   this->_handle_if_ssl(new_vc);
221 
222   do_api_callout(TS_HTTP_SSN_START_HOOK);
223 }
224 
225 void
set_upgrade_context(HTTPHdr * h)226 Http2ClientSession::set_upgrade_context(HTTPHdr *h)
227 {
228   upgrade_context.req_header = new HTTPHdr();
229   upgrade_context.req_header->copy(h);
230 
231   MIMEField *settings = upgrade_context.req_header->field_find(MIME_FIELD_HTTP2_SETTINGS, MIME_LEN_HTTP2_SETTINGS);
232   ink_release_assert(settings != nullptr);
233   int svlen;
234   const char *sv = settings->value_get(&svlen);
235 
236   if (sv && svlen > 0) {
237     // Maybe size of data decoded by Base64URL is lower than size of encoded data.
238     unsigned char out_buf[svlen];
239     size_t decoded_len;
240     ats_base64_decode(sv, svlen, out_buf, svlen, &decoded_len);
241     for (size_t nbytes = 0; nbytes < decoded_len; nbytes += HTTP2_SETTINGS_PARAMETER_LEN) {
242       Http2SettingsParameter param;
243       if (!http2_parse_settings_parameter(make_iovec(out_buf + nbytes, HTTP2_SETTINGS_PARAMETER_LEN), param) ||
244           !http2_settings_parameter_is_valid(param)) {
245         // TODO ignore incoming invalid parameters and send suitable SETTINGS
246         // frame.
247       }
248       upgrade_context.client_settings.set(static_cast<Http2SettingsIdentifier>(param.id), param.value);
249     }
250   }
251 
252   // Such intermediaries SHOULD also remove other connection-
253   // specific header fields, such as Keep-Alive, Proxy-Connection,
254   // Transfer-Encoding and Upgrade, even if they are not nominated by
255   // Connection.
256   upgrade_context.req_header->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
257   upgrade_context.req_header->field_delete(MIME_FIELD_KEEP_ALIVE, MIME_LEN_KEEP_ALIVE);
258   upgrade_context.req_header->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
259   upgrade_context.req_header->field_delete(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING);
260   upgrade_context.req_header->field_delete(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
261   upgrade_context.req_header->field_delete(MIME_FIELD_HTTP2_SETTINGS, MIME_LEN_HTTP2_SETTINGS);
262 }
263 
264 VIO *
do_io_read(Continuation * c,int64_t nbytes,MIOBuffer * buf)265 Http2ClientSession::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf)
266 {
267   if (client_vc) {
268     return this->client_vc->do_io_read(c, nbytes, buf);
269   } else {
270     return nullptr;
271   }
272 }
273 
274 VIO *
do_io_write(Continuation * c,int64_t nbytes,IOBufferReader * buf,bool owner)275 Http2ClientSession::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner)
276 {
277   if (client_vc) {
278     return this->client_vc->do_io_write(c, nbytes, buf, owner);
279   } else {
280     return nullptr;
281   }
282 }
283 
284 void
do_io_shutdown(ShutdownHowTo_t howto)285 Http2ClientSession::do_io_shutdown(ShutdownHowTo_t howto)
286 {
287   this->client_vc->do_io_shutdown(howto);
288 }
289 
290 // XXX Currently, we don't have a half-closed state, but we will need to
291 // implement that. After we send a GOAWAY, there
292 // are scenarios where we would like to complete the outstanding streams.
293 
294 void
do_io_close(int alerrno)295 Http2ClientSession::do_io_close(int alerrno)
296 {
297   REMEMBER(NO_EVENT, this->recursion)
298   Http2SsnDebug("session closed");
299 
300   ink_assert(this->mutex->thread_holding == this_ethread());
301   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_FINI, this);
302 
303   {
304     SCOPED_MUTEX_LOCK(lock, this->connection_state.mutex, this_ethread());
305     this->connection_state.release_stream();
306   }
307 
308   this->clear_session_active();
309 
310   // Clean up the write VIO in case of inactivity timeout
311   this->do_io_write(nullptr, 0, nullptr);
312 }
313 
314 void
reenable(VIO * vio)315 Http2ClientSession::reenable(VIO *vio)
316 {
317   this->client_vc->reenable(vio);
318 }
319 
320 void
set_half_close_local_flag(bool flag)321 Http2ClientSession::set_half_close_local_flag(bool flag)
322 {
323   if (!half_close_local && flag) {
324     Http2SsnDebug("session half-close local");
325   }
326   half_close_local = flag;
327 }
328 
329 int64_t
xmit(const Http2TxFrame & frame)330 Http2ClientSession::xmit(const Http2TxFrame &frame)
331 {
332   int64_t len = frame.write_to(this->write_buffer);
333 
334   if (len > 0) {
335     total_write_len += len;
336     write_reenable();
337   }
338 
339   return len;
340 }
341 
342 int
main_event_handler(int event,void * edata)343 Http2ClientSession::main_event_handler(int event, void *edata)
344 {
345   ink_assert(this->mutex->thread_holding == this_ethread());
346   int retval;
347 
348   recursion++;
349 
350   Event *e = static_cast<Event *>(edata);
351   if (e == schedule_event) {
352     schedule_event = nullptr;
353   }
354 
355   switch (event) {
356   case VC_EVENT_READ_COMPLETE:
357   case VC_EVENT_READ_READY: {
358     bool is_zombie = connection_state.get_zombie_event() != nullptr;
359     retval         = (this->*session_handler)(event, edata);
360     if (is_zombie && connection_state.get_zombie_event() != nullptr) {
361       Warning("Processed read event for zombie session %" PRId64, connection_id());
362     }
363     break;
364   }
365 
366   case HTTP2_SESSION_EVENT_REENABLE:
367     // VIO will be reenableed in this handler
368     retval = (this->*session_handler)(VC_EVENT_READ_READY, static_cast<VIO *>(e->cookie));
369     // Clear the event after calling session_handler to not reschedule REENABLE in it
370     this->_reenable_event = nullptr;
371     break;
372 
373   case VC_EVENT_ACTIVE_TIMEOUT:
374   case VC_EVENT_INACTIVITY_TIMEOUT:
375   case VC_EVENT_ERROR:
376   case VC_EVENT_EOS:
377     this->set_dying_event(event);
378     this->do_io_close();
379     if (client_vc != nullptr) {
380       client_vc->do_io_close();
381       client_vc = nullptr;
382     }
383     retval = 0;
384     break;
385 
386   case VC_EVENT_WRITE_READY:
387     retval = 0;
388     break;
389 
390   case VC_EVENT_WRITE_COMPLETE:
391     // Seems as this is being closed already
392     retval = 0;
393     break;
394 
395   case HTTP2_SESSION_EVENT_XMIT:
396   default:
397     Http2SsnDebug("unexpected event=%d edata=%p", event, edata);
398     ink_release_assert(0);
399     retval = 0;
400     break;
401   }
402 
403   if (!this->is_draining() && this->connection_state.get_shutdown_reason() == Http2ErrorCode::HTTP2_ERROR_MAX) {
404     this->connection_state.set_shutdown_state(HTTP2_SHUTDOWN_NONE);
405   }
406 
407   if (this->connection_state.get_shutdown_state() == HTTP2_SHUTDOWN_NONE) {
408     if (this->is_draining()) { // For a case we already checked Connection header and it didn't exist
409       Http2SsnDebug("Preparing for graceful shutdown because of draining state");
410       this->connection_state.set_shutdown_state(HTTP2_SHUTDOWN_NOT_INITIATED);
411     } else if (this->connection_state.get_stream_error_rate() >
412                Http2::stream_error_rate_threshold) { // For a case many stream errors happened
413       ip_port_text_buffer ipb;
414       const char *client_ip = ats_ip_ntop(get_client_addr(), ipb, sizeof(ipb));
415       Warning("HTTP/2 session error client_ip=%s session_id=%" PRId64
416               " closing a connection, because its stream error rate (%f) exceeded the threshold (%f)",
417               client_ip, connection_id(), this->connection_state.get_stream_error_rate(), Http2::stream_error_rate_threshold);
418       Http2SsnDebug("Preparing for graceful shutdown because of a high stream error rate");
419       cause_of_death = Http2SessionCod::HIGH_ERROR_RATE;
420       this->connection_state.set_shutdown_state(HTTP2_SHUTDOWN_NOT_INITIATED, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM);
421     }
422   }
423 
424   if (this->connection_state.get_shutdown_state() == HTTP2_SHUTDOWN_NOT_INITIATED) {
425     send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_SHUTDOWN_INIT, this);
426   }
427 
428   recursion--;
429   if (!connection_state.is_recursing() && this->recursion == 0 && kill_me) {
430     this->free();
431   }
432   return retval;
433 }
434 
435 int
state_read_connection_preface(int event,void * edata)436 Http2ClientSession::state_read_connection_preface(int event, void *edata)
437 {
438   VIO *vio = static_cast<VIO *>(edata);
439 
440   STATE_ENTER(&Http2ClientSession::state_read_connection_preface, event);
441   ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
442 
443   if (this->_reader->read_avail() >= static_cast<int64_t>(HTTP2_CONNECTION_PREFACE_LEN)) {
444     char buf[HTTP2_CONNECTION_PREFACE_LEN];
445     unsigned nbytes;
446 
447     nbytes = copy_from_buffer_reader(buf, this->_reader, sizeof(buf));
448     ink_release_assert(nbytes == HTTP2_CONNECTION_PREFACE_LEN);
449 
450     if (memcmp(HTTP2_CONNECTION_PREFACE, buf, nbytes) != 0) {
451       Http2SsnDebug("invalid connection preface");
452       this->do_io_close();
453       return 0;
454     }
455 
456     // Check whether data is read from early data
457     if (this->read_from_early_data > 0) {
458       this->read_from_early_data -= this->read_from_early_data > nbytes ? nbytes : this->read_from_early_data;
459     }
460 
461     Http2SsnDebug("received connection preface");
462     this->_reader->consume(nbytes);
463     HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read);
464 
465     client_vc->set_inactivity_timeout(HRTIME_SECONDS(Http2::no_activity_timeout_in));
466     client_vc->set_active_timeout(HRTIME_SECONDS(Http2::active_timeout_in));
467 
468     // XXX start the write VIO ...
469 
470     // If we have unconsumed data, start tranferring frames now.
471     if (this->_reader->is_read_avail_more_than(0)) {
472       return this->handleEvent(VC_EVENT_READ_READY, vio);
473     }
474   }
475 
476   // XXX We don't have enough data to check the connection preface. We should
477   // reset the accept inactivity
478   // timeout. We should have a maximum timeout to get the session started
479   // though.
480 
481   vio->reenable();
482   return 0;
483 }
484 
485 int
state_start_frame_read(int event,void * edata)486 Http2ClientSession::state_start_frame_read(int event, void *edata)
487 {
488   VIO *vio = static_cast<VIO *>(edata);
489 
490   STATE_ENTER(&Http2ClientSession::state_start_frame_read, event);
491   ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
492   return state_process_frame_read(event, vio, false);
493 }
494 
495 int
do_start_frame_read(Http2ErrorCode & ret_error)496 Http2ClientSession::do_start_frame_read(Http2ErrorCode &ret_error)
497 {
498   ret_error = Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
499   ink_release_assert(this->_reader->read_avail() >= (int64_t)HTTP2_FRAME_HEADER_LEN);
500 
501   uint8_t buf[HTTP2_FRAME_HEADER_LEN];
502   unsigned nbytes;
503 
504   Http2SsnDebug("receiving frame header");
505   nbytes = copy_from_buffer_reader(buf, this->_reader, sizeof(buf));
506 
507   this->cur_frame_from_early_data = false;
508   if (!http2_parse_frame_header(make_iovec(buf), this->current_hdr)) {
509     Http2SsnDebug("frame header parse failure");
510     this->do_io_close();
511     return -1;
512   }
513 
514   // Check whether data is read from early data
515   if (this->read_from_early_data > 0) {
516     this->read_from_early_data -= this->read_from_early_data > nbytes ? nbytes : this->read_from_early_data;
517     this->cur_frame_from_early_data = true;
518   }
519 
520   Http2SsnDebug("frame header length=%u, type=%u, flags=0x%x, streamid=%u", (unsigned)this->current_hdr.length,
521                 (unsigned)this->current_hdr.type, (unsigned)this->current_hdr.flags, this->current_hdr.streamid);
522 
523   this->_reader->consume(nbytes);
524 
525   if (!http2_frame_header_is_valid(this->current_hdr, this->connection_state.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE))) {
526     ret_error = Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
527     return -1;
528   }
529 
530   // If we know up front that the payload is too long, nuke this connection.
531   if (this->current_hdr.length > this->connection_state.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)) {
532     ret_error = Http2ErrorCode::HTTP2_ERROR_FRAME_SIZE_ERROR;
533     return -1;
534   }
535 
536   // CONTINUATIONs MUST follow behind HEADERS which doesn't have END_HEADERS
537   Http2StreamId continued_stream_id = this->connection_state.get_continued_stream_id();
538 
539   if (continued_stream_id != 0 &&
540       (continued_stream_id != this->current_hdr.streamid || this->current_hdr.type != HTTP2_FRAME_TYPE_CONTINUATION)) {
541     ret_error = Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
542     return -1;
543   }
544   return 0;
545 }
546 
547 int
state_complete_frame_read(int event,void * edata)548 Http2ClientSession::state_complete_frame_read(int event, void *edata)
549 {
550   VIO *vio = static_cast<VIO *>(edata);
551   STATE_ENTER(&Http2ClientSession::state_complete_frame_read, event);
552   ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
553   if (this->_reader->read_avail() < this->current_hdr.length) {
554     if (this->_should_do_something_else()) {
555       if (this->_reenable_event == nullptr) {
556         vio->disable();
557         this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio);
558       } else {
559         vio->reenable();
560       }
561     } else {
562       vio->reenable();
563     }
564     return 0;
565   }
566   Http2SsnDebug("completed frame read, %" PRId64 " bytes available", this->_reader->read_avail());
567 
568   return state_process_frame_read(event, vio, true);
569 }
570 
571 int
do_complete_frame_read()572 Http2ClientSession::do_complete_frame_read()
573 {
574   // XXX parse the frame and handle it ...
575   ink_release_assert(this->_reader->read_avail() >= this->current_hdr.length);
576 
577   Http2Frame frame(this->current_hdr, this->_reader, this->cur_frame_from_early_data);
578   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_RECV, &frame);
579   // Check whether data is read from early data
580   if (this->read_from_early_data > 0) {
581     this->read_from_early_data -=
582       this->read_from_early_data > this->current_hdr.length ? this->current_hdr.length : this->read_from_early_data;
583   }
584   this->_reader->consume(this->current_hdr.length);
585   ++(this->_n_frame_read);
586 
587   // Set the event handler if there is no more data to process a new frame
588   HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read);
589 
590   return 0;
591 }
592 
593 int
state_process_frame_read(int event,VIO * vio,bool inside_frame)594 Http2ClientSession::state_process_frame_read(int event, VIO *vio, bool inside_frame)
595 {
596   if (inside_frame) {
597     do_complete_frame_read();
598   }
599 
600   while (this->_reader->read_avail() >= static_cast<int64_t>(HTTP2_FRAME_HEADER_LEN)) {
601     // Cancel reading if there was an error or connection is closed
602     if (connection_state.tx_error_code.code != static_cast<uint32_t>(Http2ErrorCode::HTTP2_ERROR_NO_ERROR) ||
603         connection_state.is_state_closed()) {
604       Http2SsnDebug("reading a frame has been canceled (%u)", connection_state.tx_error_code.code);
605       break;
606     }
607 
608     Http2ErrorCode err = Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
609     if (this->connection_state.get_stream_error_rate() > std::min(1.0, Http2::stream_error_rate_threshold * 2.0)) {
610       ip_port_text_buffer ipb;
611       const char *client_ip = ats_ip_ntop(get_client_addr(), ipb, sizeof(ipb));
612       Warning("HTTP/2 session error client_ip=%s session_id=%" PRId64
613               " closing a connection, because its stream error rate (%f) is too high",
614               client_ip, connection_id(), this->connection_state.get_stream_error_rate());
615       err = Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM;
616     }
617 
618     // Return if there was an error
619     if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR || do_start_frame_read(err) < 0) {
620       // send an error if specified.  Otherwise, just go away
621       if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR) {
622         SCOPED_MUTEX_LOCK(lock, this->connection_state.mutex, this_ethread());
623         if (!this->connection_state.is_state_closed()) {
624           this->connection_state.send_goaway_frame(this->connection_state.get_latest_stream_id_in(), err);
625           this->set_half_close_local_flag(true);
626           this->do_io_close();
627         }
628       }
629       return 0;
630     }
631 
632     // If there is no more data to finish the frame, set up the event handler and reenable
633     if (this->_reader->read_avail() < this->current_hdr.length) {
634       HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_complete_frame_read);
635       break;
636     }
637     do_complete_frame_read();
638 
639     if (this->_should_do_something_else()) {
640       if (this->_reenable_event == nullptr) {
641         vio->disable();
642         this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio);
643         return 0;
644       }
645     }
646   }
647 
648   // If the client hasn't shut us down, reenable
649   if (!this->is_client_closed()) {
650     vio->reenable();
651   }
652   return 0;
653 }
654 
655 void
increment_current_active_client_connections_stat()656 Http2ClientSession::increment_current_active_client_connections_stat()
657 {
658   HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_ACTIVE_CLIENT_CONNECTION_COUNT, this_ethread());
659 }
660 
661 void
decrement_current_active_client_connections_stat()662 Http2ClientSession::decrement_current_active_client_connections_stat()
663 {
664   HTTP2_DECREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_ACTIVE_CLIENT_CONNECTION_COUNT, this_ethread());
665 }
666 
667 void
remember(const SourceLocation & location,int event,int reentrant)668 Http2ClientSession::remember(const SourceLocation &location, int event, int reentrant)
669 {
670   this->_history.push_back(location, event, reentrant);
671 }
672 
673 bool
_should_do_something_else()674 Http2ClientSession::_should_do_something_else()
675 {
676   // Do something else every 128 incoming frames
677   return (this->_n_frame_read & 0x7F) == 0;
678 }
679 
680 NetVConnection *
get_netvc() const681 Http2ClientSession::get_netvc() const
682 {
683   return client_vc;
684 }
685 
686 sockaddr const *
get_client_addr()687 Http2ClientSession::get_client_addr()
688 {
689   return client_vc ? client_vc->get_remote_addr() : &cached_client_addr.sa;
690 }
691 
692 sockaddr const *
get_local_addr()693 Http2ClientSession::get_local_addr()
694 {
695   return client_vc ? client_vc->get_local_addr() : &cached_local_addr.sa;
696 }
697 void
write_reenable()698 Http2ClientSession::write_reenable()
699 {
700   write_vio->reenable();
701 }
702 
703 int
get_transact_count() const704 Http2ClientSession::get_transact_count() const
705 {
706   return connection_state.get_stream_requests();
707 }
708 
709 void
release(ProxyTransaction * trans)710 Http2ClientSession::release(ProxyTransaction *trans)
711 {
712 }
713 
714 const char *
get_protocol_string() const715 Http2ClientSession::get_protocol_string() const
716 {
717   return "http/2";
718 }
719 
720 int
populate_protocol(std::string_view * result,int size) const721 Http2ClientSession::populate_protocol(std::string_view *result, int size) const
722 {
723   int retval = 0;
724   if (size > retval) {
725     result[retval++] = IP_PROTO_TAG_HTTP_2_0;
726     if (size > retval) {
727       retval += super::populate_protocol(result + retval, size - retval);
728     }
729   }
730   return retval;
731 }
732 
733 const char *
protocol_contains(std::string_view prefix) const734 Http2ClientSession::protocol_contains(std::string_view prefix) const
735 {
736   const char *retval = nullptr;
737 
738   if (prefix.size() <= IP_PROTO_TAG_HTTP_2_0.size() && strncmp(IP_PROTO_TAG_HTTP_2_0.data(), prefix.data(), prefix.size()) == 0) {
739     retval = IP_PROTO_TAG_HTTP_2_0.data();
740   } else {
741     retval = super::protocol_contains(prefix);
742   }
743   return retval;
744 }
745 
746 void
add_url_to_pushed_table(const char * url,int url_len)747 Http2ClientSession::add_url_to_pushed_table(const char *url, int url_len)
748 {
749   if (h2_pushed_urls.size() < Http2::push_diary_size) {
750     h2_pushed_urls.emplace(url);
751   }
752 }
753 
754 bool
support_sni() const755 Http2ClientSession::support_sni() const
756 {
757   return client_vc ? client_vc->support_sni() : false;
758 }
759