Skip to content

Commit b04d53f

Browse files
alessio-locatelliJWCook
authored andcommitted
fix: use request headers in CachedResponse.request_info
1 parent 9af63a2 commit b04d53f

File tree

3 files changed

+28
-6
lines changed

3 files changed

+28
-6
lines changed

HISTORY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## Unreleased
44

5-
- Fixed a race condition in SQLite backend that could lead to `sqlite3.OperationalError: database is locked` errors
5+
- Fixed a race condition in SQLite backend that could lead to `sqlite3.OperationalError: database is locked` errors.
6+
- Fixed an issue where `CachedResponse.request_info` contained response headers instead of request headers.
67

78
## 0.13.0 (2025-04-08)
89

aiohttp_client_cache/response.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
'last_used',
3636
'real_url',
3737
'request_info',
38+
'request_raw_headers',
3839
}
3940

4041
# Default attributes to add to ClientResponse objects
@@ -78,6 +79,7 @@ class CachedResponse(HeadersMixin):
7879
encoding: str = attr.ib(default='utf-8')
7980
expires: datetime | None = attr.ib(default=None)
8081
raw_headers: RawHeaders = attr.ib(factory=tuple)
82+
request_raw_headers: RawHeaders = attr.ib(factory=tuple)
8183
real_url: StrOrURL = attr.ib(default=None)
8284
history: tuple = attr.ib(factory=tuple)
8385
last_used: datetime = attr.ib(factory=utcnow)
@@ -105,6 +107,10 @@ async def from_client_response(
105107
response.expires = expires
106108
response.links = client_response.links
107109
response.real_url = client_response.request_info.real_url
110+
response.request_raw_headers = tuple(
111+
(k.encode('utf-8'), v.encode('utf-8'))
112+
for k, v in client_response.request_info.headers.items()
113+
)
108114

109115
# The encoding may be unset even if the response has been read, and
110116
# get_encoding() does not handle certain edge cases like an empty response body
@@ -185,12 +191,25 @@ def ok(self) -> bool:
185191
"""Returns ``True`` if ``status`` is less than ``400``, ``False`` if not"""
186192
return self.status < 400
187193

194+
@property
195+
def request_headers(self) -> CIMultiDictProxy[str]:
196+
"""Get request headers as an immutable, case-insensitive multidict from raw headers"""
197+
198+
def decode_header(header):
199+
"""Decode an individual (key, value) pair"""
200+
return (
201+
header[0].decode('utf-8', 'surrogateescape'),
202+
header[1].decode('utf-8', 'surrogateescape'),
203+
)
204+
205+
return CIMultiDictProxy(CIMultiDict([decode_header(h) for h in self.request_raw_headers]))
206+
188207
@property
189208
def request_info(self) -> RequestInfo:
190209
return RequestInfo(
191210
url=URL(self.url),
192211
method=self.method,
193-
headers=self.headers,
212+
headers=self.request_headers,
194213
real_url=URL(str(self.real_url)),
195214
)
196215

test/unit/test_response.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
from aiohttp_client_cache.response import CachedResponse, RequestInfo, UnsupportedExpiresError
1313

1414

15-
async def get_test_response(client_factory, url='/', **kwargs):
15+
async def get_test_response(client_factory, url='/', headers=None, **kwargs):
1616
app = web.Application()
1717
app.router.add_route('GET', '/valid_url', mock_handler)
1818
app.router.add_route('GET', '/json', json_mock_handler)
1919
app.router.add_route('GET', '/empty_content', empty_mock_handler)
2020
app.router.add_route('GET', '/null_content', null_mock_handler)
2121
client = await client_factory(app)
22-
client_response = await client.get(url)
22+
client_response = await client.get(url, headers=headers)
2323

2424
return await CachedResponse.from_client_response(client_response, **kwargs)
2525

@@ -97,14 +97,16 @@ async def test_encoding(aiohttp_client):
9797

9898

9999
async def test_request_info(aiohttp_client):
100-
response = await get_test_response(aiohttp_client, '/valid_url')
100+
response = await get_test_response(
101+
aiohttp_client, '/valid_url', headers={'Custom-Header': 'value'}
102+
)
101103
request_info = response.request_info
102104

103105
assert isinstance(request_info, RequestInfo)
104106
assert request_info.method == 'GET'
105107
assert request_info.url == request_info.real_url
106108
assert str(request_info.url).endswith('/valid_url')
107-
assert 'Content-Type' in request_info.headers and 'Content-Length' in request_info.headers
109+
assert request_info.headers['Custom-Header'] == 'value'
108110

109111

110112
async def test_headers(aiohttp_client):

0 commit comments

Comments
 (0)