-
Notifications
You must be signed in to change notification settings - Fork 29.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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. Refs: #2534 Fixes: #13391 PR-URL: #13549 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Brian White <[email protected]>
- Loading branch information
1 parent
a0f8faa
commit 2cb6f2b
Showing
3 changed files
with
119 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
test/parallel/test-http-server-keep-alive-timeout-slow-client-headers.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} | ||
})); |
50 changes: 50 additions & 0 deletions
50
test/parallel/test-http-server-keep-alive-timeout-slow-server.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
}); | ||
}); | ||
})); |