Skip to content

http: speed up rendering of object headers - #66679

Closed
anonrig wants to merge 1 commit into
nodejs:mainfrom
anonrig:http-server-perf
Closed

anonrig wants to merge 1 commit into
nodejs:mainfrom
anonrig:http-server-perf

Conversation

@anonrig

@anonrig anonrig commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

writeHead(status, headers) walked a plain headers object with for...in plus ObjectHasOwn(). Objects with many headers end up in dictionary mode, where for...in re-checks every key with extra hash lookups. This switches to ObjectKeys(), which yields the same own enumerable keys in the same order.

matchHeader() also returns early now unless a name has the length and case-folded first letter of a field that affects framing (Connection, Content-Length, Transfer-Encoding, Date, Expect, Trailer, Keep-Alive). That skips the toLowerCase() call for most other headers. Names that reach it are validated tokens, so it can't miss a match.

The new test covers key order and enumerability, a 600-header object, every framing field in odd casing, look-alike names, the setHeader() path, and a real round trip.

Benchmarks

writeHead() on its own, through the public API (fresh process per sample, 20 interleaved samples per binary, Welch's t-test):

headers change p
2 string headers +5.70% 0.14
10 string headers +11.30% 0.032
10 headers, array values +9.71% 0.098
50 headers +17.29% 0.002
50 lines, 5 arrays +6.90% 0.16
600 headers +24.52% 5.5e-5
600 lines, 6 arrays +10.73% 0.003

End to end it's small. Server CPU per request on simple.js-style responses drops 0.6–2.7%, which isn't significant. The full benchmark/http runs below (simple.js, headers.js, incoming_headers.js, end-vs-write-end.js; 10 runs each, twice) stay within noise. My machine was shared and loaded during them, so the intervals are wide (about ±15–20%).

simple.js type=buffer len=102400 chunks=1 c=50 chunkedEnc=1 looked 5–16% lower in the loaded runs, so I checked it on its own. With 30 rounds in rotating order, plus a second baseline arm as an A/A control, it's +2.93% by median (p=0.83) and −2.50% by mean (p=0.49). Two identical binaries differ by +1.14% (p=0.95), so that was noise. The JS work per request on that path went down (1.69 → 1.48 µs).

compare.js, run 1 (10 runs)
                                                                                                   confidence   improvement   accuracy (*)    (**)   (***)
http/end-vs-write-end.js duration=5 method='write' c=100 len=65536 type='asc'                    -2.32 %   ±20.47%  ±28.05%  ±38.21%
http/end-vs-write-end.js duration=5 method='end' c=100 len=65536 type='asc'                      -5.31 %   ±20.06%  ±27.62%  ±37.92%
http/end-vs-write-end.js duration=5 method='write' c=100 len=131072 type='asc'                   -5.96 %   ±17.48%  ±24.62%  ±35.08%
http/end-vs-write-end.js duration=5 method='end' c=100 len=131072 type='asc'                     -8.68 %   ±17.25%  ±24.57%  ±35.62%
http/end-vs-write-end.js duration=5 method='write' c=100 len=262144 type='asc'                   -9.46 %   ±16.93%  ±23.79%  ±33.78%
http/end-vs-write-end.js duration=5 method='end' c=100 len=262144 type='asc'                     -6.30 %   ±19.08%  ±26.69%  ±37.59%
http/end-vs-write-end.js duration=5 method='write' c=100 len=1048576 type='asc'                 -15.38 %   ±18.73%  ±25.88%  ±35.74%
http/end-vs-write-end.js duration=5 method='end' c=100 len=1048576 type='asc'                    -5.41 %   ±16.75%  ±23.34%  ±32.65%
http/end-vs-write-end.js duration=5 method='write' c=100 len=65536 type='utf'                    -3.35 %   ±18.69%  ±25.84%  ±35.71%
http/end-vs-write-end.js duration=5 method='end' c=100 len=65536 type='utf'                      -0.58 %   ±16.01%  ±22.07%  ±30.37%
http/end-vs-write-end.js duration=5 method='write' c=100 len=131072 type='utf'                   +4.06 %   ±7.20%  ±10.10%  ±14.30%
http/end-vs-write-end.js duration=5 method='end' c=100 len=131072 type='utf'                     +6.47 %   ±14.72%  ±20.64%  ±29.16%
http/end-vs-write-end.js duration=5 method='write' c=100 len=262144 type='utf'                   +5.50 %   ±14.96%  ±20.63%  ±28.41%
http/end-vs-write-end.js duration=5 method='end' c=100 len=262144 type='utf'                     +8.27 %   ±16.82%  ±23.85%  ±34.32%
http/end-vs-write-end.js duration=5 method='write' c=100 len=1048576 type='utf'                  +9.58 %   ±15.21%  ±21.74%  ±31.71%
http/end-vs-write-end.js duration=5 method='end' c=100 len=1048576 type='utf'                    +3.12 %   ±15.18%  ±20.86%  ±28.57%
http/end-vs-write-end.js duration=5 method='write' c=100 len=65536 type='buf'                    -7.91 %   ±12.43%  ±17.05%  ±23.28%
http/end-vs-write-end.js duration=5 method='end' c=100 len=65536 type='buf'                      +5.51 %   ±15.04%  ±20.73%  ±28.51%
http/end-vs-write-end.js duration=5 method='write' c=100 len=131072 type='buf'                   +0.72 %   ±8.22%  ±11.26%  ±15.35%
http/end-vs-write-end.js duration=5 method='end' c=100 len=131072 type='buf'                     +3.92 %   ±11.07%  ±15.45%  ±21.69%
http/end-vs-write-end.js duration=5 method='write' c=100 len=262144 type='buf'                   +2.56 %   ±7.49%  ±10.37%  ±14.36%
http/end-vs-write-end.js duration=5 method='end' c=100 len=262144 type='buf'                     +0.64 %   ±6.79%  ±9.33%  ±12.78%
http/end-vs-write-end.js duration=5 method='write' c=100 len=1048576 type='buf'                  -1.88 %   ±5.08%  ±7.27%  ±10.62%
http/end-vs-write-end.js duration=5 method='end' c=100 len=1048576 type='buf'                    -2.01 %   ±6.14%  ±8.72%  ±12.60%
http/headers.js group='fewHeaders' duration=5 len=1 n=10                                        +22.28 %   ±36.81%  ±50.43%  ±68.71%
http/headers.js group='fewHeaders' duration=5 len=5 n=10                                         +1.06 %   ±34.62%  ±47.44%  ±64.65%
http/headers.js group='mediumHeaders' duration=5 len=1 n=50                                      +1.90 %   ±35.56%  ±48.87%  ±66.91%
http/headers.js group='mediumHeaders' duration=5 len=10 n=50                                     -2.89 %   ±34.41%  ±47.15%  ±64.26%
http/headers.js group='manyHeaders' duration=5 len=1 n=600                                       -1.03 %   ±25.39%  ±34.81%  ±47.47%
http/headers.js group='manyHeaders' duration=5 len=100 n=600                                     +3.77 %   ±30.23%  ±41.45%  ±56.53%
http/incoming_headers.js duration=5 read=0 w=0 headers=20 connections=50                         +6.43 %   ±34.34%  ±47.12%  ±64.35%
http/incoming_headers.js duration=5 read=1 w=0 headers=20 connections=50                         +9.90 %   ±30.79%  ±42.19%  ±57.48%
http/incoming_headers.js duration=5 read=0 w=6 headers=20 connections=50                        +33.60 %   ±40.80%  ±56.00%  ±76.49%
http/incoming_headers.js duration=5 read=1 w=6 headers=20 connections=50                        +16.84 %   ±34.36%  ±47.09%  ±64.18%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=4 type='bytes'                         -10.32 %   ±30.85%  ±42.33%  ±57.80%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=4 type='bytes'                          -7.44 %   ±31.10%  ±42.66%  ±58.21%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=4 type='bytes'                         -8.56 %   ±29.33%  ±40.29%  ±55.09%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=4 type='bytes'                         -9.49 %   ±29.68%  ±40.72%  ±55.57%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=4 type='bytes'                          -7.01 %   ±32.70%  ±44.94%  ±61.53%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=4 type='bytes'                          +2.21 %   ±30.52%  ±41.83%  ±57.01%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=4 type='bytes'                         +4.58 %   ±21.16%  ±29.03%  ±39.64%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=4 type='bytes'                         +4.03 %   ±13.39%  ±18.65%  ±26.07%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=1024 type='bytes'                       +4.73 %   ±16.66%  ±23.02%  ±31.81%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=1024 type='bytes'                       -2.28 %   ±13.81%  ±18.92%  ±25.79%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=1024 type='bytes'                      -5.02 %   ±13.57%  ±18.82%  ±26.15%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=1024 type='bytes'                      -9.09 %   ±17.60%  ±24.25%  ±33.35%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=1024 type='bytes'                       -7.96 %   ±19.38%  ±26.79%  ±37.03%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=1024 type='bytes'                       -1.17 %   ±15.59%  ±21.38%  ±29.15%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=1024 type='bytes'                      +1.80 %   ±16.24%  ±22.33%  ±30.62%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=1024 type='bytes'                      +1.96 %   ±14.47%  ±19.94%  ±27.42%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=102400 type='bytes'                     +1.59 %   ±19.51%  ±26.83%  ±36.76%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=102400 type='bytes'                     +4.41 %   ±23.75%  ±32.76%  ±45.13%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=102400 type='bytes'                    +6.36 %   ±18.81%  ±26.37%  ±37.25%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=102400 type='bytes'                   +13.08 %   ±27.17%  ±38.00%  ±53.49%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=102400 type='bytes'                     +8.52 %   ±21.47%  ±29.69%  ±41.05%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=102400 type='bytes'                     +6.89 %   ±23.14%  ±32.35%  ±45.53%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=102400 type='bytes'                    +6.30 %   ±19.72%  ±27.34%  ±37.97%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=102400 type='bytes'                    -0.29 %   ±16.42%  ±22.52%  ±30.71%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=4 type='buffer'                         +4.74 %   ±23.33%  ±32.18%  ±44.32%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=4 type='buffer'                         +5.71 %   ±22.69%  ±31.11%  ±42.43%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=4 type='buffer'                        +4.05 %   ±21.25%  ±29.12%  ±39.68%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=4 type='buffer'                        -0.09 %   ±20.75%  ±28.46%  ±38.85%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=4 type='buffer'                         +2.66 %   ±22.02%  ±30.17%  ±41.10%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=4 type='buffer'                         +3.04 %   ±17.27%  ±23.67%  ±32.25%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=4 type='buffer'                        +6.34 %   ±17.74%  ±24.75%  ±34.72%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=4 type='buffer'                        +8.62 %   ±25.97%  ±35.67%  ±48.80%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=1024 type='buffer'                     +12.10 %   ±27.42%  ±37.87%  ±52.24%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=1024 type='buffer'                      +2.53 %   ±26.52%  ±36.34%  ±49.51%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=1024 type='buffer'                     +1.75 %   ±23.60%  ±32.35%  ±44.09%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=1024 type='buffer'                     +1.16 %   ±17.13%  ±23.50%  ±32.08%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=1024 type='buffer'                      -1.56 %   ±15.33%  ±21.05%  ±28.77%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=1024 type='buffer'                      +2.18 %   ±16.89%  ±23.15%  ±31.58%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=1024 type='buffer'                     +6.09 %   ±16.92%  ±23.24%  ±31.77%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=1024 type='buffer'                     -1.89 %   ±20.02%  ±27.46%  ±37.48%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=102400 type='buffer'                   -11.68 %   ±19.67%  ±27.14%  ±37.38%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=102400 type='buffer'                    +2.85 %   ±14.95%  ±20.52%  ±28.02%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=102400 type='buffer'                   -4.56 %   ±16.21%  ±22.24%  ±30.34%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=102400 type='buffer'                   -6.55 %   ±13.17%  ±18.25%  ±25.34%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=102400 type='buffer'                    -8.07 %   ±26.06%  ±35.83%  ±49.08%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=102400 type='buffer'                    +5.49 %   ±22.78%  ±31.20%  ±42.51%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=102400 type='buffer'                   +3.34 %   ±23.09%  ±31.70%  ±43.32%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=102400 type='buffer'                   +3.49 %   ±15.69%  ±21.50%  ±29.31%
compare.js, run 2 (10 runs, independent)
                                                                                                   confidence   improvement   accuracy (*)    (**)   (***)
http/end-vs-write-end.js duration=5 method='write' c=100 len=65536 type='asc'                   +14.10 %   ±25.02%  ±34.39%  ±47.07%
http/end-vs-write-end.js duration=5 method='end' c=100 len=65536 type='asc'                     +18.19 %   ±22.21%  ±30.81%  ±42.83%
http/end-vs-write-end.js duration=5 method='write' c=100 len=131072 type='asc'                  +13.93 %   ±23.29%  ±32.08%  ±44.08%
http/end-vs-write-end.js duration=5 method='end' c=100 len=131072 type='asc'                    +16.65 %   ±26.62%  ±36.67%  ±50.41%
http/end-vs-write-end.js duration=5 method='write' c=100 len=262144 type='asc'                  +14.73 %   ±27.41%  ±37.80%  ±52.04%
http/end-vs-write-end.js duration=5 method='end' c=100 len=262144 type='asc'                    +14.79 %   ±25.19%  ±34.69%  ±47.64%
http/end-vs-write-end.js duration=5 method='write' c=100 len=1048576 type='asc'                 +11.51 %   ±24.89%  ±34.11%  ±46.49%
http/end-vs-write-end.js duration=5 method='end' c=100 len=1048576 type='asc'                    +5.58 %   ±24.11%  ±33.11%  ±45.29%
http/end-vs-write-end.js duration=5 method='write' c=100 len=65536 type='utf'                    +3.06 %   ±32.32%  ±44.28%  ±60.35%
http/end-vs-write-end.js duration=5 method='end' c=100 len=65536 type='utf'                      +4.98 %   ±29.04%  ±39.82%  ±54.32%
http/end-vs-write-end.js duration=5 method='write' c=100 len=131072 type='utf'                   +5.33 %   ±30.11%  ±41.27%  ±56.28%
http/end-vs-write-end.js duration=5 method='end' c=100 len=131072 type='utf'                     +4.33 %   ±26.41%  ±36.20%  ±49.34%
http/end-vs-write-end.js duration=5 method='write' c=100 len=262144 type='utf'                   +5.23 %   ±25.53%  ±35.02%  ±47.79%
http/end-vs-write-end.js duration=5 method='end' c=100 len=262144 type='utf'                    +10.34 %   ±22.57%  ±31.11%  ±42.77%
http/end-vs-write-end.js duration=5 method='write' c=100 len=1048576 type='utf'                 +10.18 %   ±24.62%  ±33.73%  ±45.96%
http/end-vs-write-end.js duration=5 method='end' c=100 len=1048576 type='utf'                    +9.44 %   ±28.19%  ±38.83%  ±53.36%
http/end-vs-write-end.js duration=5 method='write' c=100 len=65536 type='buf'                    +1.85 %   ±18.70%  ±25.81%  ±35.58%
http/end-vs-write-end.js duration=5 method='end' c=100 len=65536 type='buf'                      +6.22 %   ±15.90%  ±22.10%  ±30.78%
http/end-vs-write-end.js duration=5 method='write' c=100 len=131072 type='buf'                   -1.44 %   ±9.21%  ±12.63%  ±17.21%
http/end-vs-write-end.js duration=5 method='end' c=100 len=131072 type='buf'                     -4.76 %   ±11.32%  ±15.97%  ±22.82%
http/end-vs-write-end.js duration=5 method='write' c=100 len=262144 type='buf'                   -5.42 %   ±10.13%  ±14.38%  ±20.74%
http/end-vs-write-end.js duration=5 method='end' c=100 len=262144 type='buf'                     -4.50 %   ±8.33%  ±11.87%  ±17.23%
http/end-vs-write-end.js duration=5 method='write' c=100 len=1048576 type='buf'                  -1.83 %   ±6.38%  ±9.02%  ±12.94%
http/end-vs-write-end.js duration=5 method='end' c=100 len=1048576 type='buf'                    -4.28 %   ±7.61%  ±10.73%  ±15.30%
http/headers.js group='fewHeaders' duration=5 len=1 n=10                                         +3.05 %   ±25.93%  ±35.53%  ±48.43%
http/headers.js group='fewHeaders' duration=5 len=5 n=10                                         +1.55 %   ±24.56%  ±33.65%  ±45.85%
http/headers.js group='mediumHeaders' duration=5 len=1 n=50                                     +11.80 %   ±28.59%  ±39.25%  ±53.66%
http/headers.js group='mediumHeaders' duration=5 len=10 n=50                                     +8.15 %   ±25.89%  ±35.51%  ±48.44%
http/headers.js group='manyHeaders' duration=5 len=1 n=600                                      +18.80 %   ±21.38%  ±29.29%  ±39.90%
http/headers.js group='manyHeaders' duration=5 len=100 n=600                                    +11.68 %   ±21.90%  ±30.00%  ±40.88%
http/incoming_headers.js duration=5 read=0 w=0 headers=20 connections=50                         +0.35 %   ±0.95%  ±1.31%  ±1.80%
http/incoming_headers.js duration=5 read=1 w=0 headers=20 connections=50                         +0.60 %   ±1.20%  ±1.67%  ±2.33%
http/incoming_headers.js duration=5 read=0 w=6 headers=20 connections=50          **             +0.82 %   ±0.51%  ±0.70%  ±0.95%
http/incoming_headers.js duration=5 read=1 w=6 headers=20 connections=50                         +1.09 %   ±1.10%  ±1.53%  ±2.15%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=4 type='bytes'                         -13.31 %   ±21.79%  ±31.00%  ±44.89%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=4 type='bytes'                         -13.69 %   ±22.65%  ±32.28%  ±46.84%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=4 type='bytes'                        -10.99 %   ±17.67%  ±25.18%  ±36.54%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=4 type='bytes'                         -9.71 %   ±18.03%  ±25.68%  ±37.25%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=4 type='bytes'                         -10.39 %   ±20.60%  ±29.24%  ±42.20%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=4 type='bytes'                         -11.64 %   ±19.97%  ±28.21%  ±40.39%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=4 type='bytes'                        -13.76 %   ±21.40%  ±30.55%  ±44.47%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=4 type='bytes'                        -12.27 %   ±19.11%  ±27.20%  ±39.38%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=1024 type='bytes'                       -9.40 %   ±17.09%  ±24.14%  ±34.56%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=1024 type='bytes'                       -7.29 %   ±18.18%  ±25.65%  ±36.65%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=1024 type='bytes'                      -4.12 %   ±10.87%  ±15.31%  ±21.82%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=1024 type='bytes'                      -6.14 %   ±14.99%  ±21.14%  ±30.15%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=1024 type='bytes'                       -5.58 %   ±17.92%  ±24.97%  ±34.96%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=1024 type='bytes'                       -5.39 %   ±17.71%  ±24.91%  ±35.42%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=1024 type='bytes'                      -8.13 %   ±16.87%  ±23.97%  ±34.61%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=1024 type='bytes'                      -5.77 %   ±18.20%  ±25.46%  ±35.84%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=102400 type='bytes'                     -5.93 %   ±15.45%  ±21.40%  ±29.66%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=102400 type='bytes'                     -6.46 %   ±12.19%  ±16.89%  ±23.41%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=102400 type='bytes'                    -6.46 %   ±17.75%  ±24.61%  ±34.16%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=102400 type='bytes'                    -3.41 %   ±15.70%  ±21.66%  ±29.84%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=102400 type='bytes'                     -5.11 %   ±21.93%  ±30.28%  ±41.74%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=102400 type='bytes'                     -4.64 %   ±23.12%  ±31.82%  ±43.64%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=102400 type='bytes'                    -2.28 %   ±20.42%  ±27.99%  ±38.16%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=102400 type='bytes'                    +0.06 %   ±20.09%  ±27.57%  ±37.66%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=4 type='buffer'                         -1.71 %   ±19.95%  ±27.35%  ±37.31%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=4 type='buffer'                         -1.82 %   ±20.16%  ±27.85%  ±38.43%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=4 type='buffer'                        -2.25 %   ±22.45%  ±30.79%  ±42.02%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=4 type='buffer'                        -1.97 %   ±22.19%  ±30.78%  ±42.75%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=4 type='buffer'                         -3.39 %   ±22.03%  ±30.60%  ±42.60%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=4 type='buffer'                         -7.68 %   ±21.25%  ±29.97%  ±42.78%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=4 type='buffer'                        -5.36 %   ±20.27%  ±28.34%  ±39.91%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=4 type='buffer'                        -4.38 %   ±22.66%  ±31.43%  ±43.68%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=1024 type='buffer'                      -5.34 %   ±24.64%  ±34.36%  ±48.14%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=1024 type='buffer'                      -2.94 %   ±23.87%  ±33.06%  ±45.81%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=1024 type='buffer'                     -3.49 %   ±23.88%  ±33.05%  ±45.74%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=1024 type='buffer'                     -3.27 %   ±19.91%  ±27.72%  ±38.75%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=1024 type='buffer'                      +4.95 %   ±21.70%  ±29.75%  ±40.59%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=1024 type='buffer'                      +4.49 %   ±20.97%  ±28.76%  ±39.25%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=1024 type='buffer'                     +2.93 %   ±22.37%  ±30.66%  ±41.78%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=1024 type='buffer'                     +8.15 %   ±23.54%  ±32.25%  ±43.94%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=1 len=102400 type='buffer'                   -12.40 %   ±15.52%  ±21.26%  ±28.97%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=1 len=102400 type='buffer'                    -4.21 %   ±17.23%  ±23.61%  ±32.19%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=1 len=102400 type='buffer'                   -0.61 %   ±5.20%  ±7.19%  ±9.93%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=1 len=102400 type='buffer'                   +0.68 %   ±2.74%  ±3.78%  ±5.20%
http/simple.js duration=5 chunkedEnc=1 c=50 chunks=4 len=102400 type='buffer'                    -4.62 %   ±16.62%  ±22.78%  ±31.06%
http/simple.js duration=5 chunkedEnc=0 c=50 chunks=4 len=102400 type='buffer'                    -2.12 %   ±15.62%  ±21.85%  ±30.76%
http/simple.js duration=5 chunkedEnc=1 c=500 chunks=4 len=102400 type='buffer'                   -5.39 %   ±23.33%  ±32.43%  ±45.22%
http/simple.js duration=5 chunkedEnc=0 c=500 chunks=4 len=102400 type='buffer'                   +3.97 %   ±14.33%  ±19.65%  ±26.80%

Chrome fetching three responses from the PR build: plain, 600 headers with upper-case CONTENT-LENGTH, and look-alike names; all framed correctly

Iterate the headers object passed to writeHead() with ObjectKeys()
instead of for...in plus ObjectHasOwn(). Both yield the same own
enumerable string keys in the same order, but an object with many
headers is in dictionary mode, where for...in re-checks every key with
extra hash lookups.

matchHeader() now returns early unless a header name has the length
and case-folded first letter of a field that affects message framing,
which skips the toLowerCase() call for most other names. Names that
reach it are validated tokens, so this cannot miss a match.

Assisted-by: Grok
Signed-off-by: Yagiz Nizipli <yagiz@nizipli.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-bot nodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Oct 12, 2026
@anonrig anonrig closed this Oct 12, 2026
@codecov

codecov Bot commented Oct 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.46%. Comparing base (3b639be) to head (cad77c0).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #66679      +/-   ##
==========================================
- Coverage   92.82%   90.46%   -2.36%     
==========================================
  Files         422      791     +369     
  Lines      194189   277113   +82924     
  Branches    30027    53282   +23255     
==========================================
+ Hits       180249   250701   +70452     
- Misses      13614    16813    +3199     
- Partials      326     9599    +9273     
Files with missing lines Coverage Δ
lib/_http_outgoing.js 98.01% <100.00%> (+0.03%) ⬆️

... and 501 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants