Skip to content

Commit

Permalink
Switch event hooks to also run on redirects. (#1806)
Browse files Browse the repository at this point in the history
* 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()
  • Loading branch information
tomchristie committed Aug 18, 2021
1 parent 4986743 commit 2d9c358
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 16 deletions.
9 changes: 8 additions & 1 deletion docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,19 @@ def raise_on_4xx_5xx(response):
client = httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]})
```

!!! note
Response event hooks are called before determining if the response body
should be read or not.

If you need access to the response body inside an event hook, you'll
need to call `response.read()`.

The hooks are also allowed to modify `request` and `response` objects.

```python
def add_timestamp(request):
request.headers['x-request-timestamp'] = datetime.now(tz=datetime.utc).isoformat()

client = httpx.Client(event_hooks={'request': [add_timestamp]})
```

Expand Down
25 changes: 12 additions & 13 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -886,9 +886,6 @@ def send(
if not stream:
response.read()

for hook in self._event_hooks["response"]:
hook(response)

return response

except Exception as exc:
Expand All @@ -907,9 +904,6 @@ def _send_handling_auth(
try:
request = next(auth_flow)

for hook in self._event_hooks["request"]:
hook(request)

while True:
response = self._send_handling_redirects(
request,
Expand Down Expand Up @@ -947,8 +941,13 @@ def _send_handling_redirects(
"Exceeded maximum allowed redirects.", request=request
)

for hook in self._event_hooks["request"]:
hook(request)

response = self._send_single_request(request, timeout)
try:
for hook in self._event_hooks["response"]:
hook(response)
response.history = list(history)

if not response.is_redirect:
Expand Down Expand Up @@ -1595,12 +1594,9 @@ async def send(
if not stream:
await response.aread()

for hook in self._event_hooks["response"]:
await hook(response)

return response

except Exception as exc:
except Exception as exc: # pragma: no cover
await response.aclose()
raise exc

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

for hook in self._event_hooks["request"]:
await hook(request)

while True:
response = await self._send_handling_redirects(
request,
Expand Down Expand Up @@ -1656,8 +1649,14 @@ async def _send_handling_redirects(
"Exceeded maximum allowed redirects.", request=request
)

for hook in self._event_hooks["request"]:
await hook(request)

response = await self._send_single_request(request, timeout)
try:
for hook in self._event_hooks["response"]:
await hook(response)

response.history = list(history)

if not response.is_redirect:
Expand Down
34 changes: 32 additions & 2 deletions tests/client/test_event_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ async def raise_on_4xx_5xx(response):

def test_event_hooks_with_redirect():
"""
A redirect request should not trigger a second 'request' event hook.
A redirect request should trigger additional 'request' and 'response' event hooks.
"""

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

assert events == [
{
"event": "request",
"headers": {
"host": "127.0.0.1:8000",
"user-agent": f"python-httpx/{httpx.__version__}",
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"connection": "keep-alive",
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
},
},
{
"event": "response",
"headers": {"location": "/", "server": "testserver"},
},
{
"event": "request",
"headers": {
Expand All @@ -157,7 +172,7 @@ def on_response(response):
@pytest.mark.usefixtures("async_environment")
async def test_async_event_hooks_with_redirect():
"""
A redirect request should not trigger a second 'request' event hook.
A redirect request should trigger additional 'request' and 'response' event hooks.
"""

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

assert events == [
{
"event": "request",
"headers": {
"host": "127.0.0.1:8000",
"user-agent": f"python-httpx/{httpx.__version__}",
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"connection": "keep-alive",
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
},
},
{
"event": "response",
"headers": {"location": "/", "server": "testserver"},
},
{
"event": "request",
"headers": {
Expand Down

0 comments on commit 2d9c358

Please sign in to comment.