Unexpected `SSLEOFError` when Receiving Redirect (307) Response with Large Data Payload
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 54.3k
- Forks
- 10.4k
- Avg merge
- 16h 43m
- Merged PRs (30d)
- 3
Description
This issue occurs only when TLS is enabled on both the client and server sides.
When a requests client sends a large PUT request and receives an HTTP 307 redirect, the server may close the connection early before the payload is fully transmitted. This happens because the server issuing the redirect does not need to process the request body, so it terminates the connection as soon as it sends the redirect response. However, requests does not handle this scenario gracefully and raises the following SSL error:
urllib3.exceptions.SSLError: EOF occurred in violation of protocol (_ssl.c:2426)
Expected Result
If an HTTP 307 redirect is received before the payload is fully transmitted, requests should handle the redirect properly without complaining on the previous early-closed connection. The client should not fail simply because the server closed the connection early.
Actual Result
requests fails with SSLEOFError instead of retrying the request to the redirected location.
This issue does not occur with small payloads (maybe because they are typically sent in a single transmission chunk and completes before the server closes it?)
Reproduction Steps
Client-Side Code
import requests
requests.put("https://localhost:5000/", data="A" * 10_000_000, verify="/path/to/openssl/certs/ca.crt")
...
>> urllib3.exceptions.SSLError: EOF occurred in violation of protocol (_ssl.c:2426)
Server-Side Code (Minimal Proxy)
import http.server
import socketserver
import ssl
import urllib.parse
CERT_FILE = "/path/to/openssl/certs/server.crt"
KEY_FILE = "/path/to/openssl/certs/server.key"
REDIRECT_BASE_URL = "https://google.com"
class ProxyHandler(http.server.BaseHTTPRequestHandler):
def do_PUT(self):
# Uncomment below to drain the request payload and prevent this issue
# content_length = int(self.headers.get("Content-Length", 0))
# if content_length:
# self.rfile.read(content_length) # Uncomment to prevent connection reset
target_url = urllib.parse.urljoin(REDIRECT_BASE_URL, self.path)
self.send_response(307)
self.send_header("Location", target_url)
self.send_header("Content-Length", "0")
self.end_headers()
if __name__ == "__main__":
PORT = 5000
with socketserver.TCPServer(("", PORT), ProxyHandler) as httpd:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
httpd.serve_forever()
SSL Certificate Generation
If needed, generate self-signed TLS certificates using the following OpenSSL commands:
openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -days 1024 -nodes -subj "/CN=localhost" \
-extensions v3_ca -config <(printf "[req]\ndistinguished_name=req\nx509_extensions=v3_ca\n[ v3_ca ]\nsubjectAltName=DNS:localhost,DNS:127.0.0.1,IP:127.0.0.1\nbasicConstraints=CA:TRUE\n")
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/C=US/ST=California/L=Santa Clara/O=COMPANY/OU=TEAM/CN=localhost" \
-config <(printf "[req]\ndistinguished_name=req\nreq_extensions = v3_req\n[ v3_req ]\nsubjectAltName=DNS:localhost,DNS:127.0.0.1,IP:127.0.0.1\n")
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -sha256 \
-extfile <(printf "[ext]\nsubjectAltName=DNS:localhost,DNS:127.0.0.1,IP:127.0.0.1\nbasicConstraints=CA:FALSE\nkeyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment\nextendedKeyUsage=serverAuth,clientAuth\n") -extensions ext
System Information
Python 3.10.12 (main, Jan 17 2025, 14:35:34) [GCC 11.4.0] on linux
$ python3 -m requests.help
{
"chardet": {
"version": null
},
"charset_normalizer": {
"version": "3.4.1"
},
"cryptography": {
"version": ""
},
"idna": {
"version": "3.10"
},
"implementation": {
"name": "CPython",
"version": "3.10.12"
},
"platform": {
"release": "5.15.0-118-generic",
"system": "Linux"
},
"pyOpenSSL": {
"openssl_version": "",
"version": null
},
"requests": {
"version": "2.32.3"
},
"system_ssl": {
"version": "30000020"
},
"urllib3": {
"version": "2.3.0"
},
"using_charset_normalizer": true,
"using_pyopenssl": false
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No repository file or test is named. Start by reproducing the large HTTPS PUT with the provided redirecting server, then trace Requests' redirect and connection-error handling; done means the 307 redirect completes without raising SSLEOFError when the original connection closes early.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100