Closed peterhorsley closed 3 weeks ago
Thank you for providing a minimal example. However, I can't reproduce the issue with the information provided. The file was successfully served in Safari and Vivaldi, two WebKit based browsers.
This would be something to report to Python, the development server is a basic wrapper around http.server
and ssl
, we're not doing anything that would affect how SSL works.
From these logs it does appear the problem is related to 206 partial response / range request handling in werkzeug, however I am not sure where to start to identify root cause as I am not an expert on these types of HTTP responses.
That's not clear from those logs, which are hundreds of lines of dense output. I'm happy to fix an issue if we're handling 206 wrong, but you'll need to investigate and report that issue instead.
Thanks for testing and responding quickly. I have reported it against python here: https://github.com/python/cpython/issues/122254
A python dev responded and confirmed it's due to a regression introduced in python 3.10, and the fix will be available in python 3.13 once that is released later this year. Unfortunately the fix will not be backported due to various reasons, so that leaves 3.10/3.11/3.12 as python versions that cannot reliably be used to serve large files over ssl with werkzeug. However, as the python dev suggests it's ok to ignore this exception and treat it like the connection has been dropped, there is a small code change that can fix this in werkzeug, which I have tested - to add ssl.SSLEOFError as an additional exception caught to handle client-side connection drops on line 365 of src/werkzeug/serving.py:
try:
execute(self.server.app)
except (ConnectionError, socket.timeout, ssl.SSLEOFError) as e:
self.connection_dropped(e, environ)
If this looks acceptable, I can raise a PR for this change.
Sure, that makes sense.
When serving static MP4 video files greater than a certain size in Webkit browsers using Python >= 3.10.0 with SSL enabled, an SSLEOFError occurs:
This has previously been reported via issue #2852, but I'm providing a tiny cut-down example app (including MP4 files) that reproduces the problem, along with additional debug logs, to help evaluate if a code change may be needed in werkzeug's handling of 206 PARTIAL RESPONSEs.
Example app: ssl-eof.zip
This is the example app code for reference.
Here's a screen capture of the example app demonstrating the problem:
https://github.com/user-attachments/assets/a744a094-a334-41d3-9461-e91b80c71db1
A 1Mb MP4 file does not trigger the error, but an 8Mb MP4 file does.
It reproduces using Chrome, Brave and Edge, but not Firefox.
Using Flask 3.0.3, Werkzeug 3.0.3 and pyOpenSSL 24.2.1 on Windows 10, I tested the example app with following versions of python and discovered the problem started with Python 3.10.0:
Using Python 3.8.10 (uses OpenSSL 1.1.1w): no error Using Python 3.9.19 (uses OpenSSL 3.0.14): no error Using Python 3.10.0 (uses OpenSSL 1.1.1w): error Using Python 3.10.14 (uses OpenSSL 3.0.14): error Using Python 3.12.4 (uses OpenSSL 3.0.14): error
I added this log line before the call to self.wfile.write(data) on line 304 of serving.py:
logging.warning(f'writing {len(data)} bytes with status {status_sent} {headers_sent}')
Attached are the console outputs for both Python 3.9.19 and 3.10.0. The only difference is the presence of the call stack (twice) in 3.10.0.
python-3.10.0-logs.txt
python-3.9.19-logs.txt
From these logs it does appear the problem is related to 206 partial response / range request handling in werkzeug, however I am not sure where to start to identify root cause as I am not an expert on these types of HTTP responses.
I did check the python 3.10.0 release notes and it does appear that something related to ssl was changed, as they mention 'PEP 644 -- Require OpenSSL 1.1.1 or newer'.
Tip - I used miniconda and the following commands to test different versions of python:
Then load https://127.0.0.1:5000/ in a webkit browser (ignore security warning).
Happy to do any further testing / debugging to help narrow this down!
Environment: Windows 10