diff --git a/CHANGES/2925.feature b/CHANGES/2925.feature new file mode 100644 index 00000000000..7c9562efb88 --- /dev/null +++ b/CHANGES/2925.feature @@ -0,0 +1 @@ +ClientResponse and RequestInfo now have real_url property, which is request url without fragment part being stripped diff --git a/aiohttp/client.py b/aiohttp/client.py index d0e0bb6b39a..2e5d4ee38cd 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -290,7 +290,6 @@ async def _request(self, method, url, *, "with AUTH argument or credentials " "encoded in URL") - url = url.with_fragment(None) cookies = self._cookie_jar.filter_cookies(url) if proxy is not None: diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 0cf5e49502a..b0a1f5e342f 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -54,6 +54,11 @@ class RequestInfo: url = attr.ib(type=URL) method = attr.ib(type=str) headers = attr.ib(type=CIMultiDictProxy) + real_url = attr.ib(type=URL) + + @real_url.default + def real_url_default(self): + return self.url class Fingerprint: @@ -191,8 +196,8 @@ def __init__(self, method, url, *, url2 = url.with_query(params) q.extend(url2.query) url = url.with_query(q) - self.url = url.with_fragment(None) self.original_url = url + self.url = url.with_fragment(None) self.method = method.upper() self.chunked = chunked self.compress = compress @@ -244,7 +249,8 @@ def port(self): @property def request_info(self): - return RequestInfo(self.url, self.method, self.headers) + return RequestInfo(self.url, self.method, + self.headers, self.original_url) def update_host(self, url): """Update destination host, port and connection type (ssl).""" @@ -582,7 +588,8 @@ def __init__(self, method, url, *, self.headers = None self.cookies = SimpleCookie() - self._url = url + self._real_url = url + self._url = url.with_fragment(None) self._body = None self._writer = writer self._continue = continue100 # None by default @@ -608,6 +615,10 @@ def url_obj(self): "Deprecated, use .url #1654", DeprecationWarning, stacklevel=2) return self._url + @property + def real_url(self): + return self._real_url + @property def host(self): return self._url.host diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 669359a92b5..0a9db4ac851 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -1059,6 +1059,12 @@ Response object URL of request (:class:`~yarl.URL`). + .. attribute:: real_url + + Unmodified URL of request (:class:`~yarl.URL`). + + .. versionadded:: 3.2 + .. attribute:: connection :class:`Connection` used for handling response. @@ -1453,6 +1459,12 @@ RequestInfo HTTP headers for request, :class:`multidict.CIMultiDict` instance. + .. attribute:: real_url + + Requested *url* with URL fragment unstripped, :class:`yarl.URL` instance. + + .. versionadded:: 3.2 + BasicAuth ^^^^^^^^^ diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index ab0166c1397..bb90d8c724c 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -273,6 +273,7 @@ unittest Unittest unix unsets +unstripped upstr url urldispatcher diff --git a/tests/test_client_request.py b/tests/test_client_request.py index b862434da4d..c95419bbcbd 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -105,6 +105,14 @@ def test_request_info(make_request): req.headers) +def test_request_info_with_fragment(make_request): + req = make_request('get', 'http://python.org/#urlfragment') + assert req.request_info == aiohttp.RequestInfo( + URL('http://python.org/'), + 'GET', req.headers, + URL('http://python.org/#urlfragment')) + + def test_version_err(make_request): with pytest.raises(ValueError): make_request('get', 'http://python.org/', version='1.c') diff --git a/tests/test_client_response.py b/tests/test_client_response.py index 820baf88a17..2ab2d5bf7ff 100644 --- a/tests/test_client_response.py +++ b/tests/test_client_response.py @@ -1018,3 +1018,18 @@ def side_effect(*args, **kwargs): trace.send_response_chunk_received.call_args == mock.call(response_body) ) + + +def test_response_real_url(loop, session): + url = URL('http://def-cl-resp.org/#urlfragment') + response = ClientResponse('get', url, + request_info=mock.Mock(), + writer=mock.Mock(), + continue100=None, + timer=TimerNoop(), + auto_decompress=True, + traces=[], + loop=loop, + session=session) + assert response.url == url.with_fragment(None) + assert response.real_url == url