Skip to content

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

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())

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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