Skip to content

[Bug]: AsyncClient blocks event loop — no real concurrency when using await client.get/post #59

@saucer-man

Description

@saucer-man

TLS Requests version

latest 1.2.5

Issue description

Summary

When using tls_requests.AsyncClient with async with and await client.get() / await client.post(), the event loop is blocked. Multiple concurrent tasks behave like sequential execution (total time ≈ single request time × number of requests), so there is no real async concurrency.

Expected behavior

  • Several await client.get(...) calls from different asyncio tasks should run concurrently.
  • Total time for N requests to a 2s-delay endpoint should be ~2–4s, not ~2s × N.

Actual behavior

  • Only one request effectively runs at a time.
  • Example: 20 concurrent requests to https://httpbin.org/delay/2 take ~65s (≈ 20 × 3.2s) instead of ~3–4s.
  • Replacing AsyncClient with the sync Client and running it in a thread pool via asyncio.run_in_executor() gives the expected concurrency (~3–4s for 20 requests).

Minimal repro

import asyncio
import time
import tls_requests
URL = "https://httpbin.org/delay/2"

async def one(i):
    async with tls_requests.AsyncClient(timeout=30, verify=False, client_identifier="chrome_133") as client:
        r = await client.get(URL, timeout=30)
        return getattr(r, "status_code", 0)

async def main():
    start = time.perf_counter()
    results = await asyncio.gather(*[one(i) for i in range(20)])
    print(f"Total: {time.perf_counter() - start:.1f}s (expected ~2–4s if concurrent)")
    print("Status codes:", results[:5])

asyncio.run(main())
  • Observed: Total time ~60–65s.
  • Expected (if truly async): Total time ~2–4s.

Environment

  • Python: 3.x
  • Package: wrapper-tls-requests (e.g. from PyPI)
  • OS: Windows (or your OS)

Question

Is AsyncClient intended to be non-blocking? If the underlying implementation is synchronous (e.g. Go TLS client called via sync bindings), would it be possible to document that and/or run the blocking part in a thread pool inside the library so that await client.get/post does not block the event loop?

Thanks for maintaining the project.

Steps to reproduce / Code Sample

import asyncio
import time
import tls_requests
URL = "https://httpbin.org/delay/2"

async def one(i):
    async with tls_requests.AsyncClient(timeout=30, verify=False, client_identifier="chrome_133") as client:
        r = await client.get(URL, timeout=30)
        return getattr(r, "status_code", 0)

async def main():
    start = time.perf_counter()
    results = await asyncio.gather(*[one(i) for i in range(20)])
    print(f"Total: {time.perf_counter() - start:.1f}s (expected ~2–4s if concurrent)")
    print("Status codes:", results[:5])

asyncio.run(main())

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions