diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 8402bd2b61aaf4..ff20c990a54f87 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -32,8 +32,10 @@ const checkIsHttpToken = common._checkIsHttpToken; const checkInvalidHeaderChar = common._checkInvalidHeaderChar; const outHeadersKey = require('internal/http').outHeadersKey; const async_id_symbol = process.binding('async_wrap').async_id_symbol; +const async_hooks = require('async_hooks'); const nextTick = require('internal/process/next_tick').nextTick; const errors = require('internal/errors'); +const kSocket = Symbol('socket'); const CRLF = common.CRLF; const debug = common.debug; @@ -116,7 +118,7 @@ function OutgoingMessage() { this.finished = false; this._headerSent = false; - this.socket = null; + this[kSocket] = null; this.connection = null; this._header = null; this[outHeadersKey] = null; @@ -125,6 +127,19 @@ function OutgoingMessage() { } util.inherits(OutgoingMessage, Stream); +Object.defineProperty(OutgoingMessage.prototype, 'socket', { + get: function() { + return this[kSocket]; + }, + set: function(socket) { + this[kSocket] = socket; + if (socket && socket[async_id_symbol] === undefined) { + socket[async_id_symbol] = async_hooks.newUid(); + async_hooks.emitInit(socket[async_id_symbol], 'not-a-socket', + async_hooks.initTriggerId(), this); + } + } +}); Object.defineProperty(OutgoingMessage.prototype, '_headers', { get: function() { diff --git a/test/parallel/test-http-outgoing-message-inheritance.js b/test/parallel/test-http-outgoing-message-inheritance.js new file mode 100644 index 00000000000000..05a241dc8bda00 --- /dev/null +++ b/test/parallel/test-http-outgoing-message-inheritance.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +const { OutgoingMessage } = require('http'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// check that OutgoingMessage can be used without a proper Socket +// Fixes: https://github.com/nodejs/node/issues/14386 +// Fixes: https://github.com/nodejs/node/issues/14381 + +class Response extends OutgoingMessage { + constructor() { + super({ method: 'GET', httpVersionMajor: 1, httpVersionMinor: 1 }); + } + + _implicitHeader() {} +} + +const res = new Response(); +const ws = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + assert(chunk.toString().match(/hello world/)); + setImmediate(callback); + }) +}); + +res.socket = ws; +ws._httpMessage = res; +res.connection = ws; + +res.end('hello world'); diff --git a/test/parallel/test-http-server-response-standalone.js b/test/parallel/test-http-server-response-standalone.js new file mode 100644 index 00000000000000..7025c734b3103d --- /dev/null +++ b/test/parallel/test-http-server-response-standalone.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const { ServerResponse } = require('http'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// check that ServerResponse can be used without a proper Socket +// Fixes: https://github.com/nodejs/node/issues/14386 +// Fixes: https://github.com/nodejs/node/issues/14381 + +const res = new ServerResponse({ + method: 'GET', + httpVersionMajor: 1, + httpVersionMinor: 1 +}); + +const ws = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + assert(chunk.toString().match(/hello world/)); + setImmediate(callback); + }) +}); + +res.assignSocket(ws); + +res.end('hello world');