mirror of
https://github.com/tenrok/axios.git
synced 2026-06-20 20:00:40 +03:00
fix(http2): fix possible race condition when handling http2 stream on almost timed out session by improving timeout out algorithm; (#7186)
This commit is contained in:
+46
-10
@@ -87,7 +87,15 @@ class Http2Sessions {
|
|||||||
|
|
||||||
const session = connect(authority, options);
|
const session = connect(authority, options);
|
||||||
|
|
||||||
session.once('close', () => {
|
let removed;
|
||||||
|
|
||||||
|
const removeSession = () => {
|
||||||
|
if (removed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
removed = true;
|
||||||
|
|
||||||
let entries = authoritySessions, len = entries.length, i = len;
|
let entries = authoritySessions, len = entries.length, i = len;
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
@@ -99,9 +107,41 @@ class Http2Sessions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
Http2Sessions.setTimeout(session, options.sessionTimeout);
|
const originalRequestFn = session.request;
|
||||||
|
|
||||||
|
const {sessionTimeout} = options;
|
||||||
|
|
||||||
|
if(sessionTimeout != null) {
|
||||||
|
|
||||||
|
let timer;
|
||||||
|
let streamsCount = 0;
|
||||||
|
|
||||||
|
session.request = function () {
|
||||||
|
const stream = originalRequestFn.apply(this, arguments);
|
||||||
|
|
||||||
|
streamsCount++;
|
||||||
|
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.once('close', () => {
|
||||||
|
if (!--streamsCount) {
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
timer = null;
|
||||||
|
removeSession();
|
||||||
|
}, sessionTimeout);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.once('close', removeSession);
|
||||||
|
|
||||||
let entries = this.sessions[authority], entry = [
|
let entries = this.sessions[authority], entry = [
|
||||||
session,
|
session,
|
||||||
@@ -112,12 +152,6 @@ class Http2Sessions {
|
|||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
static setTimeout(session, timeout = 1000) {
|
|
||||||
session && session.setTimeout(timeout, () => {
|
|
||||||
session.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const http2Sessions = new Http2Sessions();
|
const http2Sessions = new Http2Sessions();
|
||||||
@@ -284,10 +318,12 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
|||||||
let rejected = false;
|
let rejected = false;
|
||||||
let req;
|
let req;
|
||||||
|
|
||||||
httpVersion = Number(httpVersion);
|
httpVersion = +httpVersion;
|
||||||
|
|
||||||
if (Number.isNaN(httpVersion)) {
|
if (Number.isNaN(httpVersion)) {
|
||||||
throw TypeError(`Invalid protocol version: '${config.httpVersion}' is not a number`);
|
throw TypeError(`Invalid protocol version: '${config.httpVersion}' is not a number`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (httpVersion !== 1 && httpVersion !== 2) {
|
if (httpVersion !== 1 && httpVersion !== 2) {
|
||||||
throw TypeError(`Unsupported protocol version '${httpVersion}'`);
|
throw TypeError(`Unsupported protocol version '${httpVersion}'`);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-11
@@ -2423,7 +2423,7 @@ describe('supports http with nodejs', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support request cancellation', async function (){
|
it('should support request cancellation', async function (){
|
||||||
if (typeof AbortSignal !== 'function') {
|
if (typeof AbortSignal !== 'function' || !AbortSignal.timeout) {
|
||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2525,13 +2525,17 @@ describe('supports http with nodejs', function () {
|
|||||||
|
|
||||||
it("should use different sessions for different authorities", async() => {
|
it("should use different sessions for different authorities", async() => {
|
||||||
server = await startHTTPServer((req, res) => {
|
server = await startHTTPServer((req, res) => {
|
||||||
setTimeout(() => res.end('OK'), 1000);
|
setTimeout(() => {
|
||||||
|
res.end('OK');
|
||||||
|
}, 2000);
|
||||||
}, {
|
}, {
|
||||||
useHTTP2: true
|
useHTTP2: true
|
||||||
});
|
});
|
||||||
|
|
||||||
server2 = await startHTTPServer((req, res) => {
|
server2 = await startHTTPServer((req, res) => {
|
||||||
setTimeout(() => res.end('OK'), 1000);
|
setTimeout(() => {
|
||||||
|
res.end('OK');
|
||||||
|
}, 2000);
|
||||||
}, {
|
}, {
|
||||||
useHTTP2: true,
|
useHTTP2: true,
|
||||||
port: SERVER_PORT2
|
port: SERVER_PORT2
|
||||||
@@ -2559,7 +2563,9 @@ describe('supports http with nodejs', function () {
|
|||||||
|
|
||||||
it("should use different sessions for requests with different http2Options set", async() => {
|
it("should use different sessions for requests with different http2Options set", async() => {
|
||||||
server = await startHTTPServer((req, res) => {
|
server = await startHTTPServer((req, res) => {
|
||||||
setTimeout(() => res.end('OK'), 1000);
|
setTimeout(() => {
|
||||||
|
res.end('OK')
|
||||||
|
}, 1000);
|
||||||
}, {
|
}, {
|
||||||
useHTTP2: true
|
useHTTP2: true
|
||||||
});
|
});
|
||||||
@@ -2639,6 +2645,8 @@ describe('supports http with nodejs', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const data1 = await getStream(response1.data);
|
||||||
|
|
||||||
await setTimeoutAsync(5000);
|
await setTimeoutAsync(5000);
|
||||||
|
|
||||||
const response2 = await http2Axios.get(LOCAL_SERVER_URL, {
|
const response2 = await http2Axios.get(LOCAL_SERVER_URL, {
|
||||||
@@ -2648,15 +2656,12 @@ describe('supports http with nodejs', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const data2 = await getStream(response2.data);
|
||||||
|
|
||||||
assert.notStrictEqual(response1.data.session, response2.data.session);
|
assert.notStrictEqual(response1.data.session, response2.data.session);
|
||||||
|
|
||||||
assert.deepStrictEqual(
|
assert.strictEqual(data1, 'OK');
|
||||||
await Promise.all([
|
assert.strictEqual(data2, 'OK');
|
||||||
getStream(response1.data),
|
|
||||||
getStream(response2.data)
|
|
||||||
]),
|
|
||||||
['OK', 'OK']
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user