mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'fix/ota_resumption_ci_failures' into 'master'
fix: solve OTA resumption CI failures Closes IDFCI-3505 and IDFCI-3328 See merge request espressif/esp-idf!45984
This commit is contained in:
@@ -4,6 +4,7 @@ import http.server
|
||||
import multiprocessing
|
||||
import os
|
||||
import random
|
||||
import socket
|
||||
import ssl
|
||||
import struct
|
||||
import subprocess
|
||||
@@ -40,12 +41,26 @@ def restart_device_with_random_delay(dut: Dut, min_delay: int = 10, max_delay: i
|
||||
print('Device restarted after random delay.')
|
||||
|
||||
|
||||
def https_request_handler() -> Callable[..., http.server.BaseHTTPRequestHandler]:
|
||||
def https_request_handler(
|
||||
ssl_context: ssl.SSLContext | None = None,
|
||||
) -> Callable[..., http.server.BaseHTTPRequestHandler]:
|
||||
"""
|
||||
Returns a request handler class that handles broken pipe exception
|
||||
Returns a request handler class that handles broken pipe exception.
|
||||
If ssl_context is provided, SSL wrapping is done per-connection in the handler
|
||||
thread (inside setup()) so that the server's main accept loop never blocks on
|
||||
an SSL handshake — critical for OTA resumption tests where the device hard-resets
|
||||
mid-connection.
|
||||
"""
|
||||
|
||||
class RequestHandler(RangeRequestHandler):
|
||||
def setup(self) -> None:
|
||||
if ssl_context is not None:
|
||||
sock: socket.socket = self.request # type: ignore[has-type]
|
||||
sock.settimeout(10) # Timeout for SSL handshake
|
||||
self.request = ssl_context.wrap_socket(sock, server_side=True)
|
||||
self.request.settimeout(None) # Back to blocking mode for data transfer
|
||||
super().setup()
|
||||
|
||||
def finish(self) -> None:
|
||||
try:
|
||||
if not self.wfile.closed:
|
||||
@@ -66,13 +81,15 @@ def https_request_handler() -> Callable[..., http.server.BaseHTTPRequestHandler]
|
||||
|
||||
def start_https_server(ota_image_dir: str, server_ip: str, server_port: int) -> None:
|
||||
os.chdir(ota_image_dir)
|
||||
requestHandler = https_request_handler()
|
||||
httpd = http.server.HTTPServer((server_ip, server_port), requestHandler)
|
||||
|
||||
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
ssl_context.load_cert_chain(certfile=server_file, keyfile=key_file)
|
||||
|
||||
httpd.socket = ssl_context.wrap_socket(httpd.socket, server_side=True)
|
||||
# Pass ssl_context to handler — SSL is done per-connection in handler threads,
|
||||
# keeping the main accept loop on plain TCP (never blocks on SSL handshake)
|
||||
requestHandler = https_request_handler(ssl_context)
|
||||
httpd = http.server.ThreadingHTTPServer((server_ip, server_port), requestHandler)
|
||||
httpd.daemon_threads = True # Handler threads won't block server shutdown
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
@@ -94,12 +111,26 @@ def start_chunked_server(ota_image_dir: str, server_port: int) -> subprocess.Pop
|
||||
return chunked_server
|
||||
|
||||
|
||||
def redirect_handler_factory(url: str) -> Callable[..., http.server.BaseHTTPRequestHandler]:
|
||||
def redirect_handler_factory(
|
||||
url: str,
|
||||
ssl_context: ssl.SSLContext | None = None,
|
||||
) -> Callable[..., http.server.BaseHTTPRequestHandler]:
|
||||
"""
|
||||
Returns a request handler class that redirects to supplied `url`
|
||||
Returns a request handler class that redirects to supplied `url`.
|
||||
If ssl_context is provided, SSL wrapping is done per-connection in the handler
|
||||
thread (inside setup()) so that the server's main accept loop never blocks on
|
||||
an SSL handshake.
|
||||
"""
|
||||
|
||||
class RedirectHandler(http.server.SimpleHTTPRequestHandler):
|
||||
def setup(self) -> None:
|
||||
if ssl_context is not None:
|
||||
sock: socket.socket = self.request # type: ignore[has-type]
|
||||
sock.settimeout(10) # Timeout for SSL handshake
|
||||
self.request = ssl_context.wrap_socket(sock, server_side=True)
|
||||
self.request.settimeout(None) # Back to blocking mode for data transfer
|
||||
super().setup()
|
||||
|
||||
def do_GET(self) -> None:
|
||||
print('Sending resp, URL: ' + url)
|
||||
self.send_response(301)
|
||||
@@ -117,16 +148,18 @@ def redirect_handler_factory(url: str) -> Callable[..., http.server.BaseHTTPRequ
|
||||
|
||||
def start_redirect_server(ota_image_dir: str, server_ip: str, server_port: int, redirection_port: int) -> None:
|
||||
os.chdir(ota_image_dir)
|
||||
redirectHandler = redirect_handler_factory(
|
||||
'https://' + server_ip + ':' + str(redirection_port) + '/advanced_https_ota.bin'
|
||||
)
|
||||
|
||||
httpd = http.server.HTTPServer((server_ip, server_port), redirectHandler)
|
||||
|
||||
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
ssl_context.load_cert_chain(certfile=server_file, keyfile=key_file)
|
||||
|
||||
httpd.socket = ssl_context.wrap_socket(httpd.socket, server_side=True)
|
||||
# Pass ssl_context to handler — SSL is done per-connection in handler threads,
|
||||
# keeping the main accept loop on plain TCP (never blocks on SSL handshake)
|
||||
redirectHandler = redirect_handler_factory(
|
||||
'https://' + server_ip + ':' + str(redirection_port) + '/advanced_https_ota.bin',
|
||||
ssl_context,
|
||||
)
|
||||
httpd = http.server.ThreadingHTTPServer((server_ip, server_port), redirectHandler)
|
||||
httpd.daemon_threads = True # Handler threads won't block server shutdown
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user