Skip to content

Commit

Permalink
feat: respect --max-http-header-size Node.js flag (nodejs#2234)
Browse files Browse the repository at this point in the history
* feat: respect  Node.js flag

* add test

* cleaner test

* fix test

* update docs

* revert empty space

* remove already default value

* add

* don't use `globalThis.fetch` to pass test in Node.js 16

* add test under `test/fetch`

* fix lint

* import from `undici-fetch`

* use correct paths, use `request` for pure undici test

* fix Node.js 14 test
  • Loading branch information
balazsorban44 authored and crysmags committed Feb 27, 2024
1 parent 1fa9c6b commit d2d2796
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/api/Client.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Returns: `Client`
* **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.
* **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds.
* **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `1e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 1 second.
* **maxHeaderSize** `number | null` (optional) - Default: `16384` - The maximum length of request headers in bytes. Defaults to 16KiB.
* **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
* **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
* **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.
* **connect** `ConnectOptions | Function | null` (optional) - Default: `null`.
Expand Down
3 changes: 2 additions & 1 deletion lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

const assert = require('assert')
const net = require('net')
const http = require('http')
const { pipeline } = require('stream')
const util = require('./core/util')
const timers = require('./timers')
Expand Down Expand Up @@ -270,7 +271,7 @@ class Client extends DispatcherBase {
this[kConnector] = connect
this[kSocket] = null
this[kPipelining] = pipelining != null ? pipelining : 1
this[kMaxHeadersSize] = maxHeaderSize || 16384
this[kMaxHeadersSize] = maxHeaderSize || http.maxHeaderSize
this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout
this[kKeepAliveMaxTimeout] = keepAliveMaxTimeout == null ? 600e3 : keepAliveMaxTimeout
this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 1e3 : keepAliveTimeoutThreshold
Expand Down
23 changes: 23 additions & 0 deletions test/client-node-max-header-size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict'

const { execSync } = require('node:child_process')
const { test } = require('tap')

const command = 'node -e "require(\'.\').request(\'https://httpbin.org/get\')"'

test("respect Node.js' --max-http-header-size", async (t) => {
t.throws(
// TODO: Drop the `--unhandled-rejections=throw` once we drop Node.js 14
() => execSync(`${command} --max-http-header-size=1 --unhandled-rejections=throw`),
/UND_ERR_HEADERS_OVERFLOW/,
'max-http-header-size=1 should throw'
)

t.doesNotThrow(
() => execSync(command),
/UND_ERR_HEADERS_OVERFLOW/,
'default max-http-header-size should not throw'
)

t.end()
})
23 changes: 23 additions & 0 deletions test/fetch/client-node-max-header-size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict'

const { execSync } = require('node:child_process')
const { test } = require('tap')

const command = 'node -e "require(\'./undici-fetch.js\').fetch(\'https://httpbin.org/get\')"'

test("respect Node.js' --max-http-header-size", async (t) => {
t.throws(
// TODO: Drop the `--unhandled-rejections=throw` once we drop Node.js 14
() => execSync(`${command} --max-http-header-size=1 --unhandled-rejections=throw`),
/UND_ERR_HEADERS_OVERFLOW/,
'max-http-header-size=1 should throw'
)

t.doesNotThrow(
() => execSync(command),
/UND_ERR_HEADERS_OVERFLOW/,
'default max-http-header-size should not throw'
)

t.end()
})
2 changes: 1 addition & 1 deletion types/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export declare namespace Client {
export interface Options {
/** TODO */
interceptors?: OptionsInterceptors;
/** The maximum length of request headers in bytes. Default: `16384` (16KiB). */
/** The maximum length of request headers in bytes. Default: Node.js' `--max-http-header-size` or `16384` (16KiB). */
maxHeaderSize?: number;
/** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */
headersTimeout?: number;
Expand Down

0 comments on commit d2d2796

Please sign in to comment.