diff --git a/CHANGES.rst b/CHANGES.rst index 5ed048099db..06351daee04 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ Changes - Dropped "%O" in access logger #1673 +- Added `history` to `ClientResponseError`. #1741 + 2.0.2 (2017-03-21) ------------------ diff --git a/aiohttp/client.py b/aiohttp/client.py index 6937b43f973..ae46d7f12f2 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -386,6 +386,7 @@ def _ws_connect(self, url, *, if resp.status != 101: raise WSServerHandshakeError( resp.request_info, + resp.history, message='Invalid response status', code=resp.status, headers=resp.headers) @@ -393,6 +394,7 @@ def _ws_connect(self, url, *, if resp.headers.get(hdrs.UPGRADE, '').lower() != 'websocket': raise WSServerHandshakeError( resp.request_info, + resp.history, message='Invalid upgrade header', code=resp.status, headers=resp.headers) @@ -400,6 +402,7 @@ def _ws_connect(self, url, *, if resp.headers.get(hdrs.CONNECTION, '').lower() != 'upgrade': raise WSServerHandshakeError( resp.request_info, + resp.history, message='Invalid connection header', code=resp.status, headers=resp.headers) @@ -411,6 +414,7 @@ def _ws_connect(self, url, *, if key != match: raise WSServerHandshakeError( resp.request_info, + resp.history, message='Invalid challenge response', code=resp.status, headers=resp.headers) diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py index 61c5eec5056..75fbaad8058 100644 --- a/aiohttp/client_exceptions.py +++ b/aiohttp/client_exceptions.py @@ -25,11 +25,13 @@ class ClientResponseError(ClientError): :param request_info: instance of RequestInfo """ - def __init__(self, request_info, *, code=0, message='', headers=None): + def __init__(self, request_info, history, *, + code=0, message='', headers=None): self.request_info = request_info self.code = code self.message = message self.headers = headers + self.history = history super().__init__("%s, message='%s'" % (code, message)) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 673dccf3007..16d1385d64d 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -546,7 +546,7 @@ def start(self, connection, read_until_eof=False): (message, payload) = yield from self._protocol.read() except http.HttpProcessingError as exc: raise ClientResponseError( - self.request_info, + self.request_info, self.history, code=exc.code, message=exc.message, headers=exc.headers) from exc @@ -632,6 +632,7 @@ def raise_for_status(self): if 400 <= self.status: raise ClientResponseError( self.request_info, + self.history, code=self.status, message=self.reason, headers=self.headers) @@ -707,6 +708,7 @@ def json(self, *, encoding=None, loads=json.loads, if content_type not in ctype: raise ClientResponseError( self.request_info, + self.history, message=('Attempt to decode JSON with ' 'unexpected mimetype: %s' % ctype), headers=self.headers) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 70a517a77f2..5e7f5a2fb4f 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -727,6 +727,7 @@ def _create_proxy_connection(self, req): if resp.status != 200: raise ClientHttpProxyError( proxy_resp.request_info, + resp.history, code=resp.status, message=resp.reason, headers=resp.headers) diff --git a/docs/client_reference.rst b/docs/client_reference.rst index e2dc11817d8..24a308286a4 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -1335,6 +1335,9 @@ Hierarchy of exceptions: .. attribute:: request_info Instance of `RequestInfo` object, contains information about request. + .. attribute:: history + History from `ClientResponse` object, if available, else empty tuple. + * `WSServerHandshakeError` - web socket server response error - `ClientHttpProxyError` - proxy response diff --git a/tests/test_client_response.py b/tests/test_client_response.py index fd66fcabd2b..46e582cf637 100644 --- a/tests/test_client_response.py +++ b/tests/test_client_response.py @@ -491,3 +491,61 @@ def test_request_info_in_exception(): with pytest.raises(aiohttp.ClientResponseError) as cm: response.raise_for_status() assert cm.value.request_info == response.request_info + + +def test_no_redirect_history_in_exception(): + url = 'http://def-cl-resp.org' + headers = {'Content-Type': 'application/json;charset=cp1251'} + response = ClientResponse( + 'get', + URL(url), + request_info=RequestInfo( + url, + 'get', + headers + ) + ) + response.status = 409 + response.reason = 'CONFLICT' + with pytest.raises(aiohttp.ClientResponseError) as cm: + response.raise_for_status() + assert () == cm.value.history + + +def test_redirect_history_in_exception(): + hist_url = 'http://def-cl-resp.org' + url = 'http://def-cl-resp.org/index.htm' + hist_headers = {'Content-Type': 'application/json;charset=cp1251', + 'Location': url + } + headers = {'Content-Type': 'application/json;charset=cp1251'} + response = ClientResponse( + 'get', + URL(url), + request_info=RequestInfo( + url, + 'get', + headers + ) + ) + response.status = 409 + response.reason = 'CONFLICT' + + hist_response = ClientResponse( + 'get', + URL(hist_url), + request_info=RequestInfo( + url, + 'get', + headers + ) + ) + + hist_response.headers = hist_headers + hist_response.status = 301 + hist_response.reason = 'REDIRECT' + + response._history = [hist_response] + with pytest.raises(aiohttp.ClientResponseError) as cm: + response.raise_for_status() + assert [hist_response] == cm.value.history