Skip to content

Commit d245a5f

Browse files
committed
http: validate HTTP version
1 parent 75b4512 commit d245a5f

3 files changed

Lines changed: 81 additions & 15 deletions

File tree

src/llhttp/http.ts

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ const NODES: ReadonlyArray<string> = [
2727
'req_or_res_method',
2828

2929
'res_http_major',
30-
'res_http_dot',
31-
'res_http_minor',
30+
'res_http_major_0',
31+
'res_http_major_1',
32+
'res_http_major_2',
33+
'res_http_minor_0',
34+
'res_http_minor_1',
35+
'res_http_minor_2',
3236
'res_http_end',
3337
'res_status_code',
3438
'res_status_code_otherwise',
@@ -40,8 +44,12 @@ const NODES: ReadonlyArray<string> = [
4044
'req_spaces_before_url',
4145
'req_http_start',
4246
'req_http_major',
43-
'req_http_dot',
44-
'req_http_minor',
47+
'req_http_major_0',
48+
'req_http_major_1',
49+
'req_http_major_2',
50+
'req_http_minor_0',
51+
'req_http_minor_1',
52+
'req_http_minor_2',
4553
'req_http_end',
4654
'req_http_complete',
4755

@@ -250,15 +258,29 @@ export class HTTP {
250258
.otherwise(p.error(ERROR.INVALID_CONSTANT, 'Expected HTTP/'));
251259

252260
n('res_http_major')
253-
.select(MAJOR, this.store('http_major', 'res_http_dot'))
261+
.match('0', this.update('http_major', 0, 'res_http_major_0'))
262+
.match('1', this.update('http_major', 1, 'res_http_major_1'))
263+
.match('2', this.update('http_major', 2, 'res_http_major_2'))
264+
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid major version'));
265+
266+
n('res_http_major_0')
267+
.match(['.', '.9'], this.update('http_minor', 9, 'res_http_end'))
254268
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid major version'));
255269

256-
n('res_http_dot')
257-
.match('.', n('res_http_minor'))
270+
n('res_http_major_1')
271+
.match('.', n('res_http_minor_1'))
272+
.otherwise(p.error(ERROR.INVALID_VERSION, 'Expected dot'));
273+
274+
n('res_http_major_2')
275+
.match('.', n('res_http_minor_1'))
258276
.otherwise(p.error(ERROR.INVALID_VERSION, 'Expected dot'));
259277

260-
n('res_http_minor')
261-
.select(MINOR, this.store('http_minor', 'res_http_end'))
278+
n('res_http_minor_1')
279+
.select({0: 0, 1: 1}, this.store('http_minor', 'res_http_end'))
280+
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid minor version'));
281+
282+
n('res_http_minor_2')
283+
.match('0', this.update('http_minor', 0, 'res_http_end'))
262284
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid minor version'));
263285

264286
n('res_http_end')
@@ -356,15 +378,29 @@ export class HTTP {
356378
.otherwise(p.error(ERROR.INVALID_CONSTANT, 'Expected HTTP/'));
357379

358380
n('req_http_major')
359-
.select(MAJOR, this.store('http_major', 'req_http_dot'))
381+
.match('0', this.update('http_major', 0, 'req_http_major_0'))
382+
.match('1', this.update('http_major', 1, 'req_http_major_1'))
383+
.match('2', this.update('http_major', 2, 'req_http_major_2'))
360384
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid major version'));
361385

362-
n('req_http_dot')
363-
.match('.', n('req_http_minor'))
386+
n('req_http_major_0')
387+
.match(['.', '.9'], this.update('http_minor', 9, 'req_http_end'))
388+
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid major version'));
389+
390+
n('req_http_major_1')
391+
.match('.', n('req_http_minor_1'))
392+
.otherwise(p.error(ERROR.INVALID_VERSION, 'Expected dot'));
393+
394+
n('req_http_major_2')
395+
.match('.', n('req_http_minor_1'))
364396
.otherwise(p.error(ERROR.INVALID_VERSION, 'Expected dot'));
365397

366-
n('req_http_minor')
367-
.select(MINOR, this.store('http_minor', 'req_http_end'))
398+
n('req_http_minor_1')
399+
.select({0: 0, 1: 1}, this.store('http_minor', 'req_http_end'))
400+
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid minor version'));
401+
402+
n('req_http_minor_2')
403+
.match('0', this.update('http_minor', 0, 'req_http_end'))
368404
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid minor version'));
369405

370406
n('req_http_end').otherwise(this.load('method', {

test/request/invalid.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,19 @@ off=22 len=15 span[header_value]="www.example.com"
200200
off=39 header_value complete
201201
off=52 error code=10 reason="Invalid header token"
202202
```
203+
204+
### Invalid HTTP version
205+
206+
<!-- meta={"type": "request", "noScan": true} -->
207+
```http
208+
GET / HTTP/5.6
209+
210+
211+
```
212+
213+
```log
214+
off=0 message begin
215+
off=4 len=1 span[url]="/"
216+
off=6 url complete
217+
off=11 error code=9 reason="Invalid major version"
218+
```

test/response/invalid.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ HTTP/01.1 200 OK
2828

2929
```log
3030
off=0 message begin
31-
off=6 error code=9 reason="Expected dot"
31+
off=6 error code=9 reason="Invalid major version"
3232
```
3333

3434
### Extra digit in HTTP major version #2
@@ -106,3 +106,17 @@ off=21 header_field complete
106106
off=22 len=1 span[header_value]="1"
107107
off=24 error code=3 reason="Missing expected LF after header value"
108108
```
109+
110+
### Invalid HTTP version
111+
112+
<!-- meta={"type": "response"} -->
113+
```http
114+
HTTP/5.6 200 OK
115+
116+
117+
```
118+
119+
```log
120+
off=0 message begin
121+
off=5 error code=9 reason="Invalid major version"
122+
```

0 commit comments

Comments
 (0)