diff --git a/src/stream_base.h b/src/stream_base.h index faddee88c82786..35929750bfbc54 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -146,10 +146,14 @@ class StreamResource { const uv_buf_t* buf, uv_handle_type pending, void* ctx); + typedef void (*DestructCb)(void* ctx); StreamResource() : bytes_read_(0) { } - virtual ~StreamResource() = default; + virtual ~StreamResource() { + if (!destruct_cb_.is_empty()) + destruct_cb_.fn(destruct_cb_.ctx); + } virtual int DoShutdown(ShutdownWrap* req_wrap) = 0; virtual int DoTryWrite(uv_buf_t** bufs, size_t* count); @@ -186,15 +190,18 @@ class StreamResource { inline void set_alloc_cb(Callback c) { alloc_cb_ = c; } inline void set_read_cb(Callback c) { read_cb_ = c; } + inline void set_destruct_cb(Callback c) { destruct_cb_ = c; } inline Callback after_write_cb() { return after_write_cb_; } inline Callback alloc_cb() { return alloc_cb_; } inline Callback read_cb() { return read_cb_; } + inline Callback destruct_cb() { return destruct_cb_; } private: Callback after_write_cb_; Callback alloc_cb_; Callback read_cb_; + Callback destruct_cb_; uint64_t bytes_read_; friend class StreamBase; diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index d21976c7df309e..3dad65011ff85b 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -85,6 +85,7 @@ TLSWrap::TLSWrap(Environment* env, stream_->set_after_write_cb({ OnAfterWriteImpl, this }); stream_->set_alloc_cb({ OnAllocImpl, this }); stream_->set_read_cb({ OnReadImpl, this }); + stream_->set_destruct_cb({ OnDestructImpl, this }); set_alloc_cb({ OnAllocSelf, this }); set_read_cb({ OnReadSelf, this }); @@ -687,6 +688,12 @@ void TLSWrap::OnReadImpl(ssize_t nread, } +void TLSWrap::OnDestructImpl(void* ctx) { + TLSWrap* wrap = static_cast(ctx); + wrap->clear_stream(); +} + + void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) { buf->base = node::Malloc(suggested_size); buf->len = suggested_size; diff --git a/src/tls_wrap.h b/src/tls_wrap.h index d6c4b62493d10b..f1d53f3e2f5da1 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -75,6 +75,8 @@ class TLSWrap : public AsyncWrap, size_t self_size() const override { return sizeof(*this); } + void clear_stream() { stream_ = nullptr; } + protected: static const int kClearOutChunkSize = 16384; @@ -142,6 +144,7 @@ class TLSWrap : public AsyncWrap, const uv_buf_t* buf, uv_handle_type pending, void* ctx); + static void OnDestructImpl(void* ctx); void DoRead(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); diff --git a/test/parallel/test-tls-retain-handle-no-abort.js b/test/parallel/test-tls-retain-handle-no-abort.js new file mode 100644 index 00000000000000..43b3709fd5f85b --- /dev/null +++ b/test/parallel/test-tls-retain-handle-no-abort.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); + return; +} +const tls = require('tls'); +const fs = require('fs'); +const util = require('util'); + +const sent = 'hello world'; +const serverOptions = { + isServer: true, + key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem') +}; + +let ssl = null; + +process.on('exit', function() { + assert.ok(ssl !== null); + // If the internal pointer to stream_ isn't cleared properly then this + // will abort. + util.inspect(ssl); +}); + +const server = tls.createServer(serverOptions, function(s) { + s.on('data', function() { }); + s.on('end', function() { + server.close(); + s.destroy(); + }); +}).listen(0, function() { + const c = new tls.TLSSocket(); + ssl = c.ssl; + c.connect(this.address().port, function() { + c.end(sent); + }); +});