Skip to content

Commit 45d9955

Browse files
author
Cursor
committed
Merge main to get test harness
2 parents cb546a6 + a8b3055 commit 45d9955

5 files changed

Lines changed: 1438 additions & 141 deletions

File tree

docs/aiohttp.rst

Lines changed: 241 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,154 @@
11
aiohttp
22
=======
33

4-
The `aiohttp <https://docs.aiohttp.org/en/stable/index.html>`_ library is an async HTTP client/server framework for Python. This page describes how to use aiohttp with proxies and how to interact with proxy headers.
4+
The `aiohttp <https://docs.aiohttp.org/en/stable/index.html>`_ library is an async HTTP client/server framework for Python. This page describes how to use aiohttp with proxies and how to send and receive custom proxy headers.
5+
6+
Getting Started
7+
---------------
8+
9+
This section shows you how to quickly get up and running with proxy headers in aiohttp.
10+
11+
**Prerequisites:**
12+
13+
1. Install the packages:
14+
15+
.. code-block:: bash
16+
17+
pip install python-proxy-headers aiohttp
18+
19+
2. Import the module:
20+
21+
.. code-block:: python
22+
23+
from python_proxy_headers import aiohttp_proxy
24+
25+
**Quick Example - Send and Receive Proxy Headers:**
26+
27+
.. code-block:: python
28+
29+
import asyncio
30+
from python_proxy_headers import aiohttp_proxy
31+
32+
async def main():
33+
async with aiohttp_proxy.ProxyClientSession() as session:
34+
async with session.get(
35+
'https://api.ipify.org?format=json',
36+
proxy='http://PROXYHOST:PORT',
37+
proxy_headers={'X-ProxyMesh-Country': 'US'}
38+
) as response:
39+
# Access the response data
40+
data = await response.json()
41+
print(data) # {"ip": "..."}
42+
43+
# Access proxy response headers
44+
print(response.headers.get('X-ProxyMesh-IP'))
45+
46+
asyncio.run(main())
47+
48+
That's it! The ``ProxyClientSession`` handles sending your custom headers to the proxy and makes proxy response headers available in the response.
549

650
Using Proxies with aiohttp
751
---------------------------
852

953
aiohttp provides built-in support for proxies through the ``proxy`` parameter in request methods. You can specify a proxy URL for each request.
1054

11-
Basic Proxy Usage
12-
~~~~~~~~~~~~~~~~~
55+
Basic Proxy Usage (Standard aiohttp)
56+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1357

14-
To use a proxy with aiohttp, you can pass the ``proxy`` parameter to any request method:
58+
To use a proxy with standard aiohttp:
1559

1660
.. code-block:: python
1761
1862
import aiohttp
19-
async with aiohttp.ClientSession() as session:
20-
async with session.get('https://api.ipify.org?format=json',
21-
proxy="http://PROXYHOST:PORT") as r:
22-
text = await r.text()
63+
import asyncio
64+
65+
async def main():
66+
async with aiohttp.ClientSession() as session:
67+
async with session.get(
68+
'https://api.ipify.org?format=json',
69+
proxy='http://PROXYHOST:PORT'
70+
) as response:
71+
text = await response.text()
72+
print(text)
73+
74+
asyncio.run(main())
2375
2476
This routes the request through the specified proxy server.
2577

78+
Proxy Authentication
79+
~~~~~~~~~~~~~~~~~~~~
80+
81+
To use a proxy that requires authentication:
82+
83+
.. code-block:: python
84+
85+
import aiohttp
86+
import asyncio
87+
88+
async def main():
89+
async with aiohttp.ClientSession() as session:
90+
# Method 1: Include credentials in the URL
91+
async with session.get(
92+
'https://api.ipify.org?format=json',
93+
proxy='http://username:password@PROXYHOST:PORT'
94+
) as response:
95+
text = await response.text()
96+
97+
# Method 2: Use proxy_auth parameter
98+
auth = aiohttp.BasicAuth('username', 'password')
99+
async with session.get(
100+
'https://api.ipify.org?format=json',
101+
proxy='http://PROXYHOST:PORT',
102+
proxy_auth=auth
103+
) as response:
104+
text = await response.text()
105+
106+
asyncio.run(main())
107+
108+
Session-Level Proxy
109+
~~~~~~~~~~~~~~~~~~~
110+
111+
You can set a default proxy for all requests in a session:
112+
113+
.. code-block:: python
114+
115+
import aiohttp
116+
import asyncio
117+
118+
async def main():
119+
async with aiohttp.ClientSession(
120+
proxy='http://PROXYHOST:PORT',
121+
proxy_auth=aiohttp.BasicAuth('user', 'pass')
122+
) as session:
123+
# All requests will use this proxy
124+
async with session.get('https://api.ipify.org?format=json') as response:
125+
text = await response.text()
126+
127+
asyncio.run(main())
128+
129+
Environment Variables
130+
~~~~~~~~~~~~~~~~~~~~~
131+
132+
aiohttp can read proxy settings from environment variables when ``trust_env=True``:
133+
134+
.. code-block:: bash
135+
136+
export HTTP_PROXY="http://PROXYHOST:PORT"
137+
export HTTPS_PROXY="http://PROXYHOST:PORT"
138+
139+
.. code-block:: python
140+
141+
import aiohttp
142+
import asyncio
143+
144+
async def main():
145+
async with aiohttp.ClientSession(trust_env=True) as session:
146+
# Will automatically use proxies from environment variables
147+
async with session.get('https://api.ipify.org?format=json') as response:
148+
text = await response.text()
149+
150+
asyncio.run(main())
151+
26152
Sending Custom Proxy Headers
27153
-----------------------------
28154

@@ -31,28 +157,45 @@ While it's not documented, aiohttp does support passing in custom proxy headers
31157
.. code-block:: python
32158
33159
import aiohttp
34-
async with aiohttp.ClientSession() as session:
35-
async with session.get('https://api.ipify.org?format=json',
36-
proxy="http://PROXYHOST:PORT",
37-
proxy_headers={'X-ProxyMesh-Country': 'US'}) as r:
38-
text = await r.text()
160+
import asyncio
161+
162+
async def main():
163+
async with aiohttp.ClientSession() as session:
164+
async with session.get(
165+
'https://api.ipify.org?format=json',
166+
proxy='http://PROXYHOST:PORT',
167+
proxy_headers={'X-ProxyMesh-Country': 'US'}
168+
) as response:
169+
text = await response.text()
170+
171+
asyncio.run(main())
39172
40173
The ``proxy_headers`` parameter allows you to send custom headers to the proxy server. This is useful for controlling proxy behavior, such as selecting a specific country or IP address.
41174

42175
Receiving Proxy Response Headers
43176
---------------------------------
44177

45-
However, if you want to get proxy response headers, you should use our extension module ``python_proxy_headers.aiohttp_proxy``:
178+
Standard aiohttp does not expose proxy response headers from the CONNECT request. To get proxy response headers, use our extension module ``python_proxy_headers.aiohttp_proxy``:
46179

47180
.. code-block:: python
48181
182+
import asyncio
49183
from python_proxy_headers import aiohttp_proxy
50-
async with aiohttp_proxy.ProxyClientSession() as session:
51-
async with session.get('https://api.ipify.org?format=json',
52-
proxy="http://PROXYHOST:PORT",
53-
proxy_headers={'X-ProxyMesh-Country': 'US'}) as r:
54-
text = await r.text()
55-
proxy_ip = r.headers['X-ProxyMesh-IP']
184+
185+
async def main():
186+
async with aiohttp_proxy.ProxyClientSession() as session:
187+
async with session.get(
188+
'https://api.ipify.org?format=json',
189+
proxy='http://PROXYHOST:PORT',
190+
proxy_headers={'X-ProxyMesh-Country': 'US'}
191+
) as response:
192+
data = await response.json()
193+
194+
# Proxy response headers are now available
195+
proxy_ip = response.headers.get('X-ProxyMesh-IP')
196+
print(f"Request was made through: {proxy_ip}")
197+
198+
asyncio.run(main())
56199
57200
The ``ProxyClientSession`` extends the standard ``ClientSession`` to make proxy response headers available in the response headers. This allows you to access information from the proxy server, such as the IP address that was assigned to your request.
58201

@@ -68,11 +211,13 @@ Here's a complete example showing how to use aiohttp with proxy headers:
68211
69212
async def main():
70213
async with aiohttp_proxy.ProxyClientSession() as session:
71-
async with session.get('https://api.ipify.org?format=json',
72-
proxy="http://PROXYHOST:PORT",
73-
proxy_headers={'X-ProxyMesh-Country': 'US'}) as r:
74-
data = await r.json()
75-
proxy_ip = r.headers.get('X-ProxyMesh-IP')
214+
async with session.get(
215+
'https://api.ipify.org?format=json',
216+
proxy='http://PROXYHOST:PORT',
217+
proxy_headers={'X-ProxyMesh-Country': 'US'}
218+
) as response:
219+
data = await response.json()
220+
proxy_ip = response.headers.get('X-ProxyMesh-IP')
76221
print(f"Your IP: {data['ip']}")
77222
print(f"Proxy IP: {proxy_ip}")
78223
@@ -101,3 +246,74 @@ The ``ProxyClientSession`` works just like the standard ``ClientSession`` and su
101246

102247
All standard aiohttp request methods are supported: ``get``, ``post``, ``put``, ``delete``, ``patch``, ``head``, and ``options``.
103248

249+
Extension Classes
250+
-----------------
251+
252+
The ``python_proxy_headers.aiohttp_proxy`` module provides several extension classes that work together to capture and expose proxy response headers. These classes extend aiohttp's internal classes.
253+
254+
ProxyClientSession
255+
~~~~~~~~~~~~~~~~~~
256+
257+
The main entry point for using proxy headers with aiohttp. This class extends ``aiohttp.ClientSession`` and automatically configures the session to use the other extension classes.
258+
259+
.. code-block:: python
260+
261+
from python_proxy_headers.aiohttp_proxy import ProxyClientSession
262+
263+
async with ProxyClientSession() as session:
264+
async with session.get('https://example.com', proxy='http://PROXYHOST:PORT') as r:
265+
proxy_ip = r.headers.get('X-ProxyMesh-IP')
266+
267+
The ``ProxyClientSession`` constructor accepts all the same arguments as ``aiohttp.ClientSession``, and automatically sets:
268+
269+
* ``connector`` to ``ProxyTCPConnector()``
270+
* ``response_class`` to ``ProxyClientResponse``
271+
* ``request_class`` to ``ProxyClientRequest``
272+
273+
ProxyTCPConnector
274+
~~~~~~~~~~~~~~~~~
275+
276+
Extends ``aiohttp.TCPConnector`` to capture proxy response headers during HTTPS tunnel establishment. This class overrides the ``_create_proxy_connection`` method to:
277+
278+
1. Send the CONNECT request with custom proxy headers
279+
2. Capture the proxy's response headers from the CONNECT response
280+
3. Store them on the protocol object for later retrieval
281+
282+
When establishing an HTTPS connection through a proxy, the connector:
283+
284+
* Creates a CONNECT request to the proxy server
285+
* Includes any custom proxy headers you've specified
286+
* Captures the proxy's response headers (e.g., ``X-ProxyMesh-IP``)
287+
* Stores them so they can be merged into the final response
288+
289+
You typically don't need to use this class directly - it's automatically configured when using ``ProxyClientSession``.
290+
291+
ProxyClientRequest
292+
~~~~~~~~~~~~~~~~~~
293+
294+
Extends ``aiohttp.ClientRequest`` to transfer proxy headers from the connection protocol to the response object. This class overrides the ``send`` method to check if the connection's protocol has captured proxy headers and attaches them to the response.
295+
296+
This class is used internally by ``ProxyClientSession`` and typically doesn't need to be used directly.
297+
298+
ProxyClientResponse
299+
~~~~~~~~~~~~~~~~~~~
300+
301+
Extends ``aiohttp.ClientResponse`` to merge proxy response headers into the response's headers property. This class overrides the ``headers`` property to:
302+
303+
1. Check if proxy headers were captured during tunnel establishment
304+
2. If present, merge them with the target server's response headers
305+
3. Return a combined ``CIMultiDictProxy`` containing both sets of headers
306+
307+
This allows you to access proxy response headers (like ``X-ProxyMesh-IP``) directly from the response object's ``headers`` property, alongside the target server's response headers.
308+
309+
How It Works
310+
~~~~~~~~~~~~
311+
312+
The extension classes work together in the following flow:
313+
314+
1. **ProxyClientSession** creates a session configured with all the extension classes
315+
2. **ProxyTCPConnector** intercepts the CONNECT request/response during tunnel establishment and captures proxy headers
316+
3. **ProxyClientRequest** transfers the captured headers from the protocol to the response object
317+
4. **ProxyClientResponse** merges the proxy headers into the response's ``headers`` property
318+
319+
This allows proxy response headers to be transparently available in your application without any special handling.

0 commit comments

Comments
 (0)