0
# Asynchronous API
1
2
Non-blocking proxy implementations for asyncio, trio, curio, and anyio frameworks. Each framework has its own optimized implementation while maintaining a consistent API interface. The async implementations return sockets configured for non-blocking operation, suitable for integration with async I/O frameworks.
3
4
## Framework-Specific Imports
5
6
```python
7
# Asyncio
8
from python_socks.async_.asyncio import Proxy as AsyncioProxy
9
10
# Trio
11
from python_socks.async_.trio import Proxy as TrioProxy
12
13
# Curio
14
from python_socks.async_.curio import Proxy as CurioProxy
15
16
# AnyIO (with proxy chaining)
17
from python_socks.async_.anyio import Proxy as AnyioProxy, ProxyChain as AnyioProxyChain
18
```
19
20
## Capabilities
21
22
### Asyncio Proxy
23
24
Asyncio-specific proxy implementation optimized for asyncio event loops.
25
26
```python { .api }
27
import asyncio
28
import socket
29
from typing import Optional
30
31
class AsyncioProxy:
32
def __init__(
33
self,
34
proxy_type: ProxyType,
35
host: str,
36
port: int,
37
username: Optional[str] = None,
38
password: Optional[str] = None,
39
rdns: Optional[bool] = None,
40
loop: Optional[asyncio.AbstractEventLoop] = None
41
): ...
42
43
async def connect(
44
self,
45
dest_host: str,
46
dest_port: int,
47
timeout: Optional[float] = None,
48
**kwargs
49
) -> socket.socket: ...
50
51
@property
52
def proxy_host(self) -> str:
53
"""Get proxy host address."""
54
...
55
56
@property
57
def proxy_port(self) -> int:
58
"""Get proxy port number."""
59
...
60
61
@classmethod
62
def create(cls, *args, **kwargs) -> 'AsyncioProxy':
63
"""Create proxy instance (deprecated, use __init__ directly)."""
64
...
65
66
@classmethod
67
def from_url(cls, url: str, **kwargs) -> 'AsyncioProxy':
68
"""Create proxy instance from URL."""
69
...
70
```
71
72
**Asyncio-Specific Parameters:**
73
- `loop`: Optional event loop (defaults to current loop)
74
75
**Usage:**
76
77
```python
78
import asyncio
79
import ssl
80
from python_socks.async_.asyncio import Proxy
81
82
async def main():
83
# Create proxy
84
proxy = Proxy.from_url('socks5://user:pass@127.0.0.1:1080')
85
86
# Connect through proxy
87
sock = await proxy.connect('httpbin.org', 443, timeout=30)
88
89
# Use with asyncio high-level API
90
reader, writer = await asyncio.open_connection(
91
host=None,
92
port=None,
93
sock=sock,
94
ssl=ssl.create_default_context(),
95
server_hostname='httpbin.org'
96
)
97
98
# Make request
99
writer.write(b'GET /ip HTTP/1.1\r\nHost: httpbin.org\r\n\r\n')
100
await writer.drain()
101
102
response = await reader.read(4096)
103
print(response.decode())
104
105
writer.close()
106
await writer.wait_closed()
107
108
asyncio.run(main())
109
```
110
111
### Trio Proxy
112
113
Trio-specific proxy implementation for structured concurrency.
114
115
```python { .api }
116
import trio
117
from typing import Optional
118
119
class TrioProxy:
120
def __init__(
121
self,
122
proxy_type: ProxyType,
123
host: str,
124
port: int,
125
username: Optional[str] = None,
126
password: Optional[str] = None,
127
rdns: Optional[bool] = None
128
): ...
129
130
async def connect(
131
self,
132
dest_host: str,
133
dest_port: int,
134
timeout: Optional[float] = None,
135
**kwargs
136
) -> trio.socket.SocketType: ...
137
138
@property
139
def proxy_host(self) -> str:
140
"""Get proxy host address."""
141
...
142
143
@property
144
def proxy_port(self) -> int:
145
"""Get proxy port number."""
146
...
147
148
@classmethod
149
def create(cls, *args, **kwargs) -> 'TrioProxy':
150
"""Create proxy instance (deprecated, use __init__ directly)."""
151
...
152
153
@classmethod
154
def from_url(cls, url: str, **kwargs) -> 'TrioProxy':
155
"""Create proxy instance from URL."""
156
...
157
```
158
159
**Usage:**
160
161
```python
162
import trio
163
import ssl
164
from python_socks.async_.trio import Proxy
165
166
async def main():
167
# Create proxy
168
proxy = Proxy.from_url('socks5://proxy.example.com:1080')
169
170
# Connect through proxy
171
sock = await proxy.connect('example.com', 443)
172
173
# Use with trio's SSL wrapper
174
ssl_context = ssl.create_default_context()
175
stream = trio.SSLStream(
176
trio.SocketStream(sock),
177
ssl_context,
178
server_hostname='example.com'
179
)
180
181
# Make request
182
await stream.send_all(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
183
response = await stream.receive_some(4096)
184
print(response.decode())
185
186
await stream.aclose()
187
188
trio.run(main)
189
```
190
191
### Curio Proxy
192
193
Curio-specific proxy implementation for async/await concurrency.
194
195
```python { .api }
196
import curio.io
197
from typing import Optional
198
199
class CurioProxy:
200
def __init__(
201
self,
202
proxy_type: ProxyType,
203
host: str,
204
port: int,
205
username: Optional[str] = None,
206
password: Optional[str] = None,
207
rdns: Optional[bool] = None
208
): ...
209
210
async def connect(
211
self,
212
dest_host: str,
213
dest_port: int,
214
timeout: Optional[float] = None,
215
**kwargs
216
) -> curio.io.Socket: ...
217
218
@property
219
def proxy_host(self) -> str:
220
"""Get proxy host address."""
221
...
222
223
@property
224
def proxy_port(self) -> int:
225
"""Get proxy port number."""
226
...
227
228
@classmethod
229
def create(cls, *args, **kwargs) -> 'CurioProxy':
230
"""Create proxy instance (deprecated, use __init__ directly)."""
231
...
232
233
@classmethod
234
def from_url(cls, url: str, **kwargs) -> 'CurioProxy':
235
"""Create proxy instance from URL."""
236
...
237
```
238
239
**Usage:**
240
241
```python
242
import curio
243
import ssl
244
from python_socks.async_.curio import Proxy
245
246
async def main():
247
# Create proxy
248
proxy = Proxy.from_url('socks5://proxy.example.com:1080')
249
250
# Connect through proxy
251
sock = await proxy.connect('example.com', 80)
252
253
# Use with curio
254
stream = sock.as_stream()
255
256
await stream.write(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
257
response = await stream.read(4096)
258
print(response.decode())
259
260
await stream.close()
261
262
curio.run(main)
263
```
264
265
### AnyIO Proxy
266
267
AnyIO-compatible proxy implementation that works across multiple async frameworks.
268
269
```python { .api }
270
import ssl
271
from typing import Optional
272
273
class AnyioProxy:
274
def __init__(
275
self,
276
proxy_type: ProxyType,
277
host: str,
278
port: int,
279
username: Optional[str] = None,
280
password: Optional[str] = None,
281
rdns: Optional[bool] = None,
282
proxy_ssl: Optional[ssl.SSLContext] = None
283
): ...
284
285
async def connect(
286
self,
287
dest_host: str,
288
dest_port: int,
289
dest_ssl: Optional[ssl.SSLContext] = None,
290
timeout: Optional[float] = None,
291
**kwargs
292
) -> 'AnyioSocketStream': ...
293
294
@property
295
def proxy_host(self) -> str:
296
"""Get proxy host address."""
297
...
298
299
@property
300
def proxy_port(self) -> int:
301
"""Get proxy port number."""
302
...
303
304
@classmethod
305
def create(cls, *args, **kwargs) -> 'AnyioProxy':
306
"""Create proxy instance (deprecated, use __init__ directly)."""
307
...
308
309
@classmethod
310
def from_url(cls, url: str, **kwargs) -> 'AnyioProxy':
311
"""Create proxy instance from URL."""
312
...
313
```
314
315
**Usage:**
316
317
```python
318
import anyio
319
import ssl
320
from python_socks.async_.anyio import Proxy
321
322
async def main():
323
# Create proxy
324
proxy = Proxy.from_url('socks5://proxy.example.com:1080')
325
326
# Connect through proxy
327
sock = await proxy.connect('httpbin.org', 443)
328
329
# Use with anyio
330
ssl_context = ssl.create_default_context()
331
stream = await anyio.connect_tcp(
332
remote_host='httpbin.org',
333
remote_port=443,
334
sock=sock,
335
tls=True,
336
tls_hostname='httpbin.org'
337
)
338
339
await stream.send(b'GET /ip HTTP/1.1\r\nHost: httpbin.org\r\n\r\n')
340
response = await stream.receive(4096)
341
print(response.decode())
342
343
await stream.aclose()
344
345
# Works with asyncio
346
anyio.run(main, backend='asyncio')
347
348
# Also works with trio
349
anyio.run(main, backend='trio')
350
```
351
352
### AnyIO Proxy Chain
353
354
Asynchronous proxy chaining with AnyIO compatibility.
355
356
```python { .api }
357
class ProxyChain:
358
def __init__(self, proxies: Iterable[AnyioProxy]): ...
359
360
async def connect(
361
self,
362
dest_host: str,
363
dest_port: int,
364
timeout: Optional[float] = None
365
) -> socket.socket: ...
366
```
367
368
**Note:** This is the only non-deprecated proxy chain implementation in the async API.
369
370
**Usage:**
371
372
```python
373
import anyio
374
from python_socks.async_.anyio import Proxy, ProxyChain
375
376
async def main():
377
# Create proxy chain
378
proxy1 = Proxy.from_url('socks5://first-proxy:1080')
379
proxy2 = Proxy.from_url('http://second-proxy:8080')
380
381
chain = ProxyChain([proxy1, proxy2])
382
383
# Connect through chain
384
sock = await chain.connect('example.com', 80)
385
386
# Use the socket
387
stream = anyio.SocketStream(sock)
388
await stream.send(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
389
response = await stream.receive(4096)
390
print(response.decode())
391
392
await stream.aclose()
393
394
anyio.run(main, backend='asyncio')
395
```
396
397
## Advanced Usage Examples
398
399
### Concurrent Connections
400
401
```python
402
import asyncio
403
from python_socks.async_.asyncio import Proxy
404
405
async def fetch_url(proxy, url_host, url_path):
406
"""Fetch URL through proxy."""
407
try:
408
sock = await proxy.connect(url_host, 80, timeout=10)
409
410
reader, writer = await asyncio.open_connection(
411
host=None, port=None, sock=sock
412
)
413
414
request = f'GET {url_path} HTTP/1.1\r\nHost: {url_host}\r\n\r\n'
415
writer.write(request.encode())
416
await writer.drain()
417
418
response = await reader.read(4096)
419
writer.close()
420
await writer.wait_closed()
421
422
return response.decode()
423
424
except Exception as e:
425
return f"Error: {e}"
426
427
async def main():
428
proxy = Proxy.from_url('socks5://proxy.example.com:1080')
429
430
# Concurrent requests
431
tasks = [
432
fetch_url(proxy, 'httpbin.org', '/ip'),
433
fetch_url(proxy, 'httpbin.org', '/user-agent'),
434
fetch_url(proxy, 'httpbin.org', '/headers')
435
]
436
437
results = await asyncio.gather(*tasks)
438
for i, result in enumerate(results):
439
print(f"Request {i + 1}: {result[:100]}...")
440
441
asyncio.run(main())
442
```
443
444
### Timeout and Error Handling
445
446
```python
447
import asyncio
448
from python_socks import ProxyError, ProxyTimeoutError, ProxyConnectionError
449
from python_socks.async_.asyncio import Proxy
450
451
async def robust_connect(proxy_url, dest_host, dest_port, max_retries=3):
452
"""Async connection with retry logic."""
453
454
for attempt in range(max_retries):
455
try:
456
proxy = Proxy.from_url(proxy_url)
457
return await proxy.connect(dest_host, dest_port, timeout=10)
458
459
except ProxyConnectionError as e:
460
if attempt < max_retries - 1:
461
await asyncio.sleep(2 ** attempt) # Exponential backoff
462
continue
463
raise
464
465
except ProxyTimeoutError:
466
if attempt < max_retries - 1:
467
continue
468
raise
469
470
except ProxyError as e:
471
# Don't retry authentication errors
472
raise
473
474
async def main():
475
try:
476
sock = await robust_connect(
477
'socks5://proxy.example.com:1080',
478
'api.example.com',
479
443
480
)
481
print("Connected successfully!")
482
sock.close()
483
except Exception as e:
484
print(f"Connection failed: {e}")
485
486
asyncio.run(main())
487
```
488
489
### Framework Detection Pattern
490
491
```python
492
import sys
493
494
# Detect available async framework
495
if 'trio' in sys.modules:
496
from python_socks.async_.trio import Proxy
497
framework = 'trio'
498
elif 'curio' in sys.modules:
499
from python_socks.async_.curio import Proxy
500
framework = 'curio'
501
else:
502
from python_socks.async_.asyncio import Proxy
503
framework = 'asyncio'
504
505
print(f"Using {framework} proxy implementation")
506
```
507
508
### Cross-Framework Compatibility with AnyIO
509
510
```python
511
import anyio
512
from python_socks.async_.anyio import Proxy
513
514
async def universal_proxy_request(proxy_url, dest_host, dest_port, request_data):
515
"""Request that works with any anyio-supported backend."""
516
517
proxy = Proxy.from_url(proxy_url)
518
sock = await proxy.connect(dest_host, dest_port)
519
520
stream = anyio.SocketStream(sock)
521
522
try:
523
await stream.send(request_data)
524
response = await stream.receive(4096)
525
return response
526
finally:
527
await stream.aclose()
528
529
# Works with asyncio
530
async def asyncio_main():
531
result = await universal_proxy_request(
532
'socks5://proxy:1080',
533
'example.com',
534
80,
535
b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
536
)
537
print("Asyncio result:", result.decode()[:100])
538
539
# Works with trio
540
async def trio_main():
541
result = await universal_proxy_request(
542
'socks5://proxy:1080',
543
'example.com',
544
80,
545
b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
546
)
547
print("Trio result:", result.decode()[:100])
548
549
# Run with different backends
550
anyio.run(asyncio_main, backend='asyncio')
551
anyio.run(trio_main, backend='trio')
552
```
553
554
## Framework Integration Notes
555
556
### Asyncio Integration
557
- Returns sockets in non-blocking mode
558
- Compatible with `asyncio.open_connection()`
559
- Works with asyncio SSL contexts
560
- Integrates with asyncio timeout handling
561
562
### Trio Integration
563
- Optimized for trio's structured concurrency model
564
- Compatible with `trio.SocketStream`
565
- Works with trio's SSL wrapper
566
- Follows trio's cancellation semantics
567
568
### Curio Integration
569
- Designed for curio's async/await model
570
- Compatible with curio stream abstractions
571
- Integrates with curio's timeout mechanisms
572
- Follows curio's task management patterns
573
574
### AnyIO Integration
575
- Backend-agnostic implementation
576
- Works across asyncio, trio, and curio
577
- Provides consistent API regardless of backend
578
- Enables framework-portable applications