Skip to content

Commit

Permalink
http: validate HTTP version
Browse files Browse the repository at this point in the history
  • Loading branch information
ShogunPanda committed Jul 21, 2022
1 parent f84c4de commit 848c4db
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/llhttp/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export enum LENIENT_FLAGS {
CHUNKED_LENGTH = 1 << 1,
KEEP_ALIVE = 1 << 2,
TRANSFER_ENCODING = 1 << 3,
VERSION = 1 << 4,
}

export enum METHODS {
Expand Down
25 changes: 22 additions & 3 deletions src/llhttp/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,27 @@ export class HTTP {
.match('HTTP/', this.update('type', TYPE.RESPONSE, 'res_http_major'))
.otherwise(p.error(ERROR.INVALID_CONSTANT, 'Invalid word encountered'));

// Response
const checkVersion = (destination: string): Node => {
return this.testLenientFlags(LENIENT_FLAGS.VERSION,
{
1: n(destination),
},
this.load('http_major', {
0: this.load('http_minor', {
9: n(destination),
}, p.error(ERROR.INVALID_VERSION, 'Invalid HTTP version')),
1: this.load('http_minor', {
0: n(destination),
1: n(destination),
}, p.error(ERROR.INVALID_VERSION, 'Invalid HTTP version')),
2: this.load('http_minor', {
0: n(destination),
}, p.error(ERROR.INVALID_VERSION, 'Invalid HTTP version')),
}, p.error(ERROR.INVALID_VERSION, 'Invalid HTTP version')),
);
};

// Response
n('start_res')
.match('HTTP/', n('res_http_major'))
.otherwise(p.error(ERROR.INVALID_CONSTANT, 'Expected HTTP/'));
Expand All @@ -259,7 +278,7 @@ export class HTTP {
.otherwise(p.error(ERROR.INVALID_VERSION, 'Expected dot'));

n('res_http_minor')
.select(MINOR, this.store('http_minor', 'res_http_end'))
.select(MINOR, this.store('http_minor', checkVersion('res_http_end')))
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid minor version'));

n('res_http_end')
Expand Down Expand Up @@ -365,7 +384,7 @@ export class HTTP {
.otherwise(p.error(ERROR.INVALID_VERSION, 'Expected dot'));

n('req_http_minor')
.select(MINOR, this.store('http_minor', 'req_http_end'))
.select(MINOR, this.store('http_minor', checkVersion('req_http_end')))
.otherwise(p.error(ERROR.INVALID_VERSION, 'Invalid minor version'));

n('req_http_end').otherwise(this.load('method', {
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/extra.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,22 @@ void llhttp__test_init_request_lenient_transfer_encoding(llparse_t* s) {
}


void llhttp__test_init_request_lenient_version(llparse_t* s) {
llhttp__test_init_request(s);
s->lenient_flags |= LENIENT_VERSION;
}


void llhttp__test_init_response_lenient_keep_alive(llparse_t* s) {
llhttp__test_init_response(s);
s->lenient_flags |= LENIENT_KEEP_ALIVE;
}

void llhttp__test_init_response_lenient_version(llparse_t* s) {
llhttp__test_init_response(s);
s->lenient_flags |= LENIENT_VERSION;
}


void llhttp__test_finish(llparse_t* s) {
llparse__print(NULL, NULL, "finish=%d", s->finish);
Expand Down
5 changes: 4 additions & 1 deletion 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 type TestType = 'request' | 'response' | 'request-lenient-headers' |
'request-lenient-chunked-length' | 'request-lenient-transfer-encoding' |
'request-lenient-keep-alive' | 'response-lenient-keep-alive' |
'request-lenient-version' | 'response-lenient-version' |
'request-finish' | 'response-finish' |
'none' | 'url';

Expand Down Expand Up @@ -66,7 +67,9 @@ export async function build(
ty === 'request-lenient-chunked-length' ||
ty === 'request-lenient-transfer-encoding' ||
ty === 'request-lenient-keep-alive' ||
ty === 'response-lenient-keep-alive') {
ty === 'response-lenient-keep-alive' ||
ty === 'request-lenient-version' ||
ty === 'response-lenient-version') {
extra.push(
`-DLLPARSE__TEST_INIT=llhttp__test_init_${ty.replace(/-/g, '_')}`);
} else if (ty === 'request-finish' || ty === 'response-finish') {
Expand Down
35 changes: 18 additions & 17 deletions test/md-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,34 +79,30 @@ const http: IFixtureMap = {
'none': buildMode('loose', 'none'),
'request': buildMode('loose', 'request'),
'request-finish': buildMode('loose', 'request-finish'),
'request-lenient-chunked-length':
buildMode('loose', 'request-lenient-chunked-length'),
'request-lenient-chunked-length': buildMode('loose', 'request-lenient-chunked-length'),
'request-lenient-headers': buildMode('loose', 'request-lenient-headers'),
'request-lenient-keep-alive': buildMode(
'loose', 'request-lenient-keep-alive'),
'request-lenient-transfer-encoding':
buildMode('loose', 'request-lenient-transfer-encoding'),
'request-lenient-keep-alive': buildMode( 'loose', 'request-lenient-keep-alive'),
'request-lenient-transfer-encoding': buildMode('loose', 'request-lenient-transfer-encoding'),
'request-lenient-version': buildMode( 'loose', 'request-lenient-version'),
'response': buildMode('loose', 'response'),
'response-finish': buildMode('loose', 'response-finish'),
'response-lenient-keep-alive': buildMode(
'loose', 'response-lenient-keep-alive'),
'response-lenient-keep-alive': buildMode( 'loose', 'response-lenient-keep-alive'),
'response-lenient-version': buildMode( 'loose', 'response-lenient-version'),
'url': buildMode('loose', 'url'),
},
strict: {
'none': buildMode('strict', 'none'),
'request': buildMode('strict', 'request'),
'request-finish': buildMode('strict', 'request-finish'),
'request-lenient-chunked-length':
buildMode('strict', 'request-lenient-chunked-length'),
'request-lenient-chunked-length': buildMode('strict', 'request-lenient-chunked-length'),
'request-lenient-headers': buildMode('strict', 'request-lenient-headers'),
'request-lenient-keep-alive': buildMode(
'strict', 'request-lenient-keep-alive'),
'request-lenient-transfer-encoding':
buildMode('strict', 'request-lenient-transfer-encoding'),
'request-lenient-keep-alive': buildMode( 'strict', 'request-lenient-keep-alive'),
'request-lenient-transfer-encoding': buildMode('strict', 'request-lenient-transfer-encoding'),
'request-lenient-version': buildMode( 'strict', 'request-lenient-version'),
'response': buildMode('strict', 'response'),
'response-finish': buildMode('strict', 'response-finish'),
'response-lenient-keep-alive': buildMode(
'strict', 'response-lenient-keep-alive'),
'response-lenient-keep-alive': buildMode('strict', 'response-lenient-keep-alive'),
'response-lenient-version': buildMode( 'strict', 'response-lenient-version'),
'url': buildMode('strict', 'url'),
},
};
Expand Down Expand Up @@ -171,8 +167,12 @@ function run(name: string): void {
types = [ 'request-lenient-keep-alive' ];
} else if (meta.type === 'request-lenient-transfer-encoding') {
types = [ 'request-lenient-transfer-encoding' ];
} else if (meta.type === 'request-lenient-version') {
types = [ 'request-lenient-version' ];
} else if (meta.type === 'response-lenient-keep-alive') {
types = [ 'response-lenient-keep-alive' ];
} else if (meta.type === 'response-lenient-version') {
types = [ 'response-lenient-version' ];
} else if (meta.type === 'response-only') {
types = [ 'response' ];
} else if (meta.type === 'request-finish') {
Expand Down Expand Up @@ -278,6 +278,7 @@ function run(name: string): void {

run('request/sample');
run('request/lenient-headers');
run('request/lenient-version');
run('request/method');
run('request/uri');
run('request/connection');
Expand All @@ -292,5 +293,5 @@ run('response/content-length');
run('response/transfer-encoding');
run('response/invalid');
run('response/finish');

run('request/lenient-version');
run('url');
14 changes: 14 additions & 0 deletions test/request/invalid.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,18 @@ off=33 len=5 span[header_field]="Dummy"
off=39 header_field complete
off=40 len=1 span[header_value]="x"
off=42 error code=25 reason="Missing expected CR after header value"
```

### Invalid HTTP version

<!-- meta={"type": "request", "noScan": true} -->
```http
GET / HTTP/5.6
```

```log
off=0 message begin
off=4 len=1 span[url]="/"
off=6 url complete
off=14 error code=9 reason="Invalid HTTP version"
```
19 changes: 19 additions & 0 deletions test/request/lenient-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Lenient HTTP version parsing
============================

### Invalid HTTP version with lenient

<!-- meta={"type": "request-lenient-version"} -->
```http
GET / HTTP/5.6
```

```log
off=0 message begin
off=4 len=1 span[url]="/"
off=6 url complete
off=18 headers complete method=1 v=5/6 flags=0 content_length=0
off=18 message complete
```
14 changes: 14 additions & 0 deletions test/response/invalid.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,17 @@ off=21 header_field complete
off=22 len=1 span[header_value]="1"
off=24 error code=3 reason="Missing expected LF after header value"
```

### Invalid HTTP version

<!-- meta={"type": "response"} -->
```http
HTTP/5.6 200 OK
```

```log
off=0 message begin
off=8 error code=9 reason="Invalid HTTP version"
```
18 changes: 18 additions & 0 deletions test/response/lenient-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Lenient HTTP version parsing
============================

### Invalid HTTP version with lenient

<!-- meta={"type": "response-lenient-version"} -->
```http
HTTP/5.6 200 OK
```

```log
off=0 message begin
off=13 len=2 span[status]="OK"
off=17 status complete
off=19 headers complete status=200 v=5/6 flags=0 content_length=0
```

0 comments on commit 848c4db

Please sign in to comment.