Mercurial > public > mercurial-scm > hg-stable
diff mercurial/httpclient/__init__.py @ 14376:a75e0f4ba0ab
httpclient: import revision fc731618702a of py-nonblocking-http
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Tue, 17 May 2011 10:28:03 -0500 |
parents | 5c3de67e7402 |
children | c81dce8a7bb6 |
line wrap: on
line diff
--- a/mercurial/httpclient/__init__.py Mon May 16 16:59:45 2011 -0500 +++ b/mercurial/httpclient/__init__.py Tue May 17 10:28:03 2011 -0500 @@ -112,6 +112,10 @@ def complete(self): """Returns true if this response is completely loaded. + + Note that if this is a connection where complete means the + socket is closed, this will nearly always return False, even + in cases where all the data has actually been loaded. """ if self._chunked: return self._chunked_done @@ -174,10 +178,7 @@ return True logger.debug('response read %d data during _select', len(data)) if not data: - if not self.headers: - self._load_response(self._end_headers) - self._content_len = 0 - elif self._content_len == _LEN_CLOSE_IS_END: + if self.headers and self._content_len == _LEN_CLOSE_IS_END: self._content_len = len(self._body) return False else: @@ -561,17 +562,46 @@ continue if not data: logger.info('socket appears closed in read') - outgoing_headers = body = None - break + self.sock = None + self._current_response = None + # This if/elif ladder is a bit subtle, + # comments in each branch should help. + if response is not None and ( + response.complete() or + response._content_len == _LEN_CLOSE_IS_END): + # Server responded completely and then + # closed the socket. We should just shut + # things down and let the caller get their + # response. + logger.info('Got an early response, ' + 'aborting remaining request.') + break + elif was_first and response is None: + # Most likely a keepalive that got killed + # on the server's end. Commonly happens + # after getting a really large response + # from the server. + logger.info( + 'Connection appeared closed in read on first' + ' request loop iteration, will retry.') + reconnect('read') + continue + else: + # We didn't just send the first data hunk, + # and either have a partial response or no + # response at all. There's really nothing + # meaningful we can do here. + raise HTTPStateError( + 'Connection appears closed after ' + 'some request data was written, but the ' + 'response was missing or incomplete!') + logger.debug('read %d bytes in request()', len(data)) if response is None: response = self.response_class(r[0], self.timeout) response._load_response(data) - if (response._content_len == _LEN_CLOSE_IS_END - and len(data) == 0): - response._content_len = len(response._body) - if response.complete(): - w = [] - response.will_close = True + # Jump to the next select() call so we load more + # data if the server is still sending us content. + continue except socket.error, e: if e[0] != errno.EPIPE and not was_first: raise @@ -662,4 +692,7 @@ class HTTPProxyConnectFailedException(httplib.HTTPException): """Connecting to the HTTP proxy failed.""" + +class HTTPStateError(httplib.HTTPException): + """Invalid internal state encountered.""" # no-check-code