Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/dispatcher/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ class Client extends DispatcherBase {
throw new InvalidArgumentError('h2Options.settings.initialWindowSize must be a positive integer, greater than 0')
}

if (h2Options.maxConcurrentStreams != null && (!Number.isInteger(h2Options.connectionWindowSize) || h2Options.maxConcurrentStreams < 1)) {
if (h2Options.maxConcurrentStreams != null && (!Number.isInteger(h2Options.maxConcurrentStreams) || h2Options.maxConcurrentStreams < 1)) {
throw new InvalidArgumentError('h2Options.maxConcurrentStreams must be a positive integer, greater than 0')
}

Expand Down Expand Up @@ -331,7 +331,7 @@ class Client extends DispatcherBase {
// especially on high-latency networks. This matches common production HTTP/2 servers.
// - connectionWindowSize: 524288 (512KB) vs Node.js default (none set)
// Provides better flow control for the entire connection across multiple streams.
initialWindowSize: h2Options?.initialWindowSize ?? initialWindowSize ?? 262144
initialWindowSize: h2Options?.settings?.initialWindowSize ?? initialWindowSize ?? 262144

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a behavior change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, yes — and intentionally so. The existing code validated maxConcurrentStreams against connectionWindowSize (a copy-paste error from the block above), so the check was effectively dead for any caller that didn't also set connectionWindowSize. The same bug let invalid values through undetected.

This replaces the cross-wired check with one that tests maxConcurrentStreams against itself, which is what the error message has always said it does. Happy to add a note in the PR description if the intent isn't obvious from the commit message alone.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the change is valid; this is an oversight on my end, it is included within the new h2 namespacing.

It should be all right to do the move as we are supporting two ways of setting this up (legacy top-level, namepaced)

}
}

Expand Down
44 changes: 44 additions & 0 deletions test/http2-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict'

const { tspl } = require('@matteo.collina/tspl')
const { test } = require('node:test')
const { Client } = require('..')
const { kHTTP2Options } = require('../lib/core/symbols')

test('h2Options.maxConcurrentStreams is validated on its own value', async (t) => {
t = tspl(t, { plan: 4 })

const client = new Client('https://localhost', {
h2Options: { maxConcurrentStreams: 10 }
})
t.strictEqual(client[kHTTP2Options].maxConcurrentStreams, 10)
await client.close()

const withWindow = new Client('https://localhost', {
h2Options: { maxConcurrentStreams: 10, connectionWindowSize: 65535 }
})
t.strictEqual(withWindow[kHTTP2Options].maxConcurrentStreams, 10)
await withWindow.close()

t.throws(() => new Client('https://localhost', {
h2Options: { maxConcurrentStreams: 'not-a-number', connectionWindowSize: 65535 }
}), { code: 'UND_ERR_INVALID_ARG' })

t.throws(() => new Client('https://localhost', {
h2Options: { maxConcurrentStreams: 0 }
}), { code: 'UND_ERR_INVALID_ARG' })
})

test('h2Options.settings.initialWindowSize reaches the session options', async (t) => {
t = tspl(t, { plan: 2 })

const client = new Client('https://localhost', {
h2Options: { settings: { initialWindowSize: 131072 } }
})
t.strictEqual(client[kHTTP2Options].sessionOptions.initialWindowSize, 131072)
await client.close()

const defaulted = new Client('https://localhost', { h2Options: {} })
t.strictEqual(defaulted[kHTTP2Options].sessionOptions.initialWindowSize, 262144)
await defaulted.close()
})
Loading