]
-class Connection:
+class Connection(Client):
def __init__(
self,
origin: typing.Union[str, Origin],
*,
ssl: typing.Optional[SSLConfig] = None,
timeout: typing.Optional[TimeoutConfig] = None,
- stream: bool = False,
) -> Response:
assert request.url.origin == self.origin
status_code = event.status_code
headers = event.headers
body = self._body_iter(timeout)
- response = Response(
+ return Response(
status_code=status_code,
reason=reason,
headers=headers,
on_close=self._release,
)
- if not stream:
- # Read the response body.
- try:
- await response.read()
- finally:
- await response.close()
-
- return response
-
async def _connect(self, ssl: SSLConfig, timeout: TimeoutConfig) -> None:
ssl_context = await ssl.load_ssl_context() if self.origin.is_secure else None
import http
import typing
+from types import TracebackType
from urllib.parse import urlsplit
from .config import SSLConfig, TimeoutConfig
stream: bool = False,
) -> Response:
request = Request(method, url, headers=headers, body=body)
- return await self.send(request, ssl=ssl, timeout=timeout, stream=stream)
+ response = await self.send(request, ssl=ssl, timeout=timeout)
+ if not stream:
+ try:
+ await response.read()
+ finally:
+ await response.close()
+ return response
async def send(
self,
*,
ssl: typing.Optional[SSLConfig] = None,
timeout: typing.Optional[TimeoutConfig] = None,
- stream: bool = False,
) -> Response:
raise NotImplementedError() # pragma: nocover
async def close(self) -> None:
raise NotImplementedError() # pragma: nocover
+
+ async def __aenter__(self) -> "Client":
+ return self
+
+ async def __aexit__(
+ self,
+ exc_type: typing.Type[BaseException] = None,
+ exc_value: BaseException = None,
+ traceback: TracebackType = None,
+ ) -> None:
+ await self.close()
import asyncio
import typing
-from types import TracebackType
from .config import (
DEFAULT_CA_BUNDLE_PATH,
from .exceptions import PoolTimeout
-class ConnectionSemaphore:
- def __init__(self, max_connections: int = None):
- if max_connections is not None:
- self.semaphore = asyncio.BoundedSemaphore(value=max_connections)
-
- async def acquire(self) -> None:
- if hasattr(self, "semaphore"):
- await self.semaphore.acquire()
-
- def release(self) -> None:
- if hasattr(self, "semaphore"):
- self.semaphore.release()
-
-
-class ConnectionPool:
+class ConnectionPool(Client):
def __init__(
self,
*,
*,
ssl: typing.Optional[SSLConfig] = None,
timeout: typing.Optional[TimeoutConfig] = None,
- stream: bool = False,
) -> Response:
connection = await self.acquire_connection(request.url.origin, timeout=timeout)
- response = await connection.send(
- request, ssl=ssl, timeout=timeout, stream=stream
- )
+ response = await connection.send(request, ssl=ssl, timeout=timeout)
return response
@property
async def close(self) -> None:
self.is_closed = True
- async def __aenter__(self) -> "ConnectionPool":
- return self
- async def __aexit__(
- self,
- exc_type: typing.Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- await self.close()
+class ConnectionSemaphore:
+ def __init__(self, max_connections: int = None):
+ if max_connections is not None:
+ self.semaphore = asyncio.BoundedSemaphore(value=max_connections)
+
+ async def acquire(self) -> None:
+ if hasattr(self, "semaphore"):
+ await self.semaphore.acquire()
+
+ def release(self) -> None:
+ if hasattr(self, "semaphore"):
+ self.semaphore.release()
@pytest.mark.asyncio
async def test_get(server):
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request)
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/")
assert response.status_code == 200
assert response.body == b"Hello, world!"
@pytest.mark.asyncio
async def test_post(server):
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request(
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request(
"POST", "http://127.0.0.1:8000/", body=b"Hello, world!"
)
- response = await client.send(request)
assert response.status_code == 200
@pytest.mark.asyncio
async def test_stream_response(server):
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request, stream=True)
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/", stream=True)
assert response.status_code == 200
assert not hasattr(response, "body")
body = await response.read()
yield b"Hello, "
yield b"world!"
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("POST", "http://127.0.0.1:8000/", body=hello_world())
- response = await client.send(request)
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request(
+ "POST", "http://127.0.0.1:8000/", body=hello_world()
+ )
assert response.status_code == 200
@pytest.mark.asyncio
async def test_get(server):
- client = httpcore.Connection(origin="http://127.0.0.1:8000/")
- request = httpcore.Request(method="GET", url="http://127.0.0.1:8000/")
- response = await client.send(request)
+ http = httpcore.Connection(origin="http://127.0.0.1:8000/")
+ response = await http.request("GET", "http://127.0.0.1:8000/")
assert response.status_code == 200
assert response.body == b"Hello, world!"
@pytest.mark.asyncio
async def test_post(server):
- client = httpcore.Connection(origin="http://127.0.0.1:8000/")
- request = httpcore.Request(
- method="POST", url="http://127.0.0.1:8000/", body=b"Hello, world!"
+ http = httpcore.Connection(origin="http://127.0.0.1:8000/")
+ response = await http.request(
+ "POST", "http://127.0.0.1:8000/", body=b"Hello, world!"
)
- response = await client.send(request)
assert response.status_code == 200
"""
Connections should default to staying in a keep-alive state.
"""
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request)
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 1
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/")
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 1
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request)
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 1
+ response = await http.request("GET", "http://127.0.0.1:8000/")
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 1
@pytest.mark.asyncio
"""
Connnections to differing connection keys should result in multiple connections.
"""
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request)
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 1
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/")
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 1
- request = httpcore.Request("GET", "http://localhost:8000/")
- response = await client.send(request)
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 2
+ response = await http.request("GET", "http://localhost:8000/")
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 2
@pytest.mark.asyncio
"""
limits = httpcore.PoolLimits(soft_limit=1)
- async with httpcore.ConnectionPool(limits=limits) as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request)
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 1
+ async with httpcore.ConnectionPool(limits=limits) as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/")
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 1
- request = httpcore.Request("GET", "http://localhost:8000/")
- response = await client.send(request)
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 1
+ response = await http.request("GET", "http://localhost:8000/")
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 1
@pytest.mark.asyncio
"""
A streaming request should hold the connection open until the response is read.
"""
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request, stream=True)
- assert client.num_active_connections == 1
- assert client.num_keepalive_connections == 0
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/", stream=True)
+ assert http.num_active_connections == 1
+ assert http.num_keepalive_connections == 0
await response.read()
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 1
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 1
@pytest.mark.asyncio
"""
Multiple conncurrent requests should open multiple conncurrent connections.
"""
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response_a = await client.send(request, stream=True)
- assert client.num_active_connections == 1
- assert client.num_keepalive_connections == 0
+ async with httpcore.ConnectionPool() as http:
+ response_a = await http.request("GET", "http://127.0.0.1:8000/", stream=True)
+ assert http.num_active_connections == 1
+ assert http.num_keepalive_connections == 0
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response_b = await client.send(request, stream=True)
- assert client.num_active_connections == 2
- assert client.num_keepalive_connections == 0
+ response_b = await http.request("GET", "http://127.0.0.1:8000/", stream=True)
+ assert http.num_active_connections == 2
+ assert http.num_keepalive_connections == 0
await response_b.read()
- assert client.num_active_connections == 1
- assert client.num_keepalive_connections == 1
+ assert http.num_active_connections == 1
+ assert http.num_keepalive_connections == 1
await response_a.read()
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 2
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 2
@pytest.mark.asyncio
Using a `Connection: close` header should close the connection.
"""
headers = [(b"connection", b"close")]
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/", headers=headers)
- response = await client.send(request)
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 0
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/", headers=headers)
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 0
@pytest.mark.asyncio
"""
A standard close should keep the connection open.
"""
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request, stream=True)
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/", stream=True)
await response.read()
await response.close()
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 1
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 1
@pytest.mark.asyncio
"""
A premature close should close the connection.
"""
- async with httpcore.ConnectionPool() as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request, stream=True)
+ async with httpcore.ConnectionPool() as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/", stream=True)
await response.close()
- assert client.num_active_connections == 0
- assert client.num_keepalive_connections == 0
+ assert http.num_active_connections == 0
+ assert http.num_keepalive_connections == 0
async def test_read_timeout(server):
timeout = httpcore.TimeoutConfig(read_timeout=0.0001)
- async with httpcore.ConnectionPool(timeout=timeout) as client:
+ async with httpcore.ConnectionPool(timeout=timeout) as http:
with pytest.raises(httpcore.ReadTimeout):
- request = httpcore.Request("GET", "http://127.0.0.1:8000/slow_response")
- await client.send(request)
+ await http.request("GET", "http://127.0.0.1:8000/slow_response")
@pytest.mark.asyncio
async def test_connect_timeout(server):
timeout = httpcore.TimeoutConfig(connect_timeout=0.0001)
- async with httpcore.ConnectionPool(timeout=timeout) as client:
+ async with httpcore.ConnectionPool(timeout=timeout) as http:
with pytest.raises(httpcore.ConnectTimeout):
# See https://stackoverflow.com/questions/100841/
- request = httpcore.Request("GET", "http://10.255.255.1/")
- await client.send(request)
+ await http.request("GET", "http://10.255.255.1/")
@pytest.mark.asyncio
timeout = httpcore.TimeoutConfig(pool_timeout=0.0001)
limits = httpcore.PoolLimits(hard_limit=1)
- async with httpcore.ConnectionPool(timeout=timeout, limits=limits) as client:
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- response = await client.send(request, stream=True)
+ async with httpcore.ConnectionPool(timeout=timeout, limits=limits) as http:
+ response = await http.request("GET", "http://127.0.0.1:8000/", stream=True)
with pytest.raises(httpcore.PoolTimeout):
- request = httpcore.Request("GET", "http://127.0.0.1:8000/")
- await client.send(request)
+ await http.request("GET", "http://127.0.0.1:8000/")
await response.read()