Martin Panter added the comment:

I have been experimenting with a patch that changes the default to 
suppress_ragged_eofs=False.

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 
parameters.

>>> import socket, ssl
>>> s = socket.create_connection(("accounts.google.com", 443))
>>> ss = ssl.wrap_socket(s, suppress_ragged_eofs=False)
>>> ss.sendall(b"POST /o/oauth2/token HTTP/1.1\r\n"
...     b"Host: accounts.google.com\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'\r\n'
b'{\n'
b'  "error" : "invalid_request",\n'
b'  "error_description" : "Required parameter is missing: grant_type"\n'
b'}'
>>> 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/ssl.py", line 987, in recv
    return self.read(buflen)
  File "/home/proj/python/cpython/Lib/ssl.py", line 865, in read
    return self._sslobj.read(len, buffer)
  File "/home/proj/python/cpython/Lib/ssl.py", line 627, in read
    v = self._sslobj.read(len)
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: http://bugs.python.org/file44784/ragged-eofs.patch

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue27815>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to