mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'feat/added_force_close_test_for_async_handler' into 'master'
feat(http_server/async_handler): Added test for async handler Closes IDF-12971 See merge request espressif/esp-idf!43080
This commit is contained in:
@@ -185,3 +185,96 @@ def test_http_server_async_handler_same_session_sequential(dut: Dut) -> None:
|
||||
|
||||
conn.close()
|
||||
logging.info('=== Test completed successfully: Same session sequential requests work ===')
|
||||
|
||||
|
||||
@pytest.mark.ethernet
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_http_server_async_handler_force_close_and_recovery(dut: Dut) -> None:
|
||||
"""
|
||||
Test that verifies server robustness:
|
||||
1. Send /long request and forcefully close before completion
|
||||
2. Verify server doesn't crash
|
||||
3. Send another /long request and verify it completes successfully
|
||||
4. While second /long is running, hit /quick to verify concurrent handling
|
||||
"""
|
||||
# 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}')
|
||||
dut.expect('starting async req task worker', timeout=30)
|
||||
dut.expect('starting async req task worker', timeout=30)
|
||||
dut.expect(f"Starting server on port: '{got_port}'", timeout=30)
|
||||
dut.expect('Registering URI handlers', timeout=30)
|
||||
logging.info(f'Connecting to server at {got_ip}:{got_port}')
|
||||
|
||||
# Test 1: Send /long request and forcefully close connection
|
||||
logging.info('=== Test 1: Sending /long request and forcefully closing connection ===')
|
||||
conn_force_close = http.client.HTTPConnection(got_ip, got_port, timeout=10)
|
||||
conn_force_close.request('GET', '/long?test=force_close')
|
||||
|
||||
# Verify request is received
|
||||
dut.expect('uri: /long', timeout=30)
|
||||
dut.expect('Found query string => test=force_close', timeout=30)
|
||||
|
||||
# Wait a bit to let the async handler start processing
|
||||
time.sleep(10)
|
||||
|
||||
# Forcefully close the connection without reading response
|
||||
logging.info('Forcefully closing connection before /long completes')
|
||||
conn_force_close.close()
|
||||
|
||||
# Verify server doesn't crash (check for SW_CPU_RESET which indicates a crash)
|
||||
try:
|
||||
dut.expect('SW_CPU_RESET', timeout=10)
|
||||
pytest.fail('Server crashed with SW_CPU_RESET after force close - test failed')
|
||||
except Exception:
|
||||
# No SW_CPU_RESET found - this is expected, server should remain stable
|
||||
logging.info('Server remained stable after force close (no SW_CPU_RESET detected)')
|
||||
|
||||
# Test 2: Verify server is still functional by sending another /long request
|
||||
logging.info('=== Test 2: Sending another /long request to verify server recovery ===')
|
||||
conn_recovery = http.client.HTTPConnection(got_ip, got_port, timeout=70)
|
||||
conn_recovery.request('GET', '/long?test=recovery')
|
||||
|
||||
# Verify request is received
|
||||
dut.expect('uri: /long', timeout=30)
|
||||
dut.expect('Found query string => test=recovery', timeout=30)
|
||||
|
||||
# Test 3: While /long is running, hit /quick endpoint
|
||||
logging.info('=== Test 3: Hitting /quick while /long is running ===')
|
||||
time.sleep(5) # Let /long run for a bit
|
||||
|
||||
conn_quick = http.client.HTTPConnection(got_ip, got_port, timeout=10)
|
||||
conn_quick.request('GET', '/quick?test=concurrent')
|
||||
dut.expect('uri: /quick', timeout=30)
|
||||
|
||||
# Get /quick response (should complete immediately)
|
||||
response_quick = conn_quick.getresponse()
|
||||
logging.info(f'Response status for /quick: {response_quick.status}')
|
||||
assert response_quick.status == 200, 'Failed to access /quick URI while /long is running'
|
||||
quick_data = response_quick.read().decode()
|
||||
assert 'random:' in quick_data, 'Expected random number in /quick response'
|
||||
conn_quick.close()
|
||||
logging.info('/quick request completed successfully while /long was running')
|
||||
|
||||
# Now wait for the /long request to complete
|
||||
logging.info('Waiting for /long request to complete...')
|
||||
response_long = conn_recovery.getresponse()
|
||||
logging.info(f'Response status for /long: {response_long.status}')
|
||||
assert response_long.status == 200, 'Failed to complete /long URI after force close recovery'
|
||||
|
||||
# Verify we got the full response
|
||||
response_data = response_long.read().decode()
|
||||
assert 'req:' in response_data, 'Expected request count in response'
|
||||
assert '59' in response_data, 'Expected final tick (59) in response'
|
||||
|
||||
conn_recovery.close()
|
||||
logging.info(
|
||||
'=== Test completed successfully: Server recovered from force close and handled concurrent requests ==='
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user