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, true> 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
Http2ClientSession()68 Http2ClientSession::Http2ClientSession() : super() {}
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 (_vc) {
86 _vc->do_io_close();
87 _vc = nullptr;
88 }
89
90 // Make sure the we are at the bottom of the stack
91 if (connection_state.is_recursing() || this->recursion != 0) {
92 // Note that we are ready to be cleaned up
93 // One of the event handlers will catch it
94 kill_me = true;
95 return;
96 }
97
98 REMEMBER(NO_EVENT, this->recursion)
99 Http2SsnDebug("session free");
100
101 if (this->_reenable_event) {
102 this->_reenable_event->cancel();
103 this->_reenable_event = nullptr;
104 }
105
106 // Don't free active ProxySession
107 ink_release_assert(is_active() == false);
108
109 this->_milestones.mark(Http2SsnMilestone::CLOSE);
110 ink_hrtime total_time = this->_milestones.elapsed(Http2SsnMilestone::OPEN, Http2SsnMilestone::CLOSE);
111
112 // Slow Log
113 if (Http2::con_slow_log_threshold != 0 && ink_hrtime_from_msec(Http2::con_slow_log_threshold) < total_time) {
114 Error("[%" PRIu64 "] Slow H2 Connection: open: %" PRIu64 " close: %.3f", this->con_id,
115 ink_hrtime_to_msec(this->_milestones[Http2SsnMilestone::OPEN]),
116 this->_milestones.difference_sec(Http2SsnMilestone::OPEN, Http2SsnMilestone::CLOSE));
117 }
118
119 HTTP2_DECREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT, this->mutex->thread_holding);
120
121 // Update stats on how we died. May want to eliminate this. Was useful for
122 // tracking down which cases we were having problems cleaning up. But for general
123 // use probably not worth the effort
124 if (cause_of_death != Http2SessionCod::NOT_PROVIDED) {
125 switch (cause_of_death) {
126 case Http2SessionCod::HIGH_ERROR_RATE:
127 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_HIGH_ERROR_RATE, this_ethread());
128 break;
129 case Http2SessionCod::NOT_PROVIDED:
130 // Can't happen but this case is here to not have default case.
131 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_OTHER, this_ethread());
132 break;
133 }
134 } else {
135 switch (dying_event) {
136 case VC_EVENT_NONE:
137 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_DEFAULT, this_ethread());
138 break;
139 case VC_EVENT_ACTIVE_TIMEOUT:
140 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_ACTIVE, this_ethread());
141 break;
142 case VC_EVENT_INACTIVITY_TIMEOUT:
143 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_INACTIVE, this_ethread());
144 break;
145 case VC_EVENT_ERROR:
146 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_ERROR, this_ethread());
147 break;
148 case VC_EVENT_EOS:
149 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_EOS, this_ethread());
150 break;
151 default:
152 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_SESSION_DIE_OTHER, this_ethread());
153 break;
154 }
155 }
156
157 ink_release_assert(this->_vc == nullptr);
158
159 delete _h2_pushed_urls;
160 this->connection_state.destroy();
161
162 free_MIOBuffer(this->read_buffer);
163 free_MIOBuffer(this->write_buffer);
164 THREAD_FREE(this, http2ClientSessionAllocator, this_ethread());
165 }
166
167 void
start()168 Http2ClientSession::start()
169 {
170 SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
171
172 SET_HANDLER(&Http2ClientSession::main_event_handler);
173 HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_read_connection_preface);
174
175 VIO *read_vio = this->do_io_read(this, INT64_MAX, this->read_buffer);
176 write_vio = this->do_io_write(this, INT64_MAX, this->_write_buffer_reader);
177
178 this->connection_state.init();
179 send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_INIT, this);
180
181 if (this->_read_buffer_reader->is_read_avail_more_than(0)) {
182 this->handleEvent(VC_EVENT_READ_READY, read_vio);
183 }
184 }
185
186 void
new_connection(NetVConnection * new_vc,MIOBuffer * iobuf,IOBufferReader * reader)187 Http2ClientSession::new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, IOBufferReader *reader)
188 {
189 ink_assert(new_vc->mutex->thread_holding == this_ethread());
190 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT, new_vc->mutex->thread_holding);
191 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_CONNECTION_COUNT, new_vc->mutex->thread_holding);
192 this->_milestones.mark(Http2SsnMilestone::OPEN);
193
194 // Unique client session identifier.
195 this->con_id = ProxySession::next_connection_id();
196 this->_vc = new_vc;
197 _vc->set_inactivity_timeout(HRTIME_SECONDS(Http2::accept_no_activity_timeout));
198 this->schedule_event = nullptr;
199 this->mutex = new_vc->mutex;
200 this->in_destroy = false;
201
202 this->connection_state.mutex = this->mutex;
203
204 SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(new_vc);
205 if (ssl_vc != nullptr) {
206 this->read_from_early_data = ssl_vc->read_from_early_data;
207 Debug("ssl_early_data", "read_from_early_data = %" PRId64, this->read_from_early_data);
208 }
209
210 Http2SsnDebug("session born, netvc %p", this->_vc);
211
212 this->_vc->set_tcp_congestion_control(CLIENT_SIDE);
213
214 this->read_buffer = iobuf ? iobuf : new_MIOBuffer(HTTP2_HEADER_BUFFER_SIZE_INDEX);
215 this->read_buffer->water_mark = connection_state.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE);
216 this->_read_buffer_reader = reader ? reader : this->read_buffer->alloc_reader();
217
218 // This block size is the buffer size that we pass to SSLWriteBuffer
219 auto buffer_block_size_index = iobuffer_size_to_index(Http2::write_buffer_block_size, MAX_BUFFER_SIZE_INDEX);
220 this->write_buffer = new_MIOBuffer(buffer_block_size_index);
221 this->_write_buffer_reader = this->write_buffer->alloc_reader();
222 this->_write_size_threshold = index_to_buffer_size(buffer_block_size_index) * Http2::write_size_threshold;
223
224 this->_handle_if_ssl(new_vc);
225
226 do_api_callout(TS_HTTP_SSN_START_HOOK);
227 }
228
229 // XXX Currently, we don't have a half-closed state, but we will need to
230 // implement that. After we send a GOAWAY, there
231 // are scenarios where we would like to complete the outstanding streams.
232
233 void
do_io_close(int alerrno)234 Http2ClientSession::do_io_close(int alerrno)
235 {
236 REMEMBER(NO_EVENT, this->recursion)
237 Http2SsnDebug("session closed");
238
239 ink_assert(this->mutex->thread_holding == this_ethread());
240 send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_FINI, this);
241
242 {
243 SCOPED_MUTEX_LOCK(lock, this->connection_state.mutex, this_ethread());
244 this->connection_state.release_stream();
245 }
246
247 this->clear_session_active();
248
249 // Clean up the write VIO in case of inactivity timeout
250 this->do_io_write(this, 0, nullptr);
251 }
252
253 void
set_half_close_local_flag(bool flag)254 Http2ClientSession::set_half_close_local_flag(bool flag)
255 {
256 if (!half_close_local && flag) {
257 Http2SsnDebug("session half-close local");
258 }
259 half_close_local = flag;
260 }
261
262 int64_t
xmit(const Http2TxFrame & frame,bool flush)263 Http2ClientSession::xmit(const Http2TxFrame &frame, bool flush)
264 {
265 int64_t len = frame.write_to(this->write_buffer);
266 this->_pending_sending_data_size += len;
267 // Force flush for some cases
268 if (!flush) {
269 // Flush if we already use half of the buffer to avoid adding a new block to the chain.
270 // A frame size can be 16MB at maximum so blocks can be added, but that's fine.
271 if (this->_pending_sending_data_size >= this->_write_size_threshold) {
272 flush = true;
273 }
274 }
275
276 if (flush) {
277 this->flush();
278 }
279
280 return len;
281 }
282
283 void
flush()284 Http2ClientSession::flush()
285 {
286 if (this->_pending_sending_data_size > 0) {
287 this->_pending_sending_data_size = 0;
288 this->_write_buffer_last_flush = Thread::get_hrtime();
289 write_reenable();
290 }
291 }
292
293 int
main_event_handler(int event,void * edata)294 Http2ClientSession::main_event_handler(int event, void *edata)
295 {
296 ink_assert(this->mutex->thread_holding == this_ethread());
297 int retval;
298
299 recursion++;
300
301 Event *e = static_cast<Event *>(edata);
302 if (e == schedule_event) {
303 schedule_event = nullptr;
304 }
305
306 switch (event) {
307 case VC_EVENT_READ_COMPLETE:
308 case VC_EVENT_READ_READY: {
309 bool is_zombie = connection_state.get_zombie_event() != nullptr;
310 retval = (this->*session_handler)(event, edata);
311 if (is_zombie && connection_state.get_zombie_event() != nullptr) {
312 Warning("Processed read event for zombie session %" PRId64, connection_id());
313 }
314 break;
315 }
316
317 case HTTP2_SESSION_EVENT_REENABLE:
318 // VIO will be reenableed in this handler
319 retval = (this->*session_handler)(VC_EVENT_READ_READY, static_cast<VIO *>(e->cookie));
320 // Clear the event after calling session_handler to not reschedule REENABLE in it
321 this->_reenable_event = nullptr;
322 break;
323
324 case VC_EVENT_ACTIVE_TIMEOUT:
325 case VC_EVENT_INACTIVITY_TIMEOUT:
326 case VC_EVENT_ERROR:
327 case VC_EVENT_EOS:
328 Http2SsnDebug("Closing event %d", event);
329 this->set_dying_event(event);
330 this->do_io_close();
331 retval = 0;
332 break;
333
334 case VC_EVENT_WRITE_READY:
335 case VC_EVENT_WRITE_COMPLETE:
336 this->connection_state.restart_streams();
337 if ((Thread::get_hrtime() >= this->_write_buffer_last_flush + HRTIME_MSECONDS(this->_write_time_threshold))) {
338 this->flush();
339 }
340 retval = 0;
341 break;
342
343 case HTTP2_SESSION_EVENT_XMIT:
344 default:
345 Http2SsnDebug("unexpected event=%d edata=%p", event, edata);
346 ink_release_assert(0);
347 retval = 0;
348 break;
349 }
350
351 if (!this->is_draining() && this->connection_state.get_shutdown_reason() == Http2ErrorCode::HTTP2_ERROR_MAX) {
352 this->connection_state.set_shutdown_state(HTTP2_SHUTDOWN_NONE);
353 }
354
355 if (this->connection_state.get_shutdown_state() == HTTP2_SHUTDOWN_NONE) {
356 if (this->is_draining()) { // For a case we already checked Connection header and it didn't exist
357 Http2SsnDebug("Preparing for graceful shutdown because of draining state");
358 this->connection_state.set_shutdown_state(HTTP2_SHUTDOWN_NOT_INITIATED);
359 } else if (this->connection_state.get_stream_error_rate() >
360 Http2::stream_error_rate_threshold) { // For a case many stream errors happened
361 ip_port_text_buffer ipb;
362 const char *client_ip = ats_ip_ntop(get_remote_addr(), ipb, sizeof(ipb));
363 Warning("HTTP/2 session error client_ip=%s session_id=%" PRId64
364 " closing a connection, because its stream error rate (%f) exceeded the threshold (%f)",
365 client_ip, connection_id(), this->connection_state.get_stream_error_rate(), Http2::stream_error_rate_threshold);
366 Http2SsnDebug("Preparing for graceful shutdown because of a high stream error rate");
367 cause_of_death = Http2SessionCod::HIGH_ERROR_RATE;
368 this->connection_state.set_shutdown_state(HTTP2_SHUTDOWN_NOT_INITIATED, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM);
369 }
370 }
371
372 if (this->connection_state.get_shutdown_state() == HTTP2_SHUTDOWN_NOT_INITIATED) {
373 send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_SHUTDOWN_INIT, this);
374 }
375
376 recursion--;
377 if (!connection_state.is_recursing() && this->recursion == 0 && kill_me) {
378 this->free();
379 }
380 return retval;
381 }
382
383 int
state_read_connection_preface(int event,void * edata)384 Http2ClientSession::state_read_connection_preface(int event, void *edata)
385 {
386 VIO *vio = static_cast<VIO *>(edata);
387
388 STATE_ENTER(&Http2ClientSession::state_read_connection_preface, event);
389 ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
390
391 if (this->_read_buffer_reader->read_avail() >= static_cast<int64_t>(HTTP2_CONNECTION_PREFACE_LEN)) {
392 char buf[HTTP2_CONNECTION_PREFACE_LEN];
393 unsigned nbytes;
394
395 nbytes = copy_from_buffer_reader(buf, this->_read_buffer_reader, sizeof(buf));
396 ink_release_assert(nbytes == HTTP2_CONNECTION_PREFACE_LEN);
397
398 if (memcmp(HTTP2_CONNECTION_PREFACE, buf, nbytes) != 0) {
399 Http2SsnDebug("invalid connection preface");
400 this->do_io_close();
401 return 0;
402 }
403
404 // Check whether data is read from early data
405 if (this->read_from_early_data > 0) {
406 this->read_from_early_data -= this->read_from_early_data > nbytes ? nbytes : this->read_from_early_data;
407 }
408
409 Http2SsnDebug("received connection preface");
410 this->_read_buffer_reader->consume(nbytes);
411 HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read);
412
413 _vc->set_inactivity_timeout(HRTIME_SECONDS(Http2::no_activity_timeout_in));
414 _vc->set_active_timeout(HRTIME_SECONDS(Http2::active_timeout_in));
415
416 // XXX start the write VIO ...
417
418 // If we have unconsumed data, start tranferring frames now.
419 if (this->_read_buffer_reader->is_read_avail_more_than(0)) {
420 return this->handleEvent(VC_EVENT_READ_READY, vio);
421 }
422 }
423
424 // XXX We don't have enough data to check the connection preface. We should
425 // reset the accept inactivity
426 // timeout. We should have a maximum timeout to get the session started
427 // though.
428
429 vio->reenable();
430 return 0;
431 }
432
433 int
state_start_frame_read(int event,void * edata)434 Http2ClientSession::state_start_frame_read(int event, void *edata)
435 {
436 VIO *vio = static_cast<VIO *>(edata);
437
438 STATE_ENTER(&Http2ClientSession::state_start_frame_read, event);
439 ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
440 return state_process_frame_read(event, vio, false);
441 }
442
443 int
do_start_frame_read(Http2ErrorCode & ret_error)444 Http2ClientSession::do_start_frame_read(Http2ErrorCode &ret_error)
445 {
446 ret_error = Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
447 ink_release_assert(this->_read_buffer_reader->read_avail() >= (int64_t)HTTP2_FRAME_HEADER_LEN);
448
449 uint8_t buf[HTTP2_FRAME_HEADER_LEN];
450 unsigned nbytes;
451
452 Http2SsnDebug("receiving frame header");
453 nbytes = copy_from_buffer_reader(buf, this->_read_buffer_reader, sizeof(buf));
454
455 this->cur_frame_from_early_data = false;
456 if (!http2_parse_frame_header(make_iovec(buf), this->current_hdr)) {
457 Http2SsnDebug("frame header parse failure");
458 this->do_io_close();
459 return -1;
460 }
461
462 // Check whether data is read from early data
463 if (this->read_from_early_data > 0) {
464 this->read_from_early_data -= this->read_from_early_data > nbytes ? nbytes : this->read_from_early_data;
465 this->cur_frame_from_early_data = true;
466 }
467
468 Http2SsnDebug("frame header length=%u, type=%u, flags=0x%x, streamid=%u", (unsigned)this->current_hdr.length,
469 (unsigned)this->current_hdr.type, (unsigned)this->current_hdr.flags, this->current_hdr.streamid);
470
471 this->_read_buffer_reader->consume(nbytes);
472
473 if (!http2_frame_header_is_valid(this->current_hdr, this->connection_state.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE))) {
474 ret_error = Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
475 return -1;
476 }
477
478 // If we know up front that the payload is too long, nuke this connection.
479 if (this->current_hdr.length > this->connection_state.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)) {
480 ret_error = Http2ErrorCode::HTTP2_ERROR_FRAME_SIZE_ERROR;
481 return -1;
482 }
483
484 // CONTINUATIONs MUST follow behind HEADERS which doesn't have END_HEADERS
485 Http2StreamId continued_stream_id = this->connection_state.get_continued_stream_id();
486
487 if (continued_stream_id != 0 &&
488 (continued_stream_id != this->current_hdr.streamid || this->current_hdr.type != HTTP2_FRAME_TYPE_CONTINUATION)) {
489 ret_error = Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
490 return -1;
491 }
492 return 0;
493 }
494
495 int
state_complete_frame_read(int event,void * edata)496 Http2ClientSession::state_complete_frame_read(int event, void *edata)
497 {
498 VIO *vio = static_cast<VIO *>(edata);
499 STATE_ENTER(&Http2ClientSession::state_complete_frame_read, event);
500 ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
501 if (this->_read_buffer_reader->read_avail() < this->current_hdr.length) {
502 if (this->_should_do_something_else()) {
503 if (this->_reenable_event == nullptr) {
504 vio->disable();
505 this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio);
506 } else {
507 vio->reenable();
508 }
509 } else {
510 vio->reenable();
511 }
512 return 0;
513 }
514 Http2SsnDebug("completed frame read, %" PRId64 " bytes available", this->_read_buffer_reader->read_avail());
515
516 return state_process_frame_read(event, vio, true);
517 }
518
519 int
do_complete_frame_read()520 Http2ClientSession::do_complete_frame_read()
521 {
522 // XXX parse the frame and handle it ...
523 ink_release_assert(this->_read_buffer_reader->read_avail() >= this->current_hdr.length);
524
525 Http2Frame frame(this->current_hdr, this->_read_buffer_reader, this->cur_frame_from_early_data);
526 send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_RECV, &frame);
527 // Check whether data is read from early data
528 if (this->read_from_early_data > 0) {
529 this->read_from_early_data -=
530 this->read_from_early_data > this->current_hdr.length ? this->current_hdr.length : this->read_from_early_data;
531 }
532 this->_read_buffer_reader->consume(this->current_hdr.length);
533 ++(this->_n_frame_read);
534
535 // Set the event handler if there is no more data to process a new frame
536 HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read);
537
538 return 0;
539 }
540
541 int
state_process_frame_read(int event,VIO * vio,bool inside_frame)542 Http2ClientSession::state_process_frame_read(int event, VIO *vio, bool inside_frame)
543 {
544 if (inside_frame) {
545 do_complete_frame_read();
546 }
547
548 while (this->_read_buffer_reader->read_avail() >= static_cast<int64_t>(HTTP2_FRAME_HEADER_LEN)) {
549 // Cancel reading if there was an error or connection is closed
550 if (connection_state.tx_error_code.code != static_cast<uint32_t>(Http2ErrorCode::HTTP2_ERROR_NO_ERROR) ||
551 connection_state.is_state_closed()) {
552 Http2SsnDebug("reading a frame has been canceled (%u)", connection_state.tx_error_code.code);
553 break;
554 }
555
556 Http2ErrorCode err = Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
557 if (this->connection_state.get_stream_error_rate() > std::min(1.0, Http2::stream_error_rate_threshold * 2.0)) {
558 ip_port_text_buffer ipb;
559 const char *client_ip = ats_ip_ntop(get_remote_addr(), ipb, sizeof(ipb));
560 Warning("HTTP/2 session error client_ip=%s session_id=%" PRId64
561 " closing a connection, because its stream error rate (%f) exceeded the threshold (%f)",
562 client_ip, connection_id(), this->connection_state.get_stream_error_rate(), Http2::stream_error_rate_threshold);
563 err = Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM;
564 }
565
566 // Return if there was an error
567 if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR || do_start_frame_read(err) < 0) {
568 // send an error if specified. Otherwise, just go away
569 if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR) {
570 SCOPED_MUTEX_LOCK(lock, this->connection_state.mutex, this_ethread());
571 if (!this->connection_state.is_state_closed()) {
572 this->connection_state.send_goaway_frame(this->connection_state.get_latest_stream_id_in(), err);
573 this->set_half_close_local_flag(true);
574 }
575 }
576 return 0;
577 }
578
579 // If there is no more data to finish the frame, set up the event handler and reenable
580 if (this->_read_buffer_reader->read_avail() < this->current_hdr.length) {
581 HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_complete_frame_read);
582 break;
583 }
584 do_complete_frame_read();
585
586 if (this->_should_do_something_else()) {
587 if (this->_reenable_event == nullptr) {
588 vio->disable();
589 this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio);
590 return 0;
591 }
592 }
593 }
594
595 // If the client hasn't shut us down, reenable
596 if (!this->is_client_closed()) {
597 vio->reenable();
598 }
599 return 0;
600 }
601
602 void
increment_current_active_connections_stat()603 Http2ClientSession::increment_current_active_connections_stat()
604 {
605 HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_ACTIVE_CLIENT_CONNECTION_COUNT, this_ethread());
606 }
607
608 void
decrement_current_active_connections_stat()609 Http2ClientSession::decrement_current_active_connections_stat()
610 {
611 HTTP2_DECREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_ACTIVE_CLIENT_CONNECTION_COUNT, this_ethread());
612 }
613
614 void
remember(const SourceLocation & location,int event,int reentrant)615 Http2ClientSession::remember(const SourceLocation &location, int event, int reentrant)
616 {
617 this->_history.push_back(location, event, reentrant);
618 }
619
620 bool
_should_do_something_else()621 Http2ClientSession::_should_do_something_else()
622 {
623 // Do something else every 128 incoming frames if connection state isn't closed
624 return (this->_n_frame_read & 0x7F) == 0 && !connection_state.is_state_closed();
625 }
626
627 sockaddr const *
get_remote_addr() const628 Http2ClientSession::get_remote_addr() const
629 {
630 return _vc ? _vc->get_remote_addr() : &cached_client_addr.sa;
631 }
632
633 sockaddr const *
get_local_addr()634 Http2ClientSession::get_local_addr()
635 {
636 return _vc ? _vc->get_local_addr() : &cached_local_addr.sa;
637 }
638
639 int64_t
write_avail()640 Http2ClientSession::write_avail()
641 {
642 return this->write_buffer->write_avail();
643 }
644
645 void
write_reenable()646 Http2ClientSession::write_reenable()
647 {
648 write_vio->reenable();
649 }
650
651 int
get_transact_count() const652 Http2ClientSession::get_transact_count() const
653 {
654 return connection_state.get_stream_requests();
655 }
656
657 void
release(ProxyTransaction * trans)658 Http2ClientSession::release(ProxyTransaction *trans)
659 {
660 }
661
662 const char *
get_protocol_string() const663 Http2ClientSession::get_protocol_string() const
664 {
665 return "http/2";
666 }
667
668 int
populate_protocol(std::string_view * result,int size) const669 Http2ClientSession::populate_protocol(std::string_view *result, int size) const
670 {
671 int retval = 0;
672 if (size > retval) {
673 result[retval++] = IP_PROTO_TAG_HTTP_2_0;
674 if (size > retval) {
675 retval += super::populate_protocol(result + retval, size - retval);
676 }
677 }
678 return retval;
679 }
680
681 const char *
protocol_contains(std::string_view prefix) const682 Http2ClientSession::protocol_contains(std::string_view prefix) const
683 {
684 const char *retval = nullptr;
685
686 if (prefix.size() <= IP_PROTO_TAG_HTTP_2_0.size() && strncmp(IP_PROTO_TAG_HTTP_2_0.data(), prefix.data(), prefix.size()) == 0) {
687 retval = IP_PROTO_TAG_HTTP_2_0.data();
688 } else {
689 retval = super::protocol_contains(prefix);
690 }
691 return retval;
692 }
693
694 void
add_url_to_pushed_table(const char * url,int url_len)695 Http2ClientSession::add_url_to_pushed_table(const char *url, int url_len)
696 {
697 // Delay std::unordered_set allocation until when it used
698 if (_h2_pushed_urls == nullptr) {
699 this->_h2_pushed_urls = new std::unordered_set<std::string>();
700 this->_h2_pushed_urls->reserve(Http2::push_diary_size);
701 }
702
703 if (_h2_pushed_urls->size() < Http2::push_diary_size) {
704 _h2_pushed_urls->emplace(url);
705 }
706 }
707