@@ -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