@@ -27,6 +27,26 @@ const Http2Session::Callbacks Http2Session::callback_struct_saved[2] = {
2727 Callbacks (false ),
2828 Callbacks (true )};
2929
30+ Http2Scope::Http2Scope (Http2Stream* stream) : Http2Scope(stream->session ()) {}
31+
32+ Http2Scope::Http2Scope (Http2Session* session) {
33+ if (session->flags_ & (SESSION_STATE_HAS_SCOPE |
34+ SESSION_STATE_WRITE_SCHEDULED)) {
35+ // There is another scope further below on the stack, or it is already
36+ // known that a write is scheduled. In either case, there is nothing to do.
37+ return ;
38+ }
39+ session->flags_ |= SESSION_STATE_HAS_SCOPE;
40+ session_ = session;
41+ }
42+
43+ Http2Scope::~Http2Scope () {
44+ if (session_ == nullptr )
45+ return ;
46+
47+ session_->flags_ &= ~SESSION_STATE_HAS_SCOPE;
48+ session_->MaybeScheduleWrite ();
49+ }
3050
3151Http2Options::Http2Options (Environment* env) {
3252 nghttp2_option_new (&options_);
@@ -346,8 +366,6 @@ Http2Session::Http2Session(Environment* env,
346366 // be catching before it gets this far. Either way, crash if this
347367 // fails.
348368 CHECK_EQ (fn (&session_, callbacks, this , *opts), 0 );
349-
350- Start ();
351369}
352370
353371
@@ -356,40 +374,6 @@ Http2Session::~Http2Session() {
356374 Close ();
357375}
358376
359- // For every node::Http2Session instance, there is a uv_prepare_t handle
360- // whose callback is triggered on every tick of the event loop. When
361- // run, nghttp2 is prompted to send any queued data it may have stored.
362- // TODO(jasnell): Currently, this creates one uv_prepare_t per Http2Session,
363- // we should investigate to see if it's faster to create a
364- // single uv_prepare_t for all Http2Sessions, then iterate
365- // over each.
366- void Http2Session::Start () {
367- prep_ = new uv_prepare_t ();
368- uv_prepare_init (env ()->event_loop (), prep_);
369- prep_->data = static_cast <void *>(this );
370- uv_prepare_start (prep_, [](uv_prepare_t * t) {
371- Http2Session* session = static_cast <Http2Session*>(t->data );
372- HandleScope scope (session->env ()->isolate ());
373- Context::Scope context_scope (session->env ()->context ());
374-
375- // Sending data may call arbitrary JS code, so keep track of
376- // async context.
377- InternalCallbackScope callback_scope (session);
378- session->SendPendingData ();
379- });
380- }
381-
382- // Stop the uv_prep_t from further activity, destroy the handle
383- void Http2Session::Stop () {
384- DEBUG_HTTP2SESSION (this , " stopping uv_prep_t handle" );
385- CHECK_EQ (uv_prepare_stop (prep_), 0 );
386- auto prep_close = [](uv_handle_t * handle) {
387- delete reinterpret_cast <uv_prepare_t *>(handle);
388- };
389- uv_close (reinterpret_cast <uv_handle_t *>(prep_), prep_close);
390- prep_ = nullptr ;
391- }
392-
393377
394378void Http2Session::Close () {
395379 DEBUG_HTTP2SESSION (this , " closing session" );
@@ -412,8 +396,6 @@ void Http2Session::Close() {
412396 static_cast <Http2Session::Http2Ping*>(data)->Done (false );
413397 }, static_cast <void *>(ping));
414398 }
415-
416- Stop ();
417399}
418400
419401
@@ -484,6 +466,7 @@ inline void Http2Session::SubmitShutdownNotice() {
484466inline void Http2Session::Settings (const nghttp2_settings_entry iv[],
485467 size_t niv) {
486468 DEBUG_HTTP2SESSION2 (this , " submitting %d settings" , niv);
469+ Http2Scope h2scope (this );
487470 // This will fail either if the system is out of memory, or if the settings
488471 // values are not within the appropriate range. We should be catching the
489472 // latter before it gets this far so crash in either case.
@@ -736,7 +719,8 @@ Http2Stream::SubmitTrailers::SubmitTrailers(
736719
737720
738721inline void Http2Stream::SubmitTrailers::Submit (nghttp2_nv* trailers,
739- size_t length) const {
722+ size_t length) const {
723+ Http2Scope h2scope (session_);
740724 if (length == 0 )
741725 return ;
742726 DEBUG_HTTP2SESSION2 (session_, " sending trailers for stream %d, count: %d" ,
@@ -891,14 +875,37 @@ inline void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) {
891875 MakeCallback (env ()->onsettings_string (), arraysize (argv), argv);
892876}
893877
878+ void Http2Session::MaybeScheduleWrite () {
879+ CHECK_EQ (flags_ & SESSION_STATE_WRITE_SCHEDULED, 0 );
880+ if (session_ != nullptr && nghttp2_session_want_write (session_)) {
881+ flags_ |= SESSION_STATE_WRITE_SCHEDULED;
882+ env ()->SetImmediate ([](Environment* env, void * data) {
883+ Http2Session* session = static_cast <Http2Session*>(data);
884+ if (session->session_ == nullptr ||
885+ !(session->flags_ & SESSION_STATE_WRITE_SCHEDULED)) {
886+ // This can happen e.g. when a stream was reset before this turn
887+ // of the event loop, in which case SendPendingData() is called early,
888+ // or the session was destroyed in the meantime.
889+ return ;
890+ }
891+
892+ // Sending data may call arbitrary JS code, so keep track of
893+ // async context.
894+ InternalCallbackScope callback_scope (session);
895+ session->SendPendingData ();
896+ }, static_cast <void *>(this ), object ());
897+ }
898+ }
899+
894900
895- inline void Http2Session::SendPendingData () {
901+ void Http2Session::SendPendingData () {
896902 DEBUG_HTTP2SESSION (this , " sending pending data" );
897903 // Do not attempt to send data on the socket if the destroying flag has
898904 // been set. That means everything is shutting down and the socket
899905 // will not be usable.
900906 if (IsDestroying ())
901907 return ;
908+ flags_ &= ~SESSION_STATE_WRITE_SCHEDULED;
902909
903910 WriteWrap* req = nullptr ;
904911 char * dest = nullptr ;
@@ -963,6 +970,7 @@ inline Http2Stream* Http2Session::SubmitRequest(
963970 int32_t * ret,
964971 int options) {
965972 DEBUG_HTTP2SESSION (this , " submitting request" );
973+ Http2Scope h2scope (this );
966974 Http2Stream* stream = nullptr ;
967975 Http2Stream::Provider::Stream prov (options);
968976 *ret = nghttp2_submit_request (session_, prispec, nva, len, *prov, nullptr );
@@ -1019,6 +1027,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread,
10191027 uv_handle_type pending,
10201028 void * ctx) {
10211029 Http2Session* session = static_cast <Http2Session*>(ctx);
1030+ Http2Scope h2scope (session);
10221031 if (nread < 0 ) {
10231032 uv_buf_t tmp_buf;
10241033 tmp_buf.base = nullptr ;
@@ -1184,6 +1193,7 @@ inline void Http2Stream::Close(int32_t code) {
11841193
11851194
11861195inline void Http2Stream::Shutdown () {
1196+ Http2Scope h2scope (this );
11871197 flags_ |= NGHTTP2_STREAM_FLAG_SHUT;
11881198 CHECK_NE (nghttp2_session_resume_data (session_->session (), id_),
11891199 NGHTTP2_ERR_NOMEM);
@@ -1198,6 +1208,7 @@ int Http2Stream::DoShutdown(ShutdownWrap* req_wrap) {
11981208}
11991209
12001210inline void Http2Stream::Destroy () {
1211+ Http2Scope h2scope (this );
12011212 DEBUG_HTTP2STREAM (this , " destroying stream" );
12021213 // Do nothing if this stream instance is already destroyed
12031214 if (IsDestroyed ())
@@ -1249,6 +1260,7 @@ void Http2Stream::OnDataChunk(
12491260
12501261
12511262inline void Http2Stream::FlushDataChunks () {
1263+ Http2Scope h2scope (this );
12521264 if (!data_chunks_.empty ()) {
12531265 uv_buf_t buf = data_chunks_.front ();
12541266 data_chunks_.pop ();
@@ -1266,6 +1278,7 @@ inline void Http2Stream::FlushDataChunks() {
12661278inline int Http2Stream::SubmitResponse (nghttp2_nv* nva,
12671279 size_t len,
12681280 int options) {
1281+ Http2Scope h2scope (this );
12691282 DEBUG_HTTP2STREAM (this , " submitting response" );
12701283 if (options & STREAM_OPTION_GET_TRAILERS)
12711284 flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
@@ -1286,6 +1299,7 @@ inline int Http2Stream::SubmitFile(int fd,
12861299 int64_t offset,
12871300 int64_t length,
12881301 int options) {
1302+ Http2Scope h2scope (this );
12891303 DEBUG_HTTP2STREAM (this , " submitting file" );
12901304 if (options & STREAM_OPTION_GET_TRAILERS)
12911305 flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
@@ -1302,6 +1316,7 @@ inline int Http2Stream::SubmitFile(int fd,
13021316
13031317// Submit informational headers for a stream.
13041318inline int Http2Stream::SubmitInfo (nghttp2_nv* nva, size_t len) {
1319+ Http2Scope h2scope (this );
13051320 DEBUG_HTTP2STREAM2 (this , " sending %d informational headers" , len);
13061321 int ret = nghttp2_submit_headers (session_->session (),
13071322 NGHTTP2_FLAG_NONE,
@@ -1314,6 +1329,7 @@ inline int Http2Stream::SubmitInfo(nghttp2_nv* nva, size_t len) {
13141329
13151330inline int Http2Stream::SubmitPriority (nghttp2_priority_spec* prispec,
13161331 bool silent) {
1332+ Http2Scope h2scope (this );
13171333 DEBUG_HTTP2STREAM (this , " sending priority spec" );
13181334 int ret = silent ?
13191335 nghttp2_session_change_stream_priority (session_->session (),
@@ -1327,6 +1343,7 @@ inline int Http2Stream::SubmitPriority(nghttp2_priority_spec* prispec,
13271343
13281344
13291345inline int Http2Stream::SubmitRstStream (const uint32_t code) {
1346+ Http2Scope h2scope (this );
13301347 DEBUG_HTTP2STREAM2 (this , " sending rst-stream with code %d" , code);
13311348 session_->SendPendingData ();
13321349 CHECK_EQ (nghttp2_submit_rst_stream (session_->session (),
@@ -1342,6 +1359,7 @@ inline Http2Stream* Http2Stream::SubmitPushPromise(nghttp2_nv* nva,
13421359 size_t len,
13431360 int32_t * ret,
13441361 int options) {
1362+ Http2Scope h2scope (this );
13451363 DEBUG_HTTP2STREAM (this , " sending push promise" );
13461364 *ret = nghttp2_submit_push_promise (session_->session (), NGHTTP2_FLAG_NONE,
13471365 id_, nva, len, nullptr );
@@ -1381,6 +1399,7 @@ inline int Http2Stream::Write(nghttp2_stream_write_t* req,
13811399 const uv_buf_t bufs[],
13821400 unsigned int nbufs,
13831401 nghttp2_stream_write_cb cb) {
1402+ Http2Scope h2scope (this );
13841403 if (!IsWritable ()) {
13851404 if (cb != nullptr )
13861405 cb (req, UV_EOF);
@@ -1764,6 +1783,7 @@ void Http2Session::Goaway(const FunctionCallbackInfo<Value>& args) {
17641783 Environment* env = Environment::GetCurrent (args);
17651784 Local<Context> context = env->context ();
17661785 ASSIGN_OR_RETURN_UNWRAP (&session, args.Holder ());
1786+ Http2Scope h2scope (session);
17671787
17681788 uint32_t errorCode = args[0 ]->Uint32Value (context).ToChecked ();
17691789 int32_t lastStreamID = args[1 ]->Int32Value (context).ToChecked ();
@@ -2039,6 +2059,7 @@ void Http2Session::Http2Ping::Send(uint8_t* payload) {
20392059 memcpy (&data, &startTime_, arraysize (data));
20402060 payload = data;
20412061 }
2062+ Http2Scope h2scope (session_);
20422063 CHECK_EQ (nghttp2_submit_ping (**session_, NGHTTP2_FLAG_NONE, payload), 0 );
20432064}
20442065
0 commit comments