Skip to content

Commit f49fbf5

Browse files
authored
Disable chunked on obs (#196)
* http: disable chunked encoding when OBS fold is used * http: do not accept LF in headers without lenient flag
1 parent 6e54030 commit f49fbf5

10 files changed

Lines changed: 147 additions & 9 deletions

File tree

src/llhttp/http.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,9 @@ export class HTTP {
476476
n('header_value_discard_ws')
477477
.match([ ' ', '\t' ], n('header_value_discard_ws'))
478478
.match('\r', n('header_value_discard_ws_almost_done'))
479-
.match('\n', n('header_value_discard_lws'))
479+
.match('\n', this.testLenientFlags(LENIENT_FLAGS.HEADERS, {
480+
1: n('header_value_discard_lws'),
481+
}, p.error(ERROR.INVALID_HEADER_TOKEN, 'Invalid header value char')))
480482
.otherwise(span.headerValue.start(n('header_value_start')));
481483

482484
if (this.mode === 'strict') {
@@ -638,7 +640,7 @@ export class HTTP {
638640

639641
const checkLenient = this.testLenientFlags(LENIENT_FLAGS.HEADERS, {
640642
1: n('header_value_lenient'),
641-
}, n('header_value_lenient_failed'));
643+
}, span.headerValue.end(p.error(ERROR.INVALID_HEADER_TOKEN, 'Invalid header value char')));
642644

643645
n('header_value_otherwise')
644646
.peek('\r', span.headerValue.end().skipTo(n('header_value_almost_done')))
@@ -661,7 +663,11 @@ export class HTTP {
661663
'Missing expected LF after header value'));
662664

663665
n('header_value_lws')
664-
.peek([ ' ', '\t' ], span.headerValue.start(n('header_value_start')))
666+
.peek([ ' ', '\t' ],
667+
this.load('header_state', {
668+
[HEADER_STATE.TRANSFER_ENCODING_CHUNKED]:
669+
this.resetHeaderState(span.headerValue.start(n('header_value_start'))),
670+
}, span.headerValue.start(n('header_value_start'))))
665671
.otherwise(this.setHeaderFlags(onHeaderValueComplete));
666672

667673
const checkTrailing = this.testFlags(FLAGS.TRAILING, {

test/fixtures/extra.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ void llhttp__test_init_response_lenient_version(llparse_t* s) {
105105
}
106106

107107

108+
void llhttp__test_init_response_lenient_headers(llparse_t* s) {
109+
llhttp__test_init_response(s);
110+
s->lenient_flags |= LENIENT_HEADERS;
111+
}
112+
113+
108114
void llhttp__test_finish(llparse_t* s) {
109115
llparse__print(NULL, NULL, "finish=%d", s->finish);
110116
}

test/fixtures/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import * as llhttp from '../../src/llhttp';
1111
export type TestType = 'request' | 'response' | 'request-lenient-headers' |
1212
'request-lenient-chunked-length' | 'request-lenient-transfer-encoding' |
1313
'request-lenient-keep-alive' | 'response-lenient-keep-alive' |
14-
'request-lenient-version' | 'response-lenient-version' |
14+
'response-lenient-headers' | 'request-lenient-version' | 'response-lenient-version' |
1515
'request-finish' | 'response-finish' |
1616
'none' | 'url';
1717

@@ -67,8 +67,9 @@ export async function build(
6767
ty === 'request-lenient-chunked-length' ||
6868
ty === 'request-lenient-transfer-encoding' ||
6969
ty === 'request-lenient-keep-alive' ||
70-
ty === 'response-lenient-keep-alive' ||
7170
ty === 'request-lenient-version' ||
71+
ty === 'response-lenient-headers' ||
72+
ty === 'response-lenient-keep-alive' ||
7273
ty === 'response-lenient-version') {
7374
extra.push(
7475
`-DLLPARSE__TEST_INIT=llhttp__test_init_${ty.replace(/-/g, '_')}`);

test/md-test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ function run(name: string): void {
171171
types = [ 'request-lenient-version' ];
172172
} else if (meta.type === 'response-lenient-keep-alive') {
173173
types = [ 'response-lenient-keep-alive' ];
174+
} else if (meta.type === 'response-lenient-headers') {
175+
types = [ 'response-lenient-headers' ];
174176
} else if (meta.type === 'response-lenient-version') {
175177
types = [ 'response-lenient-version' ];
176178
} else if (meta.type === 'response-only') {

test/request/invalid.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,61 @@ off=21 len=1 span[header_value]="1"
8989
off=23 error code=3 reason="Missing expected LF after header value"
9090
```
9191

92+
### Headers separated by LF
93+
94+
<!-- meta={"type": "request"} -->
95+
```http
96+
POST / HTTP/1.1
97+
Host: localhost:5000
98+
x:x\nTransfer-Encoding: chunked
99+
100+
1
101+
A
102+
0
103+
104+
```
105+
106+
```log
107+
off=0 message begin
108+
off=5 len=1 span[url]="/"
109+
off=7 url complete
110+
off=17 len=4 span[header_field]="Host"
111+
off=22 header_field complete
112+
off=23 len=14 span[header_value]="localhost:5000"
113+
off=39 header_value complete
114+
off=39 len=1 span[header_field]="x"
115+
off=41 header_field complete
116+
off=41 len=1 span[header_value]="x"
117+
off=42 error code=10 reason="Invalid header value char"
118+
```
119+
120+
### Empty headers separated by LF
121+
122+
<!-- meta={"type": "request"} -->
123+
```http
124+
POST / HTTP/1.1
125+
Host: localhost:5000
126+
x:\nTransfer-Encoding: chunked
127+
128+
1
129+
A
130+
0
131+
132+
```
133+
134+
```log
135+
off=0 message begin
136+
off=5 len=1 span[url]="/"
137+
off=7 url complete
138+
off=17 len=4 span[header_field]="Host"
139+
off=22 header_field complete
140+
off=23 len=14 span[header_value]="localhost:5000"
141+
off=39 header_value complete
142+
off=39 len=1 span[header_field]="x"
143+
off=41 header_field complete
144+
off=42 error code=10 reason="Invalid header value char"
145+
```
146+
92147
### Invalid header token #1
93148

94149
<!-- meta={"type": "request", "noScan": true} -->
@@ -228,7 +283,7 @@ off=33 header_value complete
228283
off=33 len=5 span[header_field]="Dummy"
229284
off=39 header_field complete
230285
off=40 len=1 span[header_value]="x"
231-
off=42 error code=25 reason="Missing expected CR after header value"
286+
off=41 error code=10 reason="Invalid header value char"
232287
```
233288

234289
### Invalid HTTP version

test/request/lenient-headers.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,6 @@ off=4 len=4 span[url]="/url"
7777
off=9 url complete
7878
off=19 len=7 span[header_field]="Header1"
7979
off=27 header_field complete
80+
off=28 len=0 span[header_value]=""
8081
off=28 error code=10 reason="Invalid header value char"
8182
```

test/request/sample.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ off=6 url complete
357357
off=15 len=5 span[header_field]="Line1"
358358
off=21 header_field complete
359359
off=24 len=3 span[header_value]="abc"
360-
off=28 error code=25 reason="Missing expected CR after header value"
360+
off=27 error code=10 reason="Invalid header value char"
361361
```
362362

363363
## Request starting with CRLF

test/request/transfer-encoding.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,31 @@ off=47 header_value complete
600600
off=49 headers complete method=4 v=1/1 flags=208 content_length=0
601601
off=51 error code=2 reason="Invalid character in chunk parameters"
602602
```
603+
604+
## Invalid OBS fold after chunked value
605+
606+
<!-- meta={"type": "request", "mode": "strict"} -->
607+
```http
608+
PUT /url HTTP/1.1
609+
Transfer-Encoding: chunked
610+
abc
611+
612+
5
613+
World
614+
0
615+
616+
617+
```
618+
619+
```log
620+
off=0 message begin
621+
off=4 len=4 span[url]="/url"
622+
off=9 url complete
623+
off=19 len=17 span[header_field]="Transfer-Encoding"
624+
off=37 header_field complete
625+
off=38 len=7 span[header_value]="chunked"
626+
off=47 len=5 span[header_value]=" abc"
627+
off=54 header_value complete
628+
off=56 headers complete method=4 v=1/1 flags=200 content_length=0
629+
off=56 error code=15 reason="Request has invalid `Transfer-Encoding`"
630+
```

test/response/sample.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ off=16 status complete
274274
off=16 len=12 span[header_field]="Content-Type"
275275
off=29 header_field complete
276276
off=30 len=24 span[header_value]="text/html; charset=utf-8"
277-
off=55 error code=25 reason="Missing expected CR after header value"
277+
off=54 error code=10 reason="Invalid header value char"
278278
```
279279

280280
## Underscore in header key

test/response/transfer-encoding.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,43 @@ off=75 len=1 span[body]=cr
148148
off=76 len=1 span[body]=lf
149149
off=77 len=1 span[body]=cr
150150
off=78 len=1 span[body]=lf
151-
```
151+
```
152+
153+
## Invalid OBS fold after chunked value
154+
155+
<!-- meta={"type": "response" } -->
156+
```http
157+
HTTP/1.1 200 OK
158+
Transfer-Encoding: chunked
159+
abc
160+
161+
5
162+
World
163+
0
164+
165+
166+
```
167+
168+
```log
169+
off=0 message begin
170+
off=13 len=2 span[status]="OK"
171+
off=17 status complete
172+
off=17 len=17 span[header_field]="Transfer-Encoding"
173+
off=35 header_field complete
174+
off=36 len=7 span[header_value]="chunked"
175+
off=45 len=5 span[header_value]=" abc"
176+
off=52 header_value complete
177+
off=54 headers complete status=200 v=1/1 flags=200 content_length=0
178+
off=54 len=1 span[body]="5"
179+
off=55 len=1 span[body]=cr
180+
off=56 len=1 span[body]=lf
181+
off=57 len=5 span[body]="World"
182+
off=62 len=1 span[body]=cr
183+
off=63 len=1 span[body]=lf
184+
off=64 len=1 span[body]="0"
185+
off=65 len=1 span[body]=cr
186+
off=66 len=1 span[body]=lf
187+
off=67 len=1 span[body]=cr
188+
off=68 len=1 span[body]=lf
189+
```
190+

0 commit comments

Comments
 (0)