@@ -27,6 +27,26 @@ const Http2Session::Callbacks Http2Session::callback_struct_saved[2] = {
27
27
Callbacks (false ),
28
28
Callbacks (true )};
29
29
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
+ }
30
50
31
51
Http2Options::Http2Options (Environment* env) {
32
52
nghttp2_option_new (&options_);
@@ -346,8 +366,6 @@ Http2Session::Http2Session(Environment* env,
346
366
// be catching before it gets this far. Either way, crash if this
347
367
// fails.
348
368
CHECK_EQ (fn (&session_, callbacks, this , *opts), 0 );
349
-
350
- Start ();
351
369
}
352
370
353
371
@@ -356,40 +374,6 @@ Http2Session::~Http2Session() {
356
374
Close ();
357
375
}
358
376
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
-
393
377
394
378
void Http2Session::Close () {
395
379
DEBUG_HTTP2SESSION (this , " closing session" );
@@ -412,8 +396,6 @@ void Http2Session::Close() {
412
396
static_cast <Http2Session::Http2Ping*>(data)->Done (false );
413
397
}, static_cast <void *>(ping));
414
398
}
415
-
416
- Stop ();
417
399
}
418
400
419
401
@@ -484,6 +466,7 @@ inline void Http2Session::SubmitShutdownNotice() {
484
466
inline void Http2Session::Settings (const nghttp2_settings_entry iv[],
485
467
size_t niv) {
486
468
DEBUG_HTTP2SESSION2 (this , " submitting %d settings" , niv);
469
+ Http2Scope h2scope (this );
487
470
// This will fail either if the system is out of memory, or if the settings
488
471
// values are not within the appropriate range. We should be catching the
489
472
// latter before it gets this far so crash in either case.
@@ -736,7 +719,8 @@ Http2Stream::SubmitTrailers::SubmitTrailers(
736
719
737
720
738
721
inline void Http2Stream::SubmitTrailers::Submit (nghttp2_nv* trailers,
739
- size_t length) const {
722
+ size_t length) const {
723
+ Http2Scope h2scope (session_);
740
724
if (length == 0 )
741
725
return ;
742
726
DEBUG_HTTP2SESSION2 (session_, " sending trailers for stream %d, count: %d" ,
@@ -891,14 +875,37 @@ inline void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) {
891
875
MakeCallback (env ()->onsettings_string (), arraysize (argv), argv);
892
876
}
893
877
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
+
894
900
895
- inline void Http2Session::SendPendingData () {
901
+ void Http2Session::SendPendingData () {
896
902
DEBUG_HTTP2SESSION (this , " sending pending data" );
897
903
// Do not attempt to send data on the socket if the destroying flag has
898
904
// been set. That means everything is shutting down and the socket
899
905
// will not be usable.
900
906
if (IsDestroying ())
901
907
return ;
908
+ flags_ &= ~SESSION_STATE_WRITE_SCHEDULED;
902
909
903
910
WriteWrap* req = nullptr ;
904
911
char * dest = nullptr ;
@@ -963,6 +970,7 @@ inline Http2Stream* Http2Session::SubmitRequest(
963
970
int32_t * ret,
964
971
int options) {
965
972
DEBUG_HTTP2SESSION (this , " submitting request" );
973
+ Http2Scope h2scope (this );
966
974
Http2Stream* stream = nullptr ;
967
975
Http2Stream::Provider::Stream prov (options);
968
976
*ret = nghttp2_submit_request (session_, prispec, nva, len, *prov, nullptr );
@@ -1019,6 +1027,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread,
1019
1027
uv_handle_type pending,
1020
1028
void * ctx) {
1021
1029
Http2Session* session = static_cast <Http2Session*>(ctx);
1030
+ Http2Scope h2scope (session);
1022
1031
if (nread < 0 ) {
1023
1032
uv_buf_t tmp_buf;
1024
1033
tmp_buf.base = nullptr ;
@@ -1184,6 +1193,7 @@ inline void Http2Stream::Close(int32_t code) {
1184
1193
1185
1194
1186
1195
inline void Http2Stream::Shutdown () {
1196
+ Http2Scope h2scope (this );
1187
1197
flags_ |= NGHTTP2_STREAM_FLAG_SHUT;
1188
1198
CHECK_NE (nghttp2_session_resume_data (session_->session (), id_),
1189
1199
NGHTTP2_ERR_NOMEM);
@@ -1198,6 +1208,7 @@ int Http2Stream::DoShutdown(ShutdownWrap* req_wrap) {
1198
1208
}
1199
1209
1200
1210
inline void Http2Stream::Destroy () {
1211
+ Http2Scope h2scope (this );
1201
1212
DEBUG_HTTP2STREAM (this , " destroying stream" );
1202
1213
// Do nothing if this stream instance is already destroyed
1203
1214
if (IsDestroyed ())
@@ -1249,6 +1260,7 @@ void Http2Stream::OnDataChunk(
1249
1260
1250
1261
1251
1262
inline void Http2Stream::FlushDataChunks () {
1263
+ Http2Scope h2scope (this );
1252
1264
if (!data_chunks_.empty ()) {
1253
1265
uv_buf_t buf = data_chunks_.front ();
1254
1266
data_chunks_.pop ();
@@ -1266,6 +1278,7 @@ inline void Http2Stream::FlushDataChunks() {
1266
1278
inline int Http2Stream::SubmitResponse (nghttp2_nv* nva,
1267
1279
size_t len,
1268
1280
int options) {
1281
+ Http2Scope h2scope (this );
1269
1282
DEBUG_HTTP2STREAM (this , " submitting response" );
1270
1283
if (options & STREAM_OPTION_GET_TRAILERS)
1271
1284
flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
@@ -1286,6 +1299,7 @@ inline int Http2Stream::SubmitFile(int fd,
1286
1299
int64_t offset,
1287
1300
int64_t length,
1288
1301
int options) {
1302
+ Http2Scope h2scope (this );
1289
1303
DEBUG_HTTP2STREAM (this , " submitting file" );
1290
1304
if (options & STREAM_OPTION_GET_TRAILERS)
1291
1305
flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
@@ -1302,6 +1316,7 @@ inline int Http2Stream::SubmitFile(int fd,
1302
1316
1303
1317
// Submit informational headers for a stream.
1304
1318
inline int Http2Stream::SubmitInfo (nghttp2_nv* nva, size_t len) {
1319
+ Http2Scope h2scope (this );
1305
1320
DEBUG_HTTP2STREAM2 (this , " sending %d informational headers" , len);
1306
1321
int ret = nghttp2_submit_headers (session_->session (),
1307
1322
NGHTTP2_FLAG_NONE,
@@ -1314,6 +1329,7 @@ inline int Http2Stream::SubmitInfo(nghttp2_nv* nva, size_t len) {
1314
1329
1315
1330
inline int Http2Stream::SubmitPriority (nghttp2_priority_spec* prispec,
1316
1331
bool silent) {
1332
+ Http2Scope h2scope (this );
1317
1333
DEBUG_HTTP2STREAM (this , " sending priority spec" );
1318
1334
int ret = silent ?
1319
1335
nghttp2_session_change_stream_priority (session_->session (),
@@ -1327,6 +1343,7 @@ inline int Http2Stream::SubmitPriority(nghttp2_priority_spec* prispec,
1327
1343
1328
1344
1329
1345
inline int Http2Stream::SubmitRstStream (const uint32_t code) {
1346
+ Http2Scope h2scope (this );
1330
1347
DEBUG_HTTP2STREAM2 (this , " sending rst-stream with code %d" , code);
1331
1348
session_->SendPendingData ();
1332
1349
CHECK_EQ (nghttp2_submit_rst_stream (session_->session (),
@@ -1342,6 +1359,7 @@ inline Http2Stream* Http2Stream::SubmitPushPromise(nghttp2_nv* nva,
1342
1359
size_t len,
1343
1360
int32_t * ret,
1344
1361
int options) {
1362
+ Http2Scope h2scope (this );
1345
1363
DEBUG_HTTP2STREAM (this , " sending push promise" );
1346
1364
*ret = nghttp2_submit_push_promise (session_->session (), NGHTTP2_FLAG_NONE,
1347
1365
id_, nva, len, nullptr );
@@ -1381,6 +1399,7 @@ inline int Http2Stream::Write(nghttp2_stream_write_t* req,
1381
1399
const uv_buf_t bufs[],
1382
1400
unsigned int nbufs,
1383
1401
nghttp2_stream_write_cb cb) {
1402
+ Http2Scope h2scope (this );
1384
1403
if (!IsWritable ()) {
1385
1404
if (cb != nullptr )
1386
1405
cb (req, UV_EOF);
@@ -1764,6 +1783,7 @@ void Http2Session::Goaway(const FunctionCallbackInfo<Value>& args) {
1764
1783
Environment* env = Environment::GetCurrent (args);
1765
1784
Local<Context> context = env->context ();
1766
1785
ASSIGN_OR_RETURN_UNWRAP (&session, args.Holder ());
1786
+ Http2Scope h2scope (session);
1767
1787
1768
1788
uint32_t errorCode = args[0 ]->Uint32Value (context).ToChecked ();
1769
1789
int32_t lastStreamID = args[1 ]->Int32Value (context).ToChecked ();
@@ -2039,6 +2059,7 @@ void Http2Session::Http2Ping::Send(uint8_t* payload) {
2039
2059
memcpy (&data, &startTime_, arraysize (data));
2040
2060
payload = data;
2041
2061
}
2062
+ Http2Scope h2scope (session_);
2042
2063
CHECK_EQ (nghttp2_submit_ping (**session_, NGHTTP2_FLAG_NONE, payload), 0 );
2043
2064
}
2044
2065
0 commit comments