Skip to content

Commit 883de94

Browse files
committed
more
1 parent f6d1f7f commit 883de94

File tree

5 files changed

+172
-30
lines changed

5 files changed

+172
-30
lines changed

packages/bun-uws/src/ChunkedEncoding.h

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,50 +31,76 @@ namespace uWS {
3131

3232
constexpr uint64_t STATE_HAS_SIZE = 1ull << (sizeof(uint64_t) * 8 - 1);//0x80000000;
3333
constexpr uint64_t STATE_IS_CHUNKED = 1ull << (sizeof(uint64_t) * 8 - 2);//0x40000000;
34-
constexpr uint64_t STATE_SIZE_MASK = ~(3ull << (sizeof(uint64_t) * 8 - 2));//0x3FFFFFFF;
34+
constexpr uint64_t STATE_IS_CHUNKED_EXTENSION = 1ull << (sizeof(uint64_t) * 8 - 3);//0x20000000;
35+
constexpr uint64_t STATE_SIZE_MASK = ~(STATE_HAS_SIZE | STATE_IS_CHUNKED | STATE_IS_CHUNKED_EXTENSION);//0x3FFFFFFF;
3536
constexpr uint64_t STATE_IS_ERROR = ~0ull;//0xFFFFFFFF;
3637
constexpr uint64_t STATE_SIZE_OVERFLOW = 0x0Full << (sizeof(uint64_t) * 8 - 8);//0x0F000000;
3738

3839
inline unsigned int chunkSize(uint64_t state) {
3940
return state & STATE_SIZE_MASK;
4041
}
4142

43+
inline bool isParsingChunkedExtension(uint64_t state) {
44+
return (state & STATE_IS_CHUNKED_EXTENSION) != 0;
45+
}
46+
4247
/* Reads hex number until CR or out of data to consume. Updates state. Returns bytes consumed. */
4348
inline void consumeHexNumber(std::string_view &data, uint64_t &state) {
44-
/* Consume everything higher than 32 */
45-
while (data.length() && data[0] > 32) {
46-
47-
unsigned char digit = (unsigned char)data[0];
48-
if (digit >= 'a') {
49-
digit = (unsigned char) (digit - ('a' - ':'));
50-
} else if (digit >= 'A') {
51-
digit = (unsigned char) (digit - ('A' - ':'));
52-
}
49+
if(!isParsingChunkedExtension(state)){
50+
/* Consume everything higher than 32 and not ; (extension)*/
51+
while (data.length() && data[0] > 32 && data[0] != ';') {
52+
53+
unsigned char digit = (unsigned char)data[0];
54+
if (digit >= 'a') {
55+
digit = (unsigned char) (digit - ('a' - ':'));
56+
} else if (digit >= 'A') {
57+
digit = (unsigned char) (digit - ('A' - ':'));
58+
}
5359

54-
unsigned int number = ((unsigned int) digit - (unsigned int) '0');
60+
unsigned int number = ((unsigned int) digit - (unsigned int) '0');
5561

56-
if (number > 16 || (chunkSize(state) & STATE_SIZE_OVERFLOW)) {
57-
state = STATE_IS_ERROR;
58-
return;
59-
}
62+
if (number > 16 || (chunkSize(state) & STATE_SIZE_OVERFLOW)) {
63+
state = STATE_IS_ERROR;
64+
return;
65+
}
6066

61-
// extract state bits
62-
uint64_t bits = /*state &*/ STATE_IS_CHUNKED;
67+
// extract state bits
68+
uint64_t bits = /*state &*/ STATE_IS_CHUNKED;
6369

64-
state = (state & STATE_SIZE_MASK) * 16ull + number;
70+
state = (state & STATE_SIZE_MASK) * 16ull + number;
6571

66-
state |= bits;
67-
data.remove_prefix(1);
72+
state |= bits;
73+
data.remove_prefix(1);
74+
}
6875
}
69-
if(data.length() >= 2) {
70-
/* Consume \r\n */
71-
if((data[0] != '\r' || data[1] != '\n')) {
72-
state = STATE_IS_ERROR;
73-
return;
76+
77+
auto len = data.length();
78+
if(len) {
79+
// consume extension
80+
if(data[0] == ';' || isParsingChunkedExtension(state)) {
81+
// mark that we are parsing chunked extension
82+
state |= STATE_IS_CHUNKED_EXTENSION;
83+
/* we got chunk extension lets remove it*/
84+
while(data.length()) {
85+
if(data[0] == '\r') {
86+
// we are done parsing extension
87+
state &= ~STATE_IS_CHUNKED_EXTENSION;
88+
break;
89+
}
90+
data.remove_prefix(1);
91+
}
92+
}
93+
if(data.length() >= 2) {
94+
/* Consume \r\n */
95+
if((data[0] != '\r' || data[1] != '\n')) {
96+
state = STATE_IS_ERROR;
97+
return;
98+
}
99+
state += 2; // include the two last /r/n
100+
state |= STATE_HAS_SIZE | STATE_IS_CHUNKED;
101+
102+
data.remove_prefix(2);
74103
}
75-
state += 2; // include the two last /r/n
76-
state |= STATE_HAS_SIZE | STATE_IS_CHUNKED;
77-
data.remove_prefix(2);
78104
}
79105
// short read
80106
}

packages/bun-uws/src/HttpContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ struct HttpContext {
149149

150150
/* Handle HTTP data streams */
151151
us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
152+
152153
// ref the socket to make sure we process it entirely before it is closed
153154
us_socket_ref(s);
154155

packages/bun-uws/src/HttpParser.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,10 @@ namespace uWS
467467
data++;
468468

469469
}
470-
if (&data[1] == end) [[unlikely]] {
470+
if(start == data) [[unlikely]] {
471+
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_METHOD);
472+
}
473+
if (data - start < 2) [[unlikely]] {
471474
return ConsumeRequestLineResult::shortRead();
472475
}
473476

@@ -557,7 +560,6 @@ namespace uWS
557560
/* End is only used for the proxy parser. The HTTP parser recognizes "\ra" as invalid "\r\n" scan and breaks. */
558561
static HttpParserResult getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers, void *reserved, bool &isAncientHTTP, bool useStrictMethodValidation, uint64_t maxHeaderSize) {
559562
char *preliminaryKey, *preliminaryValue, *start = postPaddedBuffer;
560-
561563
#ifdef UWS_WITH_PROXY
562564
/* ProxyParser is passed as reserved parameter */
563565
ProxyParser *pp = (ProxyParser *) reserved;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
'use strict';
23+
const common = require('../common');
24+
const assert = require('assert');
25+
const http = require('http');
26+
const net = require('net');
27+
28+
const server = http.createServer(common.mustCall((req, res) => {
29+
assert.strictEqual(req.method, 'GET');
30+
assert.strictEqual(req.url, '/blah');
31+
assert.deepStrictEqual(req.headers, {
32+
host: 'example.org:443',
33+
origin: 'http://example.org',
34+
cookie: ''
35+
});
36+
}));
37+
38+
39+
server.listen(0, common.mustCall(() => {
40+
const c = net.createConnection(server.address().port);
41+
let received = '';
42+
43+
c.on('connect', common.mustCall(() => {
44+
c.write('GET /blah HTTP/1.1\r\n' +
45+
'Host: example.org:443\r\n' +
46+
'Cookie:\r\n' +
47+
'Origin: http://example.org\r\n' +
48+
'\r\n\r\nhello world'
49+
);
50+
}));
51+
c.on('data', common.mustCall((data) => {
52+
received += data.toString();
53+
}));
54+
c.on('end', common.mustCall(() => {
55+
assert.strictEqual(received,
56+
'HTTP/1.1 400 Bad Request\r\n' +
57+
'Connection: close\r\n\r\n');
58+
c.end();
59+
}));
60+
c.on('close', common.mustCall(() => server.close()));
61+
}));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
'use strict';
23+
require('../common');
24+
const assert = require('assert');
25+
const http = require('http');
26+
const Countdown = require('../common/countdown');
27+
const N = 100;
28+
29+
const server = http.createServer(function(req, res) {
30+
res.end('Hello');
31+
});
32+
33+
const countdown = new Countdown(N, () => server.close());
34+
35+
server.listen(0, function() {
36+
http.globalAgent.maxSockets = 1;
37+
let parser;
38+
for (let i = 0; i < N; ++i) {
39+
(function makeRequest(i) {
40+
const req = http.get({ port: server.address().port }, function(res) {
41+
if (!parser) {
42+
parser = req.parser;
43+
} else {
44+
assert.strictEqual(req.parser, parser);
45+
}
46+
47+
countdown.dec();
48+
res.resume();
49+
});
50+
})(i);
51+
}
52+
});

0 commit comments

Comments
 (0)