From 32b641e528210560d9cb4b524acf844f74d0d266 Mon Sep 17 00:00:00 2001 From: iskore Date: Thu, 14 May 2020 13:29:16 -0400 Subject: [PATCH] http: fixed socket.setEncoding fatal error Applied updates from previous pull-requests to disallow socket.setEncoding before a http connection is parsed. Wrapped `socket.setEncoding` to throw an error. This previously resulted in a fatal error. PR-URL: https://github.com/nodejs/node/pull/33405 Fixes: https://github.com/nodejs/node/issues/18118 Refs: https://github.com/nodejs/node/pull/18178 Refs: https://github.com/nodejs/node/pull/19344 Reviewed-By: Matteo Collina Reviewed-By: Ruben Bridgewater --- doc/api/errors.md | 6 ++++ lib/_http_server.js | 6 ++++ lib/internal/errors.js | 2 ++ .../test-http-socket-encoding-error.js | 29 +++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 test/parallel/test-http-socket-encoding-error.js diff --git a/doc/api/errors.md b/doc/api/errors.md index 1cf926c84be4a2..62b5b0e4c3c991 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -943,6 +943,11 @@ An invalid HTTP header value was specified. Status code was outside the regular status code range (100-999). + +### `ERR_HTTP_SOCKET_ENCODING` + +Changing the socket encoding is not allowed per [RFC 7230 Section 3][]. + ### `ERR_HTTP_TRAILER_INVALID` @@ -2613,6 +2618,7 @@ such as `process.stdout.on('data')`. [exports]: esm.html#esm_package_entry_points [file descriptors]: https://en.wikipedia.org/wiki/File_descriptor [policy]: policy.html +[RFC 7230 Section 3]: https://tools.ietf.org/html/rfc7230#section-3 [stream-based]: stream.html [syscall]: http://man7.org/linux/man-pages/man2/syscalls.2.html [Subresource Integrity specification]: https://www.w3.org/TR/SRI/#the-integrity-attribute diff --git a/lib/_http_server.js b/lib/_http_server.js index 2c7c5284f604a0..f11c74ff7c4303 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -63,6 +63,7 @@ const { const { ERR_HTTP_HEADERS_SENT, ERR_HTTP_INVALID_STATUS_CODE, + ERR_HTTP_SOCKET_ENCODING, ERR_INVALID_ARG_TYPE, ERR_INVALID_CHAR } = codes; @@ -476,6 +477,7 @@ function connectionListenerInternal(server, socket) { socket.on = generateSocketListenerWrapper('on'); socket.addListener = generateSocketListenerWrapper('addListener'); socket.prependListener = generateSocketListenerWrapper('prependListener'); + socket.setEncoding = socketSetEncoding; // We only consume the socket if it has never been consumed before. if (socket._handle && socket._handle.isStreamBase && @@ -493,6 +495,10 @@ function connectionListenerInternal(server, socket) { socket._paused = false; } +function socketSetEncoding() { + throw new ERR_HTTP_SOCKET_ENCODING(); +} + function updateOutgoingData(socket, state, delta) { state.outgoingData += delta; socketOnDrain(socket, state); diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 56724ba53ed092..90c6a9dba50ec9 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -935,6 +935,8 @@ E('ERR_HTTP_HEADERS_SENT', E('ERR_HTTP_INVALID_HEADER_VALUE', 'Invalid value "%s" for header "%s"', TypeError); E('ERR_HTTP_INVALID_STATUS_CODE', 'Invalid status code: %s', RangeError); +E('ERR_HTTP_SOCKET_ENCODING', + 'Changing the socket encoding is not allowed per RFC7230 Section 3.', Error); E('ERR_HTTP_TRAILER_INVALID', 'Trailers are invalid with this transfer encoding', Error); E('ERR_INCOMPATIBLE_OPTION_PAIR', diff --git a/test/parallel/test-http-socket-encoding-error.js b/test/parallel/test-http-socket-encoding-error.js new file mode 100644 index 00000000000000..0ce984896d3aea --- /dev/null +++ b/test/parallel/test-http-socket-encoding-error.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer().listen(0, connectToServer); + +server.on('connection', common.mustCall((socket) => { + assert.throws( + () => { + socket.setEncoding(''); + }, + { + code: 'ERR_HTTP_SOCKET_ENCODING', + name: 'Error', + message: 'Changing the socket encoding is not ' + + 'allowed per RFC7230 Section 3.' + } + ); + + socket.end(); +})); + +function connectToServer() { + const client = new http.Agent().createConnection(this.address().port, () => { + client.end(); + }).on('end', () => server.close()); +}