This commit is contained in:
Ciro Spaciari
2025-05-16 11:58:42 -07:00
parent f3502a0319
commit 40ce107f32
3 changed files with 81 additions and 6 deletions

View File

@@ -3466,6 +3466,22 @@ pub const H2FrameParser = struct {
const stream_id_arg = args_list.ptr[0];
bun.debugAssert(stream_id_arg.isNumber());
this.lastStreamID = stream_id_arg.to(u32);
// to set the next stream id we need to decrement because we only keep the last stream id
if (this.isServer) {
if (this.lastStreamID % 2 == 0) {
this.lastStreamID -= 2;
} else {
this.lastStreamID -= 1;
}
} else {
if (this.lastStreamID % 2 == 0) {
this.lastStreamID -= 1;
} else if (this.lastStreamID == 1) {
this.lastStreamID = 0;
} else {
this.lastStreamID -= 2;
}
}
return .undefined;
}
@@ -3477,6 +3493,9 @@ pub const H2FrameParser = struct {
JSC.markBinding(@src());
const id = this.getNextStreamID();
if (id > MAX_STREAM_ID) {
return JSC.JSValue.jsNumber(-1);
}
_ = this.handleReceivedStreamID(id) orelse {
return JSC.JSValue.jsNumber(-1);
};

View File

@@ -130,6 +130,10 @@ function emitErrorNT(self: any, error: any, destroy: boolean) {
self.emit("error", error);
}
}
function emitOutofStreamErrorNT(self: any) {
self.destroy($ERR_HTTP2_OUT_OF_STREAMS());
}
function cache() {
const d = new Date();
utcCache = d.toUTCString();
@@ -1843,7 +1847,7 @@ class Http2Stream extends Duplex {
// will destroy if it has been closed and there are no other open or
// pending streams. Delay with setImmediate so we don't do it on the
// nghttp2 stack.
if (session) {
if (session && typeof this.#id === "number") {
setImmediate(rstNextTick.bind(session, this.#id, rstCode));
}
callback(err);
@@ -3512,13 +3516,13 @@ class ClientHttp2Session extends Http2Session {
}
}
let stream_id: number = this.#parser.getNextStream();
if (stream_id < 0) {
const req = new ClientHttp2Stream(undefined, this, headers);
process.nextTick(emitOutofStreamErrorNT, req);
return req;
}
const req = new ClientHttp2Stream(stream_id, this, headers);
req.authority = authority;
if (stream_id < 0) {
const error = $ERR_HTTP2_OUT_OF_STREAMS();
this.emit("error", error);
return null;
}
req[kHeadRequest] = method === HTTP2_METHOD_HEAD;
if (typeof options === "undefined") {
this.#parser.request(stream_id, req, headers, sensitiveNames);

View File

@@ -0,0 +1,52 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const http2 = require('http2');
const Countdown = require('../common/countdown');
const server = http2.createServer();
server.on('stream', (stream) => {
stream.respond();
stream.end('ok');
});
server.listen(0, common.mustCall(() => {
const client = http2.connect(`http://127.0.0.1:${server.address().port}`);
const nextID = 2 ** 31 - 1;
client.on('connect', () => {
client.setNextStreamID(nextID);
assert.strictEqual(client.state.nextStreamID, nextID);
const countdown = new Countdown(2, () => {
server.close();
client.close();
});
{
// This one will be ok
const req = client.request();
assert.strictEqual(req.id, nextID);
req.on('error', common.mustNotCall());
req.resume();
req.on('end', () => countdown.dec());
}
{
// This one will error because there are no more stream IDs available
const req = client.request();
req.on('error', common.expectsError({
code: 'ERR_HTTP2_OUT_OF_STREAMS',
name: 'Error',
message:
'No stream ID is available because maximum stream ID has been reached'
}));
req.on('error', () => countdown.dec());
}
});
}));