diff --git a/components/esp_http_server/src/httpd_txrx.c b/components/esp_http_server/src/httpd_txrx.c index 0467f7b5a1..0393197cc4 100644 --- a/components/esp_http_server/src/httpd_txrx.c +++ b/components/esp_http_server/src/httpd_txrx.c @@ -647,6 +647,18 @@ esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out) struct httpd_req_aux *async_aux = (struct httpd_req_aux *) async->aux; struct httpd_req_aux *r_aux = (struct httpd_req_aux *) r->aux; + if (r_aux->scratch) { + async_aux->scratch = malloc(r_aux->scratch_cur_size); + if (async_aux->scratch == NULL) { + free(async_aux); + free(async); + return ESP_ERR_NO_MEM; + } + memcpy(async_aux->scratch, r_aux->scratch, r_aux->scratch_cur_size); + } else { + async_aux->scratch = NULL; + } + async_aux->resp_hdrs = calloc(hd->config.max_resp_headers, sizeof(struct resp_hdr)); if (async_aux->resp_hdrs == NULL) { free(async_aux); diff --git a/examples/protocols/http_server/async_handlers/main/main.c b/examples/protocols/http_server/async_handlers/main/main.c index c462ddbfbf..3b383acb88 100644 --- a/examples/protocols/http_server/async_handlers/main/main.c +++ b/examples/protocols/http_server/async_handlers/main/main.c @@ -95,7 +95,42 @@ static esp_err_t queue_request(httpd_req_t *req, httpd_req_handler_t handler) /* handle long request (on async thread) */ static esp_err_t long_async(httpd_req_t *req) { - ESP_LOGI(TAG, "running: /long"); + char* buf; + size_t buf_len; + + /* Get URI */ + ESP_LOGI(TAG, "Request URI: %s", req->uri); + + /* Get header value string length and allocate memory for length + 1, + * extra byte for null termination */ + buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1; + if (buf_len > 1) { + buf = malloc(buf_len); + if (buf == NULL) { + ESP_LOGE(TAG, "Failed to allocate memory for headers"); + return ESP_FAIL; + } + /* Copy null terminated value string into buffer */ + if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) { + ESP_LOGI(TAG, "Found header => Host: %s", buf); + } + free(buf); + } + /* Get query string length and allocate memory for length + 1, + * extra byte for null termination */ + buf_len = httpd_req_get_url_query_len(req) + 1; + if (buf_len > 1) { + buf = malloc(buf_len); + if (buf == NULL) { + ESP_LOGE(TAG, "Failed to allocate memory for query string"); + return ESP_FAIL; + } + /* Copy null terminated query string into buffer */ + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { + ESP_LOGI(TAG, "Found query string => %s", buf); + } + free(buf); + } // track the number of long requests static uint8_t req_count = 0; diff --git a/examples/protocols/http_server/async_handlers/pytest_http_server_async.py b/examples/protocols/http_server/async_handlers/pytest_http_server_async.py index bebd7937b1..b61bcf37a5 100644 --- a/examples/protocols/http_server/async_handlers/pytest_http_server_async.py +++ b/examples/protocols/http_server/async_handlers/pytest_http_server_async.py @@ -10,6 +10,57 @@ from pytest_embedded import Dut from pytest_embedded_idf.utils import idf_parametrize +@pytest.mark.ethernet +@idf_parametrize('target', ['esp32'], indirect=['target']) +def test_http_server_async_handler_multiple_long_requests(dut: Dut) -> None: + # Get binary file + binary_file = os.path.join(dut.app.binary_path, 'simple.bin') + bin_size = os.path.getsize(binary_file) + logging.info('http_server_bin_size : {}KB'.format(bin_size // 1024)) + logging.info('Waiting to connect with Ethernet') + + # Parse IP address of Ethernet + got_ip = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)[1].decode() + got_port = 80 # Assuming the server is running on port 80 + logging.info('Got IP : {}'.format(got_ip)) + logging.info('Connecting to server at {}:{}'.format(got_ip, got_port)) + + # Create two HTTP connections for long requests + conn_long1 = http.client.HTTPConnection(got_ip, got_port, timeout=30) + conn_long2 = http.client.HTTPConnection(got_ip, got_port, timeout=30) + + # Test first long URI with Host header and query param + long_uri1 = '/long?param=async1' + headers1 = {'Host': 'testhost1'} + logging.info('Sending first long request with Host header and query param') + conn_long1.request('GET', long_uri1, headers=headers1) + + # Test second long URI with Host header and query param + long_uri2 = '/long?param=async2' + headers2 = {'Host': 'testhost2'} + logging.info('Sending second long request with Host header and query param') + conn_long2.request('GET', long_uri2, headers=headers2) + + # Verify both requests are processed + dut.expect('Found header => Host: testhost1', timeout=30) + dut.expect('Found query string => param=async1', timeout=30) + dut.expect('Found header => Host: testhost2', timeout=30) + dut.expect('Found query string => param=async2', timeout=30) + + # Get responses + response_long1 = conn_long1.getresponse() + response_long2 = conn_long2.getresponse() + + logging.info('Response status for first long URI: {}'.format(response_long1.status)) + logging.info('Response status for second long URI: {}'.format(response_long2.status)) + + assert response_long1.status == 200, 'Failed to access first long URI' + assert response_long2.status == 200, 'Failed to access second long URI' + + conn_long1.close() + conn_long2.close() + + @pytest.mark.ethernet @idf_parametrize('target', ['esp32'], indirect=['target']) def test_http_server_async_handler(dut: Dut) -> None: