Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
devel:languages:nodejs
nodejs4
http-keep-alive.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File http-keep-alive.patch of Package nodejs4
commit b13b4a9ffb6038be1c21c20ece50d5fe367807b3 Author: Matteo Collina <hello@matteocollina.com> Date: Sat Dec 1 16:29:13 2018 +0100 http: prevent slowloris with keepalive connections Fixes: https://github.com/nodejs-private/security/issues/214 PR-URL: https://github.com/nodejs-private/node-private/pull/162 Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> commit e9ae4aaaad5947075bdb3d1f558281aa1a729b36 Author: Alexey Orlenko <eaglexrlnk@gmail.com> Date: Thu Jun 8 17:20:24 2017 +0300 http: fix timeout reset after keep-alive timeout Fix the logic of resetting the socket timeout of keep-alive HTTP connections and add two tests: * `test-http-server-keep-alive-timeout-slow-server` is a regression test for GH-13391. It ensures that the server-side keep-alive timeout will not fire during processing of a request. * `test-http-server-keep-alive-timeout-slow-client-headers` ensures that the regular socket timeout is restored as soon as a client starts sending a new request, not as soon as the whole message is received, so that the keep-alive timeout will not fire while, e.g., the client is sending large cookies. commit 06a208d3166493cc5bbc63b5cdbe473dbf4ad0ec Author: realwakka <realwakka@gmail.com> Date: Sun Jun 4 14:03:11 2017 +0900 test: refactor test-http-server-keep-alive-timeout Make the same reliability changes that were applied to the https test in ce5745bf92f586c58366e9f738441d69118f2c18. Refs: https://github.com/nodejs/node/pull/13312 PR-URL: https://github.com/nodejs/node/pull/13448 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com> commit 1c7fbdc53bbf8bf43ca32b2c6e9f513508ac90fa Author: Rich Trott <rtrott@gmail.com> Date: Tue May 30 13:06:52 2017 -0700 test: improve test-https-server-keep-alive-timeout The test is flaky under load. These changes greatly improve reliability. * Use a recurring interval to determine when the test should end rather than a timer. * Increase server timeout to 500ms to allow for events being delayed by system load Changing to an interval has the added benefit of reducing the test run time from over 2 seconds to under 1 second. Fixes: https://github.com/nodejs/node/issues/13307 PR-URL: https://github.com/nodejs/node/pull/13312 Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com> commit f23b3b6bad391cc3efe4cc815954f009c05fe63a Author: Timur Shemsedinov <timur.shemsedinov@gmail.com> Date: Thu Oct 29 21:53:43 2015 +0200 http: destroy sockets after keepAliveTimeout Implement server.keepAliveTimeout in addition to server.timeout to prevent temporary socket/memory leaking in keep-alive mode. PR-URL: https://github.com/nodejs/node/pull/2534 Author: Timur Shemsedinov <timur.shemsedinov@gmail.com> Author: Alexey Orlenko <eaglexrlnk@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Index: node-v4.9.1/lib/_http_server.js =================================================================== --- node-v4.9.1.orig/lib/_http_server.js +++ node-v4.9.1/lib/_http_server.js @@ -250,6 +250,7 @@ function Server(requestListener) { }); this.timeout = 2 * 60 * 1000; + this.keepAliveTimeout = 5000; this._pendingResponseData = 0; this.headersTimeout = 40 * 1000; // 40 seconds @@ -323,6 +324,8 @@ function connectionListener(socket) { socket.destroy(); }); + socket._keepAliveTimeoutSet = false; + var parser = parsers.alloc(); parser.reinitialize(HTTPParser.REQUEST); parser.socket = socket; @@ -373,6 +376,15 @@ function connectionListener(socket) { function socketOnData(d) { assert(!socket._paused); debug('SERVER socketOnData %d', d.length); + + if (socket._keepAliveTimeoutSet) { + socket.setTimeout(0); + if (self.timeout) { + socket.setTimeout(self.timeout); + } + socket._keepAliveTimeoutSet = false; + } + var ret = parser.execute(d); onParserExecuteCommon(ret, d); @@ -399,6 +411,8 @@ function connectionListener(socket) { } function onParserExecuteCommon(ret, d) { + resetSocketTimeout(self, socket); + if (ret instanceof Error) { debug('parse error'); socket.destroy(ret); @@ -481,8 +495,14 @@ function connectionListener(socket) { } function parserOnIncoming(req, shouldKeepAlive) { + resetSocketTimeout(self, socket); + incoming.push(req); + if (self.keepAliveTimeout > 0) { + req.on('end', resetHeadersTimeoutOnReqEnd); + } + // If the writable end isn't consuming, then stop reading // so that we don't become overwhelmed by a flood of // pipelined requests that may never be resolved. @@ -534,6 +554,12 @@ function connectionListener(socket) { if (res._last) { socket.destroySoon(); + } else if (outgoing.length === 0) { + if (self.keepAliveTimeout) { + socket.setTimeout(0); + socket.setTimeout(self.keepAliveTimeout); + socket._keepAliveTimeoutSet = true; + } } else { // start sending the next message var m = outgoing.shift(); @@ -561,6 +587,14 @@ function connectionListener(socket) { } exports._connectionListener = connectionListener; +function resetSocketTimeout(server, socket) { + if (!socket._keepAliveTimeoutSet) + return; + + socket.setTimeout(server.timeout || 0); + socket._keepAliveTimeoutSet = false; +} + function onSocketResume() { // It may seem that the socket is resumed, but this is an enemy's trick to // deceive us! `resume` is emitted asynchronously, and may be called from @@ -608,3 +642,15 @@ function socketOnWrap(ev, fn) { return res; } + +function resetHeadersTimeoutOnReqEnd() { + debug('resetHeadersTimeoutOnReqEnd'); + + var parser = this.socket.parser; + // Parser can be null if the socket was destroyed + // in that case, there is nothing to do. + if (parser !== null) { + parser.parsingHeadersStart = nowDate(); + } +} + Index: node-v4.9.1/test/parallel/test-http-server-keep-alive-timeout-slow-client-headers.js =================================================================== --- /dev/null +++ node-v4.9.1/test/parallel/test-http-server-keep-alive-timeout-slow-client-headers.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +}, 2)); + +server.keepAliveTimeout = common.platformTimeout(100); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const socket = net.connect({ port }, common.mustCall(() => { + request(common.mustCall(() => { + // Make a second request on the same socket, after the keep-alive timeout + // has been set on the server side. + request(common.mustCall(() => {})); + })); + })); + + server.on('timeout', common.mustCall(() => { + socket.end(); + server.close(); + })); + + function request(callback) { + socket.setEncoding('utf8'); + socket.on('data', onData); + let response = ''; + + // Simulate a client that sends headers slowly (with a period of inactivity + // that is longer than the keep-alive timeout). + socket.write('GET / HTTP/1.1\r\n' + + `Host: localhost:${port}\r\n`); + setTimeout(() => { + socket.write('Connection: keep-alive\r\n' + + '\r\n'); + }, common.platformTimeout(300)); + + function onData(chunk) { + response += chunk; + if (chunk.includes('\r\n')) { + socket.removeListener('data', onData); + onHeaders(); + } + } + + function onHeaders() { + assert.ok(response.includes('HTTP/1.1 200 OK\r\n')); + assert.ok(response.includes('Connection: keep-alive\r\n')); + callback(); + } + } +})); Index: node-v4.9.1/test/parallel/test-http-slow-headers-keepalive.js =================================================================== --- /dev/null +++ node-v4.9.1/test/parallel/test-http-slow-headers-keepalive.js @@ -0,0 +1,59 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); + +const headers = + 'GET / HTTP/1.1\r\n' + + 'Host: localhost\r\n' + + 'Connection: keep-alive' + + 'Agent: node\r\n'; + +let sendCharEvery = 1000; + +const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200); + res.end(); +})); + +// Pass a REAL env variable to shortening up the default +// value which is 40s otherwise this is useful for manual +// testing +if (!process.env.REAL) { + sendCharEvery = common.platformTimeout(10); + server.headersTimeout = 2 * sendCharEvery; +} + +server.once('timeout', common.mustCall((socket) => { + socket.destroy(); +})); + +server.listen(0, () => { + const client = net.connect(server.address().port); + client.write(headers); + // finish the first request + client.write('\r\n'); + // second request + client.write(headers); + client.write('X-CRASH: '); + + const interval = setInterval(() => { + client.write('a'); + }, sendCharEvery); + + client.resume(); + + const onClose = common.mustCall(() => { + client.removeListener('close', onClose); + client.removeListener('error', onClose); + client.removeListener('end', onClose); + clearInterval(interval); + server.close(); + }); + + client.on('error', onClose); + client.on('close', onClose); + client.on('end', onClose); +}); + Index: node-v4.9.1/test/parallel/test-http-server-keep-alive-timeout-slow-server.js =================================================================== --- /dev/null +++ node-v4.9.1/test/parallel/test-http-server-keep-alive-timeout-slow-server.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustCall((req, res) => { + if (req.url === '/first') { + res.end('ok'); + return; + } + setTimeout(() => { + res.end('ok'); + }, common.platformTimeout(500)); +}, 2)); + +server.keepAliveTimeout = common.platformTimeout(200); + +const agent = new http.Agent({ + keepAlive: true, + maxSockets: 1 +}); + +function request(path, callback) { + const port = server.address().port; + const req = http.request({ agent, path, port }, common.mustCall((res) => { + assert.strictEqual(res.statusCode, 200); + + res.setEncoding('utf8'); + + let result = ''; + res.on('data', (chunk) => { + result += chunk; + }); + + res.on('end', common.mustCall(() => { + assert.strictEqual(result, 'ok'); + callback(); + })); + })); + req.end(); +} + +server.listen(0, common.mustCall(() => { + request('/first', () => { + request('/second', () => { + server.close(); + }); + }); +})); Index: node-v4.9.1/doc/api/http.md =================================================================== --- node-v4.9.1.orig/doc/api/http.md +++ node-v4.9.1/doc/api/http.md @@ -729,17 +729,33 @@ customised. added: v0.9.12 --> -* {Number} Default = 120000 (2 minutes) +* {number} Timeout in milliseconds. Defaults to 120000 (2 minutes). The number of milliseconds of inactivity before a socket is presumed to have timed out. -Note that the socket timeout logic is set up on connection, so -changing this value only affects *new* connections to the server, not -any existing connections. +A value of 0 will disable the timeout behavior on incoming connections. -Set to 0 to disable any kind of automatic timeout behavior on incoming -connections. +*Note*: The socket timeout logic is set up on connection, so changing this +value only affects new connections to the server, not any existing connections. + +### server.keepAliveTimeout +<!-- YAML +added: REPLACEME +--> + +* {number} Timeout in milliseconds. Defaults to 5000 (5 seconds). + +The number of milliseconds of inactivity a server needs to wait for additional +incoming data, after it has finished writing the last response, before a socket +will be destroyed. If the server receives new data before the keep-alive +timeout has fired, it will reset the regular inactivity timeout, i.e., +[`server.timeout`][]. + +A value of 0 will disable the keep-alive timeout behavior on incoming connections. + +*Note*: The socket timeout logic is set up on connection, so changing this +value only affects new connections to the server, not any existing connections. ## Class: http.ServerResponse <!-- YAML @@ -1498,6 +1514,7 @@ There are a few special headers that sho [`response.write(data, encoding)`]: #http_response_write_chunk_encoding_callback [`response.writeContinue()`]: #http_response_writecontinue [`response.writeHead()`]: #http_response_writehead_statuscode_statusmessage_headers +[`server.timeout`]: #http_server_timeout [`socket.setKeepAlive()`]: net.html#net_socket_setkeepalive_enable_initialdelay [`socket.setNoDelay()`]: net.html#net_socket_setnodelay_nodelay [`socket.setTimeout()`]: net.html#net_socket_settimeout_timeout_callback Index: node-v4.9.1/doc/api/https.md =================================================================== --- node-v4.9.1.orig/doc/api/https.md +++ node-v4.9.1/doc/api/https.md @@ -41,6 +41,14 @@ added: v0.11.2 See [`http.Server#timeout`][]. +### server.keepAliveTimeout +<!-- YAML +added: REPLACEME +--> +- {number} Defaults to 5000 (5 seconds). + +See [`http.Server#keepAliveTimeout`][]. + ## https.createServer(options[, requestListener]) <!-- YAML added: v0.3.4 @@ -264,6 +272,7 @@ var req = https.request(options, (res) = [`globalAgent`]: #https_https_globalagent [`http.Agent`]: http.html#http_class_http_agent [`http.Server#headersTimeout`]: http.html#http_server_headerstimeout +[`http.Server#keepAliveTimeout`]: http.html#http_server_keepalivetimeout [`http.close()`]: http.html#http_server_close_callback [`http.get()`]: http.html#http_http_get_options_callback [`http.listen()`]: http.html#http_server_listen_port_hostname_backlog_callback Index: node-v4.9.1/lib/https.js =================================================================== --- node-v4.9.1.orig/lib/https.js +++ node-v4.9.1/lib/https.js @@ -34,7 +34,7 @@ function Server(opts, requestListener) { }); this.timeout = 2 * 60 * 1000; - + this.keepAliveTimeout = 5000; this.headersTimeout = 40 * 1000; // 40 seconds } inherits(Server, tls.Server); Index: node-v4.9.1/test/parallel/test-http-server-keep-alive-timeout.js =================================================================== --- /dev/null +++ node-v4.9.1/test/parallel/test-http-server-keep-alive-timeout.js @@ -0,0 +1,95 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +const tests = []; + +function test(fn) { + if (!tests.length) { + process.nextTick(run); + } + tests.push(fn); +} + +function run() { + const fn = tests.shift(); + if (fn) fn(run); +} + +test(function serverEndKeepAliveTimeoutWithPipeline(cb) { + let socket; + let destroyedSockets = 0; + let timeoutCount = 0; + let requestCount = 0; + process.on('exit', () => { + assert.strictEqual(timeoutCount, 1); + assert.strictEqual(requestCount, 3); + assert.strictEqual(destroyedSockets, 1); + }); + const server = http.createServer((req, res) => { + socket = req.socket; + requestCount++; + res.end(); + }); + server.setTimeout(200, (socket) => { + timeoutCount++; + socket.destroy(); + }); + server.keepAliveTimeout = 50; + server.listen(0, common.mustCall(() => { + const options = { + port: server.address().port, + allowHalfOpen: true + }; + const c = net.connect(options, () => { + c.write('GET /1 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /2 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /3 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + }); + setTimeout(() => { + server.close(); + if (socket.destroyed) destroyedSockets++; + cb(); + }, 1000); + })); +}); + +test(function serverNoEndKeepAliveTimeoutWithPipeline(cb) { + let socket; + let destroyedSockets = 0; + let timeoutCount = 0; + let requestCount = 0; + process.on('exit', () => { + assert.strictEqual(timeoutCount, 1); + assert.strictEqual(requestCount, 3); + assert.strictEqual(destroyedSockets, 1); + }); + const server = http.createServer((req, res) => { + socket = req.socket; + requestCount++; + }); + server.setTimeout(200, (socket) => { + timeoutCount++; + socket.destroy(); + }); + server.keepAliveTimeout = 50; + server.listen(0, common.mustCall(() => { + const options = { + port: server.address().port, + allowHalfOpen: true + }; + const c = net.connect(options, () => { + c.write('GET /1 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /2 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /3 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + }); + setTimeout(() => { + server.close(); + if (socket.destroyed) destroyedSockets++; + cb(); + }, 1000); + })); +}); Index: node-v4.9.1/test/parallel/test-https-server-keep-alive-timeout.js =================================================================== --- /dev/null +++ node-v4.9.1/test/parallel/test-https-server-keep-alive-timeout.js @@ -0,0 +1,85 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const https = require('https'); +const tls = require('tls'); +const fs = require('fs'); + +const tests = []; + +const serverOptions = { + key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem') +}; + +function test(fn) { + if (!tests.length) { + process.nextTick(run); + } + tests.push(fn); +} + +function run() { + const fn = tests.shift(); + if (fn) fn(run); +} + +test(function serverKeepAliveTimeoutWithPipeline(cb) { + let requestCount = 0; + process.on('exit', function() { + assert.strictEqual(requestCount, 3); + }); + const server = https.createServer(serverOptions, (req, res) => { + requestCount++; + res.end(); + }); + server.setTimeout(500, common.mustCall((socket) => { + // End this test and call `run()` for the next test (if any). + socket.destroy(); + server.close(); + cb(); + })); + server.keepAliveTimeout = 50; + server.listen(0, common.mustCall(() => { + const options = { + port: server.address().port, + allowHalfOpen: true, + rejectUnauthorized: false + }; + const c = tls.connect(options, () => { + c.write('GET /1 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /2 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /3 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + }); + })); +}); + +test(function serverNoEndKeepAliveTimeoutWithPipeline(cb) { + let requestCount = 0; + process.on('exit', () => { + assert.strictEqual(requestCount, 3); + }); + const server = https.createServer(serverOptions, (req, res) => { + requestCount++; + }); + server.setTimeout(500, common.mustCall((socket) => { + // End this test and call `run()` for the next test (if any). + socket.destroy(); + server.close(); + cb(); + })); + server.keepAliveTimeout = 50; + server.listen(0, common.mustCall(() => { + const options = { + port: server.address().port, + allowHalfOpen: true, + rejectUnauthorized: false + }; + const c = tls.connect(options, () => { + c.write('GET /1 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /2 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + c.write('GET /3 HTTP/1.1\r\nHost: localhost\r\n\r\n'); + }); + })); +});
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor