Skip to content

Commit 2d9c358

Browse files
authored
Switch event hooks to also run on redirects. (#1806)
* Switch event hooks to also run on redirects * Bump coverage * Add pragma: no cover, because sometime ya just gotta be pragmatic * Update docs with note about response.read()
1 parent 4986743 commit 2d9c358

File tree

3 files changed

+52
-16
lines changed

3 files changed

+52
-16
lines changed

docs/advanced.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,19 @@ def raise_on_4xx_5xx(response):
255255
client = httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]})
256256
```
257257

258+
!!! note
259+
Response event hooks are called before determining if the response body
260+
should be read or not.
261+
262+
If you need access to the response body inside an event hook, you'll
263+
need to call `response.read()`.
264+
258265
The hooks are also allowed to modify `request` and `response` objects.
259266

260267
```python
261268
def add_timestamp(request):
262269
request.headers['x-request-timestamp'] = datetime.now(tz=datetime.utc).isoformat()
263-
270+
264271
client = httpx.Client(event_hooks={'request': [add_timestamp]})
265272
```
266273

httpx/_client.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -886,9 +886,6 @@ def send(
886886
if not stream:
887887
response.read()
888888

889-
for hook in self._event_hooks["response"]:
890-
hook(response)
891-
892889
return response
893890

894891
except Exception as exc:
@@ -907,9 +904,6 @@ def _send_handling_auth(
907904
try:
908905
request = next(auth_flow)
909906

910-
for hook in self._event_hooks["request"]:
911-
hook(request)
912-
913907
while True:
914908
response = self._send_handling_redirects(
915909
request,
@@ -947,8 +941,13 @@ def _send_handling_redirects(
947941
"Exceeded maximum allowed redirects.", request=request
948942
)
949943

944+
for hook in self._event_hooks["request"]:
945+
hook(request)
946+
950947
response = self._send_single_request(request, timeout)
951948
try:
949+
for hook in self._event_hooks["response"]:
950+
hook(response)
952951
response.history = list(history)
953952

954953
if not response.is_redirect:
@@ -1595,12 +1594,9 @@ async def send(
15951594
if not stream:
15961595
await response.aread()
15971596

1598-
for hook in self._event_hooks["response"]:
1599-
await hook(response)
1600-
16011597
return response
16021598

1603-
except Exception as exc:
1599+
except Exception as exc: # pragma: no cover
16041600
await response.aclose()
16051601
raise exc
16061602

@@ -1616,9 +1612,6 @@ async def _send_handling_auth(
16161612
try:
16171613
request = await auth_flow.__anext__()
16181614

1619-
for hook in self._event_hooks["request"]:
1620-
await hook(request)
1621-
16221615
while True:
16231616
response = await self._send_handling_redirects(
16241617
request,
@@ -1656,8 +1649,14 @@ async def _send_handling_redirects(
16561649
"Exceeded maximum allowed redirects.", request=request
16571650
)
16581651

1652+
for hook in self._event_hooks["request"]:
1653+
await hook(request)
1654+
16591655
response = await self._send_single_request(request, timeout)
16601656
try:
1657+
for hook in self._event_hooks["response"]:
1658+
await hook(response)
1659+
16611660
response.history = list(history)
16621661

16631662
if not response.is_redirect:

tests/client/test_event_hooks.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ async def raise_on_4xx_5xx(response):
117117

118118
def test_event_hooks_with_redirect():
119119
"""
120-
A redirect request should not trigger a second 'request' event hook.
120+
A redirect request should trigger additional 'request' and 'response' event hooks.
121121
"""
122122

123123
events = []
@@ -136,6 +136,21 @@ def on_response(response):
136136
http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
137137

138138
assert events == [
139+
{
140+
"event": "request",
141+
"headers": {
142+
"host": "127.0.0.1:8000",
143+
"user-agent": f"python-httpx/{httpx.__version__}",
144+
"accept": "*/*",
145+
"accept-encoding": "gzip, deflate, br",
146+
"connection": "keep-alive",
147+
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
148+
},
149+
},
150+
{
151+
"event": "response",
152+
"headers": {"location": "/", "server": "testserver"},
153+
},
139154
{
140155
"event": "request",
141156
"headers": {
@@ -157,7 +172,7 @@ def on_response(response):
157172
@pytest.mark.usefixtures("async_environment")
158173
async def test_async_event_hooks_with_redirect():
159174
"""
160-
A redirect request should not trigger a second 'request' event hook.
175+
A redirect request should trigger additional 'request' and 'response' event hooks.
161176
"""
162177

163178
events = []
@@ -176,6 +191,21 @@ async def on_response(response):
176191
await http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
177192

178193
assert events == [
194+
{
195+
"event": "request",
196+
"headers": {
197+
"host": "127.0.0.1:8000",
198+
"user-agent": f"python-httpx/{httpx.__version__}",
199+
"accept": "*/*",
200+
"accept-encoding": "gzip, deflate, br",
201+
"connection": "keep-alive",
202+
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
203+
},
204+
},
205+
{
206+
"event": "response",
207+
"headers": {"location": "/", "server": "testserver"},
208+
},
179209
{
180210
"event": "request",
181211
"headers": {

0 commit comments

Comments
 (0)