Skip to content

Commit 42704b3

Browse files
committed
better
1 parent 0650b12 commit 42704b3

File tree

1 file changed

+53
-20
lines changed

1 file changed

+53
-20
lines changed

packages/bun-uws/src/HttpParser.h

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,32 @@ namespace uWS
103103
return 0;
104104
}
105105
};
106+
107+
108+
struct ConsumeRequestLineResult {
109+
char *position;
110+
bool isAncientHTTP;
111+
HTTPHeaderParserError headerParserError;
112+
113+
114+
public:
115+
116+
static ConsumeRequestLineResult error(HTTPHeaderParserError error) {
117+
return ConsumeRequestLineResult{nullptr, false, error};
118+
}
119+
120+
static ConsumeRequestLineResult success(char *position, bool isAncientHTTP) {
121+
return ConsumeRequestLineResult{position, isAncientHTTP, HTTP_HEADER_PARSER_ERROR_NONE};
122+
}
123+
124+
static ConsumeRequestLineResult shortRead(bool isAncientHTTP = false) {
125+
return ConsumeRequestLineResult{nullptr, isAncientHTTP, HTTP_HEADER_PARSER_ERROR_NONE};
126+
}
127+
128+
bool isErrorOrShortRead() {
129+
return headerParserError != HTTP_HEADER_PARSER_ERROR_NONE || position == nullptr;
130+
}
131+
};
106132

107133

108134
struct HttpRequest
@@ -424,28 +450,29 @@ namespace uWS
424450
return 0;
425451
}
426452

453+
427454
/* Puts method as key, target as value and returns non-null (or nullptr on error). */
428455
/* PS: this function on error can return char* to HTTPHeaderParserError enum, with is not the best design, this need to be refactor */
429-
static inline char *consumeRequestLine(char *data, char *end, HttpRequest::Header &header, bool &isAncientHTTP) {
456+
static inline ConsumeRequestLineResult consumeRequestLine(char *data, char *end, HttpRequest::Header &header) {
430457
/* Scan until single SP, assume next is / (origin request) */
431458
char *start = data;
432459
/* This catches the post padded CR and fails */
433460
while (data[0] > 32) {
434461
if (!isValidMethodChar(data[0]) ) {
435-
return (char *) HTTP_HEADER_PARSER_ERROR_INVALID_METHOD;
462+
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_METHOD);
436463
}
437464
data++;
438465

439466
}
440467
if (&data[1] == end) [[unlikely]] {
441-
return nullptr;
468+
return ConsumeRequestLineResult::shortRead();
442469
}
443470

444471
if (data[0] == 32 && (__builtin_expect(data[1] == '/', 1) || isHTTPorHTTPSPrefixForProxies(data + 1, end) == 1)) [[likely]] {
445472
header.key = {start, (size_t) (data - start)};
446473
data++;
447474
if(!isValidMethod(header.key)) {
448-
return (char *) HTTP_HEADER_PARSER_ERROR_INVALID_METHOD;
475+
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_METHOD);
449476
}
450477
/* Scan for less than 33 (catches post padded CR and fails) */
451478
start = data;
@@ -456,50 +483,49 @@ namespace uWS
456483
while (*(unsigned char *)data > 32) data++;
457484
/* Now we stand on space */
458485
header.value = {start, (size_t) (data - start)};
486+
auto nextPosition = data + 11;
459487
/* Check that the following is http 1.1 */
460-
if (data + 11 >= end) {
488+
if (nextPosition >= end) {
461489
/* Whatever we have must be part of the version string */
462490
if (memcmp(" HTTP/1.1\r\n", data, std::min<unsigned int>(11, (unsigned int) (end - data))) == 0) {
463-
return nullptr;
491+
return ConsumeRequestLineResult::shortRead(false);
464492
} else if (memcmp(" HTTP/1.0\r\n", data, std::min<unsigned int>(11, (unsigned int) (end - data))) == 0) {
465-
isAncientHTTP = true;
466-
return data + 11;
493+
return ConsumeRequestLineResult::shortRead(true);
467494
}
468-
return (char *) HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION;
495+
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION);
469496
}
470497
if (memcmp(" HTTP/1.1\r\n", data, 11) == 0) {
471-
return data + 11;
498+
return ConsumeRequestLineResult::success(nextPosition, false);
472499
} else if (memcmp(" HTTP/1.0\r\n", data, 11) == 0) {
473-
isAncientHTTP = true;
474-
return data + 11;
500+
return ConsumeRequestLineResult::success(nextPosition, true);
475501
}
476502
/* If we stand at the post padded CR, we have fragmented input so try again later */
477503
if (data[0] == '\r') {
478-
return nullptr;
504+
return ConsumeRequestLineResult::shortRead(false);
479505
}
480506
/* This is an error */
481-
return (char *) HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION;
507+
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION);
482508
}
483509
}
484510
}
485511

486512
/* If we stand at the post padded CR, we have fragmented input so try again later */
487513
if (data[0] == '\r') {
488-
return nullptr;
514+
return ConsumeRequestLineResult::shortRead(false);
489515
}
490516

491517
if (data[0] == 32) {
492518
switch (isHTTPorHTTPSPrefixForProxies(data + 1, end)) {
493519
// If we haven't received enough data to check if it's http:// or https://, let's try again later
494520
case -1:
495-
return nullptr;
521+
return ConsumeRequestLineResult::shortRead(false);
496522
// Otherwise, if it's not http:// or https://, return 400
497523
default:
498-
return (char *) HTTP_HEADER_PARSER_ERROR_INVALID_REQUEST;
524+
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_REQUEST);
499525
}
500526
}
501527

502-
return (char *) HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION;
528+
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION);
503529
}
504530

505531
/* RFC 9110: 5.5 Field Values (TLDR; anything above 31 is allowed; htab (9) is also allowed)
@@ -548,10 +574,11 @@ namespace uWS
548574
* which is then removed, and our counters to flip due to overflow and we end up with a crash */
549575

550576
/* The request line is different from the field names / field values */
551-
if ((char *) 4 > (postPaddedBuffer = consumeRequestLine(postPaddedBuffer, end, headers[0], isAncientHTTP))) {
577+
auto requestLineResult = consumeRequestLine(postPaddedBuffer, end, headers[0]);
578+
if (requestLineResult.isErrorOrShortRead()) {
552579
/* Error - invalid request line */
553580
/* Assuming it is 505 HTTP Version Not Supported */
554-
switch (reinterpret_cast<uintptr_t>(postPaddedBuffer)) {
581+
switch (requestLineResult.headerParserError) {
555582
case HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION:
556583
err = HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED;
557584
parserError = HTTP_PARSER_ERROR_INVALID_HTTP_VERSION;
@@ -565,12 +592,18 @@ namespace uWS
565592
parserError = HTTP_PARSER_ERROR_INVALID_METHOD;
566593
break;
567594
default: {
595+
/* Short read */
568596
err = 0;
569597
break;
570598
}
571599
}
572600
return 0;
573601
}
602+
postPaddedBuffer = requestLineResult.position;
603+
604+
if(requestLineResult.isAncientHTTP) {
605+
isAncientHTTP = true;
606+
}
574607
/* No request headers found */
575608
size_t buffer_size = end - postPaddedBuffer;
576609
if(buffer_size < 2) {

0 commit comments

Comments
 (0)