# Connections
The mechanics of sending HTTP requests is dealt with by the `ConnectionPool` and `Connection` classes.
We can introspect a `Client` instance to get some visibility onto the state of the connection pool.
```{ .python .httpx }
>>> with httpx.Client() as cli
>>> urls = [
... "https://www.wikipedia.org/",
... "https://www.theguardian.com/",
... "https://news.ycombinator.com/",
... ]
... for url in urls:
... cli.get(url)
... print(cli.transport)
... #
... print(cli.transport.connections)
... # [
... # ,
... # ,
... # ,
... # ]
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.Client() as cli
>>> urls = [
... "https://www.wikipedia.org/",
... "https://www.theguardian.com/",
... "https://news.ycombinator.com/",
... ]
... for url in urls:
... await cli.get(url)
... print(cli.transport)
... #
... print(cli.transport.connections)
... # [
... # ,
... # ,
... # ,
... # ]
```
---
## Understanding the stack
The `Client` class is responsible for handling redirects and cookies.
It also ensures that outgoing requests include a default set of headers such as `User-Agent` and `Accept-Encoding`.
```{ .python .httpx }
>>> with httpx.Client() as cli:
>>> r = cli.request("GET", "https://www.example.com/")
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.Client() as cli:
>>> r = await cli.request("GET", "https://www.example.com/")
```
The `Client` class sends requests using a `ConnectionPool`, which is responsible for managing a pool of HTTP connections. This ensures quicker and more efficient use of resources than opening and closing a TCP connection with each request. The connection pool also handles HTTP proxying if required.
A single connection pool is able to handle multiple concurrent requests, with locking in place to ensure that the pool does not become over-saturated.
```{ .python .httpx }
>>> with httpx.ConnectionPool() as pool:
>>> r = pool.request("GET", "https://www.example.com/")
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.ConnectionPool() as pool:
>>> r = await pool.request("GET", "https://www.example.com/")
```
Individual HTTP connections can be managed directly with the `Connection` class. A single connection can only handle requests sequentially. Locking is provided to ensure that requests are strictly queued sequentially.
```{ .python .httpx }
>>> with httpx.open_connection("https://www.example.com/") as conn:
>>> r = conn.request("GET", "/")
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.open_connection("https://www.example.com/") as conn:
>>> r = await conn.request("GET", "/")
```
The `NetworkBackend` is responsible for managing the TCP stream, providing a raw byte-wise interface onto the underlying socket.
---
## ConnectionPool
```{ .python .httpx }
>>> pool = httpx.ConnectionPool()
>>> pool
```
```{ .python .ahttpx .hidden }
>>> pool = ahttpx.ConnectionPool()
>>> pool
```
### `.request(method, url, headers=None, content=None)`
```{ .python .httpx }
>>> with httpx.ConnectionPool() as pool:
>>> res = pool.request("GET", "https://www.example.com")
>>> res, pool
,
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.ConnectionPool() as pool:
>>> res = await pool.request("GET", "https://www.example.com")
>>> res, pool
,
```
### `.stream(method, url, headers=None, content=None)`
```{ .python .httpx }
>>> with httpx.ConnectionPool() as pool:
>>> with pool.stream("GET", "https://www.example.com") as res:
>>> res, pool
,
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.ConnectionPool() as pool:
>>> async with await pool.stream("GET", "https://www.example.com") as res:
>>> res, pool
,
```
### `.send(request)`
```{ .python .httpx }
>>> with httpx.ConnectionPool() as pool:
>>> req = httpx.Request("GET", "https://www.example.com")
>>> with pool.send(req) as res:
>>> res.read()
>>> res, pool
,
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.ConnectionPool() as pool:
>>> req = ahttpx.Request("GET", "https://www.example.com")
>>> async with await pool.send(req) as res:
>>> await res.read()
>>> res, pool
,
```
### `.close()`
```{ .python .httpx }
>>> with httpx.ConnectionPool() as pool:
>>> pool.close()
```
```{ .python .ahttpx .hidden }
>>> async with ahttpx.ConnectionPool() as pool:
>>> await pool.close()
```
---
## Connection
*TODO*
---
## Protocol upgrades
```{ .python .httpx }
with httpx.open_connection("https://www.example.com/") as conn:
with conn.upgrade("GET", "/feed", {"Upgrade": "WebSocket"}) as stream:
...
```
```{ .python .ahttpx .hidden }
async with await ahttpx.open_connection("https://www.example.com/") as conn:
async with await conn.upgrade("GET", "/feed", {"Upgrade": "WebSocket"}) as stream:
...
```
``
## Proxy `CONNECT` requests
```{ .python .httpx }
with httpx.open_connection("http://127.0.0.1:8080") as conn:
with conn.upgrade("CONNECT", "www.encode.io:443") as stream:
stream.start_tls(ctx, hostname="www.encode.io")
...
```
```{ .python .ahttpx .hidden }
async with await ahttpx.open_connection("http://127.0.0.1:8080") as conn:
async with await conn.upgrade("CONNECT", "www.encode.io:443") as stream:
await stream.start_tls(ctx, hostname="www.encode.io")
...
```
``
---
*Describe the `Transport` interface.*
---
← [Streams](streams.md)
[Parsers](parsers.md) →