| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d288ec3 commit 596940c
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -49,6 +49,9 @@ const { | |||
| 49 | 49 | const kOnKeylog = Symbol('onkeylog'); | |
| 50 | 50 | const kRequestOptions = Symbol('requestOptions'); | |
| 51 | 51 | const kRequestAsyncResource = Symbol('requestAsyncResource'); | |
| 52 | + | ||
| 53 | + // TODO(jazelly): make this configurable | ||
| 54 | + const HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER = 1000; | ||
| 52 | 55 | // New Agent code. | |
| 53 | 56 | ||
| 54 | 57 | // The largest departure from the previous implementation is that | |
@@ -473,6 +476,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { | |||
| 473 | 476 | socket.unref(); | |
| 474 | 477 | ||
| 475 | 478 | let agentTimeout = this.options.timeout || 0; | |
| 479 | + let canKeepSocketAlive = true; | ||
| 476 | 480 | ||
| 477 | 481 | if (socket._httpMessage?.res) { | |
| 478 | 482 | const keepAliveHint = socket._httpMessage.res.headers['keep-alive']; | |
@@ -481,9 +485,15 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { | |||
| 481 | 485 | const hint = /^timeout=(\d+)/.exec(keepAliveHint)?.[1]; | |
| 482 | 486 | ||
| 483 | 487 | if (hint) { | |
| 484 | - const serverHintTimeout = NumberParseInt(hint) * 1000; | ||
| 485 | - | ||
| 486 | - if (serverHintTimeout < agentTimeout) { | ||
| 488 | + // Let the timer expire before the announced timeout to reduce | ||
| 489 | + // the likelihood of ECONNRESET errors | ||
| 490 | + let serverHintTimeout = (NumberParseInt(hint) * 1000) - HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER; | ||
| 491 | + serverHintTimeout = serverHintTimeout > 0 ? serverHintTimeout : 0; | ||
| 492 | + if (serverHintTimeout === 0) { | ||
| 493 | + // Cannot safely reuse the socket because the server timeout is | ||
| 494 | + // too short | ||
| 495 | + canKeepSocketAlive = false; | ||
| 496 | + } else if (serverHintTimeout < agentTimeout) { | ||
| 487 | 497 | agentTimeout = serverHintTimeout; | |
| 488 | 498 | } | |
| 489 | 499 | } | |
@@ -494,7 +504,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { | |||
| 494 | 504 | socket.setTimeout(agentTimeout); | |
| 495 | 505 | } | |
| 496 | 506 | ||
| 497 | - return true; | ||
| 507 | + return canKeepSocketAlive; | ||
| 498 | 508 | }; | |
| 499 | 509 | ||
| 500 | 510 | Agent.prototype.reuseSocket = function reuseSocket(socket, req) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -182,6 +182,8 @@ const kConnections = Symbol('http.server.connections'); | |||
| 182 | 182 | const kConnectionsCheckingInterval = Symbol('http.server.connectionsCheckingInterval'); | |
| 183 | 183 | ||
| 184 | 184 | const HTTP_SERVER_TRACE_EVENT_NAME = 'http.server.request'; | |
| 185 | + // TODO(jazelly): make this configurable | ||
| 186 | + const HTTP_SERVER_KEEP_ALIVE_TIMEOUT_BUFFER = 1000; | ||
| 185 | 187 | ||
| 186 | 188 | class HTTPServerAsyncResource { | |
| 187 | 189 | constructor(type, socket) { | |
@@ -998,7 +1000,9 @@ function resOnFinish(req, res, socket, state, server) { | |||
| 998 | 1000 | } | |
| 999 | 1001 | } else if (state.outgoing.length === 0) { | |
| 1000 | 1002 | if (server.keepAliveTimeout && typeof socket.setTimeout === 'function') { | |
| 1001 | - socket.setTimeout(server.keepAliveTimeout); | ||
| 1003 | + // Increase the internal timeout wrt the advertised value to reduce | ||
| 1004 | + // the likelihood of ECONNRESET errors. | ||
| 1005 | + socket.setTimeout(server.keepAliveTimeout + HTTP_SERVER_KEEP_ALIVE_TIMEOUT_BUFFER); | ||
| 1002 | 1006 | state.keepAliveTimeoutSet = true; | |
| 1003 | 1007 | } | |
| 1004 | 1008 | } else { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,38 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + const http = require('http'); | ||
| 5 | + | ||
| 6 | + const makeRequest = (port, agent) => | ||
| 7 | + new Promise((resolve, reject) => { | ||
| 8 | + const req = http.get( | ||
| 9 | + { path: '/', port, agent }, | ||
| 10 | + (res) => { | ||
| 11 | + res.resume(); | ||
| 12 | + res.on('end', () => resolve()); | ||
| 13 | + }, | ||
| 14 | + ); | ||
| 15 | + req.on('error', (e) => reject(e)); | ||
| 16 | + req.end(); | ||
| 17 | + }); | ||
| 18 | + | ||
| 19 | + const server = http.createServer( | ||
| 20 | + { keepAliveTimeout: common.platformTimeout(2000), keepAlive: true }, | ||
| 21 | + common.mustCall((req, res) => { | ||
| 22 | + const body = 'hello world\n'; | ||
| 23 | + res.writeHead(200, { 'Content-Length': body.length }); | ||
| 24 | + res.write(body); | ||
| 25 | + res.end(); | ||
| 26 | + }, 2) | ||
| 27 | + ); | ||
| 28 | + | ||
| 29 | + const agent = new http.Agent({ maxSockets: 5, keepAlive: true }); | ||
| 30 | + | ||
| 31 | + server.listen(0, common.mustCall(async function() { | ||
| 32 | + await makeRequest(this.address().port, agent); | ||
| 33 | + // Block the event loop for 2 seconds | ||
| 34 | + Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2000); | ||
| 35 | + await makeRequest(this.address().port, agent); | ||
| 36 | + server.close(); | ||
| 37 | + agent.destroy(); | ||
| 38 | + })); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments