diff --git a/aiohttp/client.py b/aiohttp/client.py index 07f69de8237..5a7c95a1045 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -141,6 +141,7 @@ def _request(self, method, url, *, raise RuntimeError('Session is closed') redirects = 0 + history = [] if not isinstance(method, upstr): method = upstr(method) @@ -193,6 +194,7 @@ def _request(self, method, url, *, # redirects if resp.status in (301, 302, 303, 307) and allow_redirects: redirects += 1 + history.append(resp) if max_redirects and redirects >= max_redirects: resp.close(force=True) break @@ -221,6 +223,7 @@ def _request(self, method, url, *, break + resp.history = history return resp def ws_connect(self, url, *, diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 544f06e7513..0655ae3f34c 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -512,6 +512,7 @@ class ClientResponse: cookies = None # Response cookies (Set-Cookie) content = None # Payload stream headers = None # Response headers, CIMultiDictProxy + history = None # List of responses, if redirects occured _connection = None # current connection flow_control_class = FlowControlStreamReader # reader flow control diff --git a/docs/client.rst b/docs/client.rst index 461df90e858..a4fb39771cd 100644 --- a/docs/client.rst +++ b/docs/client.rst @@ -527,6 +527,22 @@ If a response contains some Cookies, you can quickly access them:: ` object. +Response History +---------------- + +If a request was redirected, it is possible to view previous responses using +the history attribute:: + + >>> r = await aiohttp.get('http://example.com/some/redirect/') + >>> r + + >>> r.history + [] + +If no redirects occured or ``allow_redirects`` is set to ``False``, history will +be an empty list. + + Timeouts -------- diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 7feaf4ebd97..81b5747821c 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -954,6 +954,11 @@ Response object HTTP headers of response, :class:`CIMultiDictProxy`. + .. attribute:: history + + :class:`list` of :class:`ClientResponse` objects of preceding requests, if there + were redirects. + .. method:: close() Close response and underlying connection. diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index d4f387ea360..88ad293785c 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -333,3 +333,33 @@ def handler(request): assert 200 == resp.status finally: yield from resp.release() + + +@pytest.mark.run_loop +def test_history(create_app_and_client): + @asyncio.coroutine + def handler_redirect(request): + return web.Response(status=301, headers={'Location': '/ok'}) + + @asyncio.coroutine + def handler_ok(request): + return web.Response(status=200) + + app, client = yield from create_app_and_client() + app.router.add_route('GET', '/ok', handler_ok) + app.router.add_route('GET', '/redirect', handler_redirect) + + resp = yield from client.get('/ok') + try: + assert resp.history == [] + assert resp.status == 200 + finally: + resp.release() + + resp_redirect = yield from client.get('/redirect') + try: + assert len(resp_redirect.history) == 1 + assert resp_redirect.history[0].status == 301 + assert resp_redirect.status == 200 + finally: + resp_redirect.release()