Mercurial > hg-stable
changeset 15218:c81dce8a7bb6
httpclient: update to 07d8c356f4d1 of py-nonblocking-http
This addresses a defect when the server closes the socket before
finishing a response (if it crashes, for example) first spotted in
Issue2951.
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Mon, 10 Oct 2011 17:57:40 -0500 |
parents | 42d0d4f63bf0 |
children | 9d58569a8b92 |
files | mercurial/httpclient/__init__.py mercurial/httpclient/tests/simple_http_test.py mercurial/httpclient/tests/test_chunked_transfer.py |
diffstat | 3 files changed, 44 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/httpclient/__init__.py Mon Oct 10 13:52:54 2011 +0200 +++ b/mercurial/httpclient/__init__.py Mon Oct 10 17:57:40 2011 -0500 @@ -171,6 +171,14 @@ logger.info('cl: %r body: %r', self._content_len, self._body) try: data = self.sock.recv(INCOMING_BUFFER_SIZE) + # If the socket was readable and no data was read, that + # means the socket was closed. If this isn't a + # _CLOSE_IS_END socket, then something is wrong if we're + # here (we shouldn't enter _select() if the response is + # complete), so abort. + if not data and self._content_len != _LEN_CLOSE_IS_END: + raise HTTPRemoteClosedError( + 'server appears to have closed the socket mid-response') except socket.sslerror, e: if e.args[0] != socket.SSL_ERROR_WANT_READ: raise @@ -693,6 +701,11 @@ class HTTPProxyConnectFailedException(httplib.HTTPException): """Connecting to the HTTP proxy failed.""" + class HTTPStateError(httplib.HTTPException): """Invalid internal state encountered.""" + + +class HTTPRemoteClosedError(httplib.HTTPException): + """The server closed the remote socket in the middle of a response.""" # no-check-code
--- a/mercurial/httpclient/tests/simple_http_test.py Mon Oct 10 13:52:54 2011 +0200 +++ b/mercurial/httpclient/tests/simple_http_test.py Mon Oct 10 17:57:40 2011 -0500 @@ -380,6 +380,21 @@ con.request('GET', '/') self.assertEqual(2, len(sockets)) + def test_server_closes_before_end_of_body(self): + con = http.HTTPConnection('1.2.3.4:80') + con._connect() + s = con.sock + s.data = ['HTTP/1.1 200 OK\r\n', + 'Server: BogusServer 1.0\r\n', + 'Connection: Keep-Alive\r\n', + 'Content-Length: 16', + '\r\n\r\n', + 'You can '] # Note: this is shorter than content-length + s.close_on_empty = True + con.request('GET', '/') + r1 = con.getresponse() + self.assertRaises(http.HTTPRemoteClosedError, r1.read) + def test_no_response_raises_response_not_ready(self): con = http.HTTPConnection('foo') self.assertRaises(http.httplib.ResponseNotReady, con.getresponse)
--- a/mercurial/httpclient/tests/test_chunked_transfer.py Mon Oct 10 13:52:54 2011 +0200 +++ b/mercurial/httpclient/tests/test_chunked_transfer.py Mon Oct 10 17:57:40 2011 -0500 @@ -134,4 +134,20 @@ con.request('GET', '/') self.assertStringEqual('hi there\nthere\nthere\nthere\nthere\n', con.getresponse().read()) + + def testChunkedDownloadEarlyHangup(self): + con = http.HTTPConnection('1.2.3.4:80') + con._connect() + sock = con.sock + broken = chunkedblock('hi'*20)[:-1] + sock.data = ['HTTP/1.1 200 OK\r\n', + 'Server: BogusServer 1.0\r\n', + 'transfer-encoding: chunked', + '\r\n\r\n', + broken, + ] + sock.close_on_empty = True + con.request('GET', '/') + resp = con.getresponse() + self.assertRaises(http.HTTPRemoteClosedError, resp.read) # no-check-code