Skip to content

SSLEOFError when serving static MP4 video files greater than a certain size in Webkit browsers using Python >= 3.10.0 with SSL enabled #2926

Closed
@peterhorsley

Description

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:

ERROR:werkzeug:Error on request:
Traceback (most recent call last):
File "C:\Users\phorsley.conda\envs\ssl-eof-3.10.0\lib\site-packages\werkzeug\serving.py", line 365, in run_wsgi
execute(self.server.app)
File "C:\Users\phorsley.conda\envs\ssl-eof-3.10.0\lib\site-packages\werkzeug\serving.py", line 329, in execute
write(data)
File "C:\Users\phorsley.conda\envs\ssl-eof-3.10.0\lib\site-packages\werkzeug\serving.py", line 304, in write
self.wfile.write(data)
File "C:\Users\phorsley.conda\envs\ssl-eof-3.10.0\lib\socketserver.py", line 826, in write
self._sock.sendall(b)
File "C:\Users\phorsley.conda\envs\ssl-eof-3.10.0\lib\ssl.py", line 1236, in sendall
v = self.send(byte_view[count:])
File "C:\Users\phorsley.conda\envs\ssl-eof-3.10.0\lib\ssl.py", line 1205, in send
return self._sslobj.write(data)
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:2384)

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.

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return  "<ul>" + \
            "<li><a href=\"static/1mb.mp4\">1mb mp4 file (works)</a></li>" + \
            "<li><a href=\"static/8mb.mp4\">8mb mp4 file (ssl error)</a></li>" + \
            "</ul>"
            
if __name__ == "__main__":
    app.run(ssl_context='adhoc')

Here's a screen capture of the example app demonstrating the problem:

ssl-eof-chrome-python-3.12.4.mp4

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:

conda create --name ssl-eof-3.x.x python=3.x.x
conda activate ssl-eof-3.x.x
pip install flask pyopenssl
python hello.py

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

  • Python version: 3.10.0
  • Werkzeug version: 3.0.3

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions