Unexpected `SSLEOFError` when Receiving Redirect (307) Response with Large Data Payload
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 35/100
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.
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
}
- Dominant language
- Python
- Stars
- 54.7k
- Forks
- 10.8k
- Avg merge
- 18m
- Merged PRs (30d)
- 1
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing 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.
More from psf/requests
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
-
`iter_lines(decode_unicode=True, delimiter=<str>)` raises `TypeError` when `Response.encoding` is `None`May be free again A pull request for this issue was closed without being merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
-
get_encoding_from_headers treats any content type containing 'text' as text/* (ISO-8859-1)Possibly taken @suyashgitt claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Invalid `params` type (e.g. int) raises misleading "TypeError: Cannot mix str and non-str arguments"Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
plot_graph crashes when a mesh node has no m2m edgePossibly taken @tygvi claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
mllam/neural-lam#883 · 1 comment ·
Maintainers usually reply within 2 days
-
enhancement possible-duplicate
Difficulty 1/5 1-3 hours Newbie friendliness 78/100
AOSSIE-Org/PictoPy#1715 · 2 comments ·
Maintainers usually reply within 6 days
-
SQLite connector omits generated columns from the catalog, and drops relationships sourced on themOpen
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
NVIDIA/auto-ontology#511 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
ethereum/execution-specs#3996 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
cpp-best-practices/project_validator#267 ·
Maintainers usually reply within 1 day