BlackSheep includes an implementation of HTTP Client for HTTP 1.1.
- HTTP connection pooling
- User friendly handling of SSL contexts (safe by default)
- Support for client side middlewares
- Automatic handling of redirects (can be disabled, validates circular redirects and maximum number of redirects - redirects to URN are simply returned to code using the client)
- Automatic handling of cookies (can be disabled,
import asyncio from blacksheep.client import ClientSession async def client_example(loop): async with ClientSession() as client: response = await client.get("https://docs.python.org/3/") assert response is not None text = await response.text() print(text) loop = asyncio.get_event_loop() loop.run_until_complete(client_example(loop))
HTTP Connection pooling
The HTTP client in BlackSheep implements connection pooling. Meaning that connections to the same host and port are kept in memory and reused for different request-response cycles, when possible. By default, connections are not disposed as long as they are kept open.
Connections are created using
The HTTP Client supports middlewares. Middlewares on the server are functions that are executed in order, at every request-response cycle and enable manipulation of incoming requests and outgoing responses. Middlewares support interruption of the chain: that is, returning an HTTP response without firing all handlers in the chain, for example to return HTTP 401 Unauthorized when applying an authentication strategy. The HTTP client can benefit from the same design pattern, and this is supported in BlackSheep.
Client middleware example
async def client_example_middleware(request, next_handler): # do something before the request is sent response = await next_handler(request) # do something with the response from remote server return response client = ClientSession() client.middlewares.append(client_example_middleware) client.configure()
Considerations about the ClientSession class
To make the client more user-friendly, default connection pools are reused by
loop id. This is to prevent users from killing the performance of their
applications simply by instantiating many times
ClientSession (for example,
at every web request).
However, it is recommended to instantiate a single instance of HTTP client and register it as service of the application:
async def configure_http_client(app): http_client = ClientSession() http_client.configure() app.services.add_instance(http_client) # register a singleton app.on_start += configure_http_client async def dispose_http_client(app): http_client = app.services.get(ClientSession) # obtain the http client await http_client.close() app.on_stop += dispose_http_client
When following this approach, the http client can be automatically injected to request handlers, and services that need it, like in this example:
from blacksheep.server.responses import html @app.route("/get-python-homepage") async def get_python_homepage(http_client): response = await http_client.get("https://docs.python.org/3/") assert response is not None data = await response.text() return html(data)
Otherwise, instantiate a single connection pools and use it across several instances of HTTP clients:
from blacksheep.client import ClientSession from blacksheep.client.pool import ClientConnectionPools # instantiate a single instance of pools pools = ClientConnectionPools(loop) # loop is an asyncio loop # instantiate clients using the same pools client_one = ClientSession(pools=pools) client_two = ClientSession(pools=pools) client_three = ClientSession(pools=pools)