| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b0ba52d commit ea8930c
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -57,6 +57,9 @@ const constants = require('../llhttp/constants.js') | |||
| 57 | 57 | const EMPTY_BUF = Buffer.alloc(0) | |
| 58 | 58 | const FastBuffer = Buffer[Symbol.species] | |
| 59 | 59 | const removeAllListeners = util.removeAllListeners | |
| 60 | + const kIdleSocketValidation = Symbol('kIdleSocketValidation') | ||
| 61 | + const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout') | ||
| 62 | + const kSocketUsed = Symbol('kSocketUsed') | ||
| 60 | 63 | ||
| 61 | 64 | let extractBody | |
| 62 | 65 | ||
@@ -440,6 +443,11 @@ class Parser { | |||
| 440 | 443 | return -1 | |
| 441 | 444 | } | |
| 442 | 445 | ||
| 446 | + if (client[kRunning] === 0) { | ||
| 447 | + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) | ||
| 448 | + return -1 | ||
| 449 | + } | ||
| 450 | + | ||
| 443 | 451 | const request = client[kQueue][client[kRunningIdx]] | |
| 444 | 452 | if (!request) { | |
| 445 | 453 | return -1 | |
@@ -568,6 +576,11 @@ class Parser { | |||
| 568 | 576 | return -1 | |
| 569 | 577 | } | |
| 570 | 578 | ||
| 579 | + if (client[kRunning] === 0) { | ||
| 580 | + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) | ||
| 581 | + return -1 | ||
| 582 | + } | ||
| 583 | + | ||
| 571 | 584 | const request = client[kQueue][client[kRunningIdx]] | |
| 572 | 585 | ||
| 573 | 586 | if (!request) { | |
@@ -746,6 +759,7 @@ class Parser { | |||
| 746 | 759 | request.onComplete(headers) | |
| 747 | 760 | ||
| 748 | 761 | client[kQueue][client[kRunningIdx]++] = null | |
| 762 | + socket[kSocketUsed] = client[kPending] === 0 | ||
| 749 | 763 | ||
| 750 | 764 | if (socket[kWriting]) { | |
| 751 | 765 | assert(client[kRunning] === 0) | |
@@ -822,6 +836,9 @@ function connectH1 (client, socket) { | |||
| 822 | 836 | socket[kWriting] = false | |
| 823 | 837 | socket[kReset] = false | |
| 824 | 838 | socket[kBlocking] = false | |
| 839 | + socket[kIdleSocketValidation] = 0 | ||
| 840 | + socket[kIdleSocketValidationTimeout] = null | ||
| 841 | + socket[kSocketUsed] = false | ||
| 825 | 842 | socket[kParser] = new Parser(client, socket, llhttpInstance) | |
| 826 | 843 | ||
| 827 | 844 | util.addListener(socket, 'error', onHttpSocketError) | |
@@ -864,7 +881,7 @@ function connectH1 (client, socket) { | |||
| 864 | 881 | * @returns {boolean} | |
| 865 | 882 | */ | |
| 866 | 883 | busy (request) { | |
| 867 | - if (socket[kWriting] || socket[kReset] || socket[kBlocking]) { | ||
| 884 | + if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) { | ||
| 868 | 885 | return true | |
| 869 | 886 | } | |
| 870 | 887 | ||
@@ -944,6 +961,8 @@ function onHttpSocketEnd () { | |||
| 944 | 961 | function onHttpSocketClose () { | |
| 945 | 962 | const parser = this[kParser] | |
| 946 | 963 | ||
| 964 | + clearIdleSocketValidation(this) | ||
| 965 | + | ||
| 947 | 966 | if (parser) { | |
| 948 | 967 | if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { | |
| 949 | 968 | this[kError] = parser.finish() || this[kError] | |
@@ -990,6 +1009,28 @@ function onSocketClose () { | |||
| 990 | 1009 | this[kClosed] = true | |
| 991 | 1010 | } | |
| 992 | 1011 | ||
| 1012 | + function clearIdleSocketValidation (socket) { | ||
| 1013 | + if (socket[kIdleSocketValidationTimeout]) { | ||
| 1014 | + clearTimeout(socket[kIdleSocketValidationTimeout]) | ||
| 1015 | + socket[kIdleSocketValidationTimeout] = null | ||
| 1016 | + } | ||
| 1017 | + | ||
| 1018 | + socket[kIdleSocketValidation] = 0 | ||
| 1019 | + } | ||
| 1020 | + | ||
| 1021 | + function scheduleIdleSocketValidation (client, socket) { | ||
| 1022 | + socket[kIdleSocketValidation] = 1 | ||
| 1023 | + socket[kIdleSocketValidationTimeout] = setTimeout(() => { | ||
| 1024 | + socket[kIdleSocketValidationTimeout] = null | ||
| 1025 | + socket[kIdleSocketValidation] = 2 | ||
| 1026 | + | ||
| 1027 | + if (client[kSocket] === socket && !socket.destroyed) { | ||
| 1028 | + client[kResume]() | ||
| 1029 | + } | ||
| 1030 | + }, 0) | ||
| 1031 | + socket[kIdleSocketValidationTimeout].unref?.() | ||
| 1032 | + } | ||
| 1033 | + | ||
| 993 | 1034 | /** | |
| 994 | 1035 | * @param {import('./client.js')} client | |
| 995 | 1036 | */ | |
@@ -1007,6 +1048,32 @@ function resumeH1 (client) { | |||
| 1007 | 1048 | socket[kNoRef] = false | |
| 1008 | 1049 | } | |
| 1009 | 1050 | ||
| 1051 | + if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) { | ||
| 1052 | + if (socket[kIdleSocketValidation] === 0) { | ||
| 1053 | + scheduleIdleSocketValidation(client, socket) | ||
| 1054 | + socket[kParser].readMore() | ||
| 1055 | + if (socket.destroyed) { | ||
| 1056 | + return | ||
| 1057 | + } | ||
| 1058 | + return | ||
| 1059 | + } | ||
| 1060 | + | ||
| 1061 | + if (socket[kIdleSocketValidation] === 1) { | ||
| 1062 | + socket[kParser].readMore() | ||
| 1063 | + if (socket.destroyed) { | ||
| 1064 | + return | ||
| 1065 | + } | ||
| 1066 | + return | ||
| 1067 | + } | ||
| 1068 | + } | ||
| 1069 | + | ||
| 1070 | + if (client[kRunning] === 0) { | ||
| 1071 | + socket[kParser].readMore() | ||
| 1072 | + if (socket.destroyed) { | ||
| 1073 | + return | ||
| 1074 | + } | ||
| 1075 | + } | ||
| 1076 | + | ||
| 1010 | 1077 | if (client[kSize] === 0) { | |
| 1011 | 1078 | if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) { | |
| 1012 | 1079 | socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE) | |
@@ -1105,6 +1172,7 @@ function writeH1 (client, request) { | |||
| 1105 | 1172 | } | |
| 1106 | 1173 | ||
| 1107 | 1174 | const socket = client[kSocket] | |
| 1175 | + clearIdleSocketValidation(socket) | ||
| 1108 | 1176 | ||
| 1109 | 1177 | /** | |
| 1110 | 1178 | * @param {Error} [err] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,52 @@ | |||
| 1 | + 'use strict' | ||
| 2 | + | ||
| 3 | + const assert = require('node:assert') | ||
| 4 | + const { createServer } = require('node:http') | ||
| 5 | + const { after, test } = require('node:test') | ||
| 6 | + const { Client } = require('..') | ||
| 7 | + | ||
| 8 | + function readBody (body) { | ||
| 9 | + return new Promise((resolve, reject) => { | ||
| 10 | + let data = '' | ||
| 11 | + body.setEncoding('latin1') | ||
| 12 | + body.on('data', chunk => { data += chunk }) | ||
| 13 | + body.on('end', () => resolve(data)) | ||
| 14 | + body.on('error', reject) | ||
| 15 | + }) | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + test('should not reuse an idle socket with buffered unsolicited response bytes', async () => { | ||
| 19 | + let evilServerSocket | ||
| 20 | + | ||
| 21 | + const server = createServer((req, res) => { | ||
| 22 | + if (!evilServerSocket) { | ||
| 23 | + evilServerSocket = req.socket | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + res.end(req.url) | ||
| 27 | + }) | ||
| 28 | + after(() => server.close()) | ||
| 29 | + | ||
| 30 | + await new Promise(resolve => server.listen(0, resolve)) | ||
| 31 | + | ||
| 32 | + const client = new Client(`http://localhost:${server.address().port}`, { | ||
| 33 | + keepAliveTimeout: 300e3 | ||
| 34 | + }) | ||
| 35 | + after(() => client.close()) | ||
| 36 | + | ||
| 37 | + const response1 = await client.request({ path: '/request1', method: 'GET' }) | ||
| 38 | + assert.strictEqual(await readBody(response1.body), '/request1') | ||
| 39 | + | ||
| 40 | + evilServerSocket.write( | ||
| 41 | + 'HTTP/1.1 200 OK\r\n' + | ||
| 42 | + 'Poison-Free-Socket: true\r\n' + | ||
| 43 | + 'Connection: keep-alive\r\n' + | ||
| 44 | + 'Keep-Alive: timeout=300\r\n' + | ||
| 45 | + 'Content-Length: 0\r\n' + | ||
| 46 | + '\r\n' | ||
| 47 | + ) | ||
| 48 | + | ||
| 49 | + const response2 = await client.request({ path: '/request2', method: 'GET' }) | ||
| 50 | + assert.strictEqual(response2.headers['poison-free-socket'], undefined) | ||
| 51 | + assert.strictEqual(await readBody(response2.body), '/request2') | ||
| 52 | + }) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments