@@ -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 );
@@ -1022,6 +1030,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread,
1022
1030
uv_handle_type pending,
1023
1031
void * ctx) {
1024
1032
Http2Session* session = static_cast <Http2Session*>(ctx);
1033
+ Http2Scope h2scope (session);
1025
1034
if (nread < 0 ) {
1026
1035
uv_buf_t tmp_buf;
1027
1036
tmp_buf.base = nullptr ;
@@ -1187,6 +1196,7 @@ inline void Http2Stream::Close(int32_t code) {
1187
1196
1188
1197
1189
1198
inline void Http2Stream::Shutdown () {
1199
+ Http2Scope h2scope (this );
1190
1200
flags_ |= NGHTTP2_STREAM_FLAG_SHUT;
1191
1201
CHECK_NE (nghttp2_session_resume_data (session_->session (), id_),
1192
1202
NGHTTP2_ERR_NOMEM);
@@ -1201,6 +1211,7 @@ int Http2Stream::DoShutdown(ShutdownWrap* req_wrap) {
1201
1211
}
1202
1212
1203
1213
inline void Http2Stream::Destroy () {
1214
+ Http2Scope h2scope (this );
1204
1215
DEBUG_HTTP2STREAM (this , " destroying stream" );
1205
1216
// Do nothing if this stream instance is already destroyed
1206
1217
if (IsDestroyed ())
@@ -1252,6 +1263,7 @@ void Http2Stream::OnDataChunk(
1252
1263
1253
1264
1254
1265
inline void Http2Stream::FlushDataChunks () {
1266
+ Http2Scope h2scope (this );
1255
1267
if (!data_chunks_.empty ()) {
1256
1268
uv_buf_t buf = data_chunks_.front ();
1257
1269
data_chunks_.pop ();
@@ -1269,6 +1281,7 @@ inline void Http2Stream::FlushDataChunks() {
1269
1281
inline int Http2Stream::SubmitResponse (nghttp2_nv* nva,
1270
1282
size_t len,
1271
1283
int options) {
1284
+ Http2Scope h2scope (this );
1272
1285
DEBUG_HTTP2STREAM (this , " submitting response" );
1273
1286
if (options & STREAM_OPTION_GET_TRAILERS)
1274
1287
flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
@@ -1289,6 +1302,7 @@ inline int Http2Stream::SubmitFile(int fd,
1289
1302
int64_t offset,
1290
1303
int64_t length,
1291
1304
int options) {
1305
+ Http2Scope h2scope (this );
1292
1306
DEBUG_HTTP2STREAM (this , " submitting file" );
1293
1307
if (options & STREAM_OPTION_GET_TRAILERS)
1294
1308
flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
@@ -1305,6 +1319,7 @@ inline int Http2Stream::SubmitFile(int fd,
1305
1319
1306
1320
// Submit informational headers for a stream.
1307
1321
inline int Http2Stream::SubmitInfo (nghttp2_nv* nva, size_t len) {
1322
+ Http2Scope h2scope (this );
1308
1323
DEBUG_HTTP2STREAM2 (this , " sending %d informational headers" , len);
1309
1324
int ret = nghttp2_submit_headers (session_->session (),
1310
1325
NGHTTP2_FLAG_NONE,
@@ -1317,6 +1332,7 @@ inline int Http2Stream::SubmitInfo(nghttp2_nv* nva, size_t len) {
1317
1332
1318
1333
inline int Http2Stream::SubmitPriority (nghttp2_priority_spec* prispec,
1319
1334
bool silent) {
1335
+ Http2Scope h2scope (this );
1320
1336
DEBUG_HTTP2STREAM (this , " sending priority spec" );
1321
1337
int ret = silent ?
1322
1338
nghttp2_session_change_stream_priority (session_->session (),
@@ -1330,6 +1346,7 @@ inline int Http2Stream::SubmitPriority(nghttp2_priority_spec* prispec,
1330
1346
1331
1347
1332
1348
inline int Http2Stream::SubmitRstStream (const uint32_t code) {
1349
+ Http2Scope h2scope (this );
1333
1350
DEBUG_HTTP2STREAM2 (this , " sending rst-stream with code %d" , code);
1334
1351
session_->SendPendingData ();
1335
1352
CHECK_EQ (nghttp2_submit_rst_stream (session_->session (),
@@ -1345,6 +1362,7 @@ inline Http2Stream* Http2Stream::SubmitPushPromise(nghttp2_nv* nva,
1345
1362
size_t len,
1346
1363
int32_t * ret,
1347
1364
int options) {
1365
+ Http2Scope h2scope (this );
1348
1366
DEBUG_HTTP2STREAM (this , " sending push promise" );
1349
1367
*ret = nghttp2_submit_push_promise (session_->session (), NGHTTP2_FLAG_NONE,
1350
1368
id_, nva, len, nullptr );
@@ -1384,6 +1402,7 @@ inline int Http2Stream::Write(nghttp2_stream_write_t* req,
1384
1402
const uv_buf_t bufs[],
1385
1403
unsigned int nbufs,
1386
1404
nghttp2_stream_write_cb cb) {
1405
+ Http2Scope h2scope (this );
1387
1406
if (!IsWritable ()) {
1388
1407
if (cb != nullptr )
1389
1408
cb (req, UV_EOF);
@@ -1767,6 +1786,7 @@ void Http2Session::Goaway(const FunctionCallbackInfo<Value>& args) {
1767
1786
Environment* env = Environment::GetCurrent (args);
1768
1787
Local<Context> context = env->context ();
1769
1788
ASSIGN_OR_RETURN_UNWRAP (&session, args.Holder ());
1789
+ Http2Scope h2scope (session);
1770
1790
1771
1791
uint32_t errorCode = args[0 ]->Uint32Value (context).ToChecked ();
1772
1792
int32_t lastStreamID = args[1 ]->Int32Value (context).ToChecked ();
@@ -2042,6 +2062,7 @@ void Http2Session::Http2Ping::Send(uint8_t* payload) {
2042
2062
memcpy (&data, &startTime_, arraysize (data));
2043
2063
payload = data;
2044
2064
}
2065
+ Http2Scope h2scope (session_);
2045
2066
CHECK_EQ (nghttp2_submit_ping (**session_, NGHTTP2_FLAG_NONE, payload), 0 );
2046
2067
}
2047
2068
0 commit comments