From dee9d760cddc80cd7884694714e5196322bb90cd Mon Sep 17 00:00:00 2001 From: "hrushikesh.bhosale" Date: Mon, 15 Sep 2025 11:38:28 +0530 Subject: [PATCH] feat(esp_http_server/async_handler): Add CI test for request on same socket Added a CI test to request on same socket one after the another --- .../pytest_http_server_async.py | 95 ++++++++++++++++--- 1 file changed, 83 insertions(+), 12 deletions(-) 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 b61bcf37a5..900282d32b 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 @@ -16,14 +16,14 @@ 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(f'http_server_bin_size : {bin_size // 1024}KB') 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)) + logging.info(f'Got IP : {got_ip}') + logging.info(f'Connecting to server at {got_ip}:{got_port}') # Create two HTTP connections for long requests conn_long1 = http.client.HTTPConnection(got_ip, got_port, timeout=30) @@ -51,8 +51,8 @@ def test_http_server_async_handler_multiple_long_requests(dut: Dut) -> None: 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)) + logging.info(f'Response status for first long URI: {response_long1.status}') + logging.info(f'Response status for second long URI: {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' @@ -67,38 +67,109 @@ def test_http_server_async_handler(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(f'http_server_bin_size : {bin_size // 1024}KB') 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)) + logging.info(f'Got IP : {got_ip}') + logging.info(f'Connecting to server at {got_ip}:{got_port}') # Create HTTP connection conn_long = http.client.HTTPConnection(got_ip, got_port, timeout=15) # Test long URI long_uri = '/long' - logging.info('Sending request to long URI: {}'.format(long_uri)) + logging.info(f'Sending request to long URI: {long_uri}') conn_long.request('GET', long_uri) dut.expect('uri: /long', timeout=30) response_long = conn_long.getresponse() - logging.info('Response status for long URI: {}'.format(response_long.status)) + logging.info(f'Response status for long URI: {response_long.status}') assert response_long.status == 200, 'Failed to access long URI' # Test quick URI for i in range(3): conn_quick = http.client.HTTPConnection(got_ip, got_port, timeout=15) quick_uri = '/quick' - logging.info('Sending request to quick URI: {}'.format(quick_uri)) + logging.info(f'Sending request to quick URI: {quick_uri}') conn_quick.request('GET', quick_uri) time.sleep(1) # Adding a delay of 1 second before getting the response response_quick = conn_quick.getresponse() dut.expect('uri: /quick', timeout=30) - logging.info('Response status for quick URI: {}'.format(response_quick.status)) + logging.info(f'Response status for quick URI: {response_quick.status}') assert response_quick.status == 200, 'Failed to access quick URI' conn_quick.close() conn_long.close() + + +@pytest.mark.ethernet +@idf_parametrize('target', ['esp32'], indirect=['target']) +def test_http_server_async_handler_same_session_sequential(dut: Dut) -> None: + """ + Test that verifies async completion fix: + 1. Send /long request (async, 60 seconds) + 2. Wait for completion + 3. Send another request on same session + 4. Verify second request works (doesn't get stuck) + """ + # Get binary file + binary_file = os.path.join(dut.app.binary_path, 'simple.bin') + bin_size = os.path.getsize(binary_file) + logging.info(f'http_server_bin_size : {bin_size // 1024}KB') + 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(f'Got IP : {got_ip}') + logging.info(f'Connecting to server at {got_ip}:{got_port}') + + # Create HTTP connection for same session testing + conn = http.client.HTTPConnection(got_ip, got_port, timeout=70) # Longer timeout for async + + # Test 1: Send /long request (async, 60 seconds) + logging.info('=== Test 1: Sending /long request (async) ===') + conn.request('GET', '/long?test=sequential1') + + # Verify request is received and processed + dut.expect('uri: /long', timeout=30) + dut.expect('Found query string => test=sequential1', timeout=30) + + # Wait for async completion (60 seconds + buffer) + logging.info('Waiting for async /long request to complete (60 seconds)...') + start_time = time.time() + + # Get response (this will block until async handler completes) + response_long = conn.getresponse() + completion_time = time.time() - start_time + + logging.info(f'Response status for /long: {response_long.status}') + logging.info(f'Async request completed in {completion_time:.2f} seconds') + assert response_long.status == 200, 'Failed to access /long URI' + + # Verify we got the full response (should contain 60 ticks) + response_data = response_long.read().decode() + assert 'req: 1' in response_data, 'Expected request count in response' + assert '59' in response_data, 'Expected final tick (59) in response' + + # Test 3: Send another /long request on same session + logging.info('=== Test 2: Sending another /long request on same session ===') + conn.request('GET', '/long?test=sequential3') + + # Verify third request is processed + dut.expect('uri: /long', timeout=30) + dut.expect('Found query string => test=sequential3', timeout=30) + + # Get response for third request + response_long2 = conn.getresponse() + logging.info(f'Response status for second /long: {response_long2.status}') + assert response_long2.status == 200, 'Failed to access second /long URI on same session' + + # Verify we got the full response + response_data2 = response_long2.read().decode() + assert 'req: 2' in response_data2, 'Expected request count 2 in response' + + conn.close() + logging.info('=== Test completed successfully: Same session sequential requests work ===')