diff --git a/components/esp_http_server/include/esp_http_server.h b/components/esp_http_server/include/esp_http_server.h index 1ca768ae257..cd3299cdb37 100644 --- a/components/esp_http_server/include/esp_http_server.h +++ b/components/esp_http_server/include/esp_http_server.h @@ -380,7 +380,7 @@ esp_err_t httpd_stop(httpd_handle_t handle); */ typedef struct httpd_req { httpd_handle_t handle; /*!< Handle to server instance */ - int method; /*!< The type of HTTP request, -1 if unsupported method, HTTP_ANY for wildcard method to support every method */ + int method; /*!< The type of HTTP request (enum http_method); a request with an unrecognized method is rejected with 501 before any URI handler runs */ const char uri[CONFIG_HTTPD_MAX_URI_LEN + 1]; /*!< The URI of this request (1 byte extra for null termination) */ size_t content_len; /*!< Length of the request body */ void *aux; /*!< Internally used members */ @@ -612,10 +612,11 @@ typedef enum { */ HTTPD_500_INTERNAL_SERVER_ERROR = 0, - /* For methods not supported by http_parser. Presently - * http_parser halts parsing when such methods are - * encountered and so the server responds with 400 Bad - * Request error instead. + /* For request methods that http_parser does not recognize, + * and for requests carrying a Transfer-Encoding header (no + * transfer coding is implemented). Parsing aborted mid-request + * in both cases, so the server closes the session after the + * response regardless of what a custom error handler returns. */ HTTPD_501_METHOD_NOT_IMPLEMENTED, @@ -623,8 +624,8 @@ typedef enum { HTTPD_505_VERSION_NOT_SUPPORTED, /* Returned when http_parser halts parsing due to incorrect - * syntax of request, unsupported method in request URI or - * due to chunked encoding / upgrade field present in headers + * syntax of request or due to an upgrade field present in + * headers that the server does not handle */ HTTPD_400_BAD_REQUEST, diff --git a/components/esp_http_server/src/httpd_parse.c b/components/esp_http_server/src/httpd_parse.c index 8245ae3291a..2f8b2901f0b 100644 --- a/components/esp_http_server/src/httpd_parse.c +++ b/components/esp_http_server/src/httpd_parse.c @@ -68,12 +68,10 @@ static esp_err_t verify_url (http_parser *parser) const char *at = parser_data->last.at; size_t length = parser_data->last.length; + /* http_parser stops with HPE_INVALID_METHOD before the URL callback for + * any method token it does not know, so the method is always valid here. + * That parser error is mapped to 501 in parse_block(). */ r->method = parser->method; - if (r->method < 0) { - ESP_LOGW(TAG, LOG_FMT("HTTP method not supported (%d)"), r->method); - parser_data->error = HTTPD_501_METHOD_NOT_IMPLEMENTED; - return ESP_FAIL; - } if (sizeof(r->uri) < (length + 1)) { ESP_LOGW(TAG, LOG_FMT("URI length (%"NEWLIB_NANO_COMPAT_FORMAT") greater than supported (%"NEWLIB_NANO_COMPAT_FORMAT")"), @@ -600,6 +598,17 @@ static int read_block(httpd_req_t *req, http_parser *parser, size_t offset, size return nbytes; } +/* Map an http_parser failure to the HTTP status sent back. An unrecognized + * request method is a 501 (RFC 9110 section 15.6.2), every other parser + * error means the request syntax is malformed. */ +static httpd_err_code_t parser_errno_to_err_code(const http_parser *parser) +{ + if (HTTP_PARSER_ERRNO(parser) == HPE_INVALID_METHOD) { + return HTTPD_501_METHOD_NOT_IMPLEMENTED; + } + return HTTPD_400_BAD_REQUEST; +} + static int parse_block(http_parser *parser, size_t offset, size_t length) { parser_data_t *data = (parser_data_t *)(parser->data); @@ -661,14 +670,14 @@ static int parse_block(http_parser *parser, size_t offset, size_t length) return 0; } else if (nparsed != length) { /* http_parser error */ - data->error = HTTPD_400_BAD_REQUEST; + data->error = parser_errno_to_err_code(parser); data->status = PARSING_FAILED; ESP_LOGW(TAG, LOG_FMT("incomplete (%"NEWLIB_NANO_COMPAT_FORMAT"/%"NEWLIB_NANO_COMPAT_FORMAT") with parser error = %d"), NEWLIB_NANO_COMPAT_CAST(nparsed), NEWLIB_NANO_COMPAT_CAST(length), parser->http_errno); return -1; } else if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { /* http_parser error */ - data->error = HTTPD_400_BAD_REQUEST; + data->error = parser_errno_to_err_code(parser); data->status = PARSING_FAILED; ESP_LOGE(TAG, LOG_FMT("parser error: %s"), http_errno_description(HTTP_PARSER_ERRNO(parser))); return -1;