aiohttp Documentation

Asynchronous HTTP client/server framework for asyncio and Python

16,514 stars Python Python 1 file ยท ~53,909 tokens #aiohttp#async#asyncio#hacktoberfest#http#http-client#http-server#python
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy aiohttp on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name aiohttp --restart always aio-libs/aiohttp:latest

1. Project Overview & Quickstart (aio-libs/aiohttp)

File: docs/abc.rst

.. module:: aiohttp.abc

.. _aiohttp-abc:

Abstract Base Classes

Abstract routing

aiohttp has abstract classes for managing web interfaces.

The most part of :mod:aiohttp.web is not intended to be inherited
but few of them are.

aiohttp.web is built on top of few concepts: application, router,
request and response.

router is a pluggable part: a library user may build a router
from scratch, all other parts should work with new router seamlessly.

:class:aiohttp.abc.AbstractRouter has the only mandatory method:
:meth:aiohttp.abc.AbstractRouter.resolve coroutine. It must return an
:class:aiohttp.abc.AbstractMatchInfo instance.

If the requested URL handler is found
:meth:aiohttp.abc.AbstractMatchInfo.handler is a :term:web-handler for
requested URL and :attr:aiohttp.abc.AbstractMatchInfo.http_exception is None.

Otherwise :attr:aiohttp.abc.AbstractMatchInfo.http_exception is an instance of
:exc:~aiohttp.web.HTTPException like 404: NotFound or 405: Method
Not Allowed
. :meth:aiohttp.abc.AbstractMatchInfo.handler raises
:attr:~aiohttp.abc.AbstractMatchInfo.http_exception on call.

.. class:: AbstractRouter

Abstract router, :class:aiohttp.web.Application accepts it as
router parameter and returns as
:attr:aiohttp.web.Application.router.

.. method:: resolve(request)
:async:

text
Performs URL resolving. It's an abstract method, should be
  overridden in *router* implementation.

  :param request: :class:`aiohttp.web.Request` instance for
                  resolving, the request has
                  :attr:`aiohttp.web.Request.match_info` equals to
                  ``None`` at resolving stage.

  :return: :class:`aiohttp.abc.AbstractMatchInfo` instance.

.. class:: AbstractMatchInfo

Abstract match info, returned by :meth:aiohttp.abc.AbstractRouter.resolve call.

.. attribute:: http_exception

text
:exc:`aiohttp.web.HTTPException` if no match was found, ``None``
  otherwise.

.. method:: handler(request)
:async:

text
Abstract method performing :term:`web-handler` processing.

  :param request: :class:`aiohttp.web.Request` instance for
                  resolving, the request has
                  :attr:`aiohttp.web.Request.match_info` equals to
                  ``None`` at resolving stage.
  :return: :class:`aiohttp.web.StreamResponse` or descendants.

  :raise: :class:`aiohttp.web.HTTPException` on error

.. method:: expect_handler(request)
:async:

text
Abstract method for handling *100-continue* processing.

Abstract Class Based Views

For class based view support aiohttp has abstract
:class:AbstractView class which is awaitable (may be uses like
await Cls() or yield from Cls() and has a request as an
attribute.

.. class:: AbstractView

An abstract class, base for all class based views implementations.

Methods __iter__ and __await__ should be overridden.

.. attribute:: request

text
:class:`aiohttp.web.Request` instance for performing the request.

.. class:: AbstractCookieJar

The cookie jar instance is available as :attr:aiohttp.ClientSession.cookie_jar.

The jar contains :class:~http.cookies.Morsel items for storing
internal cookie data.

API provides a count of saved cookies::

text
len(session.cookie_jar)

These cookies may be iterated over::

text
for cookie in session.cookie_jar:
       print(cookie.key)
       print(cookie["domain"])

An abstract class for cookie storage. Implements
:class:collections.abc.Iterable and
:class:collections.abc.Sized.

.. method:: update_cookies(cookies, response_url=None)

text
Update cookies returned by server in ``Set-Cookie`` header.

  :param cookies: a :class:`collections.abc.Mapping`
     (e.g. :class:`dict`, :class:`~http.cookies.SimpleCookie`) or
     *iterable* of *pairs* with cookies returned by server's
     response.

  :param str response_url: URL of response, ``None`` for *shared
     cookies*.  Regular cookies are coupled with server's URL and
     are sent only to this server, shared ones are sent in every
     client request.

.. method:: filter_cookies(request_url)

text
Return jar's cookies acceptable for URL and available in
  ``Cookie`` header for sending client requests for given URL.

  :param str response_url: request's URL for which cookies are asked.

  :return: :class:`http.cookies.SimpleCookie` with filtered
     cookies for given URL.

.. method:: clear(predicate=None)

text
Removes all cookies from the jar if the predicate is ``None``. Otherwise remove only those :class:`~http.cookies.Morsel` that ``predicate(morsel)`` returns ``True``.

  :param predicate: callable that gets :class:`~http.cookies.Morsel` as a parameter and returns ``True`` if this :class:`~http.cookies.Morsel` must be deleted from the jar.

      .. versionadded:: 3.8

.. method:: clear_domain(domain)

text
Remove all cookies from the jar that belongs to the specified domain or its subdomains.

  :param str domain: domain for which cookies must be deleted from the jar.

  .. versionadded:: 3.8

Abstract Access Logger

.. class:: AbstractAccessLogger

An abstract class, base for all :class:aiohttp.web.RequestHandler
access_logger implementations

Method log should be overridden.

.. method:: log(request, response, time)

text
:param request: :class:`aiohttp.web.Request` object.

  :param response: :class:`aiohttp.web.Response` object.

  :param float time: Time taken to serve the request.

.. attribute:: enabled

text
Return True if logger is enabled.

    Override this property if logging is disabled to avoid the
    overhead of calculating details to feed the logger.

    This property may be omitted if logging is always enabled.

Abstract Resolver

.. class:: AbstractResolver

An abstract class, base for all resolver implementations.

Method resolve should be overridden.

.. method:: resolve(host, port, family)

text
Resolve host name to IP address.

  :param str host: host name to resolve.

  :param int port: port number.

  :param int family: socket family.

  :return: list of :class:`aiohttp.abc.ResolveResult` instances.

.. method:: close()

text
Release resolver.

.. class:: ResolveResult

Result of host name resolution.

.. attribute:: hostname

text
The host name that was provided.

.. attribute:: host

text
The IP address that was resolved.

.. attribute:: port

text
The port that was resolved.

.. attribute:: family

text
The address family that was resolved.

.. attribute:: proto

text
The protocol that was resolved.

.. attribute:: flags

text
The flags that were resolved.

File: docs/built_with.rst

.. _aiohttp-built-with:

Built with aiohttp

aiohttp is used to build useful libraries built on top of it,
and there's a page dedicated to list them: :ref:aiohttp-3rd-party.

There are also projects that leverage the power of aiohttp to
provide end-user tools, like command lines or software with
full user interfaces.

This page aims to list those projects. If you are using aiohttp
in your software and if it's playing a central role, you
can add it here in this list.

You can also add a Built with aiohttp link somewhere in your
project, pointing to <https://github.com/aio-libs/aiohttp>_.

  • Pulp <https://pulpproject.org>_ Platform for managing repositories
    of software packages and making them available to consumers.
  • repo-peek <https://github.com/rahulunair/repo-peek>_ CLI tool to open a remote repo locally quickly.
  • Molotov <http://molotov.readthedocs.io>_ Load testing tool.
  • Arsenic <https://github.com/hde/arsenic>_ Async WebDriver.
  • Home Assistant <https://home-assistant.io>_ Home Automation Platform.
  • Backend.AI <https://backend.ai>_ Code execution API service.
  • doh-proxy <https://github.com/facebookexperimental/doh-proxy>_ DNS Over HTTPS Proxy.
  • Mariner <https://gitlab.com/radek-sprta/mariner>_ Command-line torrent searcher.
  • DEEPaaS API <https://github.com/indigo-dc/DEEPaaS>_ REST API for Machine learning, Deep learning and artificial intelligence applications.
  • BentoML <https://github.com/bentoml/BentoML>_ Machine Learning model serving framework
  • salted <https://github.com/RuedigerVoigt/salted>_ fast link check library (for HTML, Markdown, LaTeX, ...) with CLI
  • Unofficial Tabdeal API <https://github.com/MohsenHNSJ/unofficial_tabdeal_api>_ A package to communicate with the Tabdeal trading platform.

File: docs/changes.rst

.. _aiohttp_changes:

========= Changelog

.. only:: not is_release

To be included in v\ |release| (if present)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. towncrier-draft-entries:: |release| [UNRELEASED DRAFT]

Released versions
^^^^^^^^^^^^^^^^^

.. include:: ../CHANGES.rst
:start-after: .. towncrier release notes start


File: docs/client.rst

.. _aiohttp-client:

Client

.. currentmodule:: aiohttp

The page contains all information about aiohttp Client API:

.. toctree::
:name: client
:maxdepth: 3

Quickstart
Advanced Usage
Client Middleware Cookbook
Reference
Tracing Reference
The aiohttp Request Lifecycle


File: docs/client_advanced.rst

.. currentmodule:: aiohttp

.. _aiohttp-client-advanced:

Advanced Client Usage

.. _aiohttp-client-session:

Client Session

:class:ClientSession is the heart and the main entry point for all
client API operations.

Create the session first, use the instance for performing HTTP
requests and initiating WebSocket connections.

The session contains a cookie storage and connection pool, thus
cookies and connections are shared between HTTP requests sent by the
same session.

Custom Request Headers

If you need to add HTTP headers to a request, pass them in a
:class:dict to the headers parameter.

For example, if you want to specify the content-type directly::

text
url = 'http://example.com/image'
payload = b'GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00'
          b'\x00\x00\x01\x00\x01\x00\x00\x02\x00;'
headers = {'content-type': 'image/gif'}

await session.post(url,
                   data=payload,
                   headers=headers)

You also can set default headers for all session requests::

text
headers={"Authorization": "Basic bG9naW46cGFzcw=="}
async with aiohttp.ClientSession(headers=headers) as session:
    async with session.get("http://httpbin.org/headers") as r:
        json_body = await r.json()
        assert json_body['headers']['Authorization'] == \
            'Basic bG9naW46cGFzcw=='

Typical use case is sending JSON body. You can specify content type
directly as shown above, but it is more convenient to use special keyword
json::

text
await session.post(url, json={'example': 'text'})

For text/plain::

text
await session.post(url, data='ะŸั€ะธะฒะตั‚, ะœะธั€!')

Authentication

For HTTP Basic Authentication, build the Authorization header using
:func:encode_basic_auth and pass it via headers::

text
from aiohttp import ClientSession, encode_basic_auth

headers = {"Authorization": encode_basic_auth("user", "pass")}
async with ClientSession(headers=headers) as session:
    ...

For HTTP digest authentication, use the :class:DigestAuthMiddleware client middleware::

text
from aiohttp import ClientSession, DigestAuthMiddleware

# Create the middleware with your credentials
digest_auth = DigestAuthMiddleware(login="user", password="password")

# Pass it to the ClientSession as a tuple
async with ClientSession(middlewares=(digest_auth,)) as session:
    # The middleware will automatically handle auth challenges
    async with session.get("https://example.com/protected") as resp:
        print(await resp.text())

The :class:DigestAuthMiddleware implements HTTP Digest Authentication according to RFC 7616,
providing a more secure alternative to Basic Authentication. It supports all
standard hash algorithms including MD5, SHA, SHA-256, SHA-512 and their session
variants, as well as both 'auth' and 'auth-int' quality of protection (qop) options.
The middleware automatically handles the authentication flow by intercepting 401 responses
and retrying with proper credentials.

Note that if the request is redirected and the redirect URL contains
credentials, those credentials will supersede any previously set credentials.
In other words, if http://[email protected] redirects to
http://[email protected], the second request will be authenticated
as other_user. Providing both the auth parameter and authentication in
the initial URL will result in a :exc:ValueError.

For other authentication flows, the Authorization header can be set
directly::

text
headers = {"Authorization": "Bearer eyJh...0M30"}
async with ClientSession(headers=headers) as session:
    ...

The authentication header for a session may be updated as and when required.
For example::

text
session.headers["Authorization"] = "Bearer eyJh...1OH0"

Note that a copy of the headers dictionary is set as an attribute when
creating a :class:ClientSession instance (as a :class:multidict.CIMultiDict
object). Updating the original dictionary does not have any effect.

In cases where the authentication header value expires periodically, an
:mod:asyncio task may be used to update the session's default headers in the
background.

.. _aiohttp-client-middleware:

Client Middleware

The client supports middleware to intercept requests and responses. This can be
useful for authentication, logging, request/response modification, retries etc.

For more examples and common middleware patterns, see the :ref:aiohttp-client-middleware-cookbook.

Creating a middleware
^^^^^^^^^^^^^^^^^^^^^

To create a middleware, define an async function (or callable class) that accepts a request object
and a handler function, and returns a response. Middlewares must follow the
:type:ClientMiddlewareType signature::

text
async def auth_middleware(req: ClientRequest, handler: ClientHandlerType) -> ClientResponse:
    req.headers["Authorization"] = get_auth_header()
    return await handler(req)

Using Middlewares
^^^^^^^^^^^^^^^^^

You can apply middlewares to a client session or to individual requests::

text
# Apply to all requests in a session
async with ClientSession(middlewares=(my_middleware,)) as session:
    resp = await session.get("http://example.com")

# Apply to a specific request
async with ClientSession() as session:
    resp = await session.get("http://example.com", middlewares=(my_middleware,))

Middleware Chaining
^^^^^^^^^^^^^^^^^^^

Multiple middlewares are applied in the order they are listed::

text
# Middlewares are applied in order: logging -> auth -> request
async with ClientSession(middlewares=(logging_middleware, auth_middleware)) as session:
    async with session.get("http://example.com") as resp:
        ...

A key aspect to understand about the middleware sequence is that the execution flow follows this pattern:

  1. The first middleware in the list is called first and executes its code before calling the handler
  2. The handler is the next middleware in the chain (or the request handler if there are no more middlewares)
  3. When the handler returns a response, execution continues from the last middleware right after the handler call
  4. This creates a nested "onion-like" pattern for execution

For example, with middlewares=(middleware1, middleware2), the execution order would be:

  1. Enter middleware1 (pre-request code)
  2. Enter middleware2 (pre-request code)
  3. Execute the actual request handler
  4. Exit middleware2 (post-response code)
  5. Exit middleware1 (post-response code)

This flat structure means that a middleware is applied on each retry attempt inside the client's retry loop,
not just once before all retries. This allows middleware to modify requests freshly on each retry attempt.

For example, if we had a retry middleware and a logging middleware, and we want every retried request to be
logged separately, then we'd need to specify middlewares=(retry_mw, logging_mw). If we reversed the order
to middlewares=(logging_mw, retry_mw), then we'd only log once regardless of how many retries are done.

.. note::

Client middleware is a powerful feature but should be used judiciously.
Each middleware adds overhead to request processing. For simple use cases
like adding static headers, you can often use request parameters
(e.g., headers) or session configuration instead.

Custom Cookies

To send your own cookies to the server, you can use the cookies
parameter of :class:ClientSession constructor::

text
url = 'http://httpbin.org/cookies'
cookies = {'cookies_are': 'working'}
async with ClientSession(cookies=cookies) as session:
    async with session.get(url) as resp:
        assert await resp.json() == {
           "cookies": {"cookies_are": "working"}}

.. note::
httpbin.org/cookies endpoint returns request cookies
in JSON-encoded body.
To access session cookies see :attr:ClientSession.cookie_jar.

:class:~aiohttp.ClientSession may be used for sharing cookies
between multiple requests::

text
async with aiohttp.ClientSession() as session:
    async with session.get(
        "http://httpbin.org/cookies/set?my_cookie=my_value",
        allow_redirects=False
    ) as resp:
        assert resp.cookies["my_cookie"].value == "my_value"
    async with session.get("http://httpbin.org/cookies") as r:
        json_body = await r.json()
        assert json_body["cookies"]["my_cookie"] == "my_value"

Response Headers and Cookies

We can view the server's response :attr:ClientResponse.headers using
a :class:~multidict.CIMultiDictProxy::

text
assert resp.headers == {
    'ACCESS-CONTROL-ALLOW-ORIGIN': '*',
    'CONTENT-TYPE': 'application/json',
    'DATE': 'Tue, 15 Jul 2014 16:49:51 GMT',
    'SERVER': 'gunicorn/18.0',
    'CONTENT-LENGTH': '331',
    'CONNECTION': 'keep-alive'}

The dictionary is special, though: it's made just for HTTP
headers. According to RFC 7230 <http://tools.ietf.org/html/rfc7230#section-3.2>_, HTTP Header names
are case-insensitive. It also supports multiple values for the same
key as HTTP protocol does.

So, we can access the headers using any capitalization we want::

text
assert resp.headers['Content-Type'] == 'application/json'

assert resp.headers.get('content-type') == 'application/json'

All headers are converted from binary data using UTF-8 with
surrogateescape option. That works fine on most cases but
sometimes unconverted data is needed if a server uses nonstandard
encoding. While these headers are malformed from :rfc:7230
perspective they may be retrieved by using
:attr:ClientResponse.raw_headers property::

text
assert resp.raw_headers == (
    (b'SERVER', b'nginx'),
    (b'DATE', b'Sat, 09 Jan 2016 20:28:40 GMT'),
    (b'CONTENT-TYPE', b'text/html; charset=utf-8'),
    (b'CONTENT-LENGTH', b'12150'),
    (b'CONNECTION', b'keep-alive'))

If a response contains some HTTP Cookies, you can quickly access them::

text
url = 'http://example.com/some/cookie/setting/url'
async with session.get(url) as resp:
    print(resp.cookies['example_cookie_name'])

.. note::

Response cookies contain only values, that were in Set-Cookie headers
of the last request in redirection chain. To gather cookies between all
redirection requests please use :ref:aiohttp.ClientSession <aiohttp-client-session> object.

Redirection History

If a request was redirected, it is possible to view previous responses using
the :attr:~ClientResponse.history attribute::

text
resp = await session.get('http://example.com/some/redirect/')
assert resp.status == 200
assert resp.url == URL('http://example.com/some/other/url/')
assert len(resp.history) == 1
assert resp.history[0].status == 301
assert resp.history[0].url == URL(
    'http://example.com/some/redirect/')

If no redirects occurred or allow_redirects is set to False,
history will be an empty sequence.

.. _aiohttp-client-cookie-safety:

Cookie Safety
^^^^^^^^^^^^^

By default :class:~aiohttp.ClientSession uses strict version of
:class:aiohttp.CookieJar. :rfc:2109 explicitly forbids cookie
accepting from URLs with IP address instead of DNS name
(e.g. http://127.0.0.1:80/cookie).

It's good but sometimes for testing we need to enable support for such
cookies. It should be done by passing unsafe=True to
:class:aiohttp.CookieJar constructor::

jar = aiohttp.CookieJar(unsafe=True)
session = aiohttp.ClientSession(cookie_jar=jar)

.. _aiohttp-client-cookie-quoting-routine:

Cookie Quoting Routine
^^^^^^^^^^^^^^^^^^^^^^

The client uses the :class:~aiohttp.SimpleCookie quoting routines
conform to the :rfc:2109, which in turn references the character definitions
from :rfc:2068. They provide a two-way quoting algorithm where any non-text
character is translated into a 4 character sequence: a forward-slash
followed by the three-digit octal equivalent of the character.
Any \ or " is quoted with a preceding \ slash.
Because of the way browsers really handle cookies (as opposed to what the RFC
says) we also encode , and ;.

Some backend systems does not support quoted cookies. You can skip this
quotation routine by passing quote_cookie=False to the
:class:~aiohttp.CookieJar constructor::

jar = aiohttp.CookieJar(quote_cookie=False)
session = aiohttp.ClientSession(cookie_jar=jar)

.. _aiohttp-client-dummy-cookie-jar:

Dummy Cookie Jar
^^^^^^^^^^^^^^^^

Sometimes cookie processing is not desirable. For this purpose it's
possible to pass :class:aiohttp.DummyCookieJar instance into client
session::

jar = aiohttp.DummyCookieJar()
session = aiohttp.ClientSession(cookie_jar=jar)

Uploading pre-compressed data

To upload data that is already compressed before passing it to
aiohttp, call the request function with the used compression algorithm
name (usually deflate or gzip) as the value of the
Content-Encoding header::

text
async def my_coroutine(session, headers, my_data):
    data = zlib.compress(my_data)
    headers = {'Content-Encoding': 'deflate'}
    async with session.post('http://httpbin.org/post',
                            data=data,
                            headers=headers)
        pass

Disabling content type validation for JSON responses

The standard explicitly restricts JSON Content-Type HTTP header to
application/json or any extended form, e.g. application/vnd.custom-type+json.
Unfortunately, some servers send a wrong type, like text/html.

This can be worked around in two ways:

  1. Pass the expected type explicitly (in this case checking will be strict, without the extended form support,
    so custom/xxx+type won't be accepted):

    await resp.json(content_type='custom/type').

  2. Disable the check entirely:

    await resp.json(content_type=None).

.. _aiohttp-client-tracing:

Client Tracing

The execution flow of a specific request can be followed attaching
listeners coroutines to the signals provided by the
:class:TraceConfig instance, this instance will be used as a
parameter for the :class:ClientSession constructor having as a
result a client that triggers the different signals supported by the
:class:TraceConfig. By default any instance of
:class:ClientSession class comes with the signals ability
disabled. The following snippet shows how the start and the end
signals of a request flow can be followed::

text
async def on_request_start(
        session, trace_config_ctx, params):
    print("Starting request")

async def on_request_end(session, trace_config_ctx, params):
    print("Ending request")

trace_config = aiohttp.TraceConfig()
trace_config.on_request_start.append(on_request_start)
trace_config.on_request_end.append(on_request_end)
async with aiohttp.ClientSession(
        trace_configs=[trace_config]) as client:
    client.get('http://example.com/some/redirect/')

The trace_configs is a list that can contain instances of
:class:TraceConfig class that allow run the signals handlers coming
from different :class:TraceConfig instances. The following example
shows how two different :class:TraceConfig that have a different
nature are installed to perform their job in each signal handle::

text
from mylib.traceconfig import AuditRequest
from mylib.traceconfig import XRay

async with aiohttp.ClientSession(
        trace_configs=[AuditRequest(), XRay()]) as client:
    client.get('http://example.com/some/redirect/')

All signals take as a parameters first, the :class:ClientSession
instance used by the specific request related to that signals and
second, a :class:~types.SimpleNamespace instance called
trace_config_ctx. The trace_config_ctx object can be used to
share the state through to the different signals that belong to the
same request and to the same :class:TraceConfig class, perhaps::

text
async def on_request_start(
        session, trace_config_ctx, params):
    trace_config_ctx.start = asyncio.get_event_loop().time()

async def on_request_end(session, trace_config_ctx, params):
    elapsed = asyncio.get_event_loop().time() - trace_config_ctx.start
    print("Request took {}".format(elapsed))

The trace_config_ctx param is by default a
:class:~types.SimpleNamespace that is initialized at the beginning of the
request flow. However, the factory used to create this object can be
overwritten using the trace_config_ctx_factory constructor param of
the :class:TraceConfig class.

The trace_request_ctx param can given at the beginning of the
request execution, accepted by all of the HTTP verbs, and will be
passed as a keyword argument for the trace_config_ctx_factory
factory. This param is useful to pass data that is only available at
request time, perhaps::

text
async def on_request_start(
        session, trace_config_ctx, params):
    print(trace_config_ctx.trace_request_ctx)


session.get('http://example.com/some/redirect/',
            trace_request_ctx={'foo': 'bar'})

.. seealso:: :ref:aiohttp-client-tracing-reference section for
more information about the different signals supported.

Connectors

To tweak or change transport layer of requests you can pass a custom
connector to :class:~aiohttp.ClientSession and family. For example::

text
conn = aiohttp.TCPConnector()
session = aiohttp.ClientSession(connector=conn)

.. note::

By default session object takes the ownership of the connector, among
other things closing the connections once the session is closed. If
you are keen on share the same connector through different session
instances you must give the connector_owner parameter as False
for each session instance.

.. seealso:: :ref:aiohttp-client-reference-connectors section for
more information about different connector types and
configuration options.

Limiting connection pool size
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To limit amount of simultaneously opened connections you can pass limit
parameter to connector::

text
conn = aiohttp.TCPConnector(limit=30)

The example limits total amount of parallel connections to 30.

The default is 100.

If you explicitly want not to have limits, pass 0. For example::

text
conn = aiohttp.TCPConnector(limit=0)

To limit amount of simultaneously opened connection to the same
endpoint ((host, port, is_ssl) triple) you can pass limit_per_host
parameter to connector::

text
conn = aiohttp.TCPConnector(limit_per_host=30)

The example limits amount of parallel connections to the same to 30.

The default is 0 (no limit on per host bases).

Tuning the DNS cache
^^^^^^^^^^^^^^^^^^^^

By default :class:~aiohttp.TCPConnector comes with the DNS cache
table enabled, and resolutions will be cached by default for 10 seconds.
This behavior can be changed either to change of the TTL for a resolution,
as can be seen in the following example::

text
conn = aiohttp.TCPConnector(ttl_dns_cache=300)

or disabling the use of the DNS cache table, meaning that all requests will
end up making a DNS resolution, as the following example shows::

text
conn = aiohttp.TCPConnector(use_dns_cache=False)

Resolving using custom nameservers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In order to specify the nameservers to when resolving the hostnames,
:term:aiodns is required::

text
from aiohttp.resolver import AsyncResolver

resolver = AsyncResolver(nameservers=["8.8.8.8", "8.8.4.4"])
conn = aiohttp.TCPConnector(resolver=resolver)

Unix domain sockets
^^^^^^^^^^^^^^^^^^^

If your HTTP server uses UNIX domain sockets you can use
:class:~aiohttp.UnixConnector::

conn = aiohttp.UnixConnector(path='/path/to/socket')
session = aiohttp.ClientSession(connector=conn)

Request URLs continue to work much the same as any other connector.
The protocol is still HTTP, therefore the http:// scheme should still be used.
While the connector always routes to the socket, the domain/port in the URL is
still used to create the Host header and must be included.
For example::

async with session.get('http://localhost/get') as resp:
...

Custom socket creation
^^^^^^^^^^^^^^^^^^^^^^

If the default socket is insufficient for your use case, pass an optional
socket_factory to the :class:~aiohttp.TCPConnector, which implements
:class:SocketFactoryType. This will be used to create all sockets for the
lifetime of the class object. For example, we may want to change the
conditions under which we consider a connection dead. The following would
make all sockets respect 9*7200 = 18 hours::

import socket

def socket_factory(addr_info):
family, type_, proto, , _ = addr_info
sock = socket.socket(family=family, type=type
, proto=proto)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7200)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 9)
return sock

conn = aiohttp.TCPConnector(socket_factory=socket_factory)

socket_factory may also be used for binding to the specific network
interface on supported platforms::

def socket_factory(addr_info):
family, type_, proto, , _ = addr_info
sock = socket.socket(family=family, type=type
, proto=proto)
sock.setsockopt(
socket.SOL_SOCKET, socket.SO_BINDTODEVICE, b'eth0'
)
return sock

conn = aiohttp.TCPConnector(socket_factory=socket_factory)

Named pipes in Windows
^^^^^^^^^^^^^^^^^^^^^^

If your HTTP server uses Named pipes you can use
:class:~aiohttp.NamedPipeConnector::

conn = aiohttp.NamedPipeConnector(path=r'\.\pipe<name-of-pipe>')
session = aiohttp.ClientSession(connector=conn)

It will only work with the ProactorEventLoop

SSL control for TCP sockets

By default aiohttp uses strict checks for HTTPS protocol. Certification
checks can be relaxed by setting ssl to False::

r = await session.get('https://example.com', ssl=False)

If you need to setup custom ssl parameters (use own certification
files for example) you can create a :class:ssl.SSLContext instance and
pass it into the :meth:ClientSession.request methods or set it for the
entire session with ClientSession(ssl=ssl_context).

There are explicit errors when ssl verification fails

:class:aiohttp.ClientConnectorSSLError::

try:
await session.get('https://expired.badssl.com/')
except aiohttp.ClientConnectorSSLError as e:
assert isinstance(e, ssl.SSLError)

:class:aiohttp.ClientConnectorCertificateError::

try:
await session.get('https://wrong.host.badssl.com/')
except aiohttp.ClientConnectorCertificateError as e:
assert isinstance(e, ssl.CertificateError)

If you need to skip both ssl related errors

:class:aiohttp.ClientSSLError::

try:
await session.get('https://expired.badssl.com/')
except aiohttp.ClientSSLError as e:
assert isinstance(e, ssl.SSLError)

try:
await session.get('https://wrong.host.badssl.com/')
except aiohttp.ClientSSLError as e:
assert isinstance(e, ssl.CertificateError)

Example: Use certifi
^^^^^^^^^^^^^^^^^^^^

By default, Python uses the system CA certificates. In rare cases, these may not be
installed or Python is unable to find them, resulting in a error like
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

One way to work around this problem is to use the certifi package::

ssl_context = ssl.create_default_context(cafile=certifi.where())
async with ClientSession(ssl=ssl_context) as sess:
...

Example: Use self-signed certificate
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you need to verify self-signed certificates, you need to add a call to
:meth:ssl.SSLContext.load_cert_chain with the key pair::

ssl_context = ssl.create_default_context()
ssl_context.load_cert_chain("/path/to/client/public/device.pem",
"/path/to/client/private/device.key")
async with sess.get("https://example.com", ssl=ssl_context) as resp:
...

Example: Verify certificate fingerprint
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You may also verify certificates via SHA256 fingerprint::

Attempt to connect to https://www.python.org

with a pin to a bogus certificate:

bad_fp = b'0'*64
exc = None
try:
r = await session.get('https://www.python.org',
ssl=aiohttp.Fingerprint(bad_fp))
except aiohttp.FingerprintMismatch as e:
exc = e
assert exc is not None
assert exc.expected == bad_fp

www.python.org cert's actual fingerprint

assert exc.got == b'...'

Note that this is the fingerprint of the DER-encoded certificate.
If you have the certificate in PEM format, you can convert it to
DER with e.g::

openssl x509 -in crt.pem -inform PEM -outform DER > crt.der

.. note::

Tip: to convert from a hexadecimal digest to a binary byte-string,
you can use :func:binascii.unhexlify.

ssl parameter could be passed to :class:ClientSession as the
session-wide default, the value from :meth:ClientSession.get and
others override it.

.. _aiohttp-client-proxy-support:

Proxy support

aiohttp supports plain HTTP proxies and HTTP proxies that can be
upgraded to HTTPS via the HTTP CONNECT method. aiohttp has a limited
support for proxies that must be connected to via https:// โ€” see
the info box below for more details.
To connect, use the proxy parameter::

async with aiohttp.ClientSession() as session:
async with session.get("http://python.org",
proxy="http://proxy.com") as resp:
print(resp.status)

It also supports proxy authorization::

from aiohttp import encode_basic_auth

async with aiohttp.ClientSession() as session:
proxy_headers = {"Proxy-Authorization": encode_basic_auth("user", "pass")}
async with session.get("http://python.org",
proxy="http://proxy.com",
proxy_headers=proxy_headers) as resp:
print(resp.status)

Authentication credentials can be passed in proxy URL::

session.get("http://python.org",
proxy="http://user:[email protected]")

And you may set default proxy::

proxy_headers = {"Proxy-Authorization": encode_basic_auth("user", "pass")}
async with aiohttp.ClientSession(
proxy="http://proxy.com", proxy_headers=proxy_headers
) as session:
async with session.get("http://python.org") as resp:
print(resp.status)

Contrary to the requests library, it won't read environment
variables by default. But you can do so by passing
trust_env=True into :class:aiohttp.ClientSession
constructor.::

async with aiohttp.ClientSession(trust_env=True) as session:
async with session.get("http://python.org") as resp:
print(resp.status)

.. note::
aiohttp uses :func:urllib.request.getproxies
for reading the proxy configuration (e.g. from the HTTP_PROXY etc. environment variables) and applies them for the HTTP, HTTPS, WS and WSS schemes.

text
Hosts defined in ``no_proxy`` will bypass the proxy.

Proxy credentials are given from ~/.netrc file if present (see
:class:aiohttp.ClientSession for more details).

.. attention::

As of now (Python 3.10), support for TLS in TLS is disabled for the transports that
:py:mod:asyncio uses. If the further release of Python (say v3.11)
toggles one attribute, it'll just workโ„ข.

aiohttp v3.8 and higher is ready for this to happen and has code in
place supports TLS-in-TLS, hence sending HTTPS requests over HTTPS
proxy tunnels.

โš ๏ธ For as long as your Python runtime doesn't declare the support for
TLS-in-TLS, please don't file bugs with aiohttp but rather try to
help the CPython upstream enable this feature. Meanwhile, if you
really need this to work, there's a patch that may help you make
it happen, include it into your app's code base:
https://github.com/aio-libs/aiohttp/discussions/6044#discussioncomment-1432443.

.. important::

When supplying a custom :py:class:ssl.SSLContext instance, bear in
mind that it will be used not only to establish a TLS session with
the HTTPS endpoint you're hitting but also to establish a TLS tunnel
to the HTTPS proxy. To avoid surprises, make sure to set up the trust
chain that would recognize TLS certificates used by both the endpoint
and the proxy.

.. _aiohttp-persistent-session:

Persistent session

Even though creating a session on demand seems like a tempting idea, we
advise against it. :class:aiohttp.ClientSession maintains a
connection pool. Contained connections can be reused if necessary to gain some
performance improvements. If you plan on reusing the session, a.k.a. creating
persistent session, you can use either :ref:aiohttp-web-signals or
:ref:aiohttp-web-cleanup-ctx. If possible we advise using :ref:aiohttp-web-cleanup-ctx,
as it results in more compact code::

text
session = aiohttp.web.AppKey("session", aiohttp.ClientSession)

@contextlib.asynccontextmanager
async def persistent_session(app):
   app[persistent_session] = session = aiohttp.ClientSession()
   yield
   await session.close()

async def my_request_handler(request):
   sess = request.app[session]
   async with sess.get("http://python.org") as resp:
       print(resp.status)

app.cleanup_ctx.append(persistent_session)

This approach can be successfully used to define numerous sessions given certain
requirements. It benefits from having a single location where :class:aiohttp.ClientSession
instances are created and where artifacts such as :class:aiohttp.BaseConnector
can be safely shared between sessions if needed.

In the end all you have to do is to close all sessions after the yield statement::

text
async def multiple_sessions(app):
   app[persistent_session_1] = session_1 = aiohttp.ClientSession()
   app[persistent_session_2] = session_2 = aiohttp.ClientSession()
   app[persistent_session_3] = session_3 = aiohttp.ClientSession()

   yield

   await asyncio.gather(
       session_1.close(),
       session_2.close(),
       session_3.close(),
   )

Graceful Shutdown

When :class:ClientSession closes at the end of an async with
block (or through a direct :meth:ClientSession.close call), the
underlying connection remains open due to asyncio internal details. In
practice, the underlying connection will close after a short
while. However, if the event loop is stopped before the underlying
connection is closed, a ResourceWarning: unclosed transport
warning is emitted (when warnings are enabled).

To avoid this situation, a small delay must be added before closing
the event loop to allow any open underlying connections to close.

For a :class:ClientSession without SSL, a simple zero-sleep (await asyncio.sleep(0)) will suffice::

text
async def read_website():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://example.org/') as resp:
            await resp.read()
    # Zero-sleep to allow underlying connections to close
    await asyncio.sleep(0)

For a :class:ClientSession with SSL, the application must wait a
short duration before closing::

text
...
# Wait 250 ms for the underlying SSL connections to close
await asyncio.sleep(0.250)

Note that the appropriate amount of time to wait will vary from
application to application.

All of this will eventually become obsolete when the asyncio internals
are changed so that aiohttp itself can wait on the underlying
connection to close. Please follow issue #1925 <https://github.com/aio-libs/aiohttp/issues/1925>_ for the progress
on this.

HTTP Pipelining

aiohttp does not support HTTP/HTTPS pipelining.

Character Set Detection

If you encounter a :exc:UnicodeDecodeError when using :meth:ClientResponse.text
this may be because the response does not include the charset needed
to decode the body.

If you know the correct encoding for a request, you can simply specify
the encoding as a parameter (e.g. resp.text("windows-1252")).

Alternatively, :class:ClientSession accepts a fallback_charset_resolver parameter which
can be used to introduce charset guessing functionality. When a charset is not found
in the Content-Type header, this function will be called to get the charset encoding. For
example, this can be used with the chardetng_py library.::

text
from chardetng_py import detect

def charset_resolver(resp: ClientResponse, body: bytes) -> str:
    tld = resp.url.host.rsplit(".", maxsplit=1)[-1]
    return detect(body, allow_utf8=True, tld=tld.encode())

ClientSession(fallback_charset_resolver=charset_resolver)

Or, if chardetng_py doesn't work for you, then charset-normalizer is another option::

text
from charset_normalizer import detect

ClientSession(fallback_charset_resolver=lambda r, b: detect(b)["encoding"] or "utf-8")

File: docs/client_quickstart.rst

.. currentmodule:: aiohttp

.. _aiohttp-client-quickstart:

=================== Client Quickstart

Eager to get started? This page gives a good introduction in how to
get started with aiohttp client API.

First, make sure that aiohttp is :ref:installed <aiohttp-installation> and up-to-date

Let's get started with some simple examples.

Make a Request

Begin by importing the aiohttp module, and asyncio::

text
import aiohttp
import asyncio

Now, let's try to get a web-page. For example let's query
http://httpbin.org/get::

text
async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://httpbin.org/get') as resp:
            print(resp.status)
            print(await resp.text())

asyncio.run(main())

Now, we have a :class:ClientSession called session and a
:class:ClientResponse object called resp. We can get all the
information we need from the response. The mandatory parameter of
:meth:ClientSession.get coroutine is an HTTP url (:class:str or
class:yarl.URL instance).

In order to make an HTTP POST request use :meth:ClientSession.post coroutine::

text
session.post('http://httpbin.org/post', data=b'data')

Other HTTP methods are available as well::

text
session.put('http://httpbin.org/put', data=b'data')
session.delete('http://httpbin.org/delete')
session.head('http://httpbin.org/get')
session.options('http://httpbin.org/get')
session.patch('http://httpbin.org/patch', data=b'data')

To make several requests to the same site more simple, the parameter base_url
of :class:ClientSession constructor can be used. For example to request different
endpoints of http://httpbin.org can be used the following code::

text
async with aiohttp.ClientSession('http://httpbin.org') as session:
    async with session.get('/get'):
        pass
    async with session.post('/post', data=b'data'):
        pass
    async with session.put('/put', data=b'data'):
        pass

.. note::

Don't create a session per request. Most likely you need a session
per application which performs all requests together.

More complex cases may require a session per site, e.g. one for
Github and other one for Facebook APIs. Anyway making a session for
every request is a very bad idea.

A session contains a connection pool inside. Connection reusage and
keep-alive (both are on by default) may speed up total performance.

You may find more information about creating persistent sessions
in :ref:aiohttp-persistent-session.

A session context manager usage is not mandatory
but await session.close() method
should be called in this case, e.g.::

text
session = aiohttp.ClientSession()
async with session.get('...'):
    # ...
await session.close()

Passing Parameters In URLs

You often want to send some sort of data in the URL's query string. If
you were constructing the URL by hand, this data would be given as key/value
pairs in the URL after a question mark, e.g. httpbin.org/get?key=val.
aiohttp allows you to provide these arguments as a :class:dict, using the
params keyword argument. As an example, if you wanted to pass
key1=value1 and key2=value2 to httpbin.org/get, you would use the
following code::

text
params = {'key1': 'value1', 'key2': 'value2'}
async with session.get('http://httpbin.org/get',
                       params=params) as resp:
    expect = 'http://httpbin.org/get?key1=value1&key2=value2'
    assert str(resp.url) == expect

You can see that the URL has been correctly encoded by printing the URL.

For sending data with multiple values for the same key
:class:~multidict.MultiDict may be used; the library support nested lists
({'key': ['value1', 'value2']}) alternative as well.

It is also possible to pass a list of 2 item tuples as parameters, in
that case you can specify multiple values for each key::

text
params = [('key', 'value1'), ('key', 'value2')]
async with session.get('http://httpbin.org/get',
                       params=params) as r:
    expect = 'http://httpbin.org/get?key=value2&key=value1'
    assert str(r.url) == expect

You can also pass :class:str content as param. The value is used as a
query string, but passing params does not disable URL
canonicalization. Note that + is not encoded::

text
async with session.get('http://httpbin.org/get',
                       params='key=value+1') as r:
        assert str(r.url) == 'http://httpbin.org/get?key=value+1'

.. note::

aiohttp internally performs URL canonicalization before sending request.

Canonicalization encodes host part by :term:IDNA codec and applies
:term:requoting to path and query parts.

For example URL('http://example.com/ะฟัƒั‚ัŒ/%30?a=%31') is converted to
URL('http://example.com/%D0%BF%D1%83%D1%82%D1%8C/0?a=1').

Sometimes canonicalization is not desirable if server accepts exact
representation and does not requote URL itself.

To disable canonicalization use encoded=True parameter for URL construction::

text
await session.get(
      URL('http://example.com/%30', encoded=True))

.. warning::

Passing params overrides encoded=True. Never use both options
if you need to preserve exact query-string bytes.
Build the full URL (including query) instead.

Response Content and Status Code

We can read the content of the server's response and its status
code. Consider the GitHub time-line again::

text
async with session.get('https://api.github.com/events') as resp:
    print(resp.status)
    print(await resp.text())

prints out something like::

text
200
'[{"created_at":"2015-06-12T14:06:22Z","public":true,"actor":{...

aiohttp automatically decodes the content from the server. You can
specify custom encoding for the :meth:~ClientResponse.text method::

text
await resp.text(encoding='windows-1251')

Binary Response Content

You can also access the response body as bytes, for non-text requests::

text
print(await resp.read())

::

text
b'[{"created_at":"2015-06-12T14:06:22Z","public":true,"actor":{...

The gzip and deflate transfer-encodings are automatically
decoded for you.

You can enable brotli transfer-encodings support,
just install Brotli <https://pypi.org/project/Brotli/>_
or brotlicffi <https://pypi.org/project/brotlicffi/>_.

You can enable zstd transfer-encodings support,
install backports.zstd <https://pypi.org/project/backports.zstd/>_.
If you are using Python >= 3.14, no dependency should be required.

JSON Request

Any of session's request methods like :func:request,
:meth:ClientSession.get, :meth:ClientSession.post etc. accept
json parameter::

async with aiohttp.ClientSession() as session:
await session.post(url, json={'test': 'object'})

By default session uses python's standard :mod:json module for
serialization. But it is possible to use a different
serializer. :class:ClientSession accepts json_serialize and
json_serialize_bytes parameters::

import orjson

async with aiohttp.ClientSession(
json_serialize_bytes=orjson.dumps) as session:
await session.post(url, json={'test': 'object'})

.. note::

orjson library is faster than standard :mod:json and is actively
maintained. Since orjson.dumps returns :class:bytes, pass it via
the json_serialize_bytes parameter to avoid unnecessary
encoding/decoding overhead.

JSON Response Content

There's also a built-in JSON decoder, in case you're dealing with JSON data::

text
async with session.get('https://api.github.com/events') as resp:
    print(await resp.json())

In case that JSON decoding fails, :meth:~ClientResponse.json will
raise an exception. It is possible to specify custom encoding and
decoder functions for the :meth:~ClientResponse.json call.

.. note::

text
The methods above reads the whole response body into memory. If you are
planning on reading lots of data, consider using the streaming response
method documented below.

Streaming Response Content

While methods :meth:~ClientResponse.read,
:meth:~ClientResponse.json and :meth:~ClientResponse.text are very
convenient you should use them carefully. All these methods load the
whole response in memory. For example if you want to download several
gigabyte sized files, these methods will load all the data in
memory. Instead you can use the :attr:~ClientResponse.content
attribute. It is an instance of the :class:aiohttp.StreamReader
class. The gzip and deflate transfer-encodings are
automatically decoded for you::

text
async with session.get('https://api.github.com/events') as resp:
    await resp.content.read(10)

In general, however, you should use a pattern like this to save what is being
streamed to a file::

text
with open(filename, 'wb') as fd:
    async for chunk in resp.content.iter_chunked(chunk_size):
        fd.write(chunk)

It is not possible to use :meth:~ClientResponse.read,
:meth:~ClientResponse.json and :meth:~ClientResponse.text after
explicit reading from :attr:~ClientResponse.content.

More complicated POST requests

Typically, you want to send some form-encoded data -- much like an HTML form.
To do this, simply pass a dictionary to the data argument. Your
dictionary of data will automatically be form-encoded when the request is made::

text
payload = {'key1': 'value1', 'key2': 'value2'}
async with session.post('http://httpbin.org/post',
                        data=payload) as resp:
    print(await resp.text())

::

text
{
  ...
  "form": {
    "key2": "value2",
    "key1": "value1"
  },
  ...
}

If you want to send data that is not form-encoded you can do it by
passing a :class:bytes instead of a :class:dict. This data will be
posted directly and content-type set to 'application/octet-stream' by
default::

text
async with session.post(url, data=b'\x00Binary-data\x00') as resp:
    ...

If you want to send JSON data::

text
async with session.post(url, json={'example': 'test'}) as resp:
    ...

To send text with appropriate content-type just use data argument::

text
async with session.post(url, data='ะขะตัั‚') as resp:
    ...

POST a Multipart-Encoded File

To upload Multipart-encoded files::

text
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

await session.post(url, data=files)

You can set the filename and content_type explicitly::

text
url = 'http://httpbin.org/post'
data = aiohttp.FormData()
data.add_field('file',
               open('report.xls', 'rb'),
               filename='report.xls',
               content_type='application/vnd.ms-excel')

await session.post(url, data=data)

If you pass a file object as data parameter, aiohttp will stream it to
the server automatically. Check :class:~aiohttp.StreamReader
for supported format information.

.. seealso:: :ref:aiohttp-multipart

Streaming uploads

:mod:aiohttp supports multiple types of streaming uploads, which allows you to
send large files without reading them into memory.

As a simple case, simply provide a file-like object for your body::

text
with open("massive-body", "rb") as f:
   await session.post("https://httpbin.org/post", data=f)

Or you can provide an asynchronous generator, for example to generate
data on the fly::

async def data_generator():
for i in range(10):
yield f"line {i}\n".encode()

async with session.post("https://httpbin.org/post",
data=data_generator()) as resp:
print(await resp.text())

.. warning::

Async generators and other non-rewindable data sources
(such as :class:~aiohttp.StreamReader) cannot be replayed if a
redirect occurs (for example, HTTP 307 or 308). If the request body
has already been streamed, :mod:aiohttp raises
:class:~aiohttp.ClientPayloadError.

If your endpoint may redirect, either:

  • Pass a seekable file-like object or :class:bytes.
  • Disable redirects with allow_redirects=False and handle them manually.

Because the :attr:~aiohttp.ClientResponse.content attribute is a
:class:~aiohttp.StreamReader (provides async iterator protocol), you
can chain get and post requests together::

resp = await session.get('http://python.org')
await session.post('http://httpbin.org/post',
data=resp.content)

.. _aiohttp-client-websockets:

WebSockets

:mod:aiohttp works with client websockets out-of-the-box.

You have to use the :meth:aiohttp.ClientSession.ws_connect coroutine
for client websocket connection. It accepts a url as a first
parameter and returns :class:ClientWebSocketResponse, with that
object you can communicate with websocket server using response's
methods::

async with session.ws_connect('http://example.org/ws') as ws:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data == 'close cmd':
await ws.close()
break
else:
await ws.send_str(msg.data + '/answer')
elif msg.type == aiohttp.WSMsgType.ERROR:
break

You must use the only websocket task for both reading (e.g. await ws.receive() or async for msg in ws:) and writing but may have
multiple writer tasks which can only send data asynchronously (by
await ws.send_str('data') for example).

.. _aiohttp-client-timeouts:

Timeouts

Timeout settings are stored in :class:ClientTimeout data structure.

By default aiohttp uses a total 300 seconds (5min) timeout, it means that the
whole operation should finish in 5 minutes. In order to allow time for DNS fallback,
the default sock_connect timeout is 30 seconds.

The value could be overridden by timeout parameter for the session (specified in seconds)::

text
timeout = aiohttp.ClientTimeout(total=60)
async with aiohttp.ClientSession(timeout=timeout) as session:
    ...

Timeout could be overridden for a request like :meth:ClientSession.get::

text
async with session.get(url, timeout=timeout) as resp:
    ...

Supported :class:ClientTimeout fields are:

total

text
The maximal number of seconds for the whole operation including connection
  establishment, request sending and response reading.

connect

text
The maximal number of seconds for
  connection establishment of a new connection or
  for waiting for a free connection from a pool if pool connection
  limits are exceeded.

sock_connect

text
The maximal number of seconds for connecting to a peer for a new connection, not
  given from a pool.

sock_read

text
The maximal number of seconds allowed for period between reading a new
  data portion from a peer.

``ceil_threshold``

  The threshold value to trigger ceiling of absolute timeout values.

All fields are floats, None or 0 disables a particular timeout check, see the
:class:ClientTimeout reference for defaults and additional details.

Thus the default timeout is::

aiohttp.ClientTimeout(total=5*60, connect=None,
sock_connect=None, sock_read=None, ceil_threshold=5)

.. note::

aiohttp ceils timeout if the value is equal or greater than 5
seconds. The timeout expires at the next integer second greater than
current_time + timeout.

The ceiling is done for the sake of optimization, when many concurrent tasks
are scheduled to wake-up at the almost same but different absolute times. It
leads to very many event loop wakeups, which kills performance.

The optimization shifts absolute wakeup times by scheduling them to exactly
the same time as other neighbors, the loop wakes up once-per-second for
timeout expiration.

Smaller timeouts are not rounded to help testing; in the real life network
timeouts usually greater than tens of seconds. However, the default threshold
value of 5 seconds can be configured using the ceil_threshold parameter.


File: docs/client_reference.rst

.. _aiohttp-client-reference:

Client Reference

.. currentmodule:: aiohttp

Client Session

Client session is the recommended interface for making HTTP requests.

Session encapsulates a connection pool (connector instance) and
supports keepalives by default. Unless you are connecting to a large,
unknown number of different servers over the lifetime of your
application, it is suggested you use a single session for the
lifetime of your application to benefit from connection pooling.

Usage example::

text
import aiohttp
 import asyncio

 async def fetch(client):
     async with client.get('http://python.org') as resp:
         assert resp.status == 200
         return await resp.text()

 async def main():
     async with aiohttp.ClientSession() as client:
         html = await fetch(client)
         print(html)

 asyncio.run(main())

The client session supports the context manager protocol for self closing.

.. class:: ClientSession(base_url=None, *,
connector=None, cookies=None,
headers=None, skip_auto_headers=None,
json_serialize=json.dumps,
request_class=ClientRequest,
response_class=ClientResponse,
ws_response_class=ClientWebSocketResponse,
version=aiohttp.HttpVersion11,
cookie_jar=None,
connector_owner=True,
raise_for_status=False,
timeout=sentinel,
auto_decompress=True,
ssl=True,
trust_env=False,
requote_redirect_url=True,
trace_configs=None,
middlewares=(),
read_bufsize=2**18,
max_line_size=8190,
max_field_size=8190,
max_headers=128,
fallback_charset_resolver=lambda r, b: "utf-8",
ssl_shutdown_timeout=0)
:canonical: aiohttp.client.ClientSession

The class for creating client sessions and making requests.

:param base_url: Base part of the URL (optional)
If set, allows to join a base part to relative URLs in request calls.
If the URL has a path it must have a trailing / (as in
https://docs.aiohttp.org/en/stable/).

text
Note that URL joining follows :rfc:`3986`. This means, in the most
  common case the request URLs should have no leading slash, e.g.::

    session = ClientSession(base_url="http://example.com/foo/")

    await session.request("GET", "bar")
    # request for http://example.com/foo/bar

    await session.request("GET", "/bar")
    # request for http://example.com/bar

  .. versionadded:: 3.8

  .. versionchanged:: 3.12

     Added support for overriding the base URL with an absolute one in client sessions.

:param aiohttp.BaseConnector connector: BaseConnector
sub-class instance to support connection pooling.

:param dict cookies: Cookies to send with the request (optional)

:param headers: HTTP Headers to send with every request (optional).

text
May be either *iterable of key-value pairs* or
               :class:`~collections.abc.Mapping`
               (e.g. :class:`dict`,
               :class:`~multidict.CIMultiDict`).

:param skip_auto_headers: set of headers for which autogeneration
should be skipped.

text
*aiohttp* autogenerates headers like ``User-Agent`` or
  ``Content-Type`` if these headers are not explicitly
  passed. Using ``skip_auto_headers`` parameter allows to skip
  that generation. Note that ``Content-Length`` autogeneration can't
  be skipped.

  Iterable of :class:`str` or :class:`~multidict.istr` (optional)

:param collections.abc.Callable json_serialize: Json serializer callable.

text
By default :func:`json.dumps` function.

:param aiohttp.ClientRequest request_class: Custom class to use for client requests.

:param ClientResponse response_class: Custom class to use for client responses.

:param ClientWebSocketResponse ws_response_class: Custom class to use for websocket responses.

:param version: supported HTTP version, HTTP 1.1 by default.

:param cookie_jar: Cookie Jar, :class:~aiohttp.abc.AbstractCookieJar instance.

text
By default every session instance has own private cookie jar for
  automatic cookies processing but user may redefine this behavior
  by providing own jar implementation.

  One example is not processing cookies at all when working in
  proxy mode.

  If no cookie processing is needed, a
  :class:`aiohttp.DummyCookieJar` instance can be
  provided.

:param bool connector_owner:

text
Close connector instance on session closing.

  Setting the parameter to ``False`` allows to share
  connection pool between sessions without sharing session state:
  cookies etc.

:param bool raise_for_status:

text
Automatically call :meth:`ClientResponse.raise_for_status` for
  each response, ``False`` by default.

  This parameter can be overridden when making a request, e.g.::

      client_session = aiohttp.ClientSession(raise_for_status=True)
      resp = await client_session.get(url, raise_for_status=False)
      async with resp:
          assert resp.status == 200

  Set the parameter to ``True`` if you need ``raise_for_status``
  for most of cases but override ``raise_for_status`` for those
  requests where you need to handle responses with status 400 or
  higher.

:param timeout: a :class:ClientTimeout settings structure, 300 seconds (5min)
total timeout, 30 seconds socket connect timeout by default.

text
.. versionadded:: 3.3

  .. versionchanged:: 3.10.9

     The default value for the ``sock_connect`` timeout has been changed to 30 seconds.

:param bool auto_decompress: Automatically decompress response body (True by default).

text
.. versionadded:: 2.3

:param ssl: Default SSL validation mode for requests made through this
session. True for default SSL check
(:func:ssl.create_default_context is used),
False for skip SSL certificate validation,
:class:aiohttp.Fingerprint for fingerprint
validation, :class:ssl.SSLContext for custom SSL
certificate validation (True by default).

text
A per-request ``ssl`` argument overrides this value.

  .. versionadded:: 3.15

:param bool trust_env: Trust environment settings for proxy configuration if the parameter
is True (False by default). See :ref:aiohttp-client-proxy-support for
more information.

text
Get proxy credentials from ``~/.netrc`` file if present.

  Get HTTP Basic Auth credentials from :file:`~/.netrc` file if present.

  If :envvar:`NETRC` environment variable is set, read from file specified
  there rather than from :file:`~/.netrc`.

  .. seealso::

     ``.netrc`` documentation: https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html

  .. versionadded:: 2.3

  .. versionchanged:: 3.0

     Added support for ``~/.netrc`` file.

  .. versionchanged:: 3.9

     Added support for reading HTTP Basic Auth credentials from :file:`~/.netrc` file.

:param bool requote_redirect_url: Apply URL requoting for redirection URLs if
automatic redirection is enabled (True by
default).

text
.. versionadded:: 3.5

:param trace_configs: A list of :class:TraceConfig instances used for client
tracing. None (default) is used for request tracing
disabling. See :ref:aiohttp-client-tracing-reference for
more information.

:param middlewares: A sequence of middleware instances to apply to all session requests.
Each middleware must match the :type:ClientMiddlewareType signature.
() (empty tuple, default) is used when no middleware is needed.
See :ref:aiohttp-client-middleware for more information.

text
.. versionadded:: 3.12

:param int read_bufsize: Size of the read buffer (:attr:ClientResponse.content).
256 KiB by default. On a WebSocket connection it
also bounds what is buffered between the handshake
and the reader being installed.

text
.. versionadded:: 3.7

:param int max_line_size: Maximum allowed size of lines in responses.

:param int max_field_size: Maximum allowed size of header name and value combined in responses.

:param int max_headers: Maximum number of headers and trailers combined in responses.

:param Callable[[ClientResponse,bytes],str] fallback_charset_resolver:
A :term:callable that accepts a :class:ClientResponse and the
:class:bytes contents, and returns a :class:str which will be used as
the encoding parameter to :meth:bytes.decode().

text
This function will be called when the charset is not known (e.g. not specified in the
  Content-Type header). The default function simply defaults to ``utf-8``.

  .. versionadded:: 3.8.6

:param float ssl_shutdown_timeout: (DEPRECATED) This parameter is deprecated
and will be removed in aiohttp 4.0. Grace period for SSL shutdown handshake on
TLS connections when the connector is closed (0 seconds by default).
By default (0), SSL connections are aborted immediately when the
connector is closed, without performing the shutdown handshake. During
normal operation, SSL connections use Python's default SSL shutdown
behavior. Setting this to a positive value (e.g., 0.1) will perform
a graceful shutdown when closing the connector, notifying the remote
peer which can help prevent "connection reset" errors at the cost of
additional cleanup time. This timeout is passed to the underlying
:class:TCPConnector when one is created automatically.
Note: On Python versions prior to 3.11, only a value of 0 is supported;
other values will trigger a warning.

text
.. versionadded:: 3.12.5

  .. versionchanged:: 3.12.11
     Changed default from ``0.1`` to ``0`` to abort SSL connections
     immediately when the connector is closed. Added support for
     ``ssl_shutdown_timeout=0`` on all Python versions. A :exc:`RuntimeWarning`
     is issued when non-zero values are passed on Python < 3.11.

  .. deprecated:: 3.12.11
     This parameter is deprecated and will be removed in aiohttp 4.0.

.. attribute:: closed

text
``True`` if the session has been closed, ``False`` otherwise.

  A read-only property.

.. attribute:: connector

text
:class:`aiohttp.BaseConnector` derived instance used
  for the session.

  A read-only property.

.. attribute:: cookie_jar

text
The session cookies, :class:`~aiohttp.abc.AbstractCookieJar` instance.

  Gives access to cookie jar's content and modifiers.

  A read-only property.

.. attribute:: requote_redirect_url

text
aiohttp re quote's redirect urls by default, but some servers
  require exact url from location header. To disable *re-quote* system
  set :attr:`requote_redirect_url` attribute to ``False``.

  .. versionadded:: 2.1

  .. note:: This parameter affects all subsequent requests.

  .. deprecated:: 3.5

     The attribute modification is deprecated.

.. attribute:: loop

text
A loop instance used for session creation.

  A read-only property.

  .. deprecated:: 3.5

.. attribute:: timeout

text
Default client timeouts, :class:`ClientTimeout` instance.  The value can
  be tuned by passing *timeout* parameter to :class:`ClientSession`
  constructor.

  .. versionadded:: 3.7

.. attribute:: headers

text
HTTP Headers that sent with every request

  May be either *iterable of key-value pairs* or
  :class:`~collections.abc.Mapping`
  (e.g. :class:`dict`,
  :class:`~multidict.CIMultiDict`).

  .. versionadded:: 3.7

.. attribute:: skip_auto_headers

text
Set of headers for which autogeneration skipped.

  :class:`frozenset` of :class:`str` or :class:`~multidict.istr` (optional)

  .. versionadded:: 3.7

.. attribute:: json_serialize

text
Json serializer callable.

  By default :func:`json.dumps` function.

  .. versionadded:: 3.7

.. attribute:: connector_owner

text
Should connector be closed on session closing

  :class:`bool` (optional)

  .. versionadded:: 3.7

.. attribute:: raise_for_status

text
Should :meth:`ClientResponse.raise_for_status` be called for each response

  Either :class:`bool` or :class:`collections.abc.Callable`

  .. versionadded:: 3.7

.. attribute:: auto_decompress

text
Should the body response be automatically decompressed

  :class:`bool` default is ``True``

  .. versionadded:: 3.7

.. attribute:: trust_env

text
Trust environment settings for proxy configuration
  or ~/.netrc file if present. See :ref:`aiohttp-client-proxy-support` for
  more information.

  :class:`bool` default is ``False``

  .. versionadded:: 3.7

.. attribute:: trace_configs

text
A list of :class:`TraceConfig` instances used for client
  tracing.  ``None`` (default) is used for request tracing
  disabling.  See :ref:`aiohttp-client-tracing-reference` for more information.

  .. versionadded:: 3.7

.. method:: request(method, url, *, params=None, data=None, json=None,
cookies=None, headers=None, skip_auto_headers=None,
allow_redirects=True,
max_redirects=10,
compress=None, chunked=None, expect100=False, raise_for_status=None,
read_until_eof=True,
proxy=None,
timeout=sentinel, ssl=True,
server_hostname=None,
proxy_headers=None,
trace_request_ctx=None,
middlewares=None,
read_bufsize=None,
auto_decompress=None,
max_line_size=None,
max_field_size=None,
max_headers=None)
:async:
:noindexentry:

text
Performs an asynchronous HTTP request. Returns a response object that
  should be used as an async context manager.

  :param str method: HTTP method

  :param url: Request URL, :class:`~yarl.URL` or :class:`str` that will
              be encoded with :class:`~yarl.URL` (see :class:`~yarl.URL`
              to skip encoding).

  :param params: Mapping, iterable of tuple of *key*/*value* pairs or
                 string to be sent as parameters in the query
                 string of the new request. Ignored for subsequent
                 redirected requests (optional)

                 Allowed values are:

                 - :class:`collections.abc.Mapping` e.g. :class:`dict`,
                   :class:`multidict.MultiDict` or
                   :class:`multidict.MultiDictProxy`
                 - :class:`collections.abc.Iterable` e.g. :class:`tuple` or
                   :class:`list`
                 - :class:`str` with preferably url-encoded content
                   (**Warning:** content will not be encoded by *aiohttp*)

  :param data: The data to send in the body of the request. This can be a
               :class:`FormData` object or anything that can be passed into
               :class:`FormData`, e.g. a dictionary, bytes, or file-like object.
               (optional)

  :param json: Any json compatible python object
               (optional). *json* and *data* parameters could not
               be used at the same time.

  :param dict cookies: HTTP Cookies to send with
                       the request (optional)

     Global session cookies and the explicitly set cookies will be merged
     when sending the request.

     .. versionadded:: 3.5

  :param dict headers: HTTP Headers to send with
                       the request (optional)

  :param skip_auto_headers: set of headers for which autogeneration
     should be skipped.

     *aiohttp* autogenerates headers like ``User-Agent`` or
     ``Content-Type`` if these headers are not explicitly
     passed. Using ``skip_auto_headers`` parameter allows to skip
     that generation.

     Iterable of :class:`str` or :class:`~multidict.istr`
     (optional)

  :param bool allow_redirects: Whether to process redirects or not.
     When ``True``, redirects are followed (up to ``max_redirects`` times)
     and logged into :attr:`ClientResponse.history` and ``trace_configs``.
     When ``False``, the original response is returned.
     ``True`` by default (optional).

  :param int max_redirects: Maximum number of redirects to follow.
     :exc:`TooManyRedirects` is raised if the number is exceeded.
     ``0`` means no limit, redirects are followed until the request
     times out. Use ``allow_redirects=False`` to not follow redirects at all.
     Ignored when ``allow_redirects=False``.
     ``10`` by default.

  :param bool compress: Set to ``True`` if request has to be compressed
     with deflate encoding. If `compress` can not be combined
     with a *Content-Encoding* and *Content-Length* headers.
     ``None`` by default (optional).

  :param int chunked: Enable chunked transfer encoding.
     It is up to the developer
     to decide how to chunk data streams. If chunking is enabled, aiohttp
     encodes the provided chunks in the "Transfer-encoding: chunked" format.
     If *chunked* is set, then the *Transfer-encoding* and *content-length*
     headers are disallowed. ``None`` by default (optional).

  :param bool expect100: Expect 100-continue response from server.
                         ``False`` by default (optional).

  :param bool raise_for_status: Automatically call :meth:`ClientResponse.raise_for_status` for
                                response if set to ``True``.
                                If set to ``None`` value from ``ClientSession`` will be used.
                                ``None`` by default (optional).

      .. versionadded:: 3.4

  :param bool read_until_eof: Read response until EOF if response
                              does not have Content-Length header.
                              ``True`` by default (optional).

  :param proxy: Proxy URL, :class:`str` or :class:`~yarl.URL` (optional)

  :param int timeout: override the session's timeout.

     .. versionchanged:: 3.3

        The parameter is :class:`ClientTimeout` instance,
        :class:`float` is still supported for sake of backward
        compatibility.

        If :class:`float` is passed it is a *total* timeout (in seconds).

  :param ssl: SSL validation mode. ``True`` for default SSL check
              (:func:`ssl.create_default_context` is used),
              ``False`` for skip SSL certificate validation,
              :class:`aiohttp.Fingerprint` for fingerprint
              validation, :class:`ssl.SSLContext` for custom SSL
              certificate validation.

              Supersedes *verify_ssl*, *ssl_context* and
              *fingerprint* parameters.

              When not given, the session's ``ssl`` value is used
              (``True`` unless set).

     .. versionadded:: 3.0

     .. versionchanged:: 3.15

        Defaults to the session's ``ssl`` value.

  :param str server_hostname: Sets or overrides the host name that the
     target server's certificate will be matched against.

     See :py:meth:`asyncio.loop.create_connection` for more information.

     .. versionadded:: 3.9

  :param collections.abc.Mapping proxy_headers: HTTP headers to send to the proxy if the
     parameter proxy has been provided.

     .. versionadded:: 2.3

  :param trace_request_ctx: Object used to give as a kw param for each new
    :class:`TraceConfig` object instantiated,
    used to give information to the
    tracers that is only available at request time.

     .. versionadded:: 3.0

  :param middlewares: A sequence of middleware instances to apply to this request only.
                     Each middleware must match the :type:`ClientMiddlewareType` signature.
                     ``None`` by default which uses session middlewares.
                     See :ref:`aiohttp-client-middleware` for more information.

     .. versionadded:: 3.12

  :param upload_tracker: An :class:`UploadTracker` instance observing
                         this request's body upload.
                         ``None`` by default (no tracking).

     .. versionadded:: 3.14.4

  :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`).
                          ``None`` by default,
                          it means that the session global value is used.

      .. versionadded:: 3.7

  :param bool auto_decompress: Automatically decompress response body.
     Overrides :attr:`ClientSession.auto_decompress`.
     May be used to enable/disable auto decompression on a per-request basis.

  :param int max_line_size: Maximum allowed size of lines in responses.

  :param int max_field_size: Maximum allowed size of header name and value combined in responses.

  :param int max_headers: Maximum number of headers and trailers combined in responses.

  :return ClientResponse: a :class:`client response <ClientResponse>`
     object.

.. method:: get(url, *, allow_redirects=True, **kwargs)
:async:

text
Perform a ``GET`` request. Returns an async context manager.

  In order to modify inner
  :meth:`request<aiohttp.ClientSession.request>`
  parameters, provide `kwargs`.

  :param url: Request URL, :class:`str` or :class:`~yarl.URL`

  :param bool allow_redirects: Whether to process redirects or not.
     When ``True``, redirects are followed and logged into
     :attr:`ClientResponse.history`.
     When ``False``, the original response is returned.
     ``True`` by default (optional).

  :return ClientResponse: a :class:`client response
                          <ClientResponse>` object.

.. method:: post(url, *, data=None, **kwargs)
:async:

text
Perform a ``POST`` request. Returns an async context manager.

  In order to modify inner
  :meth:`request<aiohttp.ClientSession.request>`
  parameters, provide `kwargs`.


  :param url: Request URL, :class:`str` or :class:`~yarl.URL`

  :param data: Data to send in the body of the request; see
               :meth:`request<aiohttp.ClientSession.request>`
               for details (optional)

  :return ClientResponse: a :class:`client response
                          <ClientResponse>` object.

.. method:: put(url, *, data=None, **kwargs)
:async:

text
Perform a ``PUT`` request. Returns an async context manager.

  In order to modify inner
  :meth:`request<aiohttp.ClientSession.request>`
  parameters, provide `kwargs`.


  :param url: Request URL, :class:`str` or :class:`~yarl.URL`

  :param data: Data to send in the body of the request; see
               :meth:`request<aiohttp.ClientSession.request>`
               for details (optional)

  :return ClientResponse: a :class:`client response
                          <ClientResponse>` object.

.. method:: delete(url, **kwargs)
:async:

text
Perform a ``DELETE`` request. Returns an async context manager.

  In order to modify inner
  :meth:`request<aiohttp.ClientSession.request>`
  parameters, provide `kwargs`.

  :param url: Request URL, :class:`str` or :class:`~yarl.URL`

  :return ClientResponse: a :class:`client response
                          <ClientResponse>` object.

.. method:: head(url, *, allow_redirects=False, **kwargs)
:async:

text
Perform a ``HEAD`` request. Returns an async context manager.

  In order to modify inner
  :meth:`request<aiohttp.ClientSession.request>`
  parameters, provide `kwargs`.

  :param url: Request URL, :class:`str` or :class:`~yarl.URL`

  :param bool allow_redirects: Whether to process redirects or not.
     When ``True``, redirects are followed and logged into
     :attr:`ClientResponse.history`.
     When ``False``, the original response is returned.
     ``False`` by default (optional).

  :return ClientResponse: a :class:`client response
                          <ClientResponse>` object.

.. method:: options(url, *, allow_redirects=True, **kwargs)
:async:

text
Perform an ``OPTIONS`` request. Returns an async context manager.

  In order to modify inner
  :meth:`request<aiohttp.ClientSession.request>`
  parameters, provide `kwargs`.


  :param url: Request URL, :class:`str` or :class:`~yarl.URL`

  :param bool allow_redirects: Whether to process redirects or not.
     When ``True``, redirects are followed and logged into
     :attr:`ClientResponse.history`.
     When ``False``, the original response is returned.
     ``True`` by default (optional).

  :return ClientResponse: a :class:`client response
                          <ClientResponse>` object.

.. method:: patch(url, *, data=None, **kwargs)
:async:

text
Perform a ``PATCH`` request. Returns an async context manager.

  In order to modify inner
  :meth:`request<aiohttp.ClientSession.request>`
  parameters, provide `kwargs`.

  :param url: Request URL, :class:`str` or :class:`~yarl.URL`

  :param data: Data to send in the body of the request; see
               :meth:`request<aiohttp.ClientSession.request>`
               for details (optional)

  :return ClientResponse: a :class:`client response
                          <ClientResponse>` object.

.. method:: ws_connect(url, *, method='GET',
protocols=(),
timeout=sentinel,
autoclose=True,
autoping=True,
heartbeat=None,
origin=None,
params=None,
headers=None,
proxy=None, ssl=True,
verify_ssl=None, fingerprint=None,
ssl_context=None, proxy_headers=None,
compress=0, max_msg_size=4194304,
decode_text=True)
:async:

text
Create a websocket connection. Returns a
  :class:`ClientWebSocketResponse` async context manager object.

  :param url: Websocket server url, :class:`~yarl.URL` or :class:`str` that
              will be encoded with :class:`~yarl.URL` (see :class:`~yarl.URL`
              to skip encoding).

  :param tuple protocols: Websocket protocols

  :param timeout: a :class:`ClientWSTimeout` timeout for websocket.
                  By default, the value
                  `ClientWSTimeout(ws_receive=None, ws_close=10.0)` is used
                  (``10.0`` seconds for the websocket to close).
                  ``None`` means no timeout will be used.

  :param bool autoclose: Automatically close websocket connection on close
                         message from server. If *autoclose* is False
                         then close procedure has to be handled manually.
                         ``True`` by default

  :param bool autoping: automatically send *pong* on *ping*
                        message from server. ``True`` by default

  :param float heartbeat: Send *ping* message every *heartbeat*
                          seconds and wait *pong* response, if
                          *pong* response is not received then
                          close connection. The timer is reset on any
                          inbound data reception (coalesced per event loop
                          iteration). (optional)

  :param str origin: Origin header to send to server(optional)

  :param params: Mapping, iterable of tuple of *key*/*value* pairs or
                 string to be sent as parameters in the query
                 string of the new request. Ignored for subsequent
                 redirected requests (optional)

                 Allowed values are:

                 - :class:`collections.abc.Mapping` e.g. :class:`dict`,
                   :class:`multidict.MultiDict` or
                   :class:`multidict.MultiDictProxy`
                 - :class:`collections.abc.Iterable` e.g. :class:`tuple` or
                   :class:`list`
                 - :class:`str` with preferably url-encoded content
                   (**Warning:** content will not be encoded by *aiohttp*)

  :param dict headers: HTTP Headers to send with
                       the request (optional)

  :param str proxy: Proxy URL, :class:`str` or :class:`~yarl.URL` (optional)

  :param ssl: SSL validation mode. ``True`` for default SSL check
              (:func:`ssl.create_default_context` is used),
              ``False`` for skip SSL certificate validation,
              :class:`aiohttp.Fingerprint` for fingerprint
              validation, :class:`ssl.SSLContext` for custom SSL
              certificate validation.

              Supersedes *verify_ssl*, *ssl_context* and
              *fingerprint* parameters.

              When not given, the session's ``ssl`` value is used
              (``True`` unless set).

     .. versionadded:: 3.0

     .. versionchanged:: 3.15

        Defaults to the session's ``ssl`` value.

  :param bool verify_ssl: Perform SSL certificate validation for
     *HTTPS* requests (enabled by default). May be disabled to
     skip validation for sites with invalid certificates.

     .. versionadded:: 2.3

     .. deprecated:: 3.0

        Use ``ssl=False``

  :param bytes fingerprint: Pass the SHA256 digest of the expected
     certificate in DER format to verify that the certificate the
     server presents matches. Useful for `certificate pinning
     <https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning>`_.

     Note: use of MD5 or SHA1 digests is insecure and deprecated.

     .. versionadded:: 2.3

     .. deprecated:: 3.0

        Use ``ssl=aiohttp.Fingerprint(digest)``

  :param ssl.SSLContext ssl_context: ssl context used for processing
     *HTTPS* requests (optional).

     *ssl_context* may be used for configuring certification
     authority channel, supported SSL options etc.

     .. versionadded:: 2.3

     .. deprecated:: 3.0

        Use ``ssl=ssl_context``

  :param dict proxy_headers: HTTP headers to send to the proxy if the
     parameter proxy has been provided.

     .. versionadded:: 2.3

  :param int compress: Enable Per-Message Compress Extension support.
                       0 for disable, 9 to 15 for window bit support.
                       Default value is 0.

     .. versionadded:: 2.3

  :param int max_msg_size: maximum size of read websocket message,
                           4 MB by default. To disable the size
                           limit use ``0``.

     .. versionadded:: 3.3

  :param str method: HTTP method to establish WebSocket connection,
                     ``'GET'`` by default.

     .. versionadded:: 3.5

  :param bool decode_text: If ``True`` (default), TEXT messages are
                           decoded to strings. If ``False``, TEXT messages
                           are returned as raw bytes, which can improve
                           performance when using JSON parsers like
                           ``orjson`` that accept bytes directly.

     .. versionadded:: 3.14

.. method:: close()
:async:

text
Close underlying connector.

  Release all acquired resources.

.. method:: detach()

text
Detach connector from session without closing the former.

  Session is switched to closed state anyway.

Basic API

While we encourage :class:ClientSession usage we also provide simple
coroutines for making HTTP requests.

Basic API is good for performing simple HTTP requests without
keepaliving, cookies and complex connection stuff like properly configured SSL
certification chaining.

.. function:: request(method, url, *, params=None, data=None,
json=None,
cookies=None, headers=None, skip_auto_headers=None,
allow_redirects=True, max_redirects=10,
compress=False, chunked=None, expect100=False, raise_for_status=None,
read_until_eof=True,
proxy=None,
timeout=sentinel, ssl=True,
server_hostname=None,
proxy_headers=None,
trace_request_ctx=None,
read_bufsize=None,
auto_decompress=None,
max_line_size=None,
max_field_size=None,
max_headers=None,
version=aiohttp.HttpVersion11,
connector=None)
:canonical: aiohttp.client.request
:async:

Asynchronous context manager for performing an asynchronous HTTP
request. Returns a :class:ClientResponse response object. Use as
an async context manager.

:param str method: HTTP method

:param url: Request URL, :class:~yarl.URL or :class:str that will
be encoded with :class:~yarl.URL (see :class:~yarl.URL
to skip encoding).

:param params: Mapping, iterable of tuple of key/value pairs or
string to be sent as parameters in the query
string of the new request. Ignored for subsequent
redirected requests (optional)

text
Allowed values are:

              - :class:`collections.abc.Mapping` e.g. :class:`dict`,
                 :class:`multidict.MultiDict` or
                 :class:`multidict.MultiDictProxy`
              - :class:`collections.abc.Iterable` e.g. :class:`tuple` or
                 :class:`list`
              - :class:`str` with preferably url-encoded content
                 (**Warning:** content will not be encoded by *aiohttp*)

:param data: The data to send in the body of the request. This can be a
:class:FormData object or anything that can be passed into
:class:FormData, e.g. a dictionary, bytes, or file-like object.
(optional)

:param json: Any json compatible python object (optional). json and data
parameters could not be used at the same time.

:param dict cookies: HTTP Cookies to send with the request (optional)

:param dict headers: HTTP Headers to send with the request (optional)

:param skip_auto_headers: set of headers for which autogeneration
should be skipped.

text
*aiohttp* autogenerates headers like ``User-Agent`` or
  ``Content-Type`` if these headers are not explicitly
  passed. Using ``skip_auto_headers`` parameter allows to skip
  that generation.

  Iterable of :class:`str` or :class:`~multidict.istr`
  (optional)

:param bool allow_redirects: Whether to process redirects or not.
When True, redirects are followed (up to max_redirects times)
and logged into :attr:ClientResponse.history and trace_configs.
When False, the original response is returned.
True by default (optional).

:param int max_redirects: Maximum number of redirects to follow.
:exc:TooManyRedirects is raised if the number is exceeded.
0 means no limit, redirects are followed until the request
times out. Use allow_redirects=False to not follow redirects at all.
Ignored when allow_redirects=False.
10 by default.

:param compress: Set to True to compress the request body with
deflate encoding, or pass "deflate" or "gzip"
explicitly to choose the content encoding. False by
default.

text
This parameter cannot be combined with
                *Content-Encoding* or *Content-Length* headers.

:param int chunked: Enables chunked transfer encoding.
It is up to the developer
to decide how to chunk data streams. If chunking is enabled, aiohttp
encodes the provided chunks in the "Transfer-encoding: chunked" format.
If chunked is set, then the Transfer-encoding and content-length
headers are disallowed. None by default (optional).

:param bool expect100: Expect 100-continue response from server.
False by default (optional).

:param bool raise_for_status: Automatically call
:meth:ClientResponse.raise_for_status
for response if set to True. If
set to None value from
ClientSession will be used.
None by default (optional).

text
.. versionadded:: 3.4

:param bool read_until_eof: Read response until EOF if response
does not have Content-Length header.
True by default (optional).

:param proxy: Proxy URL, :class:str or :class:~yarl.URL (optional)

:param timeout: a :class:ClientTimeout settings structure, 300 seconds (5min)
total timeout, 30 seconds socket connect timeout by default.

:param ssl: SSL validation mode. True for default SSL check
(:func:ssl.create_default_context is used),
False for skip SSL certificate validation,
:class:aiohttp.Fingerprint for fingerprint
validation, :class:ssl.SSLContext for custom SSL
certificate validation.

text
Supersedes *verify_ssl*, *ssl_context* and
           *fingerprint* parameters.

:param str server_hostname: Sets or overrides the host name that the
target server's certificate will be matched against.

text
See :py:meth:`asyncio.loop.create_connection`
  for more information.

:param collections.abc.Mapping proxy_headers: HTTP headers to send to the proxy
if the parameter proxy has been provided.

:param trace_request_ctx: Object used to give as a kw param for each new
:class:TraceConfig object instantiated,
used to give information to the
tracers that is only available at request time.

:param int read_bufsize: Size of the read buffer (:attr:ClientResponse.content).
None by default,
it means that the session global value is used.

text
.. versionadded:: 3.7

:param bool auto_decompress: Automatically decompress response body.
May be used to enable/disable auto decompression on a per-request basis.

:param int max_line_size: Maximum allowed size of lines in responses.

:param int max_field_size: Maximum allowed size of header name and value combined in responses.

:param int max_headers: Maximum number of headers and trailers combined in responses.

:param aiohttp.protocol.HttpVersion version: Request HTTP version,
HTTP 1.1 by default. (optional)

:param aiohttp.BaseConnector connector: BaseConnector sub-class
instance to support connection pooling. (optional)

:return ClientResponse: a :class:client response <ClientResponse> object.

Usage::

text
import aiohttp

  async def fetch():
      async with aiohttp.request('GET',
              'http://python.org/') as resp:
          assert resp.status == 200
          print(await resp.text())

.. _aiohttp-client-reference-connectors:

Connectors

Connectors are transports for aiohttp client API.

There are standard connectors:

  1. :class:TCPConnector for regular TCP sockets (both HTTP and
    HTTPS schemes supported).
  2. :class:UnixConnector for connecting via UNIX socket (it's used mostly for
    testing purposes).

All connector classes should be derived from :class:BaseConnector.

By default all connectors support keep-alive connections (behavior
is controlled by force_close constructor's parameter).

.. class:: BaseConnector(*, keepalive_timeout=15,
force_close=False, limit=100, limit_per_host=0,
enable_cleanup_closed=False, loop=None)
:canonical: aiohttp.connector.BaseConnector

Base class for all connectors.

:param keepalive_timeout: timeout for connection reusing
after releasing (optional). Set to
None to reuse connections
indefinitely, regardless of how long
they've been idle. For disabling
keep-alive feature entirely use
force_close=True flag instead.
:type keepalive_timeout: float | None

:param int limit: total number simultaneous connections. If limit is
0 the connector has no limit (default: 100).

:param int limit_per_host: limit simultaneous connections to the same
endpoint. Endpoints are the same if they are
have equal (host, port, is_ssl) triple.
If limit is 0 the connector has no limit (default: 0).

:param bool force_close: close underlying sockets after
connection releasing (optional).

:param bool enable_cleanup_closed: some SSL servers do not properly complete
SSL shutdown process, in that case asyncio leaks SSL connections.
If this parameter is set to True, aiohttp additionally aborts underlining
transport after 2 seconds. It is off by default.

text
For Python version 3.12.7+, or 3.13.1 and later,
  this parameter is ignored because the asyncio SSL connection
  leak is fixed in these versions of Python.

:param loop: :ref:event loop<asyncio-event-loop>
used for handling connections.
If param is None, :func:asyncio.get_event_loop
is used for getting default event loop.

text
.. deprecated:: 2.0

.. attribute:: closed

text
Read-only property, ``True`` if connector is closed.

.. attribute:: force_close

text
Read-only property, ``True`` if connector should ultimately
  close connections on releasing.

.. attribute:: limit

text
The total number for simultaneous connections.
  If limit is 0 the connector has no limit. The default limit size is 100.

.. attribute:: limit_per_host

text
The limit for simultaneous connections to the same
  endpoint.

  Endpoints are the same if they are have equal ``(host, port,
  is_ssl)`` triple.

  If *limit_per_host* is ``0`` the connector has no limit per host.

  Read-only property.

.. method:: close()
:async:

text
Close all opened connections.

.. method:: connect(request)
:async:

text
Get a free connection from pool or create new one if connection
  is absent in the pool.

  The call may be paused if :attr:`limit` is exhausted until used
  connections returns to pool.

  :param aiohttp.ClientRequest request: request object
                                               which is connection
                                               initiator.

  :return: :class:`Connection` object.

.. method:: _create_connection(req)
:async:

text
Abstract method for actual connection establishing, should be
  overridden in subclasses.

.. py:class:: AddrInfoType

Refer to :py:data:aiohappyeyeballs.AddrInfoType for more info.

.. warning::

Be sure to use aiohttp.AddrInfoType rather than
aiohappyeyeballs.AddrInfoType to avoid import breakage, as
it is likely to be removed from :mod:aiohappyeyeballs in the
future.

.. py:class:: SocketFactoryType

Refer to :py:data:aiohappyeyeballs.SocketFactoryType for more info.

.. warning::

Be sure to use aiohttp.SocketFactoryType rather than
aiohappyeyeballs.SocketFactoryType to avoid import breakage,
as it is likely to be removed from :mod:aiohappyeyeballs in the
future.

.. class:: TCPConnector(*, ssl=True, verify_ssl=True, fingerprint=None,
use_dns_cache=True, ttl_dns_cache=10,
family=0, ssl_context=None, local_addr=None,
resolver=None, keepalive_timeout=sentinel,
force_close=False, limit=100, limit_per_host=0,
enable_cleanup_closed=False, timeout_ceil_threshold=5,
happy_eyeballs_delay=0.25, interleave=None, loop=None,
socket_factory=None, ssl_shutdown_timeout=0)
:canonical: aiohttp.connector.TCPConnector

Connector for working with HTTP and HTTPS via TCP sockets.

The most common transport. When you don't know what connector type
to use, use a :class:TCPConnector instance.

:class:TCPConnector inherits from :class:BaseConnector.

Constructor accepts all parameters suitable for
:class:BaseConnector plus several TCP-specific ones:

text
:param ssl: **(DEPRECATED)** SSL validation mode. ``True`` for default
              SSL check (:func:`ssl.create_default_context` is used),
              ``False`` for skip SSL certificate validation,
              :class:`aiohttp.Fingerprint` for fingerprint
              validation, :class:`ssl.SSLContext` for custom SSL
              certificate validation.

              Supersedes *verify_ssl*, *ssl_context* and
              *fingerprint* parameters.

     .. versionadded:: 3.0

     .. deprecated:: 4.0

        Scheduled for removal in 5.0. Pass *ssl* to
        :class:`ClientSession` for a session-wide default, or to
        :meth:`ClientSession.get` and others per request.

:param bool verify_ssl: perform SSL certificate validation for
HTTPS requests (enabled by default). May be disabled to
skip validation for sites with invalid certificates.

text
.. deprecated:: 2.3

     Pass *verify_ssl* to ``ClientSession.get()`` etc.

:param bytes fingerprint: pass the SHA256 digest of the expected
certificate in DER format to verify that the certificate the
server presents matches. Useful for certificate pinning <https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning>_.

text
Note: use of MD5 or SHA1 digests is insecure and deprecated.

  .. deprecated:: 2.3

     Pass *verify_ssl* to ``ClientSession.get()`` etc.

:param bool use_dns_cache: use internal cache for DNS lookups, True
by default.

text
Enabling an option *may* speedup connection
  establishing a bit but may introduce some
  *side effects* also.

:param int ttl_dns_cache: expire after some seconds the DNS entries, None
means cached forever. By default 10 seconds (optional).

text
In some environments the IP addresses related to a specific HOST can
  change after a specific time. Use this option to keep the DNS cache
  updated refreshing each entry after N seconds.

:param int limit: total number simultaneous connections. If limit is
0 the connector has no limit (default: 100).

:param int limit_per_host: limit simultaneous connections to the same
endpoint. Endpoints are the same if they are
have equal (host, port, is_ssl) triple.
If limit is 0 the connector has no limit (default: 0).

:param aiohttp.abc.AbstractResolver resolver: custom resolver
instance to use. aiohttp.DefaultResolver by
default (asynchronous if aiodns>=1.1 is installed).

text
Custom resolvers allow to resolve hostnames differently than the
  way the host is configured.

  The resolver is ``aiohttp.ThreadedResolver`` by default,
  asynchronous version is pretty robust but might fail in
  very rare cases.

:param int family: TCP socket family, both IPv4 and IPv6 by default.
For IPv4 only use :data:socket.AF_INET,
for IPv6 only -- :data:socket.AF_INET6.

text
*family* is ``0`` by default, that means both
                  IPv4 and IPv6 are accepted. To specify only
                  concrete version please pass
                  :data:`socket.AF_INET` or
                  :data:`socket.AF_INET6` explicitly.

:param ssl.SSLContext ssl_context: SSL context used for processing
HTTPS requests (optional).

text
*ssl_context* may be used for configuring certification
  authority channel, supported SSL options etc.

:param tuple local_addr: tuple of (local_host, local_port) used to bind
socket locally if specified.

:param bool force_close: close underlying sockets after
connection releasing (optional).

:param bool enable_cleanup_closed: Some ssl servers do not properly complete
SSL shutdown process, in that case asyncio leaks SSL connections.
If this parameter is set to True, aiohttp additionally aborts underlining
transport after 2 seconds. It is off by default.

:param float happy_eyeballs_delay: The amount of time in seconds to wait for a
connection attempt to complete, before starting the next attempt in parallel.
This is the โ€œConnection Attempt Delayโ€ as defined in RFC 8305. To disable
Happy Eyeballs, set this to None. The default value recommended by the
RFC is 0.25 (250 milliseconds).

text
.. versionadded:: 3.10

:param int interleave: controls address reordering when a host name resolves
to multiple IP addresses. If 0 or unspecified, no reordering is done, and
addresses are tried in the order returned by the resolver. If a positive
integer is specified, the addresses are interleaved by address family, and
the given integer is interpreted as โ€œFirst Address Family Countโ€ as defined
in RFC 8305. The default is 0 if happy_eyeballs_delay is not specified, and
1 if it is.

text
.. versionadded:: 3.10

:param SocketFactoryType socket_factory: This function takes an
:py:data:AddrInfoType and is used in lieu of
:py:func:socket.socket when creating TCP connections.

text
.. versionadded:: 3.12

:param float ssl_shutdown_timeout: (DEPRECATED) This parameter is deprecated
and will be removed in aiohttp 4.0. Grace period for SSL shutdown on TLS
connections when the connector is closed (0 seconds by default).
By default (0), SSL connections are aborted immediately when the
connector is closed, without performing the shutdown handshake. During
normal operation, SSL connections use Python's default SSL shutdown
behavior. Setting this to a positive value (e.g., 0.1) will perform
a graceful shutdown when closing the connector, notifying the remote
server which can help prevent "connection reset" errors at the cost of
additional cleanup time. Note: On Python versions prior to 3.11, only
a value of 0 is supported; other values will trigger a warning.

text
.. versionadded:: 3.12.5

    .. versionchanged:: 3.12.11
       Changed default from ``0.1`` to ``0`` to abort SSL connections
       immediately when the connector is closed. Added support for
       ``ssl_shutdown_timeout=0`` on all Python versions. A :exc:`RuntimeWarning`
       is issued when non-zero values are passed on Python < 3.11.

    .. deprecated:: 3.12.11
       This parameter is deprecated and will be removed in aiohttp 4.0.

.. attribute:: family

text
*TCP* socket family e.g. :data:`socket.AF_INET` or
  :data:`socket.AF_INET6`

  Read-only property.

.. attribute:: dns_cache

text
Use quick lookup in internal *DNS* cache for host names if ``True``.

  Read-only :class:`bool` property.

.. attribute:: cached_hosts

text
The cache of resolved hosts if :attr:`dns_cache` is enabled.

  Read-only :class:`types.MappingProxyType` property.

.. method:: clear_dns_cache(self, host=None, port=None)

text
Clear internal *DNS* cache.

  Remove specific entry if both *host* and *port* are specified,
  clear all cache otherwise.

.. class:: UnixConnector(path, *, conn_timeout=None,
keepalive_timeout=30, limit=100,
force_close=False, loop=None)
:canonical: aiohttp.connector.UnixConnector

Unix socket connector.

Use :class:UnixConnector for sending HTTP/HTTPS requests
through UNIX Sockets as underlying transport.

UNIX sockets are handy for writing tests and making very fast
connections between processes on the same host.

:class:UnixConnector is inherited from :class:BaseConnector.

text
Usage::

   conn = UnixConnector(path='/path/to/socket')
   session = ClientSession(connector=conn)
   async with session.get('http://python.org') as resp:
       ...

Constructor accepts all parameters suitable for
:class:BaseConnector plus UNIX-specific one:

:param str path: Unix socket path

.. attribute:: path

text
Path to *UNIX socket*, read-only :class:`str` property.

.. class:: Connection
:canonical: aiohttp.connector.Connection

Encapsulates single connection in connector object.

End user should never create :class:Connection instances manually
but get it by :meth:BaseConnector.connect coroutine.

.. attribute:: closed

text
:class:`bool` read-only property, ``True`` if connection was
  closed, released or detached.

.. attribute:: loop

text
Event loop used for connection

  .. deprecated:: 3.5

.. attribute:: transport

text
Connection transport

.. method:: close()

text
Close connection with forcibly closing underlying socket.

.. method:: release()

text
Release connection back to connector.

  Underlying socket is not closed, the connection may be reused
  later if timeout (30 seconds by default) for connection was not
  expired.

Response object

.. class:: ClientResponse
:canonical: aiohttp.client_reqrep.ClientResponse

Client response returned by :meth:aiohttp.ClientSession.request and family.

User never creates the instance of ClientResponse class but gets it
from API calls.

:class:ClientResponse supports async context manager protocol, e.g.::

text
resp = await client_session.get(url)
   async with resp:
       assert resp.status == 200

After exiting from async with block response object will be
released (see :meth:release method).

.. attribute:: version

text
Response's version, :class:`~aiohttp.protocol.HttpVersion` instance.

.. attribute:: status

text
HTTP status code of response (:class:`int`), e.g. ``200``.

.. attribute:: reason

text
HTTP status reason of response (:class:`str`), e.g. ``"OK"``.

.. attribute:: ok

text
Boolean representation of HTTP status code (:class:`bool`).
  ``True`` if ``status`` is less than ``400``; otherwise, ``False``.

.. attribute:: method

text
Request's method (:class:`str`).

.. attribute:: url

text
URL of request (:class:`~yarl.URL`).

.. attribute:: real_url

text
Unmodified URL of request with URL fragment unstripped (:class:`~yarl.URL`).

  .. versionadded:: 3.2

.. attribute:: connection

text
:class:`Connection` used for handling response.

.. attribute:: content

text
Payload stream, which contains response's BODY (:class:`StreamReader`).
  It supports various reading methods depending on the expected format.
  When chunked transfer encoding is used by the server, allows retrieving
  the actual http chunks.

  Reading from the stream may raise
  :exc:`aiohttp.ClientPayloadError` if the response object is
  closed before response receives all data or in case if any
  transfer encoding related errors like malformed chunked
  encoding of broken compression data.

.. attribute:: cookies

text
HTTP cookies of response (*Set-Cookie* HTTP header,
  :class:`~http.cookies.SimpleCookie`).

  .. note::

     Since :class:`~http.cookies.SimpleCookie` uses cookie name as the
     key, cookies with the same name but different domains or paths will
     be overwritten. Only the last cookie with a given name will be
     accessible via this attribute.

     To access all cookies, including duplicates with the same name,
     use :meth:`response.headers.getall('Set-Cookie') <multidict.MultiDictProxy.getall>`.

     At most 50 valid ``Set-Cookie`` fields are parsed, here and for the
     session's cookie jar.

.. attribute:: headers

text
A case-insensitive multidict proxy with HTTP headers of
  response, :class:`~multidict.CIMultiDictProxy`.

.. attribute:: raw_headers

text
Unmodified HTTP headers of response as unconverted bytes, a sequence of
  ``(key, value)`` pairs.

.. attribute:: links

text
Link HTTP header parsed into a :class:`~multidict.MultiDictProxy`.

  For each link, key is link param `rel` when it exists, or link url as
  :class:`str` otherwise, and value is :class:`~multidict.MultiDictProxy`
  of link params and url at key `url` as :class:`~yarl.URL` instance.

  .. versionadded:: 3.2

.. attribute:: content_type

text
Read-only property with *content* part of *Content-Type* header.

  .. note::

     Returns ``'application/octet-stream'`` if no Content-Type header
     is present or the value contains invalid syntax according to
     :rfc:`9110`. To see the original header check
     ``resp.headers["Content-Type"]``.

     To make sure Content-Type header is not present in
     the server reply, use :attr:`headers` or :attr:`raw_headers`, e.g.
     ``'Content-Type' not in resp.headers``.

.. attribute:: charset

text
Read-only property that specifies the *encoding* for the request's BODY.

  The value is parsed from the *Content-Type* HTTP header.

  Returns :class:`str` like ``'utf-8'`` or ``None`` if no *Content-Type*
  header present in HTTP headers or it has no charset information.

.. attribute:: content_disposition

text
Read-only property that specified the *Content-Disposition* HTTP header.

  Instance of :class:`ContentDisposition` or ``None`` if no *Content-Disposition*
  header present in HTTP headers.

.. attribute:: history

text
A :class:`~collections.abc.Sequence` of :class:`ClientResponse`
  objects of preceding requests (earliest request first) if there were
  redirects, an empty sequence otherwise.

.. method:: close()

text
Close response and underlying connection.

  For :term:`keep-alive` support see :meth:`release`.

.. method:: read()
:async:

text
Read the whole response's body as :class:`bytes`.

  Close underlying connection if data reading gets an error,
  release connection otherwise.

  Raise an :exc:`aiohttp.ClientResponseError` if the data can't
  be read.

  :return bytes: read *BODY*.

  .. seealso:: :meth:`close`, :meth:`release`.

.. method:: release()

text
It is not required to call `release` on the response
  object. When the client fully receives the payload, the
  underlying connection automatically returns back to pool. If the
  payload is not fully read, the connection is closed

.. method:: raise_for_status()

text
Raise an :exc:`aiohttp.ClientResponseError` if the response
  status is 400 or higher.

  Do nothing for success responses (less than 400).

.. method:: text(encoding=None)
:async:

text
Read response's body and return decoded :class:`str` using
  specified *encoding* parameter.

  If *encoding* is ``None`` content encoding is determined from the
  Content-Type header, or using the ``fallback_charset_resolver`` function.

  Close underlying connection if data reading gets an error,
  release connection otherwise.

  :param str encoding: text encoding used for *BODY* decoding, or
                       ``None`` for encoding autodetection
                       (default).


  :raises: :exc:`UnicodeDecodeError` if decoding fails. See also
           :meth:`get_encoding`.

  :return str: decoded *BODY*

.. method:: json(*, encoding=None, loads=json.loads,
content_type='application/json')
:async:

text
Read response's body as *JSON*, return :class:`dict` using
  specified *encoding* and *loader*. If data is not still available
  a ``read`` call will be done.

  If response's `content-type` does not match `content_type` parameter
  :exc:`aiohttp.ContentTypeError` get raised.
  To disable content type check pass ``None`` value.

  :param str encoding: text encoding used for *BODY* decoding, or
                       ``None`` for encoding autodetection
                       (default).

                       By the standard JSON encoding should be
                       ``UTF-8`` but practice beats purity: some
                       servers return non-UTF
                       responses. Autodetection works pretty fine
                       anyway.

  :param collections.abc.Callable loads: :term:`callable` used for loading *JSON*
                         data, :func:`json.loads` by default.

  :param str content_type: specify response's content-type, if content type
     does not match raise :exc:`aiohttp.ClientResponseError`.
     To disable `content-type` check, pass ``None`` as value.
     (default: `application/json`).

  :return: *BODY* as *JSON* data parsed by *loads* parameter or
           ``None`` if *BODY* is empty or contains white-spaces only.

.. attribute:: request_info

text
A :class:`typing.NamedTuple` with request URL and headers from :class:`~aiohttp.ClientRequest`
   object, :class:`aiohttp.RequestInfo` instance.

.. method:: get_encoding()

text
Retrieve content encoding using ``charset`` info in ``Content-Type`` HTTP header.
  If no charset is present or the charset is not understood by Python, the
  ``fallback_charset_resolver`` function associated with the ``ClientSession`` is called.

  .. versionadded:: 3.0

ClientWebSocketResponse

To connect to a websocket server :func:aiohttp.ws_connect or
:meth:aiohttp.ClientSession.ws_connect coroutines should be used, do
not create an instance of class :class:ClientWebSocketResponse
manually.

.. class:: ClientWebSocketResponse()
:canonical: aiohttp.client_ws.ClientWebSocketResponse

Class for handling client-side websockets.

.. attribute:: closed

text
Read-only property, ``True`` if :meth:`close` has been called or
  :const:`~aiohttp.WSMsgType.CLOSE` message has been received from peer.

.. attribute:: protocol

text
Websocket *subprotocol* chosen after :meth:`start` call.

  May be ``None`` if server and client protocols are
  not overlapping.

.. method:: get_extra_info(name, default=None)

text
Reads optional extra information from the connection's transport.
  If no value associated with ``name`` is found, ``default`` is returned.

  See :meth:`asyncio.BaseTransport.get_extra_info`

  :param str name: The key to look up in the transport extra information.

  :param default: Default value to be used when no value for ``name`` is
                  found (default is ``None``).

.. method:: exception()

text
Returns exception if any occurs or returns None.

.. method:: ping(message=b'')
:async:

text
Send :const:`~aiohttp.WSMsgType.PING` to peer.

  :param message: optional payload of *ping* message,
                  :class:`str` (converted to *UTF-8* encoded bytes)
                  or :class:`bytes`.

  .. versionchanged:: 3.0

     The method is converted into :term:`coroutine`

.. method:: pong(message=b'')
:async:

text
Send :const:`~aiohttp.WSMsgType.PONG` to peer.

  :param message: optional payload of *pong* message,
                  :class:`str` (converted to *UTF-8* encoded bytes)
                  or :class:`bytes`.

  .. versionchanged:: 3.0

     The method is converted into :term:`coroutine`

.. method:: send_str(data, compress=None)
:async:

text
Send *data* to peer as :const:`~aiohttp.WSMsgType.TEXT` message.

  :param str data: data to send.

  :param int compress: sets specific level of compression for
                       single message,
                       ``None`` for not overriding per-socket setting.

  :raise TypeError: if data is not :class:`str`

  .. versionchanged:: 3.0

     The method is converted into :term:`coroutine`,
     *compress* parameter added.

.. method:: send_bytes(data, compress=None)
:async:

text
Send *data* to peer as :const:`~aiohttp.WSMsgType.BINARY` message.

  :param data: data to send.

  :param int compress: sets specific level of compression for
                       single message,
                       ``None`` for not overriding per-socket setting.

  :raise TypeError: if data is not :class:`bytes`,
                    :class:`bytearray` or :class:`memoryview`.

  .. versionchanged:: 3.0

     The method is converted into :term:`coroutine`,
     *compress* parameter added.

.. method:: send_json(data, compress=None, *, dumps=json.dumps)
:async:

text
Send *data* to peer as JSON string.

  :param data: data to send.

  :param int compress: sets specific level of compression for
                       single message,
                       ``None`` for not overriding per-socket setting.

  :param collections.abc.Callable dumps: any :term:`callable` that accepts an object and
                         returns a JSON string
                         (:func:`json.dumps` by default).

  :raise RuntimeError: if connection is not started or closing

  :raise ValueError: if data is not serializable object

  :raise TypeError: if value returned by ``dumps(data)`` is not
                    :class:`str`

  .. versionchanged:: 3.0

     The method is converted into :term:`coroutine`,
     *compress* parameter added.

.. method:: send_json_bytes(data, compress=None, *, dumps)
:async:

text
Send *data* to peer as a JSON binary frame using a bytes-returning encoder.

  :param data: data to send.

  :param int compress: sets specific level of compression for
                       single message,
                       ``None`` for not overriding per-socket setting.

  :param collections.abc.Callable dumps: any :term:`callable` that accepts an object and
                         returns JSON as :class:`bytes`
                         (e.g. ``orjson.dumps``).

  :raise RuntimeError: if connection is not started or closing

  :raise ValueError: if data is not serializable object

  :raise TypeError: if value returned by ``dumps(data)`` is not
                    :class:`bytes`

.. method:: send_frame(message, opcode, compress=None)
:async:

text
Send a :const:`~aiohttp.WSMsgType` message *message* to peer.

  This method is low-level and should be used with caution as it
  only accepts bytes which must conform to the correct message type
  for *message*.

  It is recommended to use the :meth:`send_str`, :meth:`send_bytes`
  or :meth:`send_json` methods instead of this method.

  The primary use case for this method is to send bytes that are
  have already been encoded without having to decode and
  re-encode them.

  :param bytes message: message to send.

  :param ~aiohttp.WSMsgType opcode: opcode of the message.

  :param int compress: sets specific level of compression for
                       single message,
                       ``None`` for not overriding per-socket setting.

  .. versionadded:: 3.11

.. method:: close(*, code=WSCloseCode.OK, message=b'')
:async:

text
A :ref:`coroutine<coroutine>` that initiates closing handshake by sending
  :const:`~aiohttp.WSMsgType.CLOSE` message. It waits for
  close response from server. To add a timeout to `close()` call
  just wrap the call with `asyncio.wait()` or `asyncio.wait_for()`.

  :param int code: closing code. See also :class:`~aiohttp.WSCloseCode`.

  :param message: optional payload of *close* message,
     :class:`str` (converted to *UTF-8* encoded bytes) or :class:`bytes`.

.. method:: receive()
:async:

text
A :ref:`coroutine<coroutine>` that waits upcoming *data*
  message from peer and returns it.

  The coroutine implicitly handles
  :const:`~aiohttp.WSMsgType.PING`,
  :const:`~aiohttp.WSMsgType.PONG` and
  :const:`~aiohttp.WSMsgType.CLOSE` without returning the
  message.

  It process *ping-pong game* and performs *closing handshake* internally.

  :return: :class:`~aiohttp.WSMessage`

.. method:: receive_str()
:async:

text
A :ref:`coroutine<coroutine>` that calls :meth:`receive` but
  also asserts the message type is
  :const:`~aiohttp.WSMsgType.TEXT`.

  :return str: peer's message content.

  :raise aiohttp.WSMessageTypeError: if message is not :const:`~aiohttp.WSMsgType.TEXT`.

.. method:: receive_bytes()
:async:

text
A :ref:`coroutine<coroutine>` that calls :meth:`receive` but
  also asserts the message type is
  :const:`~aiohttp.WSMsgType.BINARY`.

  :return bytes: peer's message content.

  :raise aiohttp.WSMessageTypeError: if message is not :const:`~aiohttp.WSMsgType.BINARY`.

.. method:: receive_json(*, loads=json.loads)
:async:

text
A :ref:`coroutine<coroutine>` that calls :meth:`receive_str` and loads
  the JSON string to a Python dict.

  :param collections.abc.Callable loads: any :term:`callable` that accepts
                          :class:`str` and returns :class:`dict`
                          with parsed JSON (:func:`json.loads` by
                          default).

  :return dict: loaded JSON content

  :raise TypeError: if message is :const:`~aiohttp.WSMsgType.BINARY`.
  :raise ValueError: if message is not valid JSON.

ClientRequest

.. class:: ClientRequest
:canonical: aiohttp.client_reqrep.ClientRequest

Represents an HTTP request to be sent by the client.

This object encapsulates all the details of an HTTP request before it is sent.
It is primarily used within client middleware to inspect or modify requests.

.. note::

text
You typically don't create ``ClientRequest`` instances directly. They are
  created internally by :class:`ClientSession` methods and passed to middleware.

For more information about using middleware, see :ref:aiohttp-client-middleware.

.. attribute:: body
:type: Payload | Literal[b""]

text
The request body payload (defaults to ``b""`` if no body passed).

  .. danger::

     **DO NOT set this attribute directly!** Direct assignment will cause resource
     leaks. Always use :meth:`update_body` instead:

     .. code-block:: python

        # WRONG - This will leak resources!
        request.body = b"new data"

        # CORRECT - Use update_body
        await request.update_body(b"new data")

     Setting body directly bypasses cleanup of the previous payload, which can
     leave file handles open, streams unclosed, and buffers unreleased.

     Additionally, setting body directly must be done from within an event loop
     and is not thread-safe. Setting body outside of an event loop may raise
     RuntimeError when closing file-based payloads.

.. attribute:: chunked
:type: bool | None

text
Whether to use chunked transfer encoding:

  - ``True``: Use chunked encoding
  - ``False``: Don't use chunked encoding
  - ``None``: Automatically determine based on body

.. attribute:: compress
:type: str | None

text
The compression encoding for the request body. Common values include
  ``'gzip'`` and ``'deflate'``, but any string value is technically allowed.
  ``None`` means no compression.

.. attribute:: headers
:type: multidict.CIMultiDict

text
The HTTP headers that will be sent with the request. This is a case-insensitive
  multidict that can be modified by middleware.

  .. code-block:: python

     # Add or modify headers
     request.headers['X-Custom-Header'] = 'value'
     request.headers['User-Agent'] = 'MyApp/1.0'

.. attribute:: is_ssl
:type: bool

text
``True`` if the request uses a secure scheme (e.g., HTTPS, WSS), ``False`` otherwise.

.. attribute:: method
:type: str

text
The HTTP method of the request (e.g., ``'GET'``, ``'POST'``, ``'PUT'``, etc.).

.. attribute:: original_url
:type: yarl.URL

text
The original URL passed to the request method, including any fragment.
  This preserves the exact URL as provided by the user.

.. attribute:: proxy
:type: yarl.URL | None

text
The proxy URL if the request will be sent through a proxy, ``None`` otherwise.

.. attribute:: proxy_headers
:type: multidict.CIMultiDict | None

text
Headers to be sent to the proxy server (e.g., ``Proxy-Authorization``).
  Only set when :attr:`proxy` is not ``None``.

.. attribute:: response_class
:type: type[ClientResponse]

text
The class to use for creating the response object. Defaults to
  :class:`ClientResponse` but can be customized for special handling.

.. attribute:: server_hostname
:type: str | None

text
Override the hostname for SSL certificate verification. Useful when
  connecting through proxies or to IP addresses.

.. attribute:: session
:type: ClientSession

text
The client session that created this request. Useful for accessing
  session-level configuration or making additional requests within middleware.

  .. warning::
     Be careful when making requests with the same session inside middleware
     to avoid infinite recursion. Use ``middlewares=()`` parameter when needed.

.. attribute:: ssl
:type: ssl.SSLContext | bool | Fingerprint

text
SSL validation configuration for this request:

  - ``True``: Use default SSL verification
  - ``False``: Skip SSL verification
  - :class:`ssl.SSLContext`: Custom SSL context
  - :class:`Fingerprint`: Verify specific certificate fingerprint

.. attribute:: timeout
:type: ClientTimeout

text
The timeout configuration this request runs under (read-only): the
  per-request timeout when one was passed to the request method, the
  session's default otherwise. Useful in middleware to bound waits or
  retries by the caller's time budget.

  .. versionadded:: 3.15

.. attribute:: url
:type: yarl.URL

text
The target URL of the request with the fragment (``#...``) part stripped.
  This is the actual URL that will be used for the connection.

  .. note::
     To access the original URL with fragment, use :attr:`original_url`.

.. attribute:: version
:type: HttpVersion

text
The HTTP version to use for the request (e.g., ``HttpVersion(1, 1)`` for HTTP/1.1).

.. method:: update_body(body)

text
Update the request body and close any existing payload to prevent resource leaks.

  **This is the ONLY correct way to modify a request body.** Never set the
  :attr:`body` attribute directly.

  This method is particularly useful in middleware when you need to modify the
  request body after the request has been created but before it's sent.

  :param body: The new body content. Can be:

               - ``bytes``/``bytearray``: Raw binary data
               - ``str``: Text data (encoded using charset from Content-Type)
               - :class:`FormData`: Form data encoded as multipart/form-data
               - :class:`Payload`: A pre-configured payload object
               - ``AsyncIterable[bytes]``: Async iterable of bytes chunks
               - File-like object: Will be read and sent as binary data
               - ``None``: Clears the body

  .. code-block:: python

     async def middleware(request, handler):
         # Modify request body in middleware
         if request.method == 'POST':
             # CORRECT: Always use update_body
             await request.update_body(b'{"modified": true}')

             # WRONG: Never set body directly!
             # request.body = b'{"modified": true}'  # This leaks resources!

         # Or add authentication data to form
         if isinstance(request.body, FormData):
             form = FormData()
             # Copy existing fields and add auth token
             form.add_field('auth_token', 'secret123')
             await request.update_body(form)

         return await handler(request)

  .. note::

     This method is async because it may need to close file handles or
     other resources associated with the previous payload. Always await
     this method to ensure proper cleanup.

  .. danger::

     **Never set :attr:`ClientRequest.body` directly!** Direct assignment will cause resource
     leaks. Always use this method instead. Setting the body attribute directly:

     - Bypasses cleanup of the previous payload
     - Leaves file handles and streams open
     - Can cause memory leaks
     - May result in unexpected behavior with async iterables

  .. warning::

     When updating the body, ensure that the Content-Type header is
     appropriate for the new body content. The Content-Length header
     will be updated automatically. When using :class:`FormData` or
     :class:`Payload` objects, headers are updated automatically,
     but you may need to set Content-Type manually for raw bytes or text.

     It is not recommended to change the payload type in middleware. If the
     body was already set (e.g., as bytes), it's best to keep the same type
     rather than converting it (e.g., to str) as this may result in unexpected
     behavior.

  .. versionadded:: 3.12

Utilities

.. class:: ClientTimeout(, total=560, connect=None,
sock_connect=None, sock_read=None)
:canonical: aiohttp.client.ClientTimeout

A data class for client timeout settings.

See :ref:aiohttp-client-timeouts for usage examples.

.. attribute:: total

text
Total number of seconds for the whole request.

  :class:`float`, 300 seconds (5 min) by default.

.. attribute:: connect

text
Maximal number of seconds for acquiring a connection from pool.  The time
  consists connection establishment for a new connection or
  waiting for a free connection from a pool if pool connection
  limits are exceeded.

  For pure socket connection establishment time use
  :attr:`sock_connect`.

  :class:`float`, ``None`` by default.

.. attribute:: sock_connect

text
Maximal number of seconds for connecting to a peer for a new connection, not
  given from a pool.  See also :attr:`connect`.

  :class:`float`, ``None`` by default.

.. attribute:: sock_read

text
Maximal number of seconds for reading a portion of data from a peer.

  :class:`float`, ``None`` by default.

.. class:: ClientWSTimeout(*, ws_receive=None, ws_close=None)
:canonical: aiohttp.client_ws.ClientWSTimeout

A data class for websocket client timeout settings.

.. attribute:: ws_receive

text
A timeout for websocket to receive a complete message.

  :class:`float`, ``None`` by default.

.. attribute:: ws_close

text
A timeout for the websocket to close.

  :class:`float`, ``10.0`` by default.

.. note::

text
Timeouts of 5 seconds or more are rounded for scheduling on the next
  second boundary (an absolute time where microseconds part is zero) for the
  sake of performance.

  E.g., assume a timeout is ``10``, absolute time when timeout should expire
  is ``loop.time() + 5``, and it points to ``12345.67 + 10`` which is equal
  to ``12355.67``.

  The absolute time for the timeout cancellation is ``12356``.

  It leads to grouping all close scheduled timeout expirations to exactly
  the same time to reduce amount of loop wakeups.

  .. versionchanged:: 3.7

     Rounding to the next seconds boundary is disabled for timeouts smaller
     than 5 seconds for the sake of easy debugging.

     In turn, tiny timeouts can lead to significant performance degradation
     on production environment.

.. class:: ETag(name, is_weak=False)
:canonical: aiohttp.helpers.ETag

Represents ETag identifier.

.. attribute:: value

text
Value of corresponding etag without quotes.

.. attribute:: is_weak

text
Flag indicates that etag is weak (has `W/` prefix).

.. versionadded:: 3.8

.. class:: ContentDisposition
:canonical: aiohttp.client_reqrep.ContentDisposition

text
A data class to represent the Content-Disposition header,
available as :attr:`ClientResponse.content_disposition` attribute.

.. attribute:: type

A :class:`str` instance. Value of Content-Disposition header
itself, e.g. ``attachment``.

.. attribute:: filename

A :class:`str` instance. Content filename extracted from
parameters. May be ``None``.

.. attribute:: parameters

Read-only mapping contains all parameters.

.. class:: RequestInfo()
:canonical: aiohttp.client_reqrep.RequestInfo

A :class:typing.NamedTuple with request URL and headers from :class:~aiohttp.ClientRequest
object, available as :attr:ClientResponse.request_info attribute.

.. attribute:: url

text
Requested *url*, :class:`yarl.URL` instance.

.. attribute:: method

text
Request HTTP method like ``'GET'`` or ``'POST'``, :class:`str`.

.. attribute:: headers

text
HTTP headers for request, :class:`multidict.CIMultiDict` instance.

.. attribute:: real_url

text
Requested *url* with URL fragment unstripped, :class:`yarl.URL` instance.

  .. versionadded:: 3.2

.. function:: encode_basic_auth(login, password='', encoding='utf-8')

Encode HTTP Basic Authentication credentials as a value suitable for the
Authorization (or Proxy-Authorization) header::

text
headers = {"Authorization": encode_basic_auth("user", "pass")}

:param str login: login
:param str password: password ('' by default)
:param str encoding: encoding ('utf-8' by default)
:return: a string of the form "Basic "
:rtype: str

.. versionadded:: 3.14

.. class:: UploadTracker()
:canonical: aiohttp.client_reqrep.UploadTracker

Tracks upload progress of a single client request.

Create a tracker inside a running event loop and pass it to a request
via the upload_tracker argument; read its attributes while the
request runs, e.g. from a progress-reporting task::

text
tracker = aiohttp.UploadTracker()

   async def report_progress() -> None:
       while not tracker.upload_complete.done():
           print(f"uploaded {tracker.bytes_written} bytes")
           await asyncio.sleep(0.5)

   progress = asyncio.create_task(report_progress())
   async with session.post(url, data=data, upload_tracker=tracker) as resp:
       ...
   await progress
   # Raises if the upload failed, even when the request itself
   # succeeded because the server answered early.
   await tracker.upload_complete

A tracker observes exactly one request: passing it to a second request
raises :exc:RuntimeError. Two requests uploading the same payload
each get their own tracker.

.. attribute:: bytes_written

text
Body bytes of the current upload attempt that have been sent to
  the kernel. Counted before transport-level transformations such as compression
  or chunked framing; within a partially sent chunk the value is a
  linear estimate. Resets to ``0`` when the request moves on to a new
  attempt, and equal to the full body size once the attempt has
  written everything.

  .. note::

     Because this only tracks the data sent to the kernel, which then buffers data
     itself, this feature is effectively useless on small payloads.
     Typically, payloads less than ~10 MiB will result in the progress
     appearing to be 100% immediately, even if not a single byte has left
     the machine yet. A payload ~20 MiB might see the progress jump to 40-50%
     immediately, etc. The larger the payload, the less noticeable this skew will be.

.. attribute:: attempts

text
Number of upload attempts dispatched: ``1`` for a plain request,
  incremented each time the request is resent, e.g. following a
  redirect or a middleware retry. A redirect that drops the body
  (such as a *303 See Other*) still counts a zero-byte attempt.

.. attribute:: upload_complete

text
An :class:`asyncio.Future` settled once no more of the body will
  be sent: with ``None`` when the final attempt wrote the body fully
  (even if the server responded first, the future settles when
  writing finishes); with the upload error when the final attempt
  failed (the request raises the same error unless the server
  already responded successfully); or with :exc:`UploadAbortedError`
  when the body was never fully sent, e.g. the request failed before
  writing started, was cancelled, or a middleware answered without
  sending the request.

  When the server answers before reading the whole body, the request
  succeeds without raising; await the future (or check its
  :meth:`~asyncio.Future.exception`) to observe such upload failures.

.. versionadded:: 3.14.4

.. class:: DigestAuthMiddleware(login, password, *, preemptive=True)
:canonical: aiohttp.client_middleware_digest_auth.DigestAuthMiddleware

HTTP digest authentication client middleware.

:param str login: login
:param str password: password
:param bool preemptive: Enable preemptive authentication (default: True)

This middleware supports HTTP digest authentication with both auth and
auth-int quality of protection (qop) modes, and a variety of hashing algorithms.

It automatically handles the digest authentication handshake by:

  • Parsing 401 Unauthorized responses with WWW-Authenticate: Digest headers
  • Generating appropriate Authorization: Digest headers on retry
  • Maintaining nonce counts and challenge data per request
  • When preemptive=True, reusing authentication credentials for subsequent
    requests to the same protection space (following RFC 7616 Section 3.6)

Preemptive Authentication

By default (preemptive=True), the middleware remembers successful authentication
challenges and automatically includes the Authorization header in subsequent requests
to the same protection space. This behavior:

  • Improves server efficiency by avoiding extra round trips
  • Matches how modern web browsers handle digest authentication
  • Follows the recommendation in RFC 7616 Section 3.6

The server may still respond with a 401 status and stale=true if the nonce
has expired, in which case the middleware will automatically retry with the new nonce.

Origin scoping

The credentials are scoped to the origin of the first request the middleware
handles. A request to a different origin is passed through untouched, so it
never receives a digest response computed from those credentials, unless that
origin falls within a protection space the anchor origin advertised through
the RFC 7616 domain directive. Make the first request through the
middleware against the intended origin, as the anchor is pinned to it and not
reset for the life of the instance.

To disable preemptive authentication and require a 401 challenge for every request,
set preemptive=False::

text
# Default behavior - preemptive auth enabled
   digest_auth_middleware = DigestAuthMiddleware(login="user", password="pass")

   # Disable preemptive auth - always wait for 401 challenge
   digest_auth_middleware = DigestAuthMiddleware(login="user", password="pass",
                                                  preemptive=False)

Usage::

text
digest_auth_middleware = DigestAuthMiddleware(login="user", password="pass")
   async with ClientSession(middlewares=(digest_auth_middleware,)) as session:
       async with session.get("http://protected.example.com") as resp:
           # The middleware automatically handles the digest auth handshake
           assert resp.status == 200

       # Subsequent requests include auth header preemptively
       async with session.get("http://protected.example.com/other") as resp:
           assert resp.status == 200  # No 401 round trip needed

.. versionadded:: 3.12
.. versionchanged:: 3.12.8
Added preemptive parameter to enable/disable preemptive authentication.
.. versionchanged:: 3.14.1
Credentials are scoped to the origin of the first request the middleware
handles; other origins are passed through untouched unless covered by an
RFC 7616 domain directive from the anchor origin.

.. class:: CookieJar(*, unsafe=False, quote_cookie=True, treat_as_secure_origin = [])
:canonical: aiohttp.cookiejar.CookieJar

The cookie jar instance is available as :attr:ClientSession.cookie_jar.

The jar contains :class:~http.cookies.Morsel items for storing
internal cookie data.

API provides a count of saved cookies::

text
len(session.cookie_jar)

These cookies may be iterated over::

text
for cookie in session.cookie_jar:
       print(cookie.key)
       print(cookie["domain"])

The class implements :class:collections.abc.Iterable,
:class:collections.abc.Sized and
:class:aiohttp.abc.AbstractCookieJar interfaces.

Implements cookie storage adhering to RFC 6265.

Cookies parsed from responses are limited to 50 valid Set-Cookie
fields per response, 4,096 octets for a cookie's name and value, 32
attributes per field and 1,024 octets per attribute value; anything beyond
that is ignored. Flags such as Secure ignore their value, so a value
over the limit still sets them.

The jar stores at most 180 cookies per domain and 3,300 in total, and
sends at most 150 cookies and 8,190 octets, including Cookie:, per
request header. Eviction follows Firefox: adding a cookie to a full domain
evicts expired, then non-secure, cookies, least recently used first, down
to 150 or until none are left. If the domain is full of live Secure
cookies, they are evicted for a new Secure cookie and a new non-secure
cookie is dropped. A full jar evicts expired, then least recently used,
cookies from any domain down to 3,000.

The limits do not apply to an explicit Cookie header or to the
per-request cookies argument.

:param bool unsafe: (optional) Whether to accept cookies from IPs.

:param bool quote_cookie: (optional) Whether to quote cookies according to
:rfc:2109. Some backend systems
(not compatible with RFC mentioned above)
does not support quoted cookies.

text
.. versionadded:: 3.7

:param treat_as_secure_origin: (optional) Mark origins as secure
for cookies marked as Secured.

text
Possible types are:

                              - :class:`tuple` or :class:`list` of
                                :class:`str` or :class:`yarl.URL`
                              - :class:`str`
                              - :class:`yarl.URL`

  .. versionadded:: 3.8

.. method:: update_cookies(cookies, response_url=None)

text
Update cookies returned by server in ``Set-Cookie`` header.

  :param cookies: a :class:`collections.abc.Mapping`
     (e.g. :class:`dict`, :class:`~http.cookies.SimpleCookie`) or
     *iterable* of *pairs* with cookies returned by server's
     response.

  :param ~yarl.URL response_url: URL of response, ``None`` for *shared
     cookies*.  Regular cookies are coupled with server's URL and
     are sent only to this server, shared ones are sent in every
     client request (except that shared cookies marked ``Secure``
     are only sent over encrypted connections or to origins listed
     in *treat_as_secure_origin*).

.. method:: filter_cookies(request_url)

text
Return jar's cookies acceptable for URL and available in
  ``Cookie`` header for sending client requests for given URL.

  :param ~yarl.URL response_url: request's URL for which cookies are asked.

  :return: :class:`http.cookies.SimpleCookie` with filtered
     cookies for given URL.

.. method:: save(file_path)

text
Write a JSON representation of cookies into the file
  at provided path.

  :param file_path: Path to file where cookies will be serialized,
      :class:`str` or :class:`pathlib.Path` instance.

.. method:: load(file_path)

text
Load cookies from a JSON file at the provided path.

  The jar's storage limits apply to the loaded cookies. If the file is
  malformed, an exception is raised and the jar is left unchanged.

  :param file_path: Path to file from where cookies will be
       imported, :class:`str` or :class:`pathlib.Path` instance.

.. method:: clear(predicate=None)

text
Removes all cookies from the jar if the predicate is ``None``. Otherwise remove only those :class:`~http.cookies.Morsel` that ``predicate(morsel)`` returns ``True``.

  :param predicate: callable that gets :class:`~http.cookies.Morsel` as a parameter and returns ``True`` if this :class:`~http.cookies.Morsel` must be deleted from the jar.

      .. versionadded:: 4.0

.. method:: clear_domain(domain)

text
Remove all cookies from the jar that belongs to the specified domain or its subdomains.

  :param str domain: domain for which cookies must be deleted from the jar.

  .. versionadded:: 4.0

.. attribute:: cookies

text
A read-only view of the jar's cookies as a
  :class:`~types.MappingProxyType` mapping ``(domain, path)`` tuples
  to :class:`~http.cookies.SimpleCookie` instances.

  .. versionadded:: 3.14

.. attribute:: host_only_cookies

text
A :class:`frozenset` of ``(domain, path, name)`` tuples indicating
  which cookies are host-only (not sent to subdomains).

  .. versionadded:: 3.14

  .. versionchanged:: 3.14.4

     The tuples gained the *path* element; host-only state is tracked
     per ``(domain, path, name)`` cookie identity so that same-named
     cookies on other paths cannot affect it.

.. attribute:: treat_as_secure_origin

text
A :class:`frozenset` of :class:`~yarl.URL` origins that are
  treated as secure even when the connection is not encrypted, as
  configured by the *treat_as_secure_origin* parameter.

  .. versionadded:: 3.14.4

.. class:: DummyCookieJar(*, loop=None)
:canonical: aiohttp.cookiejar.DummyCookieJar

Dummy cookie jar which does not store cookies but ignores them.

Could be useful e.g. for web crawlers to iterate over Internet
without blowing up with saved cookies information.

To install dummy cookie jar pass it into session instance::

text
jar = aiohttp.DummyCookieJar()
  session = aiohttp.ClientSession(cookie_jar=DummyCookieJar())

.. class:: Fingerprint(digest)
:canonical: aiohttp.client_reqrep.Fingerprint

Fingerprint helper for checking SSL certificates by SHA256 digest.

:param bytes digest: SHA256 digest for certificate in DER-encoded
binary form (see
:meth:ssl.SSLSocket.getpeercert).

To check fingerprint pass the object into :meth:ClientSession.get
call, e.g.::

text
import hashlib

  with open(path_to_cert, 'rb') as f:
      digest = hashlib.sha256(f.read()).digest()

  await session.get(url, ssl=aiohttp.Fingerprint(digest))

.. versionadded:: 3.0

.. function:: set_zlib_backend(lib)
:canonical: aiohttp.compression_utils.set_zlib_backend

Sets the compression backend for zlib-based operations.

This function allows you to override the default zlib backend
used internally by passing a module that implements the standard
compression interface.

The module should implement at minimum the exact interface offered by the
latest version of zlib.

:param types.ModuleType lib: A module that implements the zlib-compatible compression API.

Example usage::

text
import zlib_ng.zlib_ng as zng
  import aiohttp

  aiohttp.set_zlib_backend(zng)

.. note:: aiohttp has been tested internally with :mod:zlib, :mod:zlib_ng.zlib_ng, and :mod:isal.isal_zlib.

.. versionadded:: 3.12

FormData
^^^^^^^^

A :class:FormData object contains the form data and also handles
encoding it into a body that is either multipart/form-data or
application/x-www-form-urlencoded. multipart/form-data is
used if at least one field is an :class:io.IOBase object or was
added with at least one optional argument to :meth:add_field<aiohttp.FormData.add_field>
(content_type, filename, or content_transfer_encoding).
Otherwise, application/x-www-form-urlencoded is used.

:class:FormData instances are callable and return a :class:aiohttp.payload.Payload
on being called.

.. class:: FormData(fields, quote_fields=True, charset=None)
:canonical: aiohttp.formdata.FormData

Helper class for multipart/form-data and application/x-www-form-urlencoded body generation.

:param fields: A container for the key/value pairs of this form.

text
Possible types are:

              - :class:`dict`
              - :class:`tuple` or :class:`list`
              - :class:`io.IOBase`, e.g. a file-like object
              - :class:`multidict.MultiDict` or :class:`multidict.MultiDictProxy`

              If it is a :class:`tuple` or :class:`list`, it must be a valid argument
              for :meth:`add_fields<aiohttp.FormData.add_fields>`.

              For :class:`dict`, :class:`multidict.MultiDict`, and :class:`multidict.MultiDictProxy`,
              the keys and values must be valid `name` and `value` arguments to
              :meth:`add_field<aiohttp.FormData.add_field>`, respectively.

.. method:: add_field(name, value, content_type=None, filename=None,
content_transfer_encoding=None)

text
Add a field to the form.

  :param str name: Name of the field

  :param value: Value of the field

                Possible types are:

                - :class:`str`
                - :class:`bytes`, :class:`bytearray`, or :class:`memoryview`
                - :class:`io.IOBase`, e.g. a file-like object

  :param str content_type: The field's content-type header (optional)

  :param str filename: The field's filename (optional)

                       If this is not set and ``value`` is a :class:`bytes`, :class:`bytearray`,
                       or :class:`memoryview` object, the `name` argument is used as the filename
                       unless ``content_transfer_encoding`` is specified.

                       If ``filename`` is not set and ``value`` is an :class:`io.IOBase`
                       object, the filename is extracted from the object if possible.

  :param str content_transfer_encoding: The field's content-transfer-encoding
                                        header (optional)

.. method:: add_fields(fields)

text
Add one or more fields to the form.

  :param fields: An iterable containing:

                 - :class:`io.IOBase`, e.g. a file-like object
                 - :class:`multidict.MultiDict` or :class:`multidict.MultiDictProxy`
                 - :class:`tuple` or :class:`list` of length two, containing a name-value pair

Client exceptions

Exception hierarchy has been significantly modified in version
2.0. aiohttp defines only exceptions that covers connection handling
and server response misbehaviors. For developer specific mistakes,
aiohttp uses python standard exceptions like :exc:ValueError or
:exc:TypeError.

Reading a response content may raise a :exc:ClientPayloadError
exception. This exception indicates errors specific to the payload
encoding. Such as invalid compressed data, malformed chunked-encoded
chunks or not enough data that satisfy the content-length header.

All exceptions are available as members of aiohttp module.

.. exception:: ClientError
:canonical: aiohttp.client_exceptions.ClientError

Base class for all client specific exceptions.

Derived from :exc:Exception

.. class:: ClientPayloadError
:canonical: aiohttp.client_exceptions.ClientPayloadError

This exception can only be raised while reading the response
payload if one of these errors occurs:

  1. invalid compression
  2. malformed chunked encoding
  3. not enough data that satisfy Content-Length HTTP header.

Derived from :exc:ClientError

.. class:: UploadAbortedError
:canonical: aiohttp.client_exceptions.UploadAbortedError

Set on :attr:UploadTracker.upload_complete when the request body
was never fully sent.

Derived from :exc:ClientError

.. versionadded:: 3.14.4

.. exception:: InvalidURL
:canonical: aiohttp.client_exceptions.InvalidURL

URL used for fetching is malformed, e.g. it does not contain host
part.

Derived from :exc:ClientError and :exc:ValueError

.. attribute:: url

text
Invalid URL, :class:`yarl.URL` instance.

.. attribute:: description

  Invalid URL description, :class:`str` instance or :data:`None`.

.. exception:: InvalidUrlClientError
:canonical: aiohttp.client_exceptions.InvalidUrlClientError

Base class for all errors related to client url.

Derived from :exc:InvalidURL

.. exception:: RedirectClientError
:canonical: aiohttp.client_exceptions.RedirectClientError

Base class for all errors related to client redirects.

Derived from :exc:ClientError

.. exception:: NonHttpUrlClientError
:canonical: aiohttp.client_exceptions.NonHttpUrlClientError

Base class for all errors related to non http client urls.

Derived from :exc:ClientError

.. exception:: InvalidUrlRedirectClientError
:canonical: aiohttp.client_exceptions.InvalidUrlRedirectClientError

Redirect URL is malformed, e.g. it does not contain host part.

Derived from :exc:InvalidUrlClientError and :exc:RedirectClientError

.. exception:: NonHttpUrlRedirectClientError
:canonical: aiohttp.client_exceptions.NonHttpUrlRedirectClientError

Redirect URL does not contain http schema.

Derived from :exc:RedirectClientError and :exc:NonHttpUrlClientError

Response errors
^^^^^^^^^^^^^^^

.. exception:: ClientResponseError
:canonical: aiohttp.client_exceptions.ClientResponseError

These exceptions could happen after we get response from server.

Derived from :exc:ClientError

.. attribute:: request_info

text
Instance of :class:`RequestInfo` object, contains information
  about request.

.. attribute:: status

text
HTTP status code of response (:class:`int`), e.g. ``400``.

.. attribute:: message

text
Message of response (:class:`str`), e.g. ``"OK"``.

.. attribute:: headers

text
Headers in response, a list of pairs.

.. attribute:: history

text
History from failed response, if available, else empty tuple.

  A :class:`tuple` of :class:`ClientResponse` objects used for
  handle redirection responses.

.. attribute:: code

text
HTTP status code of response (:class:`int`), e.g. ``400``.

  .. deprecated:: 3.1

.. class:: ContentTypeError
:canonical: aiohttp.client_exceptions.ContentTypeError

Invalid content type.

Derived from :exc:ClientResponseError

.. versionadded:: 2.3

.. class:: TooManyRedirects
:canonical: aiohttp.client_exceptions.TooManyRedirects

Client was redirected too many times.

Maximum number of redirects can be configured by using
parameter max_redirects in :meth:request<aiohttp.ClientSession.request>.

Derived from :exc:ClientResponseError

.. versionadded:: 3.2

.. class:: WSServerHandshakeError
:canonical: aiohttp.client_exceptions.WSServerHandshakeError

Web socket server response error.

Derived from :exc:ClientResponseError

.. exception:: WSMessageTypeError
:canonical: aiohttp.client_exceptions.WSMessageTypeError

Received WebSocket message of unexpected type

Derived from :exc:TypeError

Connection errors
^^^^^^^^^^^^^^^^^

.. class:: ClientConnectionError
:canonical: aiohttp.client_exceptions.ClientConnectionError

These exceptions related to low-level connection problems.

Derived from :exc:ClientError

.. class:: ClientConnectionResetError
:canonical: aiohttp.client_exceptions.ClientConnectionResetError

Derived from :exc:ClientConnectionError and :exc:ConnectionResetError

.. class:: ClientOSError
:canonical: aiohttp.client_exceptions.ClientOSError

Subset of connection errors that are initiated by an :exc:OSError
exception.

Derived from :exc:ClientConnectionError and :exc:OSError

.. class:: ClientConnectorError
:canonical: aiohttp.client_exceptions.ClientConnectorError

Connector related exceptions.

Derived from :exc:ClientOSError

.. attribute:: ssl

text
The value passed as the ``ssl`` parameter of the request: an
  :class:`ssl.SSLContext`, a :class:`bool`, or a
  :class:`~aiohttp.Fingerprint`.

.. class:: ClientConnectorDNSError
:canonical: aiohttp.client_exceptions.ClientConnectorDNSError

DNS resolution error.

Derived from :exc:ClientConnectorError

.. class:: ClientProxyConnectionError
:canonical: aiohttp.client_exceptions.ClientProxyConnectionError

Derived from :exc:ClientConnectorError

.. class:: ClientSSLError
:canonical: aiohttp.client_exceptions.ClientSSLError

Derived from :exc:ClientConnectorError

.. class:: ClientConnectorSSLError
:canonical: aiohttp.client_exceptions.ClientConnectorSSLError

Response ssl error.

Derived from :exc:ClientSSLError and :exc:ssl.SSLError

.. class:: ClientConnectorCertificateError
:canonical: aiohttp.client_exceptions.ClientConnectorCertificateError

Response certificate error.

Derived from :exc:ClientSSLError and :exc:ssl.CertificateError

.. class:: UnixClientConnectorError
:canonical: aiohttp.client_exceptions.UnixClientConnectorError

Derived from :exc:ClientConnectorError

.. class:: ServerConnectionError
:canonical: aiohttp.client_exceptions.ServerConnectionError

Derived from :exc:ClientConnectionError

.. class:: ServerDisconnectedError
:canonical: aiohttp.client_exceptions.ServerDisconnectedError

Server disconnected.

Derived from :exc:~aiohttp.ServerConnectionError

.. attribute:: message

text
Partially parsed HTTP message (optional).

.. class:: ServerFingerprintMismatch
:canonical: aiohttp.client_exceptions.ServerFingerprintMismatch

Server fingerprint mismatch.

Derived from :exc:ServerConnectionError

.. class:: ServerTimeoutError
:canonical: aiohttp.client_exceptions.ServerTimeoutError

Server operation timeout: read timeout, etc.

To catch all timeouts, including the total timeout, use
:exc:asyncio.TimeoutError.

Derived from :exc:ServerConnectionError and :exc:asyncio.TimeoutError

.. class:: ConnectionTimeoutError
:canonical: aiohttp.client_exceptions.ConnectionTimeoutError

Connection timeout on connect and sock_connect timeouts.

Derived from :exc:ServerTimeoutError

.. class:: SocketTimeoutError
:canonical: aiohttp.client_exceptions.SocketTimeoutError

Reading from socket timeout on sock_read timeout.

Derived from :exc:ServerTimeoutError

Hierarchy of exceptions
^^^^^^^^^^^^^^^^^^^^^^^

  • :exc:ClientError

    • :exc:ClientConnectionError

      • :exc:ClientConnectionResetError

      • :exc:ClientOSError

        • :exc:ClientConnectorError

          • :exc:ClientProxyConnectionError

          • :exc:ClientConnectorDNSError

          • :exc:ClientSSLError

            • :exc:ClientConnectorCertificateError

            • :exc:ClientConnectorSSLError

          • :exc:UnixClientConnectorError

      • :exc:ServerConnectionError

        • :exc:ServerDisconnectedError

        • :exc:ServerFingerprintMismatch

        • :exc:ServerTimeoutError

          • :exc:ConnectionTimeoutError

          • :exc:SocketTimeoutError

    • :exc:ClientPayloadError

    • :exc:UploadAbortedError

    • :exc:ClientResponseError

      • :exc:~aiohttp.ClientHttpProxyError

      • :exc:ContentTypeError

      • :exc:TooManyRedirects

      • :exc:WSServerHandshakeError

    • :exc:InvalidURL

      • :exc:InvalidUrlClientError

        • :exc:InvalidUrlRedirectClientError
    • :exc:NonHttpUrlClientError

      • :exc:NonHttpUrlRedirectClientError
    • :exc:RedirectClientError

      • :exc:InvalidUrlRedirectClientError

      • :exc:NonHttpUrlRedirectClientError

Client Types

.. type:: ClientMiddlewareType

Type alias for client middleware functions. Middleware functions must have this signature::

text
Callable[
      [ClientRequest, ClientHandlerType],
      Awaitable[ClientResponse]
  ]

.. type:: ClientHandlerType

Type alias for client request handler functions::

text
Callable[[ClientRequest], Awaitable[ClientResponse]]

File: docs/contributing.rst

.. _aiohttp-contributing:

Contributing

(:doc:contributing-admins)

Instructions for contributors

In order to make a clone of the GitHub_ repo: open the link and press the "Fork" button on the upper-right menu of the web page.

I hope everybody knows how to work with git and github nowadays :)

Workflow is pretty straightforward:

  1. Make sure you are reading the latest version of this document.
    It can be found in the GitHub_ repo in the docs subdirectory.

  2. Clone the GitHub_ repo using the --recurse-submodules argument

  3. Setup your machine with the required development environment

  4. Make a change

  5. Make sure all tests passed

  6. Add a file into the CHANGES folder (see Changelog update_ for how).

  7. Commit changes to your own aiohttp clone

  8. Make a pull request from the github page of your clone against the master branch

  9. Optionally make backport Pull Request(s) for landing a bug fix into released aiohttp versions.

.. note::

The project uses Squash-and-Merge strategy for GitHub Merge button.

Basically it means that there is no need to rebase a Pull Request against
master branch. Just git merge master into your working copy (a fork) if
needed. The Pull Request is automatically squashed into the single commit
once the PR is accepted.

.. note::

GitHub issue and pull request threads are automatically locked when there has
not been any recent activity for one year. Please open a new issue <https://github.com/aio-libs/aiohttp/issues/new>_ for related bugs.

If you feel like there are important points in the locked discussions,
please include those excerpts into that new issue.

Preconditions for running aiohttp test suite

We expect you to use a python virtual environment to run our tests.

There are several ways to make a virtual environment.

If you like to use virtualenv please run:

.. code-block:: shell

$ cd aiohttp
$ virtualenv --python=which python3 venv
$ . venv/bin/activate

For standard python venv:

.. code-block:: shell

$ cd aiohttp
$ python3 -m venv venv
$ . venv/bin/activate

For virtualenvwrapper:

.. code-block:: shell

$ cd aiohttp
$ mkvirtualenv --python=which python3 aiohttp

There are other tools like pyvenv but you know the rule of thumb now: create a python3 virtual environment and activate it.

After that please install libraries required for development:

.. code-block:: shell

$ make install-dev

.. note::

For now, the development tooling depends on make and assumes an Unix OS If you wish to contribute to aiohttp from a Windows machine, the easiest way is probably to configure the WSL <https://docs.microsoft.com/en-us/windows/wsl/install-win10>_ so you can use the same instructions. If it's not possible for you or if it doesn't work, please contact us so we can find a solution together.

Install pre-commit hooks:

.. code-block:: shell

$ pre-commit install

.. warning::

If you plan to use temporary print(), pdb or ipdb within the test suite, execute it with -s
and without the parallel pytest-xdist workers that are enabled by default:

.. code-block:: shell

text
$ pytest tests -s --numprocesses=0

in order to run the tests without output capturing.

Congratulations, you are ready to run the test suite!

.. include:: ../vendor/README.rst

Run autoformatter

The project uses black_ + isort_ formatters to keep the source code style.
Please run make fmt after every change before starting tests.

.. code-block:: shell

text
$ make fmt

Run aiohttp test suite

After all the preconditions are met you can run tests typing the next
command:

.. code-block:: shell

$ make test

The command at first will run the linters (sorry, we don't accept
pull requests with pyflakes, black, isort, or mypy errors).

On lint success the tests will be run.

Please take a look on the produced output.

Any extra texts (print statements and so on) should be removed.

Code coverage

We use codecov.io as an indispensable tool for analyzing our coverage
results. Visit https://codecov.io/gh/aio-libs/aiohttp to see coverage
reports for the master branch, history, pull requests etc.

We'll use an example from a real PR to demonstrate how we use this.
Once the tests run in a PR, you'll see a comment posted by codecov.
The most important thing to check here is whether there are any new
missed or partial lines in the report:

.. image:: _static/img/contributing-cov-comment.svg

Here, the PR has introduced 1 miss and 2 partials. Now we
click the link in the comment header to open the full report:

.. image:: _static/img/contributing-cov-header.svg
:alt: Codecov report

Now, if we look through the diff under 'Files changed' we find one of
our partials:

.. image:: _static/img/contributing-cov-partial.svg
:alt: A while loop with partial coverage.

In this case, the while loop is never skipped in our tests. This is
probably not worth writing a test for (and may be a situation that is
impossible to trigger anyway), so we leave this alone.

We're still missing a partial and a miss, so we switch to the
'Indirect changes' tab and take a look through the diff there. This
time we find the remaining 2 lines:

.. image:: _static/img/contributing-cov-miss.svg
:alt: An if statement that isn't covered anymore.

After reviewing the PR, we find that this code is no longer needed as
the changes mean that this method will never be called under those
conditions. Thanks to this report, we were able to remove some
redundant code from a performance-critical part of our codebase (this
check would have been run, probably multiple times, for every single
incoming request).

.. tip::
Sometimes the diff on codecov.io doesn't make sense. This is usually
caused by the branch being out of sync with master. Try merging
master into the branch and it will likely fix the issue. Failing
that, try checking coverage locally as described in the next section.

Other tools

The browser extension https://docs.codecov.io/docs/browser-extension
is also a useful tool for analyzing the coverage directly from Files
Changed
tab on the GitHub Pull Request review page.

You can also produce coverage reports locally with make cov-dev
or just adding --cov-report=html to pytest.

This will run the test suite and collect coverage information. Once
finished, coverage results can be view by opening:

console
$ python -m webbrowser -n file://"$(pwd)"/htmlcov/index.html

Documentation

We encourage documentation improvements.

Please before making a Pull Request about documentation changes run:

.. code-block:: shell

$ make doc

Once it finishes it will output the index html page
open file:///.../aiohttp/docs/_build/html/index.html.

Go to the link and make sure your doc changes looks good.

Spell checking

We use pyenchant and sphinxcontrib-spelling for running spell
checker for documentation:

.. code-block:: shell

$ make doc-spelling

Unfortunately there are problems with running spell checker on MacOS X.

To run spell checker on Linux box you should install it first:

.. code-block:: shell

$ sudo apt-get install enchant
$ pip install sphinxcontrib-spelling

Preparing a pull request

When making a pull request, please include a short summary of the changes
and a reference to any issue tickets that the PR is intended to solve.
All PRs with code changes should include tests. All changes should
include a changelog entry.

Changelog update

.. include:: ../CHANGES/README.rst

Making a pull request

After finishing all steps make a GitHub_ Pull Request with master base branch.

Backporting

All Pull Requests are created against master git branch.

If the Pull Request is not a new functionality but bug fixing
backport to maintenance branch would be desirable.

aiohttp project committer may ask for making a backport of the PR
into maintained branch(es), in this case he or she adds a github label
like needs backport to 3.1.

Backporting is performed after main PR merging into master.
Please do the following steps:

  1. Find Pull Request's commit for cherry-picking.

    aiohttp does squashing PRs on merging, so open your PR page on
    github and scroll down to message like asvetlov merged commit f7b8921 into master 9 days ago. f7b8921 is the required commit number.

  2. Run cherry_picker <https://github.com/python/core-workflow/tree/master/cherry_picker>_
    tool for making backport PR (the tool is already pre-installed from
    ./requirements/dev.txt), e.g. cherry_picker f7b8921 3.1.

  3. In case of conflicts fix them and continue cherry-picking by
    cherry_picker --continue.

    cherry_picker --abort stops the process.

    cherry_picker --status shows current cherry-picking status
    (like git status)

  4. After all conflicts are done the tool opens a New Pull Request page
    in a browser with pre-filed information. Create a backport Pull
    Request and wait for review/merging.

  5. aiohttp committer should remove backport Git label after
    merging the backport.

How to become an aiohttp committer

Contribute!

The easiest way is providing Pull Requests for issues in our bug
tracker. But if you have a great idea for the library improvement
-- please make an issue and Pull Request.

The rules for committers are simple:

  1. No wild commits! Everything should go through PRs.
  2. Take a part in reviews. It's very important part of maintainer's activity.
  3. Pickup issues created by others, especially if they are simple.
  4. Keep test suite comprehensive. In practice it means leveling up
    coverage. 97% is not bad but we wish to have 100% someday. Well, 99%
    is good target too.
  5. Don't hesitate to improve our docs. Documentation is a very important
    thing, it's the key for project success. The documentation should
    not only cover our public API but help newbies to start using the
    project and shed a light on non-obvious gotchas.

After positive answer aiohttp committer creates an issue on github
with the proposal for nomination. If the proposal will collect only
positive votes and no strong objection -- you'll be a new member in
our team.

.. _GitHub: https://github.com/aio-libs/aiohttp

.. _ipdb: https://pypi.python.org/pypi/ipdb

.. _black: https://pypi.python.org/pypi/black

.. _isort: https://pypi.python.org/pypi/isort


File: docs/contributing-admins.rst

:orphan:

Instructions for aiohttp admins

This page is intended to document certain processes for admins of the aiohttp repository.
For regular contributors, return to :doc:contributing.

.. contents::
:local:
:depth: 1

.. highlight:: none

Running reproducer code

.. warning::

When evaluating a bug report or vulnerability report, treat reproducer code as
untrusted. If you don't understand what it does or are unfamiliar with a library
it imports do not run it (and ask the reporter to provide a simpler reproducer).
We also recommend that any reproducers you do run are executed in a container.

Creating a new release

.. note:: The example commands assume that origin refers to the aio-libs repository.

To create a new release:

#. Start on the branch for the release you are planning (e.g. 3.8 for v3.8.6): git checkout 3.8 && git pull
#. Update the version number in __init__.py.
#. Run towncrier.
#. Check and cleanup the changes in CHANGES.rst.
#. Checkout a new branch: e.g. git checkout -b release/v3.8.6
#. Commit and create a PR. Verify the changelog and release notes look good on Read the Docs. Once PR is merged, continue.
#. Go back to the release branch: e.g. git checkout 3.8 && git pull
#. Add a tag: e.g. git tag -a v3.8.6 -m 'Release 3.8.6' -s
#. Push the tag: e.g. git push origin v3.8.6
#. Monitor CI to ensure release process completes without errors.

Once released, we need to complete some cleanup steps (no further steps are needed for
non-stable releases though). If doing a patch release, we need to do the below steps twice,
first merge into the newer release branch (e.g. 3.8 into 3.9) and then to master
(e.g. 3.9 into master). If a new minor release, then just merge to master.

#. Switch to target branch: e.g. git checkout 3.9 && git pull
#. Start a merge: e.g. git merge 3.8 --no-commit --no-ff --gpg-sign
#. Carefully review the changes and revert anything that should not be included (most
things outside the changelog). Use git checkout 3.9 path/to/file to revert files
without aborting the merge.
#. To ensure change fragments are cleaned up properly, run: python tools/cleanup_changes.py
#. Complete the merge: git merge --continue.
#. Push the branch directly to Github (because a PR would get squashed). When pushing,
you may get a rejected message. Follow these steps to resolve:

#. Checkout to a new branch and push: e.g. git checkout -b do-not-merge && git push
#. Open a draft PR with a title of 'DO NOT MERGE'.
#. Once the CI has completed on that branch, you should be able to switch back and push
the target branch (as tests have passed on the merge commit now).
#. This should automatically consider the PR merged and delete the temporary branch.

Back on the original release branch, bump the version number and append .dev0 in __init__.py.

Post the release announcement to social media:

If doing a minor release:

#. Create a new release branch for future features to go to: e.g. git checkout -b 3.10 3.9 && git push
#. Update both target-branch backports for Dependabot to reference the new branch name in .github/dependabot.yml.
#. Delete the older backport label (e.g. backport-3.8): https://github.com/aio-libs/aiohttp/labels
#. Add a new backport label (e.g. backport-3.10).

Incident response

This section covers responding to a reported security vulnerability and to
three classes of compromise -- of the supply chain, of a maintainer account,
and of the project infrastructure. It picks up after a report or a problem
is in hand.

.. note::

Vulnerability reports arrive through the organization's SECURITY.md
-- GitHub private vulnerability reporting, or email to the security coordinators.
Never triage a suspected vulnerability in a public issue or pull request.

The security coordinator who first triages a report is its incident lead,
and may explicitly hand off to another coordinator. The lead owns the GitHub
Security Advisory (GHSA) draft, fix coordination, the release, and notification.

Severity tiers

text
aiohttp is volunteer-maintained, so there is no response-time commitment. The
tier guides prioritization, the release decision, and how widely the fix is
announced. All security fixes -- regardless of tier -- land on ``master`` and
are backported to the currently supported ``x.y`` branch (and the next
``x.y`` branch, if one is in development).

High
   A concrete remote impact an attacker can actually achieve against a
   default deployment. Examples: a single request that stops the server from
   handling further requests (a server-wide denial of service), reading files
   on the server outside the project root, remote code execution,
   authentication bypass. Cut a dedicated security release once the fix lands.

Medium
   Bounded impact, or impact that needs a non-default option, an unusual
   configuration, or a local position. Examples: request smuggling demonstrated
   to cause a real issue against a common proxy, or a DoS that use significant server
   resources with a low effort sustained attack.
   Cut a dedicated security release.

Low
   Limited or hard-to-exploit impact, or impact confined to debug or
   non-default paths. Examples: minor version or error-message disclosure,
   parser leniency with no demonstrated security impact, or request smuggling
   that has not been demonstrated to cause a real issue against a common
   proxy. May ride the next routine release rather than cutting a dedicated
   security release.

Severity is rated by demonstrated attacker impact. A denial of service that
disables further request handling for the whole server from a single request
leans High; one that requires sustained low-effort traffic to consume
significant server resources leans Medium. Request smuggling and parser bugs
are rated by what an attacker can actually achieve against a common proxy or
in a realistic deployment, not by the shape of the bug.

Responding to a reported vulnerability

#. Acknowledge and confirm. Reply to the reporter in the GitHub private
report or the email thread. Reproduce the issue
(but don't run untrusted code <Running reproducer code_>_) against
master and the maintained x.y branches. If it is not reproducible
or is out of scope, record the decision and close the report.
#. Open the GHSA draft. For an email report, if the reporter can't use Github
for some reason, then open a new advisory to track the issue.
#. Develop the fix privately in the GHSA's temporary private fork ("Start a
temporary private fork" in the draft) -- never on a public branch or in a
public pull request. Include a regression test unless it would provide too
much information for an attacker to replicate.
#. Title the advisory with the vulnerability class and affected component.
#. Write the description using the GHSA description template::

text
### Summary

  <one or two sentences describing the vulnerability>

  ### Impact

  <who is affected, prerequisites, and what an attacker achieves>

  ### Workaround

  

  ---

  Patch: <link to commit fixing the issue on the stable x.y branch>

An important detail that Github will use to decide the CVE severity is exploit
maturity. Try to clarify the likelihood of a vulnerability being attacked
(e.g. if exploit code is available, or active attacks in-the-wild already exist).
#. Set the affected and fixed versions.
#. Assign a severity tier (see Severity tiers).
#. Credit the reporter (if report came by email) in the GHSA Credits field.
#. Credit the developer using "Remediation developer" if different from Reporter.
#. Request a CVE by clicking the button in the advisory.
#. Get the fix reviewed in the private fork by at least one other maintainer.
#. Consider early notify for high severity issues
(see Notifying about a disclosed vulnerability
).
#. Coordinate a release.

#. Agree the timing. For an embargoed high-severity incident, set the
release date to align with the lift of any private-list embargo.
#. Merge all the private forks and create and merge the backports for
each.
#. Create the release. Follow Creating a new release.
#. Update patch link in GHSA description.
#. Publish the GHSA usually around 1 day after release.
#. Notify according to the severity tier
(see Notifying about a disclosed vulnerability
).
#. Run the post-incident steps (see Post-incident_).

Notifying about a disclosed vulnerability

text
Notification is cumulative -- each tier adds to the ones below it. Mind the
timing: the ``linux-distros`` pre-notification happens under embargo, *before*
the release, while everything else happens at or after the release.

Baseline (all severities)
   The published GHSA (which feeds the CVE and the GitHub Advisory Database).

Higher (Medium-High)
   For most high severity issues, additionally post to the public ``oss-security``
   mailing list once the advisory is public.

Highest (High-Critical)
   For the most serious issues, additionally *before* the public release, send
   an embargoed pre-notification to the private distribution-security lists -- primarily
   ``linux-distros`` -- and attempt to notify affected downstreams. Use the
   `vuln_search.py dependents-enumeration script
   <https://github.com/aio-libs/.github/blob/master/tools/vuln_search.py>`_
   to search aiohttp's GitHub dependents; it needs a GitHub token and may need to be
   run over a few days, so start it early.

Keep the embargo as short as practical. Typically an embargo of 1 or 2 days is expected.

Template for the embargoed ``linux-distros`` pre-notification::

    To: [email protected]
    Subject: aiohttp: <one-line summary of the vulnerability>

    We are coordinating disclosure of a security vulnerability in aiohttp
    (https://github.com/aio-libs/aiohttp), the asyncio HTTP client/server
    library.

    Summary
      <2-3 sentences: the flaw and its impact>

    Affected versions
      <e.g. aiohttp < 3.12.15 (all 3.11.x and 3.12.x)>

    CVE
      <CVE-XXXX-XXXXX, or "requested via GitHub Security Advisory, ID pending">

    Fix
      <link to the patch, or attach the diff>

    Proposed public disclosure date
      <YYYY-MM-DD> -- as short as practical. On that date we will publish the
      GitHub Security Advisory, ship a patched PyPI release, and post to
      oss-security.

    This issue is not yet public; please observe the embargo until that date.

    Contact: 

Template for the public ``oss-security`` disclosure::

    To: [email protected]
    Subject: CVE-XXXX-XXXXX: aiohttp <one-line summary>

    A security vulnerability has been fixed in aiohttp, the asyncio HTTP
    client/server library (https://github.com/aio-libs/aiohttp).

    CVE: CVE-XXXX-XXXXX
    Advisory: <GHSA URL>

    Affected versions: <...>
    Fixed versions:    <...>

    Description
      <what the flaw is>

    Impact
      <what an attacker can achieve, and any prerequisites>

    Mitigation
      <workaround, or "upgrade to <version>">

Post-incident
~~~~~~~~~~~~~

#. **Update** ``THREAT_MODEL.md``. Have an AI coding agent fetch the new advisories
   and update the document. Review the changes.
#. **File follow-up hardening** as ordinary public issues or pull requests.

Supply-chain or release compromise
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A malicious or tampered release on PyPI, or a compromised publish pipeline.

#. **Pull the bad artifact.** On PyPI, *yank* the affected release -- this
   hides it from new resolves while keeping existing pins working. *Delete* it
   only if it is actively malicious and removal is the safer choice. This needs
   a PyPI project owner or maintainer account.
#. **Verify the legitimate artifacts with Sigstore.** Every genuine release is
   signed by the release pipeline, with the ``.sigstore`` bundles attached to
   the GitHub Release. Verify the ``sdist`` and wheels; a divergence from what is on
   PyPI localizes the tampering.
#. **Lock the publish path.** Publishing uses a PyPI OIDC trusted publisher, so
   there is no long-lived token to rotate. Instead, on PyPI temporarily remove
   the trusted-publisher binding, and on GitHub restrict or pause the ``pypi``
   deployment environment used by the release job.
#. **Audit the release inputs.** Review recent changes to the release workflow,
   its tag trigger, and the third-party action versions it uses.
#. **Audit the committed Cython sources.** The generated ``.c`` files ship in
   the ``sdist`` and are not checked against their ``.pyx`` sources in CI (see
   ``THREAT_MODEL.md`` section 5.19). Regenerate them with ``make cythonize``
   and ``git diff`` against the released revision. Re-verify the
   ``vendor/llhttp`` pin and ``package-lock.json``.
#. **Reissue a clean release** via `Creating a new release`_ once the cause is
   fixed. Never re-publish over a yanked version number.
#. **Notify** per severity -- a malicious published release is High.

Maintainer account compromise
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A GitHub or PyPI account takeover, or leaked publishing or signing material.

#. **Suspend the account.** Another GitHub organization owner can suspend it,
   revoke its repository access, and start the audit-log review. If the
   compromised account is the sole organization owner, or no other owner is
   reachable, contact GitHub Support at https://support.github.com/contact
   for emergency account lock down and organization recovery.
#. **Kill sessions and credentials** on the affected account. On GitHub: change
   the password, sign out all sessions, revoke every personal access token,
   OAuth app, and SSH or GPG key, then re-enroll two-factor authentication.
   On PyPI: change the password, re-enroll two-factor authentication,
   and revoke API tokens.
#. **Audit PyPI project ownership.** OIDC publishing means there is usually no
   long-lived PyPI token to leak, but a compromised project owner can add a
   trusted publisher or upload directly. Review the project collaborators and
   trusted-publisher bindings, and remove anything unrecognized.
#. **Audit what the account could have done** from the GitHub audit log:
   pushes, tag creation, branch-protection or settings changes, new secrets or
   deploy keys, new collaborators, and workflow edits.
#. **Revert and re-verify.** Force-revert any unauthorized commits or tags, and
   re-verify recent releases against their ``Sigstore`` bundles. If a malicious
   release shipped, escalate to `Supply-chain or release compromise`_.
#. **Handle signing material.** ``Sigstore`` signing is key-less, so there is no
   static signing key to rotate. Any separate GPG key used for signed tags must
   be treated as compromised, then revoked and rotated.

CI or infrastructure compromise
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A compromise of GitHub Actions, repository settings, or branch protections.

#. **Freeze the release path.** Pause the ``pypi`` deployment environment and
   stop tag-triggered deploys until the compromise is scoped.
#. **Review branch protection and required reviews** on ``master`` and the
   ``x.y`` branches. These are enforced on GitHub and are not visible in the
   repository; confirm nothing was relaxed and restore the known-good
   configuration.
#. **Audit Actions secrets and environments** -- the repository and
   organization secrets, the ``pypi`` environment's protection rules and
   reviewers, and any new deploy keys. Confirm the workflow keeps empty
   top-level permissions and that ``id-token: write`` is scoped to the deploy
   job only.
#. **Diff recently changed workflows** with ``git log -- .github/workflows/``,
   especially the release workflow, the auto-merge workflow (which runs in the
   privileged ``pull_request_target`` context), and the CodeQL workflow. Look
   for added steps, changed action references, or new triggers.
#. **Check the committed Cython sources** as in `Supply-chain or release
   compromise`_ -- regenerate them and ``git diff``.
#. **Re-pin and rebuild.** If a third-party action was compromised, pin the
   affected actions by full commit hash, re-run CI from a clean known-good
   revision, and re-verify with ``Sigstore`` any release made during the suspected
   window.
#. **Escalate** to `Supply-chain or release compromise`_ if a release shipped
   through the compromised pipeline.

## 2. Official Technical Reference & Guides (aio-libs/aio-libs.github.io)

## File: content/pages/projects/aiohttp.md

Title: Aiohttp
Links: Documentation=https://docs.aiohttp.org/en/stable/
       Github=https://github.com/aio-libs/aiohttp/
       Sponsorship=https://opencollective.com/aio-libs/projects/aiohttp

An asynchronous HTTP Client/Server for Python's [asyncio](https://docs.python.org/3/library/asyncio.html), built on the [llhttp](https://llhttp.org/) C parser with Cython-accelerated hot paths.
Sharing client and server in a single, well-tested dependency โ€” with [WebSockets](https://docs.aiohttp.org/en/stable/client_quickstart.html#websockets) and [middlewares](https://docs.aiohttp.org/en/stable/web_advanced.html#middlewares) on both sides โ€” keeps things simple when you're building services that both consume and serve HTTP.

aiohttp powers [Home Assistant](https://www.home-assistant.io/), [Pulp](https://pulpproject.org/), [aiogram](https://aiogram.dev/), the [Molotov](https://molotov.readthedocs.io/) load tester, and [many others](https://docs.aiohttp.org/en/stable/built_with.html).

As a [client](https://docs.aiohttp.org/en/stable/client.html), aiohttp provides:

  - Connection pooling and a shared cookie jar across requests.
  - Streaming uploads and downloads.
  - [WebSocket](https://docs.aiohttp.org/en/stable/client_quickstart.html#websockets) support.
  - [Middlewares](https://docs.aiohttp.org/en/stable/client_advanced.html#client-middleware) to customise request/response processing.
  - [Tracing hooks](https://docs.aiohttp.org/en/stable/tracing_reference.html) for observability.

As a [web server/framework](https://docs.aiohttp.org/en/stable/web.html), aiohttp provides:

  - [WebSocket](https://docs.aiohttp.org/en/stable/web_quickstart.html#websockets) support.
  - [Middlewares](https://docs.aiohttp.org/en/stable/web_advanced.html#middlewares) to customise
request/response processing.
  - Optional [handler cancellation](https://docs.aiohttp.org/en/stable/web_advanced.html#web-handler-cancellation) when a client disconnects.
  - First-party [testing utilities](https://docs.aiohttp.org/en/stable/testing.html), including a [pytest-aiohttp](https://github.com/aio-libs/pytest-aiohttp) plugin.
  - An extensive collection of [first and third party libraries](https://docs.aiohttp.org/en/stable/third_party.html) extending the core functionality.

---

## File: content/pages/index.md

Title: About us
Status: hidden
save_as: index.html

We are a co-operative of developers who maintain open-source libraries for use with Python's [asyncio](https://docs.python.org/3/library/asyncio.html), including the popular [[aiohttp]] library.

## Sponsors

Aio-libs would not be possible without our amazing sponsors. Please consider [joining them](/sponsor_us/) to help fund our work.

{% sponsors gold %}
{% sponsors silver %}

## Aio-libs

This website exists to showcase the work that we do across aio-libs.

Under projects you can view the various libraries we maintain (more to be added soon).

Under news you can read the latest posts from us where we talk about some of the things we've been working on and give you some behind-the-scenes views of the work we've been doing.
If you want to follow along, there is an Atom feed for all news, as well as a feed for each category if you're only interested in particular areas of our work.

---

## File: content/pages/projects.md

Title: Projects
Template: projects
Status: hidden

The various libraries we maintain.

---

## File: content/pages/sponsorship.md

Title: Sponsor us

Aio-libs would not be possible without our amazing sponsors. Please consider joining them to help fund our work.
Our sponsorships are managed by [Open Collective](https://opencollective.com/aio-libs).

{% sponsors gold %}
{% sponsors silver %}
{% sponsors bronze %}

---

## File: content/news/aiohttp/2026-05-18_introducing-aiohttp.md

Title: Introducing aiohttp
Tags: aio-libs, aiohttp

Most of you reading this already know [[aiohttp]] as the longest-running asynchronous HTTP client for Python's [asyncio](https://docs.python.org/3/library/asyncio.html).
What is less widely known is that the same package also ships a full-featured web server and framework, available under `aiohttp.web`.

As a [client](https://docs.aiohttp.org/en/stable/client.html), aiohttp offers an ergonomic API for concurrent HTTP and WebSocket traffic, with [middlewares](https://docs.aiohttp.org/en/stable/client_advanced.html#client-middleware) to customise request and response processing and [tracing hooks](https://docs.aiohttp.org/en/stable/tracing_reference.html) for observability.

As a [web server and framework](https://docs.aiohttp.org/en/stable/web.html), `aiohttp.web` covers what you'd expect from a modern framework - WebSockets, middlewares, and an extensive ecosystem of [first and third party libraries](https://docs.aiohttp.org/en/stable/third_party.html) extending the core functionality.
Rarer is its optional [handler cancellation](https://docs.aiohttp.org/en/stable/web_advanced.html#web-handler-cancellation) when a client disconnects, sparing long-running handlers from finishing work no one is waiting for.
It has been quietly powering production services for years and is the backbone of Home Assistant.

Sharing client and server in a single, well-tested dependency keeps things simple when you're building services that both consume and serve HTTP, and aiohttp's focus on performance means you rarely have to reach for something else as traffic grows.

We'll be posting more about aiohttp here over time - new releases, deep dives, and behind-the-scenes notes from the maintainers.
If you'd like to follow along, subscribe to the [aiohttp tag feed](https://aio-libs.org/feeds/aiohttp.atom.xml).

---

## File: content/news/aio-libs/2026-02-18_new-site.md

Title: Welcome to the aio-libs site

We've just launched the new home page for aio-libs.

This is where we'll share updates and behind-the-scene looks at the work we've been doing across our projects.

You can subscribe to the [Atom feed](https://aio-libs.org/feeds/atom.xml) to get the latest updates as we post them.
The [aio-libs](https://aio-libs.org/feeds/aio-libs.atom.xml) tag will be used to discuss general updates around the
group and introduce new projects. More tags will be added later to cover other areas.

---

## File: content/news/aio-libs/2026-08-17_security-at-aio-libs.md

Title: How we handle security at aio-libs
Tags: aio-libs, aiohttp

[[aiohttp]] and the wider aio-libs family sit underneath a big slice of the Python ecosystem, which means our bugs become everyone else's bugs.
Security has always been part of the job, but the last year has seen a run of dedicated work on it - two external audits, a funded security program, and a lot of new processes.

## The day-to-day process

Everything starts with the reporting pipeline described in our organisation-wide [security policy](https://github.com/aio-libs/.github/blob/master/SECURITY.md).
We typically receive several reports per month and try to act on them promptly.
You can read how we handle these reports in our [incident response plan](https://docs.aiohttp.org/en/stable/contributing-admins.html#incident-response).

We typically issue CVEs for anything that could plausibly affect users, which means we may allocate more CVEs than some similar projects.
Additionally, aiohttp is a complex project and we must consider security relating to networking, HTTP parsing, general web framework and client code.
Most similar projects only deal with one of these areas.

On the supply-chain side, releases are published with PyPI trusted publishing (so there are no long-lived tokens to steal) and signed with [Sigstore](https://www.sigstore.dev/), CodeQL and Dependabot run on the repositories, and aiohttp's parsers have been fuzzed continuously by [OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/aiohttp) since 2022.

## A security audit, via NLnet

In 2025, [NLnet](https://nlnet.nl/) funded development work on aiohttp - [improving the type annotations](https://nlnet.nl/project/Aiohttp-typecheck/) - through the NGI Zero Commons Fund, backed by the European Commission's Next Generation Internet programme.
NGI Zero grants come with more than money though: projects also get access to [support services](https://nlnet.nl/NGI0/services/), including a professional security audit.

Ours was performed by [Radically Open Security](https://www.radicallyopensecurity.com/) - particular thanks to Thomas Rinsma - and its findings resulted in five CVEs: denial-of-service vectors like resource exhaustion via large payloads and an infinite loop when Python runs with assertions disabled, a brute-forceable path disclosure in static file handling, and parsing discrepancies around Unicode digits.
The fixes shipped in [aiohttp 3.13.3](https://github.com/aio-libs/aiohttp/releases/tag/v3.13.3) in January, [disclosed in a coordinated batch of eight advisories](https://www.openwall.com/lists/oss-security/2026/01/05/14) together with three issues from independent reporters.
Our thanks to NLnet for funding both the audit and the development time spent fixing the findings.

One of those reports is worth calling out explicitly: a high-severity decompression bomb ([CVE-2025-69223](https://github.com/aio-libs/aiohttp/security/advisories/GHSA-6mq8-rvhq-8wgg)), where a tiny compressed message inflates into a huge one when aiohttp decompresses it automatically.
It also wasn't ours to fix alone - the [brotli](https://github.com/google/brotli) library had no way to cap how much data a decompression call could produce - so the issue was disclosed to Google and we waited for them to provide a fix in brotli 1.2 before we could resolve it.
Due to architectural difficulties, the initial fix capped decompressed output at 32 MiB per call, which also blocked some legitimate highly-compressed payloads, so [aiohttp 3.14.0](https://github.com/aio-libs/aiohttp/releases/tag/v3.14.0) followed up with an overhaul of the decompression code that processes payloads incrementally in bounded chunks, safely handling any valid payload.
The final fix also reduced the amount of data in memory from 32 MiB to 256 KiB, matching most memory assumptions in the rest of the aiohttp code.
Coordinated disclosure sometimes has to run up the dependency chain as well as down it.

## The GitHub Secure Open Source Fund

In April we joined Session 4 of the [GitHub Secure Open Source Fund](https://github.com/open-source/github-secure-open-source-fund), alongside 49 other projects (including friends from the Python ecosystem).
The program pairs $10,000 of funding through GitHub Sponsors with a three-week security sprint - threat modeling, secure coding, vulnerability management and AI security, with a curriculum from GitHub Security Lab - and check-ins over the following year.

The funding is welcome, but the bigger value for us was momentum: a structured push, with security experts on hand, to clear a backlog of security process work.
While we found that we were already doing most of the recommendations covered in the program, we still found plenty of things to take our security to the next level.
So far, we have fixed up license files, written an incident response plan and a detailed threat model, plugged a gap in aiohttp's SBOM, and ran an AI-assisted audit over all our codebases.

The [threat model](https://docs.aiohttp.org/en/stable/threat_model.html) is the biggest piece: a STRIDE analysis of aiohttp covering nineteen subsystems, from the HTTP/1 parser through cookies and DNS resolution to the build and release supply chain.
For each one it records what we trust, what can go wrong, and which mitigations exist or are still wanted - including the mitigations that are the responsibility of applications built on aiohttp, which are explicitly marked as such.
We're still reviewing and publishing the remaining chapters, but expect to complete this over the next few months.
It's a living document: our incident-response process now ends with feeding each new advisory back into it.
This is really important in the age of AI-generated security reports; this is a tool that AI agents can use to verify if their report is actually valid and in scope, reducing the number of false positives getting reported to us.

The [incident response plan](https://docs.aiohttp.org/en/stable/contributing-admins.html#incident-response) writes down what used to be tribal knowledge: severity tiers, the advisory-to-release runbook, the disclosure and notification procedures, and - hopefully never needed - playbooks for a compromised release pipeline, maintainer account or CI.

The SBOM work fixes a blind spot: aiohttp vendors the [llhttp](https://github.com/nodejs/llhttp) parser as a git submodule, which GitHub's dependency graph can't see.
A [workflow](https://github.com/aio-libs/aiohttp/blob/master/.github/workflows/dependency-submission.yml) now registers the vendored llhttp version with the dependency graph on every push, so it appears in the exported SBOM and gets Dependabot alerts like any other dependency.

And the AI-assisted audit, run with GitHub Security Lab's [taskflow agent](https://github.com/GitHubSecurityLab/seclab-taskflows), found one more minor vulnerability in aiohttp - and, more usefully, several issues across other aio-libs projects that haven't seen the same level of scrutiny yet.

There's still more planned from the program: achieving [OpenSSF Baseline](https://baseline.openssf.org/) level 3, reviewing several new tools and configuration of existing ones, bringing the OSS-Fuzz integration into our repo, and generating runtime SBOMs to ship in the wheels.

## Patch the Planet

In June, aiohttp was one of the first projects taken through [Patch the Planet](https://openai.com/index/patch-the-planet/), an OpenAI initiative built with [Trail of Bits](https://blog.trailofbits.com/2026/06/22/introducing-patch-the-planet/) that points AI-assisted vulnerability research - validated by human security engineers - at critical open source projects, at no cost to the maintainers.

Trail of Bits privately reported a cluster of 10 issues (resulting in 8 CVEs) across our client and server code, including cookies that could regain a broader scope after a save and reload, digest credentials that would answer a challenge from the wrong origin, and resource limits that ran only after attacker-controlled buffering.
All fixes were written, reviewed and merged a couple of days later and shipped in [aiohttp 3.14.1](https://github.com/aio-libs/aiohttp/releases/tag/v3.14.1).

## Keeping it going

None of this makes aiohttp bug-free - every audit above found something, which is exactly why we keep inviting them.
Each round of fresh eyes turns into fixes, regression tests and threat model updates, and each program leaves the process a little stronger than it found it.
Our thanks to NLnet, Radically Open Security, GitHub, OpenAI, Trail of Bits, and every reporter credited in our advisories.

If you believe you've found a vulnerability in any aio-libs project, please report it privately by following our [security policy](https://github.com/aio-libs/.github/blob/master/SECURITY.md).
And if your business relies on this work, consider [sponsoring aio-libs](/sponsorship/) - almost everything above was done by volunteers.