Skip to content

Commit 604957d

Browse files
committed
Merge branch 'fix/async_handler_example_not_working' into 'master'
Fix double freeing issue in Async handler example See merge request espressif/esp-idf!37987
2 parents 112a955 + 17a1a0d commit 604957d

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

components/esp_http_server/src/httpd_txrx.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,18 @@ esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out)
647647
struct httpd_req_aux *async_aux = (struct httpd_req_aux *) async->aux;
648648
struct httpd_req_aux *r_aux = (struct httpd_req_aux *) r->aux;
649649

650+
if (r_aux->scratch) {
651+
async_aux->scratch = malloc(r_aux->scratch_cur_size);
652+
if (async_aux->scratch == NULL) {
653+
free(async_aux);
654+
free(async);
655+
return ESP_ERR_NO_MEM;
656+
}
657+
memcpy(async_aux->scratch, r_aux->scratch, r_aux->scratch_cur_size);
658+
} else {
659+
async_aux->scratch = NULL;
660+
}
661+
650662
async_aux->resp_hdrs = calloc(hd->config.max_resp_headers, sizeof(struct resp_hdr));
651663
if (async_aux->resp_hdrs == NULL) {
652664
free(async_aux);

examples/protocols/http_server/async_handlers/main/main.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,42 @@ static esp_err_t queue_request(httpd_req_t *req, httpd_req_handler_t handler)
9595
/* handle long request (on async thread) */
9696
static esp_err_t long_async(httpd_req_t *req)
9797
{
98-
ESP_LOGI(TAG, "running: /long");
98+
char* buf;
99+
size_t buf_len;
100+
101+
/* Get URI */
102+
ESP_LOGI(TAG, "Request URI: %s", req->uri);
103+
104+
/* Get header value string length and allocate memory for length + 1,
105+
* extra byte for null termination */
106+
buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
107+
if (buf_len > 1) {
108+
buf = malloc(buf_len);
109+
if (buf == NULL) {
110+
ESP_LOGE(TAG, "Failed to allocate memory for headers");
111+
return ESP_FAIL;
112+
}
113+
/* Copy null terminated value string into buffer */
114+
if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
115+
ESP_LOGI(TAG, "Found header => Host: %s", buf);
116+
}
117+
free(buf);
118+
}
119+
/* Get query string length and allocate memory for length + 1,
120+
* extra byte for null termination */
121+
buf_len = httpd_req_get_url_query_len(req) + 1;
122+
if (buf_len > 1) {
123+
buf = malloc(buf_len);
124+
if (buf == NULL) {
125+
ESP_LOGE(TAG, "Failed to allocate memory for query string");
126+
return ESP_FAIL;
127+
}
128+
/* Copy null terminated query string into buffer */
129+
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
130+
ESP_LOGI(TAG, "Found query string => %s", buf);
131+
}
132+
free(buf);
133+
}
99134

100135
// track the number of long requests
101136
static uint8_t req_count = 0;

examples/protocols/http_server/async_handlers/pytest_http_server_async.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,57 @@
1010
from pytest_embedded_idf.utils import idf_parametrize
1111

1212

13+
@pytest.mark.ethernet
14+
@idf_parametrize('target', ['esp32'], indirect=['target'])
15+
def test_http_server_async_handler_multiple_long_requests(dut: Dut) -> None:
16+
# Get binary file
17+
binary_file = os.path.join(dut.app.binary_path, 'simple.bin')
18+
bin_size = os.path.getsize(binary_file)
19+
logging.info('http_server_bin_size : {}KB'.format(bin_size // 1024))
20+
logging.info('Waiting to connect with Ethernet')
21+
22+
# Parse IP address of Ethernet
23+
got_ip = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)[1].decode()
24+
got_port = 80 # Assuming the server is running on port 80
25+
logging.info('Got IP : {}'.format(got_ip))
26+
logging.info('Connecting to server at {}:{}'.format(got_ip, got_port))
27+
28+
# Create two HTTP connections for long requests
29+
conn_long1 = http.client.HTTPConnection(got_ip, got_port, timeout=30)
30+
conn_long2 = http.client.HTTPConnection(got_ip, got_port, timeout=30)
31+
32+
# Test first long URI with Host header and query param
33+
long_uri1 = '/long?param=async1'
34+
headers1 = {'Host': 'testhost1'}
35+
logging.info('Sending first long request with Host header and query param')
36+
conn_long1.request('GET', long_uri1, headers=headers1)
37+
38+
# Test second long URI with Host header and query param
39+
long_uri2 = '/long?param=async2'
40+
headers2 = {'Host': 'testhost2'}
41+
logging.info('Sending second long request with Host header and query param')
42+
conn_long2.request('GET', long_uri2, headers=headers2)
43+
44+
# Verify both requests are processed
45+
dut.expect('Found header => Host: testhost1', timeout=30)
46+
dut.expect('Found query string => param=async1', timeout=30)
47+
dut.expect('Found header => Host: testhost2', timeout=30)
48+
dut.expect('Found query string => param=async2', timeout=30)
49+
50+
# Get responses
51+
response_long1 = conn_long1.getresponse()
52+
response_long2 = conn_long2.getresponse()
53+
54+
logging.info('Response status for first long URI: {}'.format(response_long1.status))
55+
logging.info('Response status for second long URI: {}'.format(response_long2.status))
56+
57+
assert response_long1.status == 200, 'Failed to access first long URI'
58+
assert response_long2.status == 200, 'Failed to access second long URI'
59+
60+
conn_long1.close()
61+
conn_long2.close()
62+
63+
1364
@pytest.mark.ethernet
1465
@idf_parametrize('target', ['esp32'], indirect=['target'])
1566
def test_http_server_async_handler(dut: Dut) -> None:

0 commit comments

Comments
 (0)