4

I want to display all the HTTP headers (the ones I added and the auto generated) of a request. I tried using traces (https://aiohttp.readthedocs.io/en/stable/tracing_reference.html#aiohttp-client-tracing-reference) :

#!/usr/bin/env python3                                                                                                      

import aiohttp
import asyncio

async def on_request_start(session, trace_config_ctx, params):
    print("Starting %s request for %s. I will send: %s" % (params.method, params.url, params.headers))

async def on_request_end(session, trace_config_ctx, params):
    print("Ending %s request for %s. I sent: %s" % (params.method, params.url, params.headers))

async def fetch(session, url):
    async with session.get(url) as response:
        return response

async def main():
    trace_config = aiohttp.TraceConfig()
    trace_config.on_request_start.append(on_request_start)
    trace_config.on_request_end.append(on_request_end)
    async with aiohttp.ClientSession(trace_configs=[trace_config]) as session:
        r = await fetch(session, 'http://stackoverflow.com')
        print(r)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

With this code, I get the method and the URL but the dict of headers is always empty:

% ./test-debug.py
Starting GET request for http://stackoverflow.com. I will send: <CIMultiDict()>
Ending GET request for https://stackoverflow.com/. I sent: <CIMultiDict()>

What did I miss?

Python 3.7.2

% pip show aiohttp
Name: aiohttp
Version: 3.5.4
Summary: Async http client/server framework (asyncio)
Home-page: https://github.com/aio-libs/aiohttp
Author: Nikolay Kim
Author-email: fafhrd91@gmail.com
License: Apache 2
Location: /usr/lib/python3.7/site-packages
Requires: async-timeout, attrs, multidict, yarl, chardet
Required-by: 
bortzmeyer
  • 34,164
  • 12
  • 67
  • 91

2 Answers2

6

After having read library source code carefully, request_start is too early, it is called even before the request object is created, so it will never see the full request and its headers; the timer is started after and the loop to send stuff.

But in request_end you have access to the full response object, which is tied to the request object and hence all headers.

With this change:

async def on_request_end(session, trace_config_ctx, params):
    print("Ending %s request for %s. I sent: %s" % (params.method, params.url, params.headers))
    print('Sent headers: %s' % params.response.request_info.headers)

I get:

Sent headers: <CIMultiDictProxy('Host': 'stackoverflow.com', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Python/3.7 aiohttp/3.5.4', 'Cookie': 'prov=f4fad342-c1f7-bcc2-5d25-0e30ae5cdbf6')>

You may also need to look at params.response.history in case of redirects. It is a sequence of ClientResponse object so you should be able to call request_info.headers on each of them.

Patrick Mevzek
  • 10,995
  • 16
  • 38
  • 54
2

I get the same:

$ ./test-debug.py
Starting GET request for http://stackoverflow.com. I will send: <CIMultiDict()>
Ending GET request for https://stackoverflow.com/. I sent: <CIMultiDict()>
<ClientResponse(https://stackoverflow.com/) [200 OK]>
<CIMultiDictProxy('Cache-Control': 'private', 'Content-Type': 'text/html; charset=utf-8', 'Content-Encoding': 'gzip', 'X-Frame-Options': 'SAMEORIGIN', 'X-Request-Guid': 'c89dd68d-cb88-43c1-b08d-f2a07bf81043', 'Strict-Transport-Security': 'max-age=15552000', 'Content-Security-Policy': 'upgrade-insecure-requests', 'Content-Length': '52698', 'Accept-Ranges': 'bytes', 'Date': 'Tue, 15 Jan 2019 08:06:32 GMT', 'Via': '1.1 varnish', 'Connection': 'keep-alive', 'X-Served-By': 'cache-cdg20748-CDG', 'X-Cache': 'MISS', 'X-Cache-Hits': '0', 'X-Timer': 'S1547539592.382231,VS0,VE120', 'Vary': 'Accept-Encoding,Fastly-SSL', 'X-DNS-Prefetch-Control': 'off')>

$ python --version
Python 3.7.1

$ python -c "import aiohttp; print(aiohttp.__version__)"
3.4.4

If I add a header to ClientSession explicitely,

    async with aiohttp.ClientSession(trace_configs=[trace_config], headers={"Host": "stackoverflow.com"}) as session: 

I see it in the trace:

$ ./test-debug.py
Starting GET request for http://stackoverflow.com. I will send: <CIMultiDict('Host': 'stackoverflow.com')>
Ending GET request for http://stackoverflow.com. I sent: <CIMultiDict('Host': 'stackoverflow.com')>
Sylvain Lesage
  • 831
  • 2
  • 12
  • 17