Martin Panter added the comment:

I have been experimenting with a patch that changes the default to 

One disadvantage of this change is it could make servers less robust. E.g. in 
the tests, I explicitly enabled suppress_ragged_eofs=True in a server, because 
otherwise I would have to add extra cleanup if an exception is raised on the 
server side. In another test server, based on socketserver, I added special 
code to silence logging an SSLEOFError exception (a side effect of a particular 
test case).

But ideally, real-world servers should already handle other exceptions that are 
triggered outside the server’s control like, ECONNRESET and 
TLSV1_ALERT_UNKNOWN_CA. Socketserver already logs all these kinds of errors. 
Plus I think SSLEOFError has always been possible in the handshake phase.

With HTTP I didn’t have to go further than Google to find a server that 
terminates the response with a non-SSL shutdown (although because truncated 
JSON is invalid, it is not a big deal). For the record, here is a stripped-down 
demo. The same problem also happens for a more complete request with valid 

>>> import socket, ssl
>>> s = socket.create_connection(("", 443))
>>> ss = ssl.wrap_socket(s, suppress_ragged_eofs=False)
>>> ss.sendall(b"POST /o/oauth2/token HTTP/1.1\r\n"
...     b"Host:\r\n"
...     b"Content-Length: 0\r\n"
...     b"Connection: close\r\n"
...     b"\r\n")
>>> print("\n".join(map(repr, ss.recv(3000).splitlines(keepends=True))))
b'HTTP/1.1 400 Bad Request\r\n'
b'Content-Type: application/json; charset=utf-8\r\n'
b'Cache-Control: no-cache, no-store, max-age=0, must-revalidate\r\n'
b'Pragma: no-cache\r\n'
b'Expires: Mon, 01 Jan 1990 00:00:00 GMT\r\n'
b'Date: Thu, 22 Sep 2016 03:10:20 GMT\r\n'
b'X-Content-Type-Options: nosniff\r\n'
b'X-Frame-Options: SAMEORIGIN\r\n'
b'X-XSS-Protection: 1; mode=block\r\n'
b'Server: GSE\r\n'
b'Alt-Svc: quic=":443"; ma=2592000; v="36,35,34,33,32"\r\n'
b'Accept-Ranges: none\r\n'
b'Vary: Accept-Encoding\r\n'
b'Connection: close\r\n'
b'  "error" : "invalid_request",\n'
b'  "error_description" : "Required parameter is missing: grant_type"\n'
>>> ss.recv(3000)  # HTTP-level client does not know that this is the EOF yet
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/proj/python/cpython/Lib/", line 987, in recv
  File "/home/proj/python/cpython/Lib/", line 865, in read
    return, buffer)
  File "/home/proj/python/cpython/Lib/", line 627, in read
    v =
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:2176)

In this case, if the client does not send “Connection: close”, the server uses 
chunked encoding and there is no problem. So this is another instance of Issue 
12849 (Python’s unusual request triggering a server bug).

I wonder if a solution would be to use suppress_ragged_eofs=False by default, 
but add a way to let the user of the http.client module explicitly allow 
SSLEOFError to signal a proper EOF.

keywords: +patch
versions: +Python 3.7 -Python 3.6
Added file:

Python tracker <>
Python-bugs-list mailing list

Reply via email to