From 29e23b0a463720c0c3a4f91ad308679f65608f10 Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Tue, 12 May 2026 06:41:35 +0200 Subject: [PATCH] fix: Correctly handle tab after connection-close header value. Signed-off-by: Paolo Insogna --- src/llhttp/http.ts | 2 +- test/request/connection.md | 78 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/llhttp/http.ts b/src/llhttp/http.ts index dbc9c607..09c909f8 100644 --- a/src/llhttp/http.ts +++ b/src/llhttp/http.ts @@ -815,7 +815,7 @@ export class HTTP { n('header_value_connection_ws') .match(',', this.setHeaderFlags('header_value_connection')) - .match(' ', n('header_value_connection_ws')) + .match([ ' ', '\t' ], n('header_value_connection_ws')) .peek([ '\r', '\n' ], n('header_value_otherwise')) .otherwise(this.resetHeaderState('header_value_connection_token')); diff --git a/test/request/connection.md b/test/request/connection.md index d1e6b905..41b96624 100644 --- a/test/request/connection.md +++ b/test/request/connection.md @@ -261,6 +261,34 @@ off=40 headers complete method=4 v=1/1 flags=2 content_length=0 off=40 message complete ``` +### Setting flag on `close` followed by tab + + +```http +PUT /url HTTP/1.1 +Connection: close\t + + +``` + +```log +off=0 message begin +off=0 len=3 span[method]="PUT" +off=3 method complete +off=4 len=4 span[url]="/url" +off=9 url complete +off=9 len=4 span[protocol]="HTTP" +off=13 protocol complete +off=14 len=3 span[version]="1.1" +off=17 version complete +off=19 len=10 span[header_field]="Connection" +off=30 header_field complete +off=31 len=6 span[header_value]="close\t" +off=39 header_value complete +off=41 headers complete method=4 v=1/1 flags=2 content_length=0 +off=41 message complete +``` + ### CRLF between requests, explicit `close` `close` means closed connection @@ -311,6 +339,56 @@ off=133 message complete off=138 error code=5 reason="Data after `Connection: close`" ``` +### CRLF between requests, explicit `close` followed by tab + +`close` means closed connection even when followed by optional whitespace. + + +```http +POST / HTTP/1.1 +Host: www.example.com +Content-Type: application/x-www-form-urlencoded +Content-Length: 4 +Connection: close\t + +q=42 + +GET / HTTP/1.1 +``` +_Note the trailing CRLF above_ + +```log +off=0 message begin +off=0 len=4 span[method]="POST" +off=4 method complete +off=5 len=1 span[url]="/" +off=7 url complete +off=7 len=4 span[protocol]="HTTP" +off=11 protocol complete +off=12 len=3 span[version]="1.1" +off=15 version complete +off=17 len=4 span[header_field]="Host" +off=22 header_field complete +off=23 len=15 span[header_value]="www.example.com" +off=40 header_value complete +off=40 len=12 span[header_field]="Content-Type" +off=53 header_field complete +off=54 len=33 span[header_value]="application/x-www-form-urlencoded" +off=89 header_value complete +off=89 len=14 span[header_field]="Content-Length" +off=104 header_field complete +off=105 len=1 span[header_value]="4" +off=108 header_value complete +off=108 len=10 span[header_field]="Connection" +off=119 header_field complete +off=120 len=6 span[header_value]="close\t" +off=128 header_value complete +off=130 headers complete method=3 v=1/1 flags=22 content_length=4 +off=130 len=4 span[body]="q=42" +off=134 message complete +off=139 error code=5 reason="Data after `Connection: close`" +``` + ### CRLF between requests, explicit `close` (lenient) Loose mode is more lenient, and allows further requests.