Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Support all LF messages. #239

Merged
merged 2 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 8 additions & 3 deletions src/llhttp/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,9 @@ export class HTTP {
const span = this.span;
const n = (name: string): Match => this.node<Match>(name);

const onInvalidHeaderFieldChar =
p.error(ERROR.INVALID_HEADER_TOKEN, 'Invalid header field char');

n('headers_start')
.match(' ',
this.testLenientFlags(LENIENT_FLAGS.HEADERS, {
Expand All @@ -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')
Expand All @@ -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'),
Expand Down
18 changes: 18 additions & 0 deletions test/fixtures/extra.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' |
Expand All @@ -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',
Expand Down
8 changes: 4 additions & 4 deletions test/request/content-length.md
Original file line number Diff line number Diff line change
Expand Up @@ -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


```
Expand All @@ -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
Expand Down
35 changes: 35 additions & 0 deletions test/response/invalid.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

<!-- meta={"type": "response"} -->
```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)

<!-- meta={"type": "response-lenient-all"} -->
```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
```