diff --git a/src/llhttp/http.ts b/src/llhttp/http.ts index 7e76dc91..1c850676 100644 --- a/src/llhttp/http.ts +++ b/src/llhttp/http.ts @@ -495,6 +495,9 @@ export class HTTP { const span = this.span; const n = (name: string): Match => this.node(name); + const onInvalidHeaderFieldChar = + p.error(ERROR.INVALID_HEADER_TOKEN, 'Invalid header field char'); + n('headers_start') .match(' ', this.testLenientFlags(LENIENT_FLAGS.HEADERS, { @@ -505,6 +508,11 @@ export class HTTP { n('header_field_start') .match('\r', n('headers_almost_done')) + .match('\n', + this.testLenientFlags(LENIENT_FLAGS.HEADERS, { + 1: n('headers_done'), + }, onInvalidHeaderFieldChar), + ) .otherwise(span.headerField.start(n('header_field'))); n('header_field') @@ -517,9 +525,6 @@ export class HTTP { 'on_header_field_complete', ERROR.CB_HEADER_FIELD_COMPLETE, n('header_value_discard_ws'), ); - const onInvalidHeaderFieldChar = - p.error(ERROR.INVALID_HEADER_TOKEN, 'Invalid header field char'); - const checkLenientFlagsOnColon = this.testLenientFlags(LENIENT_FLAGS.HEADERS, { 1: n('header_field_colon_discard_ws'), diff --git a/test/fixtures/extra.c b/test/fixtures/extra.c index 238f2a7d..8a59635f 100644 --- a/test/fixtures/extra.c +++ b/test/fixtures/extra.c @@ -76,6 +76,24 @@ void llhttp__test_init_response(llparse_t* s) { } +void llhttp__test_init_request_lenient_all(llparse_t* s) { + llhttp__test_init_request(s); + s->lenient_flags |= + LENIENT_HEADERS | LENIENT_CHUNKED_LENGTH | LENIENT_KEEP_ALIVE | + LENIENT_TRANSFER_ENCODING | LENIENT_VERSION | LENIENT_DATA_AFTER_CLOSE | + LENIENT_OPTIONAL_LF_AFTER_CR | LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; +} + + +void llhttp__test_init_response_lenient_all(llparse_t* s) { + llhttp__test_init_response(s); + s->lenient_flags |= + LENIENT_HEADERS | LENIENT_CHUNKED_LENGTH | LENIENT_KEEP_ALIVE | + LENIENT_TRANSFER_ENCODING | LENIENT_VERSION | LENIENT_DATA_AFTER_CLOSE | + LENIENT_OPTIONAL_LF_AFTER_CR | LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; +} + + void llhttp__test_init_request_lenient_headers(llparse_t* s) { llhttp__test_init_request(s); s->lenient_flags |= LENIENT_HEADERS; diff --git a/test/fixtures/index.ts b/test/fixtures/index.ts index a5b74e37..8604ee2b 100644 --- a/test/fixtures/index.ts +++ b/test/fixtures/index.ts @@ -11,6 +11,7 @@ import * as llhttp from '../../src/llhttp'; export { FixtureResult }; export type TestType = 'request' | 'response' | 'request-finish' | 'response-finish' | + 'request-lenient-all' | 'response-lenient-all' | 'request-lenient-headers' | 'request-lenient-chunked-length' | 'request-lenient-transfer-encoding' | 'request-lenient-keep-alive' | 'response-lenient-keep-alive' | 'response-lenient-headers' | 'request-lenient-version' | 'response-lenient-version' | @@ -24,6 +25,8 @@ export const allowedTypes: TestType[] = [ 'response', 'request-finish', 'response-finish', + 'request-lenient-all', + 'response-lenient-all', 'request-lenient-headers', 'response-lenient-headers', 'request-lenient-keep-alive', diff --git a/test/request/content-length.md b/test/request/content-length.md index 83e09c27..e05f27ae 100644 --- a/test/request/content-length.md +++ b/test/request/content-length.md @@ -220,7 +220,7 @@ off=79 error code=22 reason="Pause on CONNECT/Upgrade" ```http PUT /url HTTP/1.1 Content-Length: 1 -Transfer-Encoding: chunked +Transfer-Encoding: identity ``` @@ -239,9 +239,9 @@ off=35 len=1 span[header_value]="1" off=38 header_value complete off=38 len=17 span[header_field]="Transfer-Encoding" off=56 header_field complete -off=57 len=7 span[header_value]="chunked" -off=66 header_value complete -off=68 headers complete method=4 v=1/1 flags=228 content_length=1 +off=57 len=8 span[header_value]="identity" +off=67 header_value complete +off=69 headers complete method=4 v=1/1 flags=220 content_length=1 ``` ## Funky `Content-Length` with body diff --git a/test/response/invalid.md b/test/response/invalid.md index d12e7914..8e113f91 100644 --- a/test/response/invalid.md +++ b/test/response/invalid.md @@ -196,4 +196,39 @@ off=0 message begin off=5 len=3 span[version]="1.1" off=8 version complete off=10 error code=13 reason="Invalid status code" +``` + +### Only LFs present + + +```http +HTTP/1.1 200 OK\nContent-Length: 0\n\n +``` + +```log +off=0 message begin +off=5 len=3 span[version]="1.1" +off=8 version complete +off=13 len=2 span[status]="OK" +off=16 error code=2 reason="Expected LF after CR" +``` + +### Only LFs present (lenient) + + +```http +HTTP/1.1 200 OK\nContent-Length: 0\n\n +``` + +```log +off=0 message begin +off=5 len=3 span[version]="1.1" +off=8 version complete +off=13 len=2 span[status]="OK" +off=16 status complete +off=16 len=14 span[header_field]="Content-Length" +off=31 header_field complete +off=32 len=1 span[header_value]="0" +off=34 header_value complete +off=35 message complete ``` \ No newline at end of file