From 2090cc34ab15c1e044a00748dd728ba69012d34a Mon Sep 17 00:00:00 2001 From: Vizonex Date: Tue, 7 Apr 2026 23:26:03 -0500 Subject: [PATCH 1/2] add lentient flag that probitings multiple host headers or none at all. --- src/llhttp/constants.ts | 4 ++++ src/llhttp/http.ts | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/llhttp/constants.ts b/src/llhttp/constants.ts index dd64c623..ae99b521 100644 --- a/src/llhttp/constants.ts +++ b/src/llhttp/constants.ts @@ -24,6 +24,8 @@ export const ERROR = { INVALID_STATUS: 13, INVALID_EOF_STATE: 14, INVALID_TRANSFER_ENCODING: 15, + HOST_PREVIOUSLY_SEEN: 39, + HOST_NOT_PROVIDED: 40, CB_MESSAGE_BEGIN: 16, CB_HEADERS_COMPLETE: 17, @@ -66,6 +68,7 @@ export const FLAGS = { TRAILING: 1 << 7, // 1 << 8 is unused TRANSFER_ENCODING: 1 << 9, + HOST_SEEN: 1 << 10, } as const; export const LENIENT_FLAGS = { @@ -80,6 +83,7 @@ export const LENIENT_FLAGS = { OPTIONAL_CR_BEFORE_LF: 1 << 8, SPACES_AFTER_CHUNK_SIZE: 1 << 9, HEADER_VALUE_RELAXED: 1 << 10, + HOST_RELAXED: 1 << 11, } as const; export const STATUSES = { diff --git a/src/llhttp/http.ts b/src/llhttp/http.ts index dbc9c607..db27555e 100644 --- a/src/llhttp/http.ts +++ b/src/llhttp/http.ts @@ -550,6 +550,21 @@ export class HTTP { this.buildHeaderValue(); } + private buildHostCheck(next: Node): Node { + // Check if the lentient flag given is not set to HOST_RELAXED + // This will reject repetative versions of the host header + const p = this.llparse; + return this.testLenientFlags( + ~LENIENT_FLAGS.HOST_RELAXED, + {1: next}, + this.testFlags( + FLAGS.HOST_SEEN, + {1: p.error(ERROR.HOST_PREVIOUSLY_SEEN, "host provided multiple times.")}, + this.setFlag(FLAGS.HOST_SEEN, next), + ) + ); + } + private buildHeaderField(): void { const p = this.llparse; const span = this.span; @@ -578,12 +593,18 @@ export class HTTP { ) .peek(':', p.error(ERROR.INVALID_HEADER_TOKEN, 'Invalid header token')) .otherwise(span.headerField.start(n('header_field'))); + + + const reset_header_state = this.resetHeaderState('header_field_general'); n('header_field') .transform(p.transform.toLower()) // Match headers that need special treatment .select(SPECIAL_HEADERS, this.store('header_state', 'header_field_colon')) - .otherwise(this.resetHeaderState('header_field_general')); + // check to see if host was given once or multiple times which if not + // relaxed should be easily rejected. + .match('host', this.buildHostCheck(reset_header_state)) + .otherwise(reset_header_state); /* https://www.rfc-editor.org/rfc/rfc7230.html#section-3.3.3, paragraph 3. * @@ -1165,7 +1186,17 @@ export class HTTP { beforeHeadersComplete.otherwise(onHeadersComplete); - return beforeHeadersComplete; + // before leaving header state if Host is not set to being + // relaxed see if no host has been provided at all... + return this.testLenientFlags( + ~LENIENT_FLAGS.HOST_RELAXED, + {1:this.testFlags( + FLAGS.HOST_SEEN, + {1: beforeHeadersComplete}, + p.error(ERROR.HOST_NOT_PROVIDED, "No host header or value was provided.") + )}, + beforeHeadersComplete, + ); } private node(name: string | T): T { From c903dff2a3e02b6b8814e67280c37f2f3ca9734e Mon Sep 17 00:00:00 2001 From: Vizonex Date: Tue, 7 Apr 2026 23:27:28 -0500 Subject: [PATCH 2/2] add lentient flag that probiting multiple host headers or none at all. --- src/native/api.c | 8 ++++++++ src/native/api.h | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/src/native/api.c b/src/native/api.c index ae5e862d..e168cfc0 100644 --- a/src/native/api.c +++ b/src/native/api.c @@ -324,6 +324,14 @@ void llhttp_set_lenient_header_value_relaxed(llhttp_t* parser, int enabled) { } } +void llhttp_set_lenient_host_relaxed(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_HOST_RELAXED; + } else { + parser->lenient_flags &= ~LENIENT_HOST_RELAXED; + } +} + /* Callbacks */ diff --git a/src/native/api.h b/src/native/api.h index 0a58d4e0..816b8629 100644 --- a/src/native/api.h +++ b/src/native/api.h @@ -370,6 +370,15 @@ void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled); LLHTTP_EXPORT void llhttp_set_lenient_header_value_relaxed(llhttp_t* parser, int enabled); + +/* Enables/disables relaxed handling of the host header, which can allow multiple + * or no host headers, when disabled it strictly prohibits these form of requests + * from being accepted. + */ +LLHTTP_EXPORT +void llhttp_set_lenient_host_relaxed(llhttp_t* parser, int enabled); + + #ifdef __cplusplus } /* extern "C" */ #endif