From 3490359ac18f5fcf05b7bedcd15248ce6041c26d Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 25 Dec 2017 20:16:54 +0100 Subject: [PATCH] http2: keep session objects alive during Http2Scope Keep a local handle as a reference to the JS `Http2Session` object so that it will not be garbage collected when inside an `Http2Scope`, because the presence of the latter usually indicates that further actions on the session object are expected. Strictly speaking, storing the `session_handle_` as a property on the scope object is not necessary, but this is not very costly and makes the code more obviously correct. Fixes: https://github.com/nodejs/node/issues/17840 Backport-PR-URL: https://github.com/nodejs/node/pull/18050 PR-URL: https://github.com/nodejs/node/pull/17863 Fixes: https://github.com/nodejs/node/issues/17840 Reviewed-By: Anatoli Papirovski Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- src/node_http2.cc | 7 +++++++ src/node_http2.h | 1 + test/parallel/test-http2-createwritereq.js | 11 +++++++++++ 3 files changed, 19 insertions(+) diff --git a/src/node_http2.cc b/src/node_http2.cc index c86a1e54b66a30..34c96b04e8c026 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -71,6 +71,11 @@ Http2Scope::Http2Scope(Http2Session* session) { } session->flags_ |= SESSION_STATE_HAS_SCOPE; session_ = session; + + // Always keep the session object alive for at least as long as + // this scope is active. + session_handle_ = session->object(); + CHECK(!session_handle_.IsEmpty()); } Http2Scope::~Http2Scope() { @@ -512,6 +517,7 @@ void Http2Session::Unconsume() { } Http2Session::~Http2Session() { + CHECK_EQ(flags_ & SESSION_STATE_HAS_SCOPE, 0); if (!object().IsEmpty()) ClearWrap(object()); persistent().Reset(); @@ -1365,6 +1371,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread, void* ctx) { Http2Session* session = static_cast(ctx); Http2Scope h2scope(session); + CHECK_NE(session->stream_, nullptr); DEBUG_HTTP2SESSION2(session, "receiving %d bytes", nread); if (nread < 0) { uv_buf_t tmp_buf; diff --git a/src/node_http2.h b/src/node_http2.h index a207fc07432caa..fe8c3f4a5ee72f 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -444,6 +444,7 @@ class Http2Scope { private: Http2Session* session_ = nullptr; + Local session_handle_; }; // The Http2Options class is used to parse the options object passed in to diff --git a/test/parallel/test-http2-createwritereq.js b/test/parallel/test-http2-createwritereq.js index 40d0ddc117c5ad..1d2b31676284d0 100644 --- a/test/parallel/test-http2-createwritereq.js +++ b/test/parallel/test-http2-createwritereq.js @@ -1,5 +1,7 @@ 'use strict'; +// Flags: --expose-gc + const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); @@ -62,6 +64,15 @@ server.listen(0, common.mustCall(function() { } })); + // Ref: https://github.com/nodejs/node/issues/17840 + const origDestroy = req.destroy; + req.destroy = function(...args) { + // Schedule a garbage collection event at the end of the current + // MakeCallback() run. + process.nextTick(global.gc); + return origDestroy.call(this, ...args); + }; + req.end(); }); }));