This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author martin.panter
Recipients christian.heimes, martin.panter
Date 2016-09-22.09:24:49
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1474536290.71.0.506277276066.issue27815@psf.upfronthosting.co.za>
In-reply-to
Content
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.
History
Date User Action Args
2016-09-22 09:24:52martin.pantersetrecipients: + martin.panter, christian.heimes
2016-09-22 09:24:50martin.pantersetmessageid: <1474536290.71.0.506277276066.issue27815@psf.upfronthosting.co.za>
2016-09-22 09:24:50martin.panterlinkissue27815 messages
2016-09-22 09:24:50martin.pantercreate