@@ -113,53 +113,56 @@ Http2Scope::~Http2Scope() { |
113 | 113 | // uses a single TypedArray instance that is shared with the JavaScript side |
114 | 114 | // to more efficiently pass values back and forth. |
115 | 115 | Http2Options::Http2Options(Environment* env, nghttp2_session_type type) { |
116 | | - nghttp2_option_new(&options_); |
| 116 | + nghttp2_option* option; |
| 117 | + CHECK_EQ(nghttp2_option_new(&option), 0); |
| 118 | + CHECK_NOT_NULL(option); |
| 119 | + options_.reset(option); |
117 | 120 |
|
118 | 121 | // Make sure closed connections aren't kept around, taking up memory. |
119 | 122 | // Note that this breaks the priority tree, which we don't use. |
120 | | - nghttp2_option_set_no_closed_streams(options_, 1); |
| 123 | + nghttp2_option_set_no_closed_streams(option, 1); |
121 | 124 |
|
122 | 125 | // We manually handle flow control within a session in order to |
123 | 126 | // implement backpressure -- that is, we only send WINDOW_UPDATE |
124 | 127 | // frames to the remote peer as data is actually consumed by user |
125 | 128 | // code. This ensures that the flow of data over the connection |
126 | 129 | // does not move too quickly and limits the amount of data we |
127 | 130 | // are required to buffer. |
128 | | - nghttp2_option_set_no_auto_window_update(options_, 1); |
| 131 | + nghttp2_option_set_no_auto_window_update(option, 1); |
129 | 132 |
|
130 | 133 | // Enable built in support for receiving ALTSVC and ORIGIN frames (but |
131 | 134 | // only on client side sessions |
132 | 135 | if (type == NGHTTP2_SESSION_CLIENT) { |
133 | | - nghttp2_option_set_builtin_recv_extension_type(options_, NGHTTP2_ALTSVC); |
134 | | - nghttp2_option_set_builtin_recv_extension_type(options_, NGHTTP2_ORIGIN); |
| 136 | + nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC); |
| 137 | + nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ORIGIN); |
135 | 138 | } |
136 | 139 |
|
137 | 140 | AliasedUint32Array& buffer = env->http2_state()->options_buffer; |
138 | 141 | uint32_t flags = buffer[IDX_OPTIONS_FLAGS]; |
139 | 142 |
|
140 | 143 | if (flags & (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) { |
141 | 144 | nghttp2_option_set_max_deflate_dynamic_table_size( |
142 | | - options_, |
| 145 | + option, |
143 | 146 | buffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE]); |
144 | 147 | } |
145 | 148 |
|
146 | 149 | if (flags & (1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS)) { |
147 | 150 | nghttp2_option_set_max_reserved_remote_streams( |
148 | | - options_, |
| 151 | + option, |
149 | 152 | buffer[IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS]); |
150 | 153 | } |
151 | 154 |
|
152 | 155 | if (flags & (1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH)) { |
153 | 156 | nghttp2_option_set_max_send_header_block_length( |
154 | | - options_, |
| 157 | + option, |
155 | 158 | buffer[IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH]); |
156 | 159 | } |
157 | 160 |
|
158 | 161 | // Recommended default |
159 | | - nghttp2_option_set_peer_max_concurrent_streams(options_, 100); |
| 162 | + nghttp2_option_set_peer_max_concurrent_streams(option, 100); |
160 | 163 | if (flags & (1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS)) { |
161 | 164 | nghttp2_option_set_peer_max_concurrent_streams( |
162 | | - options_, |
| 165 | + option, |
163 | 166 | buffer[IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS]); |
164 | 167 | } |
165 | 168 |
|
@@ -178,27 +181,24 @@ Http2Options::Http2Options(Environment* env, nghttp2_session_type type) { |
178 | 181 | // header pairs the session may accept. This is a hard limit.. that is, |
179 | 182 | // if the remote peer sends more than this amount, the stream will be |
180 | 183 | // automatically closed with an RST_STREAM. |
181 | | - if (flags & (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS)) { |
| 184 | + if (flags & (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS)) |
182 | 185 | SetMaxHeaderPairs(buffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS]); |
183 | | - } |
184 | 186 |
|
185 | 187 | // The HTTP2 specification places no limits on the number of HTTP2 |
186 | 188 | // PING frames that can be sent. In order to prevent PINGS from being |
187 | 189 | // abused as an attack vector, however, we place a strict upper limit |
188 | 190 | // on the number of unacknowledged PINGS that can be sent at any given |
189 | 191 | // time. |
190 | | - if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS)) { |
| 192 | + if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS)) |
191 | 193 | SetMaxOutstandingPings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS]); |
192 | | - } |
193 | 194 |
|
194 | 195 | // The HTTP2 specification places no limits on the number of HTTP2 |
195 | 196 | // SETTINGS frames that can be sent. In order to prevent PINGS from being |
196 | 197 | // abused as an attack vector, however, we place a strict upper limit |
197 | 198 | // on the number of unacknowledged SETTINGS that can be sent at any given |
198 | 199 | // time. |
199 | | - if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS)) { |
| 200 | + if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS)) |
200 | 201 | SetMaxOutstandingSettings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS]); |
201 | | - } |
202 | 202 |
|
203 | 203 | // The HTTP2 specification places no limits on the amount of memory |
204 | 204 | // that a session can consume. In order to prevent abuse, we place a |
@@ -208,9 +208,8 @@ Http2Options::Http2Options(Environment* env, nghttp2_session_type type) { |
208 | 208 | // created. |
209 | 209 | // Important: The maxSessionMemory option in javascript is expressed in |
210 | 210 | // terms of MB increments (i.e. the value 1 == 1 MB) |
211 | | - if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY)) { |
| 211 | + if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY)) |
212 | 212 | SetMaxSessionMemory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 1e6); |
213 | | - } |
214 | 213 | } |
215 | 214 |
|
216 | 215 | void Http2Session::Http2Settings::Init() { |
@@ -420,42 +419,39 @@ Origins::Origins(Isolate* isolate, |
420 | 419 | // Sets the various callback functions that nghttp2 will use to notify us |
421 | 420 | // about significant events while processing http2 stuff. |
422 | 421 | Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) { |
423 | | - CHECK_EQ(nghttp2_session_callbacks_new(&callbacks), 0); |
| 422 | + nghttp2_session_callbacks* callbacks_; |
| 423 | + CHECK_EQ(nghttp2_session_callbacks_new(&callbacks_), 0); |
| 424 | + callbacks.reset(callbacks_); |
424 | 425 |
|
425 | 426 | nghttp2_session_callbacks_set_on_begin_headers_callback( |
426 | | - callbacks, OnBeginHeadersCallback); |
| 427 | + callbacks_, OnBeginHeadersCallback); |
427 | 428 | nghttp2_session_callbacks_set_on_header_callback2( |
428 | | - callbacks, OnHeaderCallback); |
| 429 | + callbacks_, OnHeaderCallback); |
429 | 430 | nghttp2_session_callbacks_set_on_frame_recv_callback( |
430 | | - callbacks, OnFrameReceive); |
| 431 | + callbacks_, OnFrameReceive); |
431 | 432 | nghttp2_session_callbacks_set_on_stream_close_callback( |
432 | | - callbacks, OnStreamClose); |
| 433 | + callbacks_, OnStreamClose); |
433 | 434 | nghttp2_session_callbacks_set_on_data_chunk_recv_callback( |
434 | | - callbacks, OnDataChunkReceived); |
| 435 | + callbacks_, OnDataChunkReceived); |
435 | 436 | nghttp2_session_callbacks_set_on_frame_not_send_callback( |
436 | | - callbacks, OnFrameNotSent); |
| 437 | + callbacks_, OnFrameNotSent); |
437 | 438 | nghttp2_session_callbacks_set_on_invalid_header_callback2( |
438 | | - callbacks, OnInvalidHeader); |
| 439 | + callbacks_, OnInvalidHeader); |
439 | 440 | nghttp2_session_callbacks_set_error_callback( |
440 | | - callbacks, OnNghttpError); |
| 441 | + callbacks_, OnNghttpError); |
441 | 442 | nghttp2_session_callbacks_set_send_data_callback( |
442 | | - callbacks, OnSendData); |
| 443 | + callbacks_, OnSendData); |
443 | 444 | nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( |
444 | | - callbacks, OnInvalidFrame); |
| 445 | + callbacks_, OnInvalidFrame); |
445 | 446 | nghttp2_session_callbacks_set_on_frame_send_callback( |
446 | | - callbacks, OnFrameSent); |
| 447 | + callbacks_, OnFrameSent); |
447 | 448 |
|
448 | 449 | if (kHasGetPaddingCallback) { |
449 | 450 | nghttp2_session_callbacks_set_select_padding_callback( |
450 | | - callbacks, OnSelectPadding); |
| 451 | + callbacks_, OnSelectPadding); |
451 | 452 | } |
452 | 453 | } |
453 | 454 |
|
454 | | - |
455 | | -Http2Session::Callbacks::~Callbacks() { |
456 | | - nghttp2_session_callbacks_del(callbacks); |
457 | | -} |
458 | | - |
459 | 455 | void Http2Session::StopTrackingRcbuf(nghttp2_rcbuf* buf) { |
460 | 456 | StopTrackingMemory(buf); |
461 | 457 | } |
@@ -499,9 +495,6 @@ Http2Session::Http2Session(Environment* env, |
499 | 495 | bool hasGetPaddingCallback = |
500 | 496 | padding_strategy_ != PADDING_STRATEGY_NONE; |
501 | 497 |
|
502 | | - nghttp2_session_callbacks* callbacks |
503 | | - = callback_struct_saved[hasGetPaddingCallback ? 1 : 0].callbacks; |
504 | | - |
505 | 498 | auto fn = type == NGHTTP2_SESSION_SERVER ? |
506 | 499 | nghttp2_session_server_new3 : |
507 | 500 | nghttp2_session_client_new3; |
@@ -513,7 +506,14 @@ Http2Session::Http2Session(Environment* env, |
513 | 506 | // of the options are out of acceptable range, which we should |
514 | 507 | // be catching before it gets this far. Either way, crash if this |
515 | 508 | // fails. |
516 | | - CHECK_EQ(fn(&session_, callbacks, this, *opts, &alloc_info), 0); |
| 509 | + nghttp2_session* session; |
| 510 | + CHECK_EQ(fn( |
| 511 | + &session, |
| 512 | + callback_struct_saved[hasGetPaddingCallback ? 1 : 0].callbacks.get(), |
| 513 | + this, |
| 514 | + *opts, |
| 515 | + &alloc_info), 0); |
| 516 | + session_.reset(session); |
517 | 517 |
|
518 | 518 | outgoing_storage_.reserve(1024); |
519 | 519 | outgoing_buffers_.reserve(32); |
@@ -533,7 +533,9 @@ Http2Session::Http2Session(Environment* env, |
533 | 533 | Http2Session::~Http2Session() { |
534 | 534 | CHECK_EQ(flags_ & SESSION_STATE_HAS_SCOPE, 0); |
535 | 535 | Debug(this, "freeing nghttp2 session"); |
536 | | - nghttp2_session_del(session_); |
| 536 | + // Explicitly reset session_ so the subsequent |
| 537 | + // current_nghttp2_memory_ check passes. |
| 538 | + session_.reset(); |
537 | 539 | CHECK_EQ(current_nghttp2_memory_, 0); |
538 | 540 | } |
539 | 541 |
|
@@ -629,7 +631,7 @@ void Http2Session::Close(uint32_t code, bool socket_closed) { |
629 | 631 | // make a best effort. |
630 | 632 | if (!socket_closed) { |
631 | 633 | Debug(this, "terminating session with code %d", code); |
632 | | - CHECK_EQ(nghttp2_session_terminate_session(session_, code), 0); |
| 634 | + CHECK_EQ(nghttp2_session_terminate_session(session_.get(), code), 0); |
633 | 635 | SendPendingData(); |
634 | 636 | } else if (stream_ != nullptr) { |
635 | 637 | stream_->RemoveStreamListener(this); |
@@ -669,7 +671,7 @@ inline Http2Stream* Http2Session::FindStream(int32_t id) { |
669 | 671 | inline bool Http2Session::CanAddStream() { |
670 | 672 | uint32_t maxConcurrentStreams = |
671 | 673 | nghttp2_session_get_local_settings( |
672 | | - session_, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); |
| 674 | + session_.get(), NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); |
673 | 675 | size_t maxSize = |
674 | 676 | std::min(streams_.max_size(), static_cast<size_t>(maxConcurrentStreams)); |
675 | 677 | // We can add a new stream so long as we are less than the current |
@@ -735,10 +737,10 @@ ssize_t Http2Session::ConsumeHTTP2Data() { |
735 | 737 | // multiple side effects. |
736 | 738 | Debug(this, "receiving %d bytes [wants data? %d]", |
737 | 739 | read_len, |
738 | | - nghttp2_session_want_read(session_)); |
| 740 | + nghttp2_session_want_read(session_.get())); |
739 | 741 | flags_ &= ~SESSION_STATE_NGHTTP2_RECV_PAUSED; |
740 | 742 | ssize_t ret = |
741 | | - nghttp2_session_mem_recv(session_, |
| 743 | + nghttp2_session_mem_recv(session_.get(), |
742 | 744 | reinterpret_cast<uint8_t*>(stream_buf_.base) + |
743 | 745 | stream_buf_offset_, |
744 | 746 | read_len); |
@@ -1437,7 +1439,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) { |
1437 | 1439 |
|
1438 | 1440 | if ((flags_ & SESSION_STATE_READING_STOPPED) && |
1439 | 1441 | !(flags_ & SESSION_STATE_WRITE_IN_PROGRESS) && |
1440 | | - nghttp2_session_want_read(session_)) { |
| 1442 | + nghttp2_session_want_read(session_.get())) { |
1441 | 1443 | flags_ &= ~SESSION_STATE_READING_STOPPED; |
1442 | 1444 | stream_->ReadStart(); |
1443 | 1445 | } |
@@ -1465,16 +1467,16 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) { |
1465 | 1467 | // queue), but only if a write has not already been scheduled. |
1466 | 1468 | void Http2Session::MaybeScheduleWrite() { |
1467 | 1469 | CHECK_EQ(flags_ & SESSION_STATE_WRITE_SCHEDULED, 0); |
1468 | | - if (UNLIKELY(session_ == nullptr)) |
| 1470 | + if (UNLIKELY(!session_)) |
1469 | 1471 | return; |
1470 | 1472 |
|
1471 | | - if (nghttp2_session_want_write(session_)) { |
| 1473 | + if (nghttp2_session_want_write(session_.get())) { |
1472 | 1474 | HandleScope handle_scope(env()->isolate()); |
1473 | 1475 | Debug(this, "scheduling write"); |
1474 | 1476 | flags_ |= SESSION_STATE_WRITE_SCHEDULED; |
1475 | 1477 | BaseObjectPtr<Http2Session> strong_ref{this}; |
1476 | 1478 | env()->SetImmediate([this, strong_ref](Environment* env) { |
1477 | | - if (session_ == nullptr || !(flags_ & SESSION_STATE_WRITE_SCHEDULED)) { |
| 1479 | + if (!session_ || !(flags_ & SESSION_STATE_WRITE_SCHEDULED)) { |
1478 | 1480 | // This can happen e.g. when a stream was reset before this turn |
1479 | 1481 | // of the event loop, in which case SendPendingData() is called early, |
1480 | 1482 | // or the session was destroyed in the meantime. |
@@ -1492,7 +1494,7 @@ void Http2Session::MaybeScheduleWrite() { |
1492 | 1494 |
|
1493 | 1495 | void Http2Session::MaybeStopReading() { |
1494 | 1496 | if (flags_ & SESSION_STATE_READING_STOPPED) return; |
1495 | | - int want_read = nghttp2_session_want_read(session_); |
| 1497 | + int want_read = nghttp2_session_want_read(session_.get()); |
1496 | 1498 | Debug(this, "wants read? %d", want_read); |
1497 | 1499 | if (want_read == 0 || (flags_ & SESSION_STATE_WRITE_IN_PROGRESS)) { |
1498 | 1500 | flags_ |= SESSION_STATE_READING_STOPPED; |
@@ -1590,7 +1592,7 @@ uint8_t Http2Session::SendPendingData() { |
1590 | 1592 |
|
1591 | 1593 | // Part One: Gather data from nghttp2 |
1592 | 1594 |
|
1593 | | - while ((src_length = nghttp2_session_mem_send(session_, &src)) > 0) { |
| 1595 | + while ((src_length = nghttp2_session_mem_send(session_.get(), &src)) > 0) { |
1594 | 1596 | Debug(this, "nghttp2 has %d bytes to send", src_length); |
1595 | 1597 | CopyDataIntoOutgoing(src, src_length); |
1596 | 1598 | } |
@@ -1715,7 +1717,7 @@ Http2Stream* Http2Session::SubmitRequest( |
1715 | 1717 | Http2Stream* stream = nullptr; |
1716 | 1718 | Http2Stream::Provider::Stream prov(options); |
1717 | 1719 | *ret = nghttp2_submit_request( |
1718 | | - session_, |
| 1720 | + session_.get(), |
1719 | 1721 | prispec, |
1720 | 1722 | headers.data(), |
1721 | 1723 | headers.length(), |
@@ -2485,9 +2487,9 @@ void Http2Session::Goaway(uint32_t code, |
2485 | 2487 | Http2Scope h2scope(this); |
2486 | 2488 | // the last proc stream id is the most recently created Http2Stream. |
2487 | 2489 | if (lastStreamID <= 0) |
2488 | | - lastStreamID = nghttp2_session_get_last_proc_stream_id(session_); |
| 2490 | + lastStreamID = nghttp2_session_get_last_proc_stream_id(session_.get()); |
2489 | 2491 | Debug(this, "submitting goaway"); |
2490 | | - nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, |
| 2492 | + nghttp2_submit_goaway(session_.get(), NGHTTP2_FLAG_NONE, |
2491 | 2493 | lastStreamID, code, data, len); |
2492 | 2494 | } |
2493 | 2495 |
|
@@ -2676,13 +2678,16 @@ void Http2Session::AltSvc(int32_t id, |
2676 | 2678 | uint8_t* value, |
2677 | 2679 | size_t value_len) { |
2678 | 2680 | Http2Scope h2scope(this); |
2679 | | - CHECK_EQ(nghttp2_submit_altsvc(session_, NGHTTP2_FLAG_NONE, id, |
| 2681 | + CHECK_EQ(nghttp2_submit_altsvc(session_.get(), NGHTTP2_FLAG_NONE, id, |
2680 | 2682 | origin, origin_len, value, value_len), 0); |
2681 | 2683 | } |
2682 | 2684 |
|
2683 | 2685 | void Http2Session::Origin(nghttp2_origin_entry* ov, size_t count) { |
2684 | 2686 | Http2Scope h2scope(this); |
2685 | | - CHECK_EQ(nghttp2_submit_origin(session_, NGHTTP2_FLAG_NONE, ov, count), 0); |
| 2687 | + CHECK_EQ(nghttp2_submit_origin( |
| 2688 | + session_.get(), |
| 2689 | + NGHTTP2_FLAG_NONE, |
| 2690 | + ov, count), 0); |
2686 | 2691 | } |
2687 | 2692 |
|
2688 | 2693 | // Submits an AltSvc frame to be sent to the connected peer. |
|