Merge branch 'bugfix/http_header_parser_resolution_v3.2' into 'release/v3.2'
(backport v3.2) esp_http_server : Bugfix in parsing of empty header values See merge request espressif/esp-idf!6040
This commit is contained in:
commit
62f201b4bd
4 changed files with 169 additions and 2 deletions
|
@ -262,6 +262,23 @@ static esp_err_t cb_header_value(http_parser *parser, const char *at, size_t len
|
|||
parser_data->last.at = at;
|
||||
parser_data->last.length = 0;
|
||||
parser_data->status = PARSING_HDR_VALUE;
|
||||
|
||||
if (length == 0) {
|
||||
/* As per behavior of http_parser, when length > 0,
|
||||
* `at` points to the start of CRLF. But, in the
|
||||
* case when header value is empty (zero length),
|
||||
* then `at` points to the position right after
|
||||
* the CRLF. Since for our purpose we need `last.at`
|
||||
* to point to exactly where the CRLF starts, it
|
||||
* needs to be adjusted by the right offset */
|
||||
char *at_adj = (char *)parser_data->last.at;
|
||||
/* Find the end of header field string */
|
||||
while (*(--at_adj) != ':');
|
||||
/* Now skip leading spaces' */
|
||||
while (*(++at_adj) == ' ');
|
||||
/* Now we are at the right position */
|
||||
parser_data->last.at = at_adj;
|
||||
}
|
||||
} else if (parser_data->status != PARSING_HDR_VALUE) {
|
||||
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
|
||||
parser_data->status = PARSING_FAILED;
|
||||
|
|
|
@ -129,6 +129,8 @@ def test_examples_protocol_http_server_advanced(env, extra_data):
|
|||
failed = True
|
||||
if not client.get_false_uri(got_ip, got_port):
|
||||
failed = True
|
||||
if not client.get_test_headers(got_ip, got_port):
|
||||
failed = True
|
||||
|
||||
Utility.console_log("Error code tests...")
|
||||
if not client.code_500_server_error_test(got_ip, got_port):
|
||||
|
|
|
@ -28,7 +28,107 @@ esp_err_t hello_get_handler(httpd_req_t *req)
|
|||
#undef STR
|
||||
}
|
||||
|
||||
esp_err_t hello_type_get_handler(httpd_req_t *req)
|
||||
/* This handler is intended to check what happens in case of empty values of headers.
|
||||
* Here `Header2` is an empty header and `Header1` and `Header3` will have `Value1`
|
||||
* and `Value3` in them. */
|
||||
static esp_err_t test_header_get_handler(httpd_req_t *req)
|
||||
{
|
||||
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
|
||||
int buf_len;
|
||||
char *buf;
|
||||
|
||||
buf_len = httpd_req_get_hdr_value_len(req, "Header1");
|
||||
if (buf_len > 0) {
|
||||
buf = malloc(++buf_len);
|
||||
if (!buf) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", buf_len);
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
/* Copy null terminated value string into buffer */
|
||||
if (httpd_req_get_hdr_value_str(req, "Header1", buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Header1 content: %s", buf);
|
||||
if (strcmp("Value1", buf) != 0) {
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
free(buf);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Expected value and received value matched for Header1");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Error in getting value of Header1");
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
free(buf);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
free(buf);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Header1 not found");
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
buf_len = httpd_req_get_hdr_value_len(req, "Header3");
|
||||
if (buf_len > 0) {
|
||||
buf = malloc(++buf_len);
|
||||
if (!buf) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", buf_len);
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
/* Copy null terminated value string into buffer */
|
||||
if (httpd_req_get_hdr_value_str(req, "Header3", buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Header3 content: %s", buf);
|
||||
if (strcmp("Value3", buf) != 0) {
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
free(buf);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Expected value and received value matched for Header3");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Error in getting value of Header3");
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
free(buf);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
free(buf);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Header3 not found");
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
buf_len = httpd_req_get_hdr_value_len(req, "Header2");
|
||||
buf = malloc(++buf_len);
|
||||
if (!buf) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", buf_len);
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (httpd_req_get_hdr_value_str(req, "Header2", buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Header2 content: %s", buf);
|
||||
httpd_resp_send(req, buf, strlen(buf));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Header2 not found");
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
httpd_resp_send(req, "Memory allocation failed", sizeof("Memory allocation failed"));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t hello_type_get_handler(httpd_req_t *req)
|
||||
{
|
||||
#define STR "Hello World!"
|
||||
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
|
||||
|
@ -213,6 +313,11 @@ httpd_uri_t basic_handlers[] = {
|
|||
.handler = hello_type_get_handler,
|
||||
.user_ctx = NULL,
|
||||
},
|
||||
{ .uri = "/test_header",
|
||||
.method = HTTP_GET,
|
||||
.handler = test_header_get_handler,
|
||||
.user_ctx = NULL,
|
||||
},
|
||||
{ .uri = "/hello",
|
||||
.method = HTTP_GET,
|
||||
.handler = hello_get_handler,
|
||||
|
@ -270,6 +375,8 @@ httpd_handle_t test_httpd_start()
|
|||
pre_start_mem = esp_get_free_heap_size();
|
||||
httpd_handle_t hd;
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
/* Modify this setting to match the number of test URI handlers */
|
||||
config.max_uri_handlers = 9;
|
||||
config.server_port = 1234;
|
||||
|
||||
/* This check should be a part of http_server */
|
||||
|
|
|
@ -144,7 +144,20 @@ import http.client
|
|||
import sys
|
||||
import string
|
||||
import random
|
||||
import Utility
|
||||
|
||||
|
||||
try:
|
||||
import Utility
|
||||
except ImportError:
|
||||
import os
|
||||
|
||||
# This environment variable is expected on the host machine
|
||||
# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
|
||||
test_fw_path = os.getenv("TEST_FW_PATH")
|
||||
if test_fw_path and test_fw_path not in sys.path:
|
||||
sys.path.insert(0, test_fw_path)
|
||||
|
||||
import Utility
|
||||
|
||||
_verbose_ = False
|
||||
|
||||
|
@ -419,6 +432,33 @@ def get_echo(dut, port):
|
|||
conn.close()
|
||||
return True
|
||||
|
||||
def get_test_headers(dut, port):
|
||||
# GET /test_header returns data of Header2'
|
||||
Utility.console_log("[test] GET /test_header =>", end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
custom_header = {"Header1": "Value1", "Header3": "Value3"}
|
||||
header2_values = ["", " ", "Value2", " Value2", "Value2 ", " Value2 "]
|
||||
for val in header2_values:
|
||||
custom_header["Header2"] = val
|
||||
conn.request("GET", "/test_header", headers=custom_header)
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 200, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
hdr_val_start_idx = val.find("Value2")
|
||||
if hdr_val_start_idx == -1:
|
||||
if not test_val("header: Header2", "", resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
else:
|
||||
if not test_val("header: Header2", val[hdr_val_start_idx:], resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
resp.read()
|
||||
Utility.console_log("Success")
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
def get_hello_type(dut, port):
|
||||
# GET /hello/type_html returns text/html as Content-Type'
|
||||
Utility.console_log("[test] GET /hello/type_html has Content-Type of text/html =>", end=' ')
|
||||
|
@ -835,6 +875,7 @@ if __name__ == '__main__':
|
|||
get_hello_type(dut, port)
|
||||
get_hello_status(dut, port)
|
||||
get_false_uri(dut, port)
|
||||
get_test_headers(dut, port)
|
||||
|
||||
Utility.console_log("### Error code tests")
|
||||
code_500_server_error_test(dut, port)
|
||||
|
|
Loading…
Reference in a new issue