Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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: 4 additions & 0 deletions src/llhttp/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = {
Expand All @@ -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 = {
Expand Down
35 changes: 33 additions & 2 deletions src/llhttp/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Comment thread
Vizonex marked this conversation as resolved.
Outdated
{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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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<T extends Node>(name: string | T): T {
Expand Down
8 changes: 8 additions & 0 deletions src/native/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */


Expand Down
9 changes: 9 additions & 0 deletions src/native/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading