Bug 1854496: update vendored aiohttp/requests and add new dependencies r=firefox-build-system-reviewers,mach-reviewers,ahochheiden

Update `aiohttp` to version 3.8.5 and `requests` to version 2.31.0,
and vendor their respective dependencies. Add all the new dependencies
to the various required site virtualenv requirements files.

Differential Revision: https://phabricator.services.mozilla.com/D188904
This commit is contained in:
Connor Sheehan 2023-09-25 14:22:11 +00:00
Родитель 6355de0665
Коммит fcfe38a1ef
263 изменённых файлов: 54838 добавлений и 18110 удалений

Просмотреть файл

@ -10,11 +10,14 @@ vendored:testing/web-platform/tests/tools/wptserve
vendored:third_party/python/Jinja2 vendored:third_party/python/Jinja2
vendored:third_party/python/PyYAML/lib/ vendored:third_party/python/PyYAML/lib/
vendored:third_party/python/aiohttp vendored:third_party/python/aiohttp
vendored:third_party/python/aiosignal
vendored:third_party/python/appdirs vendored:third_party/python/appdirs
vendored:third_party/python/arrow vendored:third_party/python/arrow
vendored:third_party/python/async_timeout vendored:third_party/python/async_timeout
vendored:third_party/python/asynctest
vendored:third_party/python/binaryornot vendored:third_party/python/binaryornot
vendored:third_party/python/chardet vendored:third_party/python/chardet
vendored:third_party/python/charset_normalizer
vendored:third_party/python/compare_locales vendored:third_party/python/compare_locales
vendored:third_party/python/cookiecutter vendored:third_party/python/cookiecutter
vendored:third_party/python/diskcache vendored:third_party/python/diskcache
@ -22,6 +25,7 @@ vendored:third_party/python/dlmanager
vendored:third_party/python/ecdsa vendored:third_party/python/ecdsa
vendored:third_party/python/fluent.migrate vendored:third_party/python/fluent.migrate
vendored:third_party/python/fluent.syntax vendored:third_party/python/fluent.syntax
vendored:third_party/python/frozenlist
vendored:third_party/python/giturlparse vendored:third_party/python/giturlparse
vendored:third_party/python/glean_parser vendored:third_party/python/glean_parser
vendored:third_party/python/gyp/pylib vendored:third_party/python/gyp/pylib

Просмотреть файл

@ -9,12 +9,15 @@ vendored:testing/web-platform/tests/tools/wptserve
vendored:third_party/python/MarkupSafe/src vendored:third_party/python/MarkupSafe/src
vendored:third_party/python/PyYAML/lib/ vendored:third_party/python/PyYAML/lib/
vendored:third_party/python/aiohttp vendored:third_party/python/aiohttp
vendored:third_party/python/aiosignal
vendored:third_party/python/appdirs vendored:third_party/python/appdirs
vendored:third_party/python/arrow vendored:third_party/python/arrow
vendored:third_party/python/async_timeout vendored:third_party/python/async_timeout
vendored:third_party/python/asynctest
vendored:third_party/python/binaryornot vendored:third_party/python/binaryornot
vendored:third_party/python/cbor2 vendored:third_party/python/cbor2
vendored:third_party/python/chardet vendored:third_party/python/chardet
vendored:third_party/python/charset_normalizer
vendored:third_party/python/compare_locales vendored:third_party/python/compare_locales
vendored:third_party/python/cookiecutter vendored:third_party/python/cookiecutter
vendored:third_party/python/cookies vendored:third_party/python/cookies
@ -25,6 +28,7 @@ vendored:third_party/python/ecdsa
vendored:third_party/python/esprima vendored:third_party/python/esprima
vendored:third_party/python/fluent.migrate vendored:third_party/python/fluent.migrate
vendored:third_party/python/fluent.syntax vendored:third_party/python/fluent.syntax
vendored:third_party/python/frozenlist
vendored:third_party/python/giturlparse vendored:third_party/python/giturlparse
vendored:third_party/python/glean_parser vendored:third_party/python/glean_parser
vendored:third_party/python/gyp/pylib vendored:third_party/python/gyp/pylib

Просмотреть файл

@ -49,14 +49,18 @@ vendored:third_party/python/Jinja2
vendored:third_party/python/MarkupSafe/src vendored:third_party/python/MarkupSafe/src
vendored:third_party/python/PyYAML/lib/ vendored:third_party/python/PyYAML/lib/
vendored:third_party/python/aiohttp vendored:third_party/python/aiohttp
vendored:third_party/python/aiosignal
vendored:third_party/python/appdirs vendored:third_party/python/appdirs
vendored:third_party/python/arrow vendored:third_party/python/arrow
vendored:third_party/python/async_timeout vendored:third_party/python/async_timeout
vendored:third_party/python/asynctest
vendored:third_party/python/binaryornot vendored:third_party/python/binaryornot
vendored:third_party/python/certifi vendored:third_party/python/certifi
vendored:third_party/python/chardet vendored:third_party/python/chardet
vendored:third_party/python/charset_normalizer
vendored:third_party/python/cookiecutter vendored:third_party/python/cookiecutter
vendored:third_party/python/fluent.syntax vendored:third_party/python/fluent.syntax
vendored:third_party/python/frozenlist
vendored:third_party/python/giturlparse vendored:third_party/python/giturlparse
vendored:third_party/python/jinja2_time vendored:third_party/python/jinja2_time
vendored:third_party/python/json-e vendored:third_party/python/json-e

Просмотреть файл

@ -1,14 +1,18 @@
vendored:third_party/python/PyYAML/lib/ vendored:third_party/python/PyYAML/lib/
vendored:third_party/python/aiohttp vendored:third_party/python/aiohttp
vendored:third_party/python/aiosignal
vendored:third_party/python/appdirs vendored:third_party/python/appdirs
vendored:third_party/python/arrow vendored:third_party/python/arrow
vendored:third_party/python/async_timeout vendored:third_party/python/async_timeout
vendored:third_party/python/asynctest
vendored:third_party/python/binaryornot vendored:third_party/python/binaryornot
vendored:third_party/python/chardet vendored:third_party/python/chardet
vendored:third_party/python/charset_normalizer
vendored:third_party/python/compare_locales vendored:third_party/python/compare_locales
vendored:third_party/python/cookiecutter vendored:third_party/python/cookiecutter
vendored:third_party/python/esprima vendored:third_party/python/esprima
vendored:third_party/python/fluent.syntax vendored:third_party/python/fluent.syntax
vendored:third_party/python/frozenlist
vendored:third_party/python/giturlparse vendored:third_party/python/giturlparse
vendored:third_party/python/jinja2_time vendored:third_party/python/jinja2_time
vendored:third_party/python/json-e vendored:third_party/python/json-e

Просмотреть файл

@ -61,6 +61,7 @@ vendored:third_party/python/attrs
vendored:third_party/python/blessed vendored:third_party/python/blessed
vendored:third_party/python/certifi vendored:third_party/python/certifi
vendored:third_party/python/chardet vendored:third_party/python/chardet
vendored:third_party/python/charset_normalizer
vendored:third_party/python/click vendored:third_party/python/click
vendored:third_party/python/colorama vendored:third_party/python/colorama
vendored:third_party/python/distro vendored:third_party/python/distro

Просмотреть файл

@ -10,12 +10,15 @@ vendored:testing/web-platform/tests/tools/wptserve
vendored:third_party/python/MarkupSafe/src vendored:third_party/python/MarkupSafe/src
vendored:third_party/python/PyYAML/lib/ vendored:third_party/python/PyYAML/lib/
vendored:third_party/python/aiohttp vendored:third_party/python/aiohttp
vendored:third_party/python/aiosignal
vendored:third_party/python/appdirs vendored:third_party/python/appdirs
vendored:third_party/python/arrow vendored:third_party/python/arrow
vendored:third_party/python/async_timeout vendored:third_party/python/async_timeout
vendored:third_party/python/asynctest
vendored:third_party/python/binaryornot vendored:third_party/python/binaryornot
vendored:third_party/python/cbor2 vendored:third_party/python/cbor2
vendored:third_party/python/chardet vendored:third_party/python/chardet
vendored:third_party/python/charset_normalizer
vendored:third_party/python/compare_locales vendored:third_party/python/compare_locales
vendored:third_party/python/cookiecutter vendored:third_party/python/cookiecutter
vendored:third_party/python/cookies vendored:third_party/python/cookies
@ -26,6 +29,7 @@ vendored:third_party/python/ecdsa
vendored:third_party/python/esprima vendored:third_party/python/esprima
vendored:third_party/python/fluent.migrate vendored:third_party/python/fluent.migrate
vendored:third_party/python/fluent.syntax vendored:third_party/python/fluent.syntax
vendored:third_party/python/frozenlist
vendored:third_party/python/giturlparse vendored:third_party/python/giturlparse
vendored:third_party/python/glean_parser vendored:third_party/python/glean_parser
vendored:third_party/python/gyp/pylib vendored:third_party/python/gyp/pylib

394
third_party/python/aiohttp/CHANGES.rst поставляемый
Просмотреть файл

@ -1,7 +1,3 @@
=========
Changelog
=========
.. ..
You should *NOT* be adding new change log entries to this file, this You should *NOT* be adding new change log entries to this file, this
file is managed by towncrier. You *may* edit previous change logs to file is managed by towncrier. You *may* edit previous change logs to
@ -14,6 +10,396 @@ Changelog
.. towncrier release notes start .. towncrier release notes start
3.8.5 (2023-07-19)
==================
Security bugfixes
-----------------
- Upgraded the vendored copy of llhttp_ to v8.1.1 -- by :user:`webknjaz`
and :user:`Dreamsorcerer`.
Thanks to :user:`sethmlarson` for reporting this and providing us with
comprehensive reproducer, workarounds and fixing details! For more
information, see
https://github.com/aio-libs/aiohttp/security/advisories/GHSA-45c4-8wx5-qw6w.
.. _llhttp: https://llhttp.org
`#7346 <https://github.com/aio-libs/aiohttp/issues/7346>`_
Features
--------
- Added information to C parser exceptions to show which character caused the error. -- by :user:`Dreamsorcerer`
`#7366 <https://github.com/aio-libs/aiohttp/issues/7366>`_
Bugfixes
--------
- Fixed a transport is :data:`None` error -- by :user:`Dreamsorcerer`.
`#3355 <https://github.com/aio-libs/aiohttp/issues/3355>`_
----
3.8.4 (2023-02-12)
==================
Bugfixes
--------
- Fixed incorrectly overwriting cookies with the same name and domain, but different path.
`#6638 <https://github.com/aio-libs/aiohttp/issues/6638>`_
- Fixed ``ConnectionResetError`` not being raised after client disconnection in SSL environments.
`#7180 <https://github.com/aio-libs/aiohttp/issues/7180>`_
----
3.8.3 (2022-09-21)
==================
.. attention::
This is the last :doc:`aiohttp <index>` release tested under
Python 3.6. The 3.9 stream is dropping it from the CI and the
distribution package metadata.
Bugfixes
--------
- Increased the upper boundary of the :doc:`multidict:index` dependency
to allow for the version 6 -- by :user:`hugovk`.
It used to be limited below version 7 in :doc:`aiohttp <index>` v3.8.1 but
was lowered in v3.8.2 via :pr:`6550` and never brought back, causing
problems with dependency pins when upgrading. :doc:`aiohttp <index>` v3.8.3
fixes that by recovering the original boundary of ``< 7``.
`#6950 <https://github.com/aio-libs/aiohttp/issues/6950>`_
----
3.8.2 (2022-09-20, subsequently yanked on 2022-09-21)
=====================================================
.. note::
This release has some compatibility fixes for Python 3.11 but it may
still have some quirks. Some tests are still flaky in the CI.
.. caution::
This release has been yanked from PyPI. Modern pip will not pick it
up automatically. The reason is that is has ``multidict < 6`` set in
the distribution package metadata (see :pr:`6950`). Please, use
``aiohttp ~= 3.8.3, != 3.8.1`` instead, if you can.
Bugfixes
--------
- Added support for registering :rfc:`OPTIONS <9110#OPTIONS>`
HTTP method handlers via :py:class:`~aiohttp.web.RouteTableDef`.
`#4663 <https://github.com/aio-libs/aiohttp/issues/4663>`_
- Started supporting :rfc:`authority-form <9112#authority-form>` and
:rfc:`absolute-form <9112#absolute-form>` URLs on the server-side.
`#6227 <https://github.com/aio-libs/aiohttp/issues/6227>`_
- Fixed Python 3.11 incompatibilities by using Cython 0.29.25.
`#6396 <https://github.com/aio-libs/aiohttp/issues/6396>`_
- Extended the ``sock`` argument typing declaration of the
:py:func:`~aiohttp.web.run_app` function as optionally
accepting iterables.
`#6401 <https://github.com/aio-libs/aiohttp/issues/6401>`_
- Fixed a regression where :py:exc:`~asyncio.CancelledError`
occurs on client disconnection.
`#6719 <https://github.com/aio-libs/aiohttp/issues/6719>`_
- Started exporting :py:class:`~aiohttp.web.PrefixedSubAppResource`
under :py:mod:`aiohttp.web` -- by :user:`Dreamsorcerer`.
This fixes a regression introduced by :pr:`3469`.
`#6889 <https://github.com/aio-libs/aiohttp/issues/6889>`_
- Dropped the :class:`object` type possibility from
the :py:attr:`aiohttp.ClientSession.timeout`
property return type declaration.
`#6917 <https://github.com/aio-libs/aiohttp/issues/6917>`_,
`#6923 <https://github.com/aio-libs/aiohttp/issues/6923>`_
Improved Documentation
----------------------
- Added clarification on configuring the app object with
settings such as a database connection.
`#4137 <https://github.com/aio-libs/aiohttp/issues/4137>`_
- Extended the ``sock`` argument typing declaration of the
:py:func:`~aiohttp.web.run_app` function as optionally
accepting iterables.
`#6401 <https://github.com/aio-libs/aiohttp/issues/6401>`_
- Dropped the :class:`object` type possibility from
the :py:attr:`aiohttp.ClientSession.timeout`
property return type declaration.
`#6917 <https://github.com/aio-libs/aiohttp/issues/6917>`_,
`#6923 <https://github.com/aio-libs/aiohttp/issues/6923>`_
Deprecations and Removals
-------------------------
- Dropped Python 3.5 support, :doc:`aiohttp <index>` only works
under Python 3.6 and higher from now on.
`#4046 <https://github.com/aio-libs/aiohttp/issues/4046>`_
Misc
----
- Removed a deprecated usage of :py:func:`pytest.warns(None)
<pytest.warns>` in tests.
`#6663 <https://github.com/aio-libs/aiohttp/issues/6663>`_
- `#6369 <https://github.com/aio-libs/aiohttp/issues/6369>`_, `#6399 <https://github.com/aio-libs/aiohttp/issues/6399>`_, `#6550 <https://github.com/aio-libs/aiohttp/issues/6550>`_, `#6708 <https://github.com/aio-libs/aiohttp/issues/6708>`_, `#6757 <https://github.com/aio-libs/aiohttp/issues/6757>`_, `#6857 <https://github.com/aio-libs/aiohttp/issues/6857>`_, `#6872 <https://github.com/aio-libs/aiohttp/issues/6872>`_.
----
3.8.1 (2021-11-14)
==================
Bugfixes
--------
- Fix the error in handling the return value of `getaddrinfo`.
`getaddrinfo` will return an `(int, bytes)` tuple, if CPython could not handle the address family.
It will cause a index out of range error in aiohttp. For example, if user compile CPython with
`--disable-ipv6` option but his system enable the ipv6.
`#5901 <https://github.com/aio-libs/aiohttp/issues/5901>`_
- Do not install "examples" as a top-level package.
`#6189 <https://github.com/aio-libs/aiohttp/issues/6189>`_
- Restored ability to connect IPv6-only host.
`#6195 <https://github.com/aio-libs/aiohttp/issues/6195>`_
- Remove ``Signal`` from ``__all__``, replace ``aiohttp.Signal`` with ``aiosignal.Signal`` in docs
`#6201 <https://github.com/aio-libs/aiohttp/issues/6201>`_
- Made chunked encoding HTTP header check stricter.
`#6305 <https://github.com/aio-libs/aiohttp/issues/6305>`_
Improved Documentation
----------------------
- update quick starter demo codes.
`#6240 <https://github.com/aio-libs/aiohttp/issues/6240>`_
- Added an explanation of how tiny timeouts affect performance to the client reference document.
`#6274 <https://github.com/aio-libs/aiohttp/issues/6274>`_
- Add flake8-docstrings to flake8 configuration, enable subset of checks.
`#6276 <https://github.com/aio-libs/aiohttp/issues/6276>`_
- Added information on running complex applications with additional tasks/processes -- :user:`Dreamsorcerer`.
`#6278 <https://github.com/aio-libs/aiohttp/issues/6278>`_
Misc
----
- `#6205 <https://github.com/aio-libs/aiohttp/issues/6205>`_
----
3.8.0 (2021-10-31)
==================
Features
--------
- Added a ``GunicornWebWorker`` feature for extending the aiohttp server configuration by allowing the 'wsgi' coroutine to return ``web.AppRunner`` object.
`#2988 <https://github.com/aio-libs/aiohttp/issues/2988>`_
- Switch from ``http-parser`` to ``llhttp``
`#3561 <https://github.com/aio-libs/aiohttp/issues/3561>`_
- Use Brotli instead of brotlipy
`#3803 <https://github.com/aio-libs/aiohttp/issues/3803>`_
- Disable implicit switch-back to pure python mode. The build fails loudly if aiohttp
cannot be compiled with C Accelerators. Use AIOHTTP_NO_EXTENSIONS=1 to explicitly
disable C Extensions complication and switch to Pure-Python mode. Note that Pure-Python
mode is significantly slower than compiled one.
`#3828 <https://github.com/aio-libs/aiohttp/issues/3828>`_
- Make access log use local time with timezone
`#3853 <https://github.com/aio-libs/aiohttp/issues/3853>`_
- Implemented ``readuntil`` in ``StreamResponse``
`#4054 <https://github.com/aio-libs/aiohttp/issues/4054>`_
- FileResponse now supports ETag.
`#4594 <https://github.com/aio-libs/aiohttp/issues/4594>`_
- Add a request handler type alias ``aiohttp.typedefs.Handler``.
`#4686 <https://github.com/aio-libs/aiohttp/issues/4686>`_
- ``AioHTTPTestCase`` is more async friendly now.
For people who use unittest and are used to use :py:exc:`~unittest.TestCase`
it will be easier to write new test cases like the sync version of the :py:exc:`~unittest.TestCase` class,
without using the decorator `@unittest_run_loop`, just `async def test_*`.
The only difference is that for the people using python3.7 and below a new dependency is needed, it is ``asynctestcase``.
`#4700 <https://github.com/aio-libs/aiohttp/issues/4700>`_
- Add validation of HTTP header keys and values to prevent header injection.
`#4818 <https://github.com/aio-libs/aiohttp/issues/4818>`_
- Add predicate to ``AbstractCookieJar.clear``.
Add ``AbstractCookieJar.clear_domain`` to clean all domain and subdomains cookies only.
`#4942 <https://github.com/aio-libs/aiohttp/issues/4942>`_
- Add keepalive_timeout parameter to web.run_app.
`#5094 <https://github.com/aio-libs/aiohttp/issues/5094>`_
- Tracing for client sent headers
`#5105 <https://github.com/aio-libs/aiohttp/issues/5105>`_
- Make type hints for http parser stricter
`#5267 <https://github.com/aio-libs/aiohttp/issues/5267>`_
- Add final declarations for constants.
`#5275 <https://github.com/aio-libs/aiohttp/issues/5275>`_
- Switch to external frozenlist and aiosignal libraries.
`#5293 <https://github.com/aio-libs/aiohttp/issues/5293>`_
- Don't send secure cookies by insecure transports.
By default, the transport is secure if https or wss scheme is used.
Use `CookieJar(treat_as_secure_origin="http://127.0.0.1")` to override the default security checker.
`#5571 <https://github.com/aio-libs/aiohttp/issues/5571>`_
- Always create a new event loop in ``aiohttp.web.run_app()``.
This adds better compatibility with ``asyncio.run()`` or if trying to run multiple apps in sequence.
`#5572 <https://github.com/aio-libs/aiohttp/issues/5572>`_
- Add ``aiohttp.pytest_plugin.AiohttpClient`` for static typing of pytest plugin.
`#5585 <https://github.com/aio-libs/aiohttp/issues/5585>`_
- Added a ``socket_factory`` argument to ``BaseTestServer``.
`#5844 <https://github.com/aio-libs/aiohttp/issues/5844>`_
- Add compression strategy parameter to enable_compression method.
`#5909 <https://github.com/aio-libs/aiohttp/issues/5909>`_
- Added support for Python 3.10 to Github Actions CI/CD workflows and fix the related deprecation warnings -- :user:`Hanaasagi`.
`#5927 <https://github.com/aio-libs/aiohttp/issues/5927>`_
- Switched ``chardet`` to ``charset-normalizer`` for guessing the HTTP payload body encoding -- :user:`Ousret`.
`#5930 <https://github.com/aio-libs/aiohttp/issues/5930>`_
- Added optional auto_decompress argument for HttpRequestParser
`#5957 <https://github.com/aio-libs/aiohttp/issues/5957>`_
- Added support for HTTPS proxies to the extent CPython's
:py:mod:`asyncio` supports it -- by :user:`bmbouter`,
:user:`jborean93` and :user:`webknjaz`.
`#5992 <https://github.com/aio-libs/aiohttp/issues/5992>`_
- Added ``base_url`` parameter to the initializer of :class:`~aiohttp.ClientSession`.
`#6013 <https://github.com/aio-libs/aiohttp/issues/6013>`_
- Add Trove classifier and create binary wheels for 3.10. -- :user:`hugovk`.
`#6079 <https://github.com/aio-libs/aiohttp/issues/6079>`_
- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes — :user:`asvetlov`.
`#6139 <https://github.com/aio-libs/aiohttp/issues/6139>`_
- Started shipping platform-specific arm64 wheels for Apple Silicon — :user:`asvetlov`.
`#6139 <https://github.com/aio-libs/aiohttp/issues/6139>`_
Bugfixes
--------
- Modify _drain_helper() to handle concurrent `await resp.write(...)` or `ws.send_json(...)` calls without race-condition.
`#2934 <https://github.com/aio-libs/aiohttp/issues/2934>`_
- Started using `MultiLoopChildWatcher` when it's available under POSIX while setting up the test I/O loop.
`#3450 <https://github.com/aio-libs/aiohttp/issues/3450>`_
- Only encode content-disposition filename parameter using percent-encoding.
Other parameters are encoded to quoted-string or RFC2231 extended parameter
value.
`#4012 <https://github.com/aio-libs/aiohttp/issues/4012>`_
- Fixed HTTP client requests to honor ``no_proxy`` environment variables.
`#4431 <https://github.com/aio-libs/aiohttp/issues/4431>`_
- Fix supporting WebSockets proxies configured via environment variables.
`#4648 <https://github.com/aio-libs/aiohttp/issues/4648>`_
- Change return type on URLDispatcher to UrlMappingMatchInfo to improve type annotations.
`#4748 <https://github.com/aio-libs/aiohttp/issues/4748>`_
- Ensure a cleanup context is cleaned up even when an exception occurs during startup.
`#4799 <https://github.com/aio-libs/aiohttp/issues/4799>`_
- Added a new exception type for Unix socket client errors which provides a more useful error message.
`#4984 <https://github.com/aio-libs/aiohttp/issues/4984>`_
- Remove Transfer-Encoding and Content-Type headers for 204 in StreamResponse
`#5106 <https://github.com/aio-libs/aiohttp/issues/5106>`_
- Only depend on typing_extensions for Python <3.8
`#5107 <https://github.com/aio-libs/aiohttp/issues/5107>`_
- Add ABNORMAL_CLOSURE and BAD_GATEWAY to WSCloseCode
`#5192 <https://github.com/aio-libs/aiohttp/issues/5192>`_
- Fix cookies disappearing from HTTPExceptions.
`#5233 <https://github.com/aio-libs/aiohttp/issues/5233>`_
- StaticResource prefixes no longer match URLs with a non-folder prefix. For example ``routes.static('/foo', '/foo')`` no longer matches the URL ``/foobar``. Previously, this would attempt to load the file ``/foo/ar``.
`#5250 <https://github.com/aio-libs/aiohttp/issues/5250>`_
- Acquire the connection before running traces to prevent race condition.
`#5259 <https://github.com/aio-libs/aiohttp/issues/5259>`_
- Add missing slots to ```_RequestContextManager`` and ``_WSRequestContextManager``
`#5329 <https://github.com/aio-libs/aiohttp/issues/5329>`_
- Ensure sending a zero byte file does not throw an exception (round 2)
`#5380 <https://github.com/aio-libs/aiohttp/issues/5380>`_
- Set "text/plain" when data is an empty string in client requests.
`#5392 <https://github.com/aio-libs/aiohttp/issues/5392>`_
- Stop automatically releasing the ``ClientResponse`` object on calls to the ``ok`` property for the failed requests.
`#5403 <https://github.com/aio-libs/aiohttp/issues/5403>`_
- Include query parameters from `params` keyword argument in tracing `URL`.
`#5432 <https://github.com/aio-libs/aiohttp/issues/5432>`_
- Fix annotations
`#5466 <https://github.com/aio-libs/aiohttp/issues/5466>`_
- Fixed the multipart POST requests processing to always release file
descriptors for the ``tempfile.Temporaryfile``-created
``_io.BufferedRandom`` instances of files sent within multipart request
bodies via HTTP POST requests -- by :user:`webknjaz`.
`#5494 <https://github.com/aio-libs/aiohttp/issues/5494>`_
- Fix 0 being incorrectly treated as an immediate timeout.
`#5527 <https://github.com/aio-libs/aiohttp/issues/5527>`_
- Fixes failing tests when an environment variable <scheme>_proxy is set.
`#5554 <https://github.com/aio-libs/aiohttp/issues/5554>`_
- Replace deprecated app handler design in ``tests/autobahn/server.py`` with call to ``web.run_app``; replace deprecated ``aiohttp.ws_connect`` calls in ``tests/autobahn/client.py`` with ``aiohttp.ClienSession.ws_connect``.
`#5606 <https://github.com/aio-libs/aiohttp/issues/5606>`_
- Fixed test for ``HTTPUnauthorized`` that access the ``text`` argument. This is not used in any part of the code, so it's removed now.
`#5657 <https://github.com/aio-libs/aiohttp/issues/5657>`_
- Remove incorrect default from docs
`#5727 <https://github.com/aio-libs/aiohttp/issues/5727>`_
- Remove external test dependency to http://httpbin.org
`#5840 <https://github.com/aio-libs/aiohttp/issues/5840>`_
- Don't cancel current task when entering a cancelled timer.
`#5853 <https://github.com/aio-libs/aiohttp/issues/5853>`_
- Added ``params`` keyword argument to ``ClientSession.ws_connect``. -- :user:`hoh`.
`#5868 <https://github.com/aio-libs/aiohttp/issues/5868>`_
- Uses :py:class:`~asyncio.ThreadedChildWatcher` under POSIX to allow setting up test loop in non-main thread.
`#5877 <https://github.com/aio-libs/aiohttp/issues/5877>`_
- Fix the error in handling the return value of `getaddrinfo`.
`getaddrinfo` will return an `(int, bytes)` tuple, if CPython could not handle the address family.
It will cause a index out of range error in aiohttp. For example, if user compile CPython with
`--disable-ipv6` option but his system enable the ipv6.
`#5901 <https://github.com/aio-libs/aiohttp/issues/5901>`_
- Removed the deprecated ``loop`` argument from the ``asyncio.sleep``/``gather`` calls
`#5905 <https://github.com/aio-libs/aiohttp/issues/5905>`_
- Return ``None`` from ``request.if_modified_since``, ``request.if_unmodified_since``, ``request.if_range`` and ``response.last_modified`` when corresponding http date headers are invalid.
`#5925 <https://github.com/aio-libs/aiohttp/issues/5925>`_
- Fix resetting `SIGCHLD` signals in Gunicorn aiohttp Worker to fix `subprocesses` that capture output having an incorrect `returncode`.
`#6130 <https://github.com/aio-libs/aiohttp/issues/6130>`_
- Raise ``400: Content-Length can't be present with Transfer-Encoding`` if both ``Content-Length`` and ``Transfer-Encoding`` are sent by peer by both C and Python implementations
`#6182 <https://github.com/aio-libs/aiohttp/issues/6182>`_
Improved Documentation
----------------------
- Refactored OpenAPI/Swagger aiohttp addons, added ``aio-openapi``
`#5326 <https://github.com/aio-libs/aiohttp/issues/5326>`_
- Fixed docs on request cookies type, so it matches what is actually used in the code (a
read-only dictionary-like object).
`#5725 <https://github.com/aio-libs/aiohttp/issues/5725>`_
- Documented that the HTTP client ``Authorization`` header is removed
on redirects to a different host or protocol.
`#5850 <https://github.com/aio-libs/aiohttp/issues/5850>`_
Misc
----
- `#3927 <https://github.com/aio-libs/aiohttp/issues/3927>`_, `#4247 <https://github.com/aio-libs/aiohttp/issues/4247>`_, `#4247 <https://github.com/aio-libs/aiohttp/issues/4247>`_, `#5389 <https://github.com/aio-libs/aiohttp/issues/5389>`_, `#5457 <https://github.com/aio-libs/aiohttp/issues/5457>`_, `#5486 <https://github.com/aio-libs/aiohttp/issues/5486>`_, `#5494 <https://github.com/aio-libs/aiohttp/issues/5494>`_, `#5515 <https://github.com/aio-libs/aiohttp/issues/5515>`_, `#5625 <https://github.com/aio-libs/aiohttp/issues/5625>`_, `#5635 <https://github.com/aio-libs/aiohttp/issues/5635>`_, `#5648 <https://github.com/aio-libs/aiohttp/issues/5648>`_, `#5657 <https://github.com/aio-libs/aiohttp/issues/5657>`_, `#5890 <https://github.com/aio-libs/aiohttp/issues/5890>`_, `#5914 <https://github.com/aio-libs/aiohttp/issues/5914>`_, `#5932 <https://github.com/aio-libs/aiohttp/issues/5932>`_, `#6002 <https://github.com/aio-libs/aiohttp/issues/6002>`_, `#6045 <https://github.com/aio-libs/aiohttp/issues/6045>`_, `#6131 <https://github.com/aio-libs/aiohttp/issues/6131>`_, `#6156 <https://github.com/aio-libs/aiohttp/issues/6156>`_, `#6165 <https://github.com/aio-libs/aiohttp/issues/6165>`_, `#6166 <https://github.com/aio-libs/aiohttp/issues/6166>`_
----
3.7.4.post0 (2021-03-06) 3.7.4.post0 (2021-03-06)
======================== ========================

30
third_party/python/aiohttp/CONTRIBUTORS.txt поставляемый
Просмотреть файл

@ -3,9 +3,11 @@
A. Jesse Jiryu Davis A. Jesse Jiryu Davis
Adam Bannister Adam Bannister
Adam Cooper Adam Cooper
Adam Horacek
Adam Mills Adam Mills
Adrian Krupa Adrian Krupa
Adrián Chaves Adrián Chaves
Ahmed Tahri
Alan Tse Alan Tse
Alec Hanefeld Alec Hanefeld
Alejandro Gómez Alejandro Gómez
@ -30,6 +32,7 @@ Alexey Stepanov
Amin Etesamian Amin Etesamian
Amit Tulshyan Amit Tulshyan
Amy Boyle Amy Boyle
Anas El Amraoui
Anders Melchiorsen Anders Melchiorsen
Andrei Ursulenko Andrei Ursulenko
Andrej Antonov Andrej Antonov
@ -38,21 +41,27 @@ Andrew Lytvyn
Andrew Svetlov Andrew Svetlov
Andrew Zhou Andrew Zhou
Andrii Soldatenko Andrii Soldatenko
Anes Abismail
Antoine Pietri Antoine Pietri
Anton Kasyanov Anton Kasyanov
Anton Zhdan-Pushkin Anton Zhdan-Pushkin
Arseny Timoniq Arseny Timoniq
Artem Yushkovskiy Artem Yushkovskiy
Arthur Darcet Arthur Darcet
Austin Scola
Ben Bader Ben Bader
Ben Greiner
Ben Timby Ben Timby
Benedikt Reinartz Benedikt Reinartz
Bob Haddleton
Boris Feld Boris Feld
Boyi Chen Boyi Chen
Brett Cannon Brett Cannon
Brian Bouterse
Brian C. Lane Brian C. Lane
Brian Muller Brian Muller
Bruce Merry Bruce Merry
Bruno Souza Cabral
Bryan Kok Bryan Kok
Bryce Drennan Bryce Drennan
Carl George Carl George
@ -67,6 +76,7 @@ Claudiu Popa
Colin Dunklau Colin Dunklau
Cong Xu Cong Xu
Damien Nadé Damien Nadé
Dan King
Dan Xu Dan Xu
Daniel García Daniel García
Daniel Grossmann-Kavanagh Daniel Grossmann-Kavanagh
@ -76,6 +86,7 @@ David Bibb
David Michael Brown David Michael Brown
Denilson Amorim Denilson Amorim
Denis Matiychuk Denis Matiychuk
Denis Moshensky
Dennis Kliban Dennis Kliban
Dima Veselov Dima Veselov
Dimitar Dimitrov Dimitar Dimitrov
@ -106,6 +117,7 @@ Felix Yan
Fernanda Guimarães Fernanda Guimarães
FichteFoll FichteFoll
Florian Scheffler Florian Scheffler
Franek Magiera
Frederik Gladhorn Frederik Gladhorn
Frederik Peter Aalund Frederik Peter Aalund
Gabriel Tremblay Gabriel Tremblay
@ -119,10 +131,13 @@ Gustavo Carneiro
Günther Jena Günther Jena
Hans Adema Hans Adema
Harmon Y. Harmon Y.
Harry Liu
Hiroshi Ogawa
Hrishikesh Paranjape Hrishikesh Paranjape
Hu Bo Hu Bo
Hugh Young Hugh Young
Hugo Herter Hugo Herter
Hugo van Kemenade
Hynek Schlawack Hynek Schlawack
Igor Alexandrov Igor Alexandrov
Igor Davydenko Igor Davydenko
@ -137,6 +152,7 @@ Jaesung Lee
Jake Davis Jake Davis
Jakob Ackermann Jakob Ackermann
Jakub Wilk Jakub Wilk
Jan Buchar
Jashandeep Sohi Jashandeep Sohi
Jens Steinhauser Jens Steinhauser
Jeonghun Lee Jeonghun Lee
@ -152,6 +168,7 @@ Jonas Obrist
Jonathan Wright Jonathan Wright
Jonny Tan Jonny Tan
Joongi Kim Joongi Kim
Jordan Borean
Josep Cugat Josep Cugat
Josh Junon Josh Junon
Joshu Coats Joshu Coats
@ -186,6 +203,8 @@ Manuel Miranda
Marat Sharafutdinov Marat Sharafutdinov
Marco Paolini Marco Paolini
Mariano Anaya Mariano Anaya
Mariusz Masztalerczuk
Marko Kohtala
Martijn Pieters Martijn Pieters
Martin Melka Martin Melka
Martin Richard Martin Richard
@ -193,6 +212,7 @@ Mathias Fröjdman
Mathieu Dugré Mathieu Dugré
Matthieu Hauglustaine Matthieu Hauglustaine
Matthieu Rigal Matthieu Rigal
Meet Mangukiya
Michael Ihnatenko Michael Ihnatenko
Michał Górny Michał Górny
Mikhail Burshteyn Mikhail Burshteyn
@ -208,6 +228,8 @@ Navid Sheikhol
Nicolas Braem Nicolas Braem
Nikolay Kim Nikolay Kim
Nikolay Novik Nikolay Novik
Nikolay Tiunov
Nándor Mátravölgyi
Oisin Aylward Oisin Aylward
Olaf Conradi Olaf Conradi
Pahaz Blinov Pahaz Blinov
@ -219,11 +241,14 @@ Paulius Šileikis
Paulus Schoutsen Paulus Schoutsen
Pavel Kamaev Pavel Kamaev
Pavel Polyakov Pavel Polyakov
Pavel Sapezhko
Pavol Vargovčík
Pawel Kowalski Pawel Kowalski
Pawel Miech Pawel Miech
Pepe Osca Pepe Osca
Philipp A. Philipp A.
Pieter van Beek Pieter van Beek
Qiao Han
Rafael Viotti Rafael Viotti
Raphael Bialon Raphael Bialon
Raúl Cumplido Raúl Cumplido
@ -250,6 +275,7 @@ Stanislav Prokop
Stefan Tjarks Stefan Tjarks
Stepan Pletnev Stepan Pletnev
Stephan Jaensch Stephan Jaensch
Stephen Cirelli
Stephen Granade Stephen Granade
Steven Seguin Steven Seguin
Sunghyun Hwang Sunghyun Hwang
@ -292,6 +318,7 @@ Vladyslav Bondar
W. Trevor King W. Trevor King
Wei Lin Wei Lin
Weiwei Wang Weiwei Wang
Will Fatherley
Will McGugan Will McGugan
Willem de Groot Willem de Groot
William Grzybowski William Grzybowski
@ -305,8 +332,11 @@ Yegor Roganov
Yifei Kong Yifei Kong
Young-Ho Cha Young-Ho Cha
Yuriy Shatrov Yuriy Shatrov
Yury Pliner
Yury Selivanov Yury Selivanov
Yusuke Tsutsumi Yusuke Tsutsumi
Yuval Ofir
Zeal Wierslee
Zlatan Sičanica Zlatan Sičanica
Марк Коренберг Марк Коренберг
Семён Марьясин Семён Марьясин

190
third_party/python/aiohttp/LICENSE.txt поставляемый
Просмотреть файл

@ -1,192 +1,4 @@
Apache License Copyright aio-libs contributors.
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2013-2020 aiohttp maintainers
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

68
third_party/python/aiohttp/Makefile поставляемый
Просмотреть файл

@ -7,9 +7,10 @@ CYS := $(wildcard aiohttp/*.pyx) $(wildcard aiohttp/*.pyi) $(wildcard aiohttp/*
PYXS := $(wildcard aiohttp/*.pyx) PYXS := $(wildcard aiohttp/*.pyx)
CS := $(wildcard aiohttp/*.c) CS := $(wildcard aiohttp/*.c)
PYS := $(wildcard aiohttp/*.py) PYS := $(wildcard aiohttp/*.py)
REQS := $(wildcard requirements/*.txt) IN := doc-spelling lint cython dev
ALLS := $(sort $(CYS) $(CS) $(PYS) $(REQS)) ALLS := $(sort $(CYS) $(CS) $(PYS) $(REQS))
.PHONY: all .PHONY: all
all: test all: test
@ -45,9 +46,11 @@ endif
# Enumerate intermediate files to don't remove them automatically. # Enumerate intermediate files to don't remove them automatically.
.SECONDARY: $(call to-hash,$(ALLS)) .SECONDARY: $(call to-hash,$(ALLS))
.update-pip:
@python -m pip install --upgrade pip
.install-cython: $(call to-hash,requirements/cython.txt) .install-cython: .update-pip $(call to-hash,requirements/cython.txt)
pip install -r requirements/cython.txt @python -m pip install -r requirements/cython.txt -c requirements/constraints.txt
@touch .install-cython @touch .install-cython
aiohttp/_find_header.c: $(call to-hash,aiohttp/hdrs.py ./tools/gen.py) aiohttp/_find_header.c: $(call to-hash,aiohttp/hdrs.py ./tools/gen.py)
@ -57,12 +60,21 @@ aiohttp/_find_header.c: $(call to-hash,aiohttp/hdrs.py ./tools/gen.py)
aiohttp/%.c: aiohttp/%.pyx $(call to-hash,$(CYS)) aiohttp/_find_header.c aiohttp/%.c: aiohttp/%.pyx $(call to-hash,$(CYS)) aiohttp/_find_header.c
cython -3 -o $@ $< -I aiohttp cython -3 -o $@ $< -I aiohttp
vendor/llhttp/node_modules: vendor/llhttp/package.json
cd vendor/llhttp; npm install
.llhttp-gen: vendor/llhttp/node_modules
$(MAKE) -C vendor/llhttp generate
@touch .llhttp-gen
.PHONY: generate-llhttp
generate-llhttp: .llhttp-gen
.PHONY: cythonize .PHONY: cythonize
cythonize: .install-cython $(PYXS:.pyx=.c) cythonize: .install-cython $(PYXS:.pyx=.c)
.install-deps: .install-cython $(PYXS:.pyx=.c) $(call to-hash,$(CYS) $(REQS)) .install-deps: .install-cython $(PYXS:.pyx=.c) $(call to-hash,$(CYS) $(REQS))
pip install -r requirements/dev.txt @python -m pip install -r requirements/dev.txt -c requirements/constraints.txt
@touch .install-deps @touch .install-deps
.PHONY: lint .PHONY: lint
@ -74,10 +86,10 @@ fmt format:
.PHONY: mypy .PHONY: mypy
mypy: mypy:
mypy aiohttp mypy
.develop: .install-deps $(call to-hash,$(PYS) $(CYS) $(CS)) .develop: .install-deps generate-llhttp $(call to-hash,$(PYS) $(CYS) $(CS))
pip install -e . python -m pip install -e . -c requirements/constraints.txt
@touch .develop @touch .develop
.PHONY: test .PHONY: test
@ -92,6 +104,30 @@ vtest: .develop
vvtest: .develop vvtest: .develop
@pytest -vv @pytest -vv
define run_tests_in_docker
DOCKER_BUILDKIT=1 docker build --build-arg PYTHON_VERSION=$(1) --build-arg AIOHTTP_NO_EXTENSIONS=$(2) -t "aiohttp-test-$(1)-$(2)" -f tools/testing/Dockerfile .
docker run --rm -ti -v `pwd`:/src -w /src "aiohttp-test-$(1)-$(2)" $(TEST_SPEC)
endef
.PHONY: test-3.7-no-extensions test-3.7 test-3.8-no-extensions test-3.8 test-3.9-no-extensions test-3.9 test-3.10-no-extensions test-3.10
test-3.7-no-extensions:
$(call run_tests_in_docker,3.7,y)
test-3.7:
$(call run_tests_in_docker,3.7,n)
test-3.8-no-extensions:
$(call run_tests_in_docker,3.8,y)
test-3.8:
$(call run_tests_in_docker,3.8,n)
test-3.9-no-extensions:
$(call run_tests_in_docker,3.9,y)
test-3.9:
$(call run_tests_in_docker,3.9,n)
test-3.10-no-extensions:
$(call run_tests_in_docker,3.10,y)
test-3.10:
$(call run_tests_in_docker,3.10,n)
.PHONY: clean .PHONY: clean
clean: clean:
@rm -rf `find . -name __pycache__` @rm -rf `find . -name __pycache__`
@ -125,20 +161,28 @@ clean:
@rm -rf aiohttp.egg-info @rm -rf aiohttp.egg-info
@rm -f .install-deps @rm -f .install-deps
@rm -f .install-cython @rm -f .install-cython
@rm -rf vendor/llhttp/node_modules
@rm -f .llhttp-gen
@$(MAKE) -C vendor/llhttp clean
.PHONY: doc .PHONY: doc
doc: doc:
@make -C docs html SPHINXOPTS="-W --keep-going -E" @make -C docs html SPHINXOPTS="-W --keep-going -n -E"
@echo "open file://`pwd`/docs/_build/html/index.html" @echo "open file://`pwd`/docs/_build/html/index.html"
.PHONY: doc-spelling .PHONY: doc-spelling
doc-spelling: doc-spelling:
@make -C docs spelling SPHINXOPTS="-W -E" @make -C docs spelling SPHINXOPTS="-W --keep-going -n -E"
.PHONY: compile-deps
compile-deps: .update-pip $(REQS)
pip-compile --no-header --allow-unsafe -q --strip-extras \
-o requirements/constraints.txt \
requirements/constraints.in
.PHONY: install .PHONY: install
install: install: .update-pip
@pip install -U 'pip' @python -m pip install -r requirements/dev.txt -c requirements/constraints.txt
@pip install -Ur requirements/dev.txt
.PHONY: install-dev .PHONY: install-dev
install-dev: .develop install-dev: .develop

1169
third_party/python/aiohttp/PKG-INFO поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

23
third_party/python/aiohttp/README.rst поставляемый
Просмотреть файл

@ -2,7 +2,7 @@
Async http client/server framework Async http client/server framework
================================== ==================================
.. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/_static/aiohttp-icon-128x128.png .. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/aiohttp-plain.svg
:height: 64px :height: 64px
:width: 64px :width: 64px
:alt: aiohttp logo :alt: aiohttp logo
@ -25,13 +25,13 @@ Async http client/server framework
:target: https://docs.aiohttp.org/ :target: https://docs.aiohttp.org/
:alt: Latest Read The Docs :alt: Latest Read The Docs
.. image:: https://img.shields.io/discourse/status?server=https%3A%2F%2Faio-libs.discourse.group .. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat
:target: https://aio-libs.discourse.group :target: https://matrix.to/#/%23aio-libs:matrix.org
:alt: Discourse status :alt: Matrix Room — #aio-libs:matrix.org
.. image:: https://badges.gitter.im/Join%20Chat.svg .. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat
:target: https://gitter.im/aio-libs/Lobby :target: https://matrix.to/#/%23aio-libs-space:matrix.org
:alt: Chat on Gitter :alt: Matrix Space — #aio-libs-space:matrix.org
Key Features Key Features
@ -67,8 +67,7 @@ To get something from the web:
html = await response.text() html = await response.text()
print("Body:", html[:15], "...") print("Body:", html[:15], "...")
loop = asyncio.get_event_loop() asyncio.run(main())
loop.run_until_complete(main())
This prints: This prints:
@ -161,17 +160,19 @@ Requirements
- Python >= 3.6 - Python >= 3.6
- async-timeout_ - async-timeout_
- attrs_ - attrs_
- chardet_ - charset-normalizer_
- multidict_ - multidict_
- yarl_ - yarl_
- frozenlist_
Optionally you may install the cChardet_ and aiodns_ libraries (highly Optionally you may install the cChardet_ and aiodns_ libraries (highly
recommended for sake of speed). recommended for sake of speed).
.. _chardet: https://pypi.python.org/pypi/chardet .. _charset-normalizer: https://pypi.org/project/charset-normalizer
.. _aiodns: https://pypi.python.org/pypi/aiodns .. _aiodns: https://pypi.python.org/pypi/aiodns
.. _attrs: https://github.com/python-attrs/attrs .. _attrs: https://github.com/python-attrs/attrs
.. _multidict: https://pypi.python.org/pypi/multidict .. _multidict: https://pypi.python.org/pypi/multidict
.. _frozenlist: https://pypi.org/project/frozenlist/
.. _yarl: https://pypi.python.org/pypi/yarl .. _yarl: https://pypi.python.org/pypi/yarl
.. _async-timeout: https://pypi.python.org/pypi/async_timeout .. _async-timeout: https://pypi.python.org/pypi/async_timeout
.. _cChardet: https://pypi.python.org/pypi/cchardet .. _cChardet: https://pypi.python.org/pypi/cchardet

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -12,8 +12,6 @@ aiohttp/_cparser.pxd
aiohttp/_find_header.c aiohttp/_find_header.c
aiohttp/_find_header.h aiohttp/_find_header.h
aiohttp/_find_header.pxd aiohttp/_find_header.pxd
aiohttp/_frozenlist.c
aiohttp/_frozenlist.pyx
aiohttp/_headers.pxi aiohttp/_headers.pxi
aiohttp/_helpers.c aiohttp/_helpers.c
aiohttp/_helpers.pyi aiohttp/_helpers.pyi
@ -34,8 +32,6 @@ aiohttp/client_ws.py
aiohttp/connector.py aiohttp/connector.py
aiohttp/cookiejar.py aiohttp/cookiejar.py
aiohttp/formdata.py aiohttp/formdata.py
aiohttp/frozenlist.py
aiohttp/frozenlist.pyi
aiohttp/hdrs.py aiohttp/hdrs.py
aiohttp/helpers.py aiohttp/helpers.py
aiohttp/http.py aiohttp/http.py
@ -51,8 +47,6 @@ aiohttp/payload_streamer.py
aiohttp/py.typed aiohttp/py.typed
aiohttp/pytest_plugin.py aiohttp/pytest_plugin.py
aiohttp/resolver.py aiohttp/resolver.py
aiohttp/signals.py
aiohttp/signals.pyi
aiohttp/streams.py aiohttp/streams.py
aiohttp/tcp_helpers.py aiohttp/tcp_helpers.py
aiohttp/test_utils.py aiohttp/test_utils.py
@ -76,19 +70,17 @@ aiohttp/worker.py
aiohttp.egg-info/PKG-INFO aiohttp.egg-info/PKG-INFO
aiohttp.egg-info/SOURCES.txt aiohttp.egg-info/SOURCES.txt
aiohttp.egg-info/dependency_links.txt aiohttp.egg-info/dependency_links.txt
aiohttp.egg-info/not-zip-safe
aiohttp.egg-info/requires.txt aiohttp.egg-info/requires.txt
aiohttp.egg-info/top_level.txt aiohttp.egg-info/top_level.txt
aiohttp/.hash/_cparser.pxd.hash aiohttp/.hash/_cparser.pxd.hash
aiohttp/.hash/_find_header.pxd.hash aiohttp/.hash/_find_header.pxd.hash
aiohttp/.hash/_frozenlist.pyx.hash
aiohttp/.hash/_helpers.pyi.hash aiohttp/.hash/_helpers.pyi.hash
aiohttp/.hash/_helpers.pyx.hash aiohttp/.hash/_helpers.pyx.hash
aiohttp/.hash/_http_parser.pyx.hash aiohttp/.hash/_http_parser.pyx.hash
aiohttp/.hash/_http_writer.pyx.hash aiohttp/.hash/_http_writer.pyx.hash
aiohttp/.hash/_websocket.pyx.hash aiohttp/.hash/_websocket.pyx.hash
aiohttp/.hash/frozenlist.pyi.hash
aiohttp/.hash/hdrs.py.hash aiohttp/.hash/hdrs.py.hash
aiohttp/.hash/signals.pyi.hash
docs/Makefile docs/Makefile
docs/abc.rst docs/abc.rst
docs/aiohttp-icon.svg docs/aiohttp-icon.svg
@ -119,7 +111,6 @@ docs/new_router.rst
docs/old-logo.png docs/old-logo.png
docs/old-logo.svg docs/old-logo.svg
docs/powered_by.rst docs/powered_by.rst
docs/signals.rst
docs/spelling_wordlist.txt docs/spelling_wordlist.txt
docs/streams.rst docs/streams.rst
docs/structures.rst docs/structures.rst
@ -135,7 +126,9 @@ docs/web_reference.rst
docs/websocket_utilities.rst docs/websocket_utilities.rst
docs/whats_new_1_1.rst docs/whats_new_1_1.rst
docs/whats_new_3_0.rst docs/whats_new_3_0.rst
docs/_static/aiohttp-icon-128x128.png docs/_snippets/cchardet-unmaintained-admonition.rst
docs/_static/css/logo-adjustments.css
examples/__init__.py
examples/background_tasks.py examples/background_tasks.py
examples/cli_app.py examples/cli_app.py
examples/client_auth.py examples/client_auth.py
@ -157,16 +150,16 @@ examples/web_srv_route_deco.py
examples/web_srv_route_table.py examples/web_srv_route_table.py
examples/web_ws.py examples/web_ws.py
examples/websocket.html examples/websocket.html
examples/legacy/crawl.py
examples/legacy/srv.py
examples/legacy/tcp_protocol_parser.py
tests/aiohttp.jpg tests/aiohttp.jpg
tests/aiohttp.png tests/aiohttp.png
tests/conftest.py tests/conftest.py
tests/data.unknown_mime_type tests/data.unknown_mime_type
tests/data.zero_bytes tests/data.zero_bytes
tests/hello.txt.gz tests/hello.txt.gz
tests/sample.txt
tests/test___all__.py
tests/test_base_protocol.py tests/test_base_protocol.py
tests/test_circular_imports.py
tests/test_classbasedview.py tests/test_classbasedview.py
tests/test_client_connection.py tests/test_client_connection.py
tests/test_client_exceptions.py tests/test_client_exceptions.py
@ -182,7 +175,6 @@ tests/test_connector.py
tests/test_cookiejar.py tests/test_cookiejar.py
tests/test_flowcontrol_streams.py tests/test_flowcontrol_streams.py
tests/test_formdata.py tests/test_formdata.py
tests/test_frozenlist.py
tests/test_helpers.py tests/test_helpers.py
tests/test_http_exceptions.py tests/test_http_exceptions.py
tests/test_http_parser.py tests/test_http_parser.py
@ -198,7 +190,6 @@ tests/test_pytest_plugin.py
tests/test_resolver.py tests/test_resolver.py
tests/test_route_def.py tests/test_route_def.py
tests/test_run_app.py tests/test_run_app.py
tests/test_signals.py
tests/test_streams.py tests/test_streams.py
tests/test_tcp_helpers.py tests/test_tcp_helpers.py
tests/test_test_utils.py tests/test_test_utils.py
@ -210,7 +201,6 @@ tests/test_web_exceptions.py
tests/test_web_functional.py tests/test_web_functional.py
tests/test_web_log.py tests/test_web_log.py
tests/test_web_middleware.py tests/test_web_middleware.py
tests/test_web_protocol.py
tests/test_web_request.py tests/test_web_request.py
tests/test_web_request_handler.py tests/test_web_request_handler.py
tests/test_web_response.py tests/test_web_response.py
@ -225,22 +215,76 @@ tests/test_websocket_handshake.py
tests/test_websocket_parser.py tests/test_websocket_parser.py
tests/test_websocket_writer.py tests/test_websocket_writer.py
tests/test_worker.py tests/test_worker.py
tests/autobahn/client.py tests/autobahn/Dockerfile.aiohttp
tests/autobahn/fuzzingclient.json tests/autobahn/Dockerfile.autobahn
tests/autobahn/fuzzingserver.json tests/autobahn/test_autobahn.py
tests/autobahn/server.py tests/autobahn/client/client.py
vendor/http-parser/.git tests/autobahn/client/fuzzingserver.json
vendor/http-parser/.gitignore tests/autobahn/server/fuzzingclient.json
vendor/http-parser/.mailmap tests/autobahn/server/server.py
vendor/http-parser/.travis.yml vendor/README.rst
vendor/http-parser/AUTHORS vendor/llhttp/.dockerignore
vendor/http-parser/LICENSE-MIT vendor/llhttp/.eslintrc.js
vendor/http-parser/Makefile vendor/llhttp/.git
vendor/http-parser/README.md vendor/llhttp/.gitignore
vendor/http-parser/bench.c vendor/llhttp/.npmrc
vendor/http-parser/http_parser.c vendor/llhttp/CMakeLists.txt
vendor/http-parser/http_parser.gyp vendor/llhttp/CNAME
vendor/http-parser/http_parser.h vendor/llhttp/CODE_OF_CONDUCT.md
vendor/http-parser/test.c vendor/llhttp/Dockerfile
vendor/http-parser/contrib/parsertrace.c vendor/llhttp/LICENSE-MIT
vendor/http-parser/contrib/url_parser.c vendor/llhttp/Makefile
vendor/llhttp/README.md
vendor/llhttp/_config.yml
vendor/llhttp/libllhttp.pc.in
vendor/llhttp/package-lock.json
vendor/llhttp/package.json
vendor/llhttp/tsconfig.json
vendor/llhttp/tslint.json
vendor/llhttp/.github/workflows/ci.yaml
vendor/llhttp/bench/index.ts
vendor/llhttp/bin/build_wasm.ts
vendor/llhttp/bin/generate.ts
vendor/llhttp/build/llhttp.h
vendor/llhttp/build/c/llhttp.c
vendor/llhttp/docs/releasing.md
vendor/llhttp/examples/wasm.ts
vendor/llhttp/images/http-loose-none.png
vendor/llhttp/images/http-strict-none.png
vendor/llhttp/src/common.gypi
vendor/llhttp/src/llhttp.gyp
vendor/llhttp/src/llhttp.ts
vendor/llhttp/src/llhttp/c-headers.ts
vendor/llhttp/src/llhttp/constants.ts
vendor/llhttp/src/llhttp/http.ts
vendor/llhttp/src/llhttp/url.ts
vendor/llhttp/src/llhttp/utils.ts
vendor/llhttp/src/native/api.c
vendor/llhttp/src/native/api.h
vendor/llhttp/src/native/http.c
vendor/llhttp/test/md-test.ts
vendor/llhttp/test/url.md
vendor/llhttp/test/fixtures/extra.c
vendor/llhttp/test/fixtures/index.ts
vendor/llhttp/test/fuzzers/fuzz_parser.c
vendor/llhttp/test/request/connection.md
vendor/llhttp/test/request/content-length.md
vendor/llhttp/test/request/finish.md
vendor/llhttp/test/request/invalid.md
vendor/llhttp/test/request/lenient-headers.md
vendor/llhttp/test/request/lenient-version.md
vendor/llhttp/test/request/method.md
vendor/llhttp/test/request/pausing.md
vendor/llhttp/test/request/pipelining.md
vendor/llhttp/test/request/sample.md
vendor/llhttp/test/request/transfer-encoding.md
vendor/llhttp/test/request/uri.md
vendor/llhttp/test/response/connection.md
vendor/llhttp/test/response/content-length.md
vendor/llhttp/test/response/finish.md
vendor/llhttp/test/response/invalid.md
vendor/llhttp/test/response/lenient-version.md
vendor/llhttp/test/response/pausing.md
vendor/llhttp/test/response/pipelining.md
vendor/llhttp/test/response/sample.md
vendor/llhttp/test/response/transfer-encoding.md

1
third_party/python/aiohttp/aiohttp.egg-info/not-zip-safe поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@

Просмотреть файл

@ -1,14 +1,21 @@
attrs>=17.3.0 attrs>=17.3.0
chardet<5.0,>=2.0 charset-normalizer<4.0,>=2.0
multidict<7.0,>=4.5 multidict<7.0,>=4.5
async_timeout<4.0,>=3.0 async_timeout<5.0,>=4.0.0a3
yarl<2.0,>=1.0 yarl<2.0,>=1.0
typing_extensions>=3.6.5 frozenlist>=1.1.1
aiosignal>=1.1.2
[:python_version < "3.7"] [:python_version < "3.7"]
idna-ssl>=1.0 idna-ssl>=1.0
[:python_version < "3.8"]
asynctest==0.13.0
typing_extensions>=3.7.4
[speedups] [speedups]
aiodns aiodns
brotlipy Brotli
[speedups:python_version < "3.10"]
cchardet cchardet

Просмотреть файл

@ -1 +1 @@
b60c37d122fa91049ccf318c94c871d82ba17ff3bc3fc64f8a65426fce7120b7 /home/runner/work/aiohttp/aiohttp/aiohttp/_cparser.pxd e6d134d56d5f516ab2b5c3b295d0d440a3bef911f4384d506204018895a1f833 /home/runner/work/aiohttp/aiohttp/aiohttp/_cparser.pxd

Просмотреть файл

@ -1 +0,0 @@
043f0b704444c6c59da38ab3bae43ce1ff8bfe91d5ce45103b494400e7b71688 /home/runner/work/aiohttp/aiohttp/aiohttp/_frozenlist.pyx

Просмотреть файл

@ -1 +1 @@
f0688fb2e81ea92bf0a17822260d9591a30979101da12a4b873113fc459fb5fa /home/runner/work/aiohttp/aiohttp/aiohttp/_http_parser.pyx 43bc2c42b9dbb09c19d0782c7aefd1a656a039b31c57a9fa809f82c2807eeaa9 /home/runner/work/aiohttp/aiohttp/aiohttp/_http_parser.pyx

Просмотреть файл

@ -1 +1 @@
4e7b7f7baa5c65954e85a5b7c8db7786a0ec3498081b0a9420f792a803086281 /home/runner/work/aiohttp/aiohttp/aiohttp/_http_writer.pyx 6881c0a7c838655e646c645d99971efaf5e310bc3633a7c62b226e39d81842ac /home/runner/work/aiohttp/aiohttp/aiohttp/_http_writer.pyx

Просмотреть файл

@ -1 +0,0 @@
6d134aa08da3d6ba0f76d81fc7f9ec7836a7bc1a99b1950d1c3aa65ed7e3951a /home/runner/work/aiohttp/aiohttp/aiohttp/frozenlist.pyi

Просмотреть файл

@ -1 +1 @@
5ac8c3258003604c8993bfa8357361036337330b722e4849024972ccbb5c95f5 /home/runner/work/aiohttp/aiohttp/aiohttp/hdrs.py a30351c34760a1d7835b2a1b0552e463cf1d2db90da0cdb473313dc66e34a031 /home/runner/work/aiohttp/aiohttp/aiohttp/hdrs.py

Просмотреть файл

@ -1 +0,0 @@
48b4df50f771d7e8385524ea0a7057ca1482974f8a43e674982b04b08bc17d5e /home/runner/work/aiohttp/aiohttp/aiohttp/signals.pyi

Просмотреть файл

@ -1,4 +1,4 @@
__version__ = "3.7.4.post0" __version__ = "3.8.5"
from typing import Tuple from typing import Tuple
@ -38,7 +38,7 @@ from .client import (
) )
from .cookiejar import CookieJar as CookieJar, DummyCookieJar as DummyCookieJar from .cookiejar import CookieJar as CookieJar, DummyCookieJar as DummyCookieJar
from .formdata import FormData as FormData from .formdata import FormData as FormData
from .helpers import BasicAuth as BasicAuth, ChainMapProxy as ChainMapProxy from .helpers import BasicAuth, ChainMapProxy, ETag
from .http import ( from .http import (
HttpVersion as HttpVersion, HttpVersion as HttpVersion,
HttpVersion10 as HttpVersion10, HttpVersion10 as HttpVersion10,
@ -78,7 +78,6 @@ from .resolver import (
DefaultResolver as DefaultResolver, DefaultResolver as DefaultResolver,
ThreadedResolver as ThreadedResolver, ThreadedResolver as ThreadedResolver,
) )
from .signals import Signal as Signal
from .streams import ( from .streams import (
EMPTY_PAYLOAD as EMPTY_PAYLOAD, EMPTY_PAYLOAD as EMPTY_PAYLOAD,
DataQueue as DataQueue, DataQueue as DataQueue,
@ -147,6 +146,7 @@ __all__: Tuple[str, ...] = (
# helpers # helpers
"BasicAuth", "BasicAuth",
"ChainMapProxy", "ChainMapProxy",
"ETag",
# http # http
"HttpVersion", "HttpVersion",
"HttpVersion10", "HttpVersion10",
@ -183,8 +183,7 @@ __all__: Tuple[str, ...] = (
"AsyncResolver", "AsyncResolver",
"DefaultResolver", "DefaultResolver",
"ThreadedResolver", "ThreadedResolver",
# signals # streams
"Signal",
"DataQueue", "DataQueue",
"EMPTY_PAYLOAD", "EMPTY_PAYLOAD",
"EofStream", "EofStream",

Просмотреть файл

@ -1,140 +1,190 @@
from libc.stdint cimport uint16_t, uint32_t, uint64_t from libc.stdint cimport (
int8_t,
int16_t,
int32_t,
int64_t,
uint8_t,
uint16_t,
uint32_t,
uint64_t,
)
cdef extern from "../vendor/http-parser/http_parser.h": cdef extern from "../vendor/llhttp/build/llhttp.h":
ctypedef int (*http_data_cb) (http_parser*,
const char *at,
size_t length) except -1
ctypedef int (*http_cb) (http_parser*) except -1 struct llhttp__internal_s:
int32_t _index
struct http_parser: void* _span_pos0
unsigned int type void* _span_cb0
unsigned int flags int32_t error
unsigned int state const char* reason
unsigned int header_state const char* error_pos
unsigned int index void* data
void* _current
uint32_t nread
uint64_t content_length uint64_t content_length
uint8_t type
uint8_t method
uint8_t http_major
uint8_t http_minor
uint8_t header_state
uint8_t lenient_flags
uint8_t upgrade
uint8_t finish
uint16_t flags
uint16_t status_code
void* settings
unsigned short http_major ctypedef llhttp__internal_s llhttp__internal_t
unsigned short http_minor ctypedef llhttp__internal_t llhttp_t
unsigned int status_code
unsigned int method
unsigned int http_errno
unsigned int upgrade ctypedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length) except -1
ctypedef int (*llhttp_cb)(llhttp_t*) except -1
void *data struct llhttp_settings_s:
llhttp_cb on_message_begin
llhttp_data_cb on_url
llhttp_data_cb on_status
llhttp_data_cb on_header_field
llhttp_data_cb on_header_value
llhttp_cb on_headers_complete
llhttp_data_cb on_body
llhttp_cb on_message_complete
llhttp_cb on_chunk_header
llhttp_cb on_chunk_complete
struct http_parser_settings: llhttp_cb on_url_complete
http_cb on_message_begin llhttp_cb on_status_complete
http_data_cb on_url llhttp_cb on_header_field_complete
http_data_cb on_status llhttp_cb on_header_value_complete
http_data_cb on_header_field
http_data_cb on_header_value
http_cb on_headers_complete
http_data_cb on_body
http_cb on_message_complete
http_cb on_chunk_header
http_cb on_chunk_complete
enum http_parser_type: ctypedef llhttp_settings_s llhttp_settings_t
enum llhttp_errno:
HPE_OK,
HPE_INTERNAL,
HPE_STRICT,
HPE_LF_EXPECTED,
HPE_UNEXPECTED_CONTENT_LENGTH,
HPE_CLOSED_CONNECTION,
HPE_INVALID_METHOD,
HPE_INVALID_URL,
HPE_INVALID_CONSTANT,
HPE_INVALID_VERSION,
HPE_INVALID_HEADER_TOKEN,
HPE_INVALID_CONTENT_LENGTH,
HPE_INVALID_CHUNK_SIZE,
HPE_INVALID_STATUS,
HPE_INVALID_EOF_STATE,
HPE_INVALID_TRANSFER_ENCODING,
HPE_CB_MESSAGE_BEGIN,
HPE_CB_HEADERS_COMPLETE,
HPE_CB_MESSAGE_COMPLETE,
HPE_CB_CHUNK_HEADER,
HPE_CB_CHUNK_COMPLETE,
HPE_PAUSED,
HPE_PAUSED_UPGRADE,
HPE_USER
ctypedef llhttp_errno llhttp_errno_t
enum llhttp_flags:
F_CONNECTION_KEEP_ALIVE,
F_CONNECTION_CLOSE,
F_CONNECTION_UPGRADE,
F_CHUNKED,
F_UPGRADE,
F_CONTENT_LENGTH,
F_SKIPBODY,
F_TRAILING,
F_TRANSFER_ENCODING
enum llhttp_lenient_flags:
LENIENT_HEADERS,
LENIENT_CHUNKED_LENGTH
enum llhttp_type:
HTTP_REQUEST, HTTP_REQUEST,
HTTP_RESPONSE, HTTP_RESPONSE,
HTTP_BOTH HTTP_BOTH
enum http_errno: enum llhttp_finish_t:
HPE_OK, HTTP_FINISH_SAFE,
HPE_CB_message_begin, HTTP_FINISH_SAFE_WITH_CB,
HPE_CB_url, HTTP_FINISH_UNSAFE
HPE_CB_header_field,
HPE_CB_header_value,
HPE_CB_headers_complete,
HPE_CB_body,
HPE_CB_message_complete,
HPE_CB_status,
HPE_CB_chunk_header,
HPE_CB_chunk_complete,
HPE_INVALID_EOF_STATE,
HPE_HEADER_OVERFLOW,
HPE_CLOSED_CONNECTION,
HPE_INVALID_VERSION,
HPE_INVALID_STATUS,
HPE_INVALID_METHOD,
HPE_INVALID_URL,
HPE_INVALID_HOST,
HPE_INVALID_PORT,
HPE_INVALID_PATH,
HPE_INVALID_QUERY_STRING,
HPE_INVALID_FRAGMENT,
HPE_LF_EXPECTED,
HPE_INVALID_HEADER_TOKEN,
HPE_INVALID_CONTENT_LENGTH,
HPE_INVALID_CHUNK_SIZE,
HPE_INVALID_CONSTANT,
HPE_INVALID_INTERNAL_STATE,
HPE_STRICT,
HPE_PAUSED,
HPE_UNKNOWN
enum flags: enum llhttp_method:
F_CHUNKED, HTTP_DELETE,
F_CONNECTION_KEEP_ALIVE, HTTP_GET,
F_CONNECTION_CLOSE, HTTP_HEAD,
F_CONNECTION_UPGRADE, HTTP_POST,
F_TRAILING, HTTP_PUT,
F_UPGRADE, HTTP_CONNECT,
F_SKIPBODY, HTTP_OPTIONS,
F_CONTENTLENGTH HTTP_TRACE,
HTTP_COPY,
HTTP_LOCK,
HTTP_MKCOL,
HTTP_MOVE,
HTTP_PROPFIND,
HTTP_PROPPATCH,
HTTP_SEARCH,
HTTP_UNLOCK,
HTTP_BIND,
HTTP_REBIND,
HTTP_UNBIND,
HTTP_ACL,
HTTP_REPORT,
HTTP_MKACTIVITY,
HTTP_CHECKOUT,
HTTP_MERGE,
HTTP_MSEARCH,
HTTP_NOTIFY,
HTTP_SUBSCRIBE,
HTTP_UNSUBSCRIBE,
HTTP_PATCH,
HTTP_PURGE,
HTTP_MKCALENDAR,
HTTP_LINK,
HTTP_UNLINK,
HTTP_SOURCE,
HTTP_PRI,
HTTP_DESCRIBE,
HTTP_ANNOUNCE,
HTTP_SETUP,
HTTP_PLAY,
HTTP_PAUSE,
HTTP_TEARDOWN,
HTTP_GET_PARAMETER,
HTTP_SET_PARAMETER,
HTTP_REDIRECT,
HTTP_RECORD,
HTTP_FLUSH
enum http_method: ctypedef llhttp_method llhttp_method_t;
DELETE, GET, HEAD, POST, PUT, CONNECT, OPTIONS, TRACE, COPY,
LOCK, MKCOL, MOVE, PROPFIND, PROPPATCH, SEARCH, UNLOCK, BIND,
REBIND, UNBIND, ACL, REPORT, MKACTIVITY, CHECKOUT, MERGE,
MSEARCH, NOTIFY, SUBSCRIBE, UNSUBSCRIBE, PATCH, PURGE, MKCALENDAR,
LINK, UNLINK
void http_parser_init(http_parser *parser, http_parser_type type) void llhttp_settings_init(llhttp_settings_t* settings)
void llhttp_init(llhttp_t* parser, llhttp_type type,
const llhttp_settings_t* settings)
size_t http_parser_execute(http_parser *parser, llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len)
const http_parser_settings *settings, llhttp_errno_t llhttp_finish(llhttp_t* parser)
const char *data,
size_t len)
int http_should_keep_alive(const http_parser *parser) int llhttp_message_needs_eof(const llhttp_t* parser)
void http_parser_settings_init(http_parser_settings *settings) int llhttp_should_keep_alive(const llhttp_t* parser)
const char *http_errno_name(http_errno err) void llhttp_pause(llhttp_t* parser)
const char *http_errno_description(http_errno err) void llhttp_resume(llhttp_t* parser)
const char *http_method_str(http_method m)
# URL Parser void llhttp_resume_after_upgrade(llhttp_t* parser)
enum http_parser_url_fields: llhttp_errno_t llhttp_get_errno(const llhttp_t* parser)
UF_SCHEMA = 0, const char* llhttp_get_error_reason(const llhttp_t* parser)
UF_HOST = 1, void llhttp_set_error_reason(llhttp_t* parser, const char* reason)
UF_PORT = 2, const char* llhttp_get_error_pos(const llhttp_t* parser)
UF_PATH = 3, const char* llhttp_errno_name(llhttp_errno_t err)
UF_QUERY = 4,
UF_FRAGMENT = 5,
UF_USERINFO = 6,
UF_MAX = 7
struct http_parser_url_field_data: const char* llhttp_method_name(llhttp_method_t method)
uint16_t off
uint16_t len
struct http_parser_url: void llhttp_set_lenient_headers(llhttp_t* parser, int enabled)
uint16_t field_set void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled)
uint16_t port
http_parser_url_field_data[<int>UF_MAX] field_data
void http_parser_url_init(http_parser_url *u)
int http_parser_parse_url(const char *buf,
size_t buflen,
int is_connect,
http_parser_url *u)

589
third_party/python/aiohttp/aiohttp/_helpers.c поставляемый
Просмотреть файл

@ -1,14 +1,16 @@
/* Generated by Cython 0.29.21 */ /* Generated by Cython 0.29.32 */
#ifndef PY_SSIZE_T_CLEAN
#define PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN
#endif /* PY_SSIZE_T_CLEAN */
#include "Python.h" #include "Python.h"
#ifndef Py_PYTHON_H #ifndef Py_PYTHON_H
#error Python headers needed to compile C extensions, please install development version of Python. #error Python headers needed to compile C extensions, please install development version of Python.
#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000) #elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
#error Cython requires Python 2.6+ or Python 3.3+. #error Cython requires Python 2.6+ or Python 3.3+.
#else #else
#define CYTHON_ABI "0_29_21" #define CYTHON_ABI "0_29_32"
#define CYTHON_HEX_VERSION 0x001D15F0 #define CYTHON_HEX_VERSION 0x001D20F0
#define CYTHON_FUTURE_DIVISION 1 #define CYTHON_FUTURE_DIVISION 1
#include <stddef.h> #include <stddef.h>
#ifndef offsetof #ifndef offsetof
@ -47,6 +49,7 @@
#define CYTHON_COMPILING_IN_PYPY 1 #define CYTHON_COMPILING_IN_PYPY 1
#define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_PYSTON 0
#define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_CPYTHON 0
#define CYTHON_COMPILING_IN_NOGIL 0
#undef CYTHON_USE_TYPE_SLOTS #undef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 0 #define CYTHON_USE_TYPE_SLOTS 0
#undef CYTHON_USE_PYTYPE_LOOKUP #undef CYTHON_USE_PYTYPE_LOOKUP
@ -83,10 +86,14 @@
#define CYTHON_USE_DICT_VERSIONS 0 #define CYTHON_USE_DICT_VERSIONS 0
#undef CYTHON_USE_EXC_INFO_STACK #undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0 #define CYTHON_USE_EXC_INFO_STACK 0
#ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
#define CYTHON_UPDATE_DESCRIPTOR_DOC (PYPY_VERSION_HEX >= 0x07030900)
#endif
#elif defined(PYSTON_VERSION) #elif defined(PYSTON_VERSION)
#define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYPY 0
#define CYTHON_COMPILING_IN_PYSTON 1 #define CYTHON_COMPILING_IN_PYSTON 1
#define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_CPYTHON 0
#define CYTHON_COMPILING_IN_NOGIL 0
#ifndef CYTHON_USE_TYPE_SLOTS #ifndef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 1 #define CYTHON_USE_TYPE_SLOTS 1
#endif #endif
@ -124,10 +131,59 @@
#define CYTHON_USE_DICT_VERSIONS 0 #define CYTHON_USE_DICT_VERSIONS 0
#undef CYTHON_USE_EXC_INFO_STACK #undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0 #define CYTHON_USE_EXC_INFO_STACK 0
#ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
#define CYTHON_UPDATE_DESCRIPTOR_DOC 0
#endif
#elif defined(PY_NOGIL)
#define CYTHON_COMPILING_IN_PYPY 0
#define CYTHON_COMPILING_IN_PYSTON 0
#define CYTHON_COMPILING_IN_CPYTHON 0
#define CYTHON_COMPILING_IN_NOGIL 1
#ifndef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 1
#endif
#undef CYTHON_USE_PYTYPE_LOOKUP
#define CYTHON_USE_PYTYPE_LOOKUP 0
#ifndef CYTHON_USE_ASYNC_SLOTS
#define CYTHON_USE_ASYNC_SLOTS 1
#endif
#undef CYTHON_USE_PYLIST_INTERNALS
#define CYTHON_USE_PYLIST_INTERNALS 0
#ifndef CYTHON_USE_UNICODE_INTERNALS
#define CYTHON_USE_UNICODE_INTERNALS 1
#endif
#undef CYTHON_USE_UNICODE_WRITER
#define CYTHON_USE_UNICODE_WRITER 0
#undef CYTHON_USE_PYLONG_INTERNALS
#define CYTHON_USE_PYLONG_INTERNALS 0
#ifndef CYTHON_AVOID_BORROWED_REFS
#define CYTHON_AVOID_BORROWED_REFS 0
#endif
#ifndef CYTHON_ASSUME_SAFE_MACROS
#define CYTHON_ASSUME_SAFE_MACROS 1
#endif
#ifndef CYTHON_UNPACK_METHODS
#define CYTHON_UNPACK_METHODS 1
#endif
#undef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 0
#undef CYTHON_FAST_PYCALL
#define CYTHON_FAST_PYCALL 0
#ifndef CYTHON_PEP489_MULTI_PHASE_INIT
#define CYTHON_PEP489_MULTI_PHASE_INIT 1
#endif
#ifndef CYTHON_USE_TP_FINALIZE
#define CYTHON_USE_TP_FINALIZE 1
#endif
#undef CYTHON_USE_DICT_VERSIONS
#define CYTHON_USE_DICT_VERSIONS 0
#undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0
#else #else
#define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYPY 0
#define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_PYSTON 0
#define CYTHON_COMPILING_IN_CPYTHON 1 #define CYTHON_COMPILING_IN_CPYTHON 1
#define CYTHON_COMPILING_IN_NOGIL 0
#ifndef CYTHON_USE_TYPE_SLOTS #ifndef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 1 #define CYTHON_USE_TYPE_SLOTS 1
#endif #endif
@ -155,7 +211,7 @@
#ifndef CYTHON_USE_UNICODE_INTERNALS #ifndef CYTHON_USE_UNICODE_INTERNALS
#define CYTHON_USE_UNICODE_INTERNALS 1 #define CYTHON_USE_UNICODE_INTERNALS 1
#endif #endif
#if PY_VERSION_HEX < 0x030300F0 #if PY_VERSION_HEX < 0x030300F0 || PY_VERSION_HEX >= 0x030B00A2
#undef CYTHON_USE_UNICODE_WRITER #undef CYTHON_USE_UNICODE_WRITER
#define CYTHON_USE_UNICODE_WRITER 0 #define CYTHON_USE_UNICODE_WRITER 0
#elif !defined(CYTHON_USE_UNICODE_WRITER) #elif !defined(CYTHON_USE_UNICODE_WRITER)
@ -170,11 +226,14 @@
#ifndef CYTHON_UNPACK_METHODS #ifndef CYTHON_UNPACK_METHODS
#define CYTHON_UNPACK_METHODS 1 #define CYTHON_UNPACK_METHODS 1
#endif #endif
#ifndef CYTHON_FAST_THREAD_STATE #if PY_VERSION_HEX >= 0x030B00A4
#undef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 0
#elif !defined(CYTHON_FAST_THREAD_STATE)
#define CYTHON_FAST_THREAD_STATE 1 #define CYTHON_FAST_THREAD_STATE 1
#endif #endif
#ifndef CYTHON_FAST_PYCALL #ifndef CYTHON_FAST_PYCALL
#define CYTHON_FAST_PYCALL 1 #define CYTHON_FAST_PYCALL (PY_VERSION_HEX < 0x030A0000)
#endif #endif
#ifndef CYTHON_PEP489_MULTI_PHASE_INIT #ifndef CYTHON_PEP489_MULTI_PHASE_INIT
#define CYTHON_PEP489_MULTI_PHASE_INIT (PY_VERSION_HEX >= 0x03050000) #define CYTHON_PEP489_MULTI_PHASE_INIT (PY_VERSION_HEX >= 0x03050000)
@ -185,15 +244,23 @@
#ifndef CYTHON_USE_DICT_VERSIONS #ifndef CYTHON_USE_DICT_VERSIONS
#define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX >= 0x030600B1) #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX >= 0x030600B1)
#endif #endif
#ifndef CYTHON_USE_EXC_INFO_STACK #if PY_VERSION_HEX >= 0x030B00A4
#undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0
#elif !defined(CYTHON_USE_EXC_INFO_STACK)
#define CYTHON_USE_EXC_INFO_STACK (PY_VERSION_HEX >= 0x030700A3) #define CYTHON_USE_EXC_INFO_STACK (PY_VERSION_HEX >= 0x030700A3)
#endif #endif
#ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
#define CYTHON_UPDATE_DESCRIPTOR_DOC 1
#endif
#endif #endif
#if !defined(CYTHON_FAST_PYCCALL) #if !defined(CYTHON_FAST_PYCCALL)
#define CYTHON_FAST_PYCCALL (CYTHON_FAST_PYCALL && PY_VERSION_HEX >= 0x030600B1) #define CYTHON_FAST_PYCCALL (CYTHON_FAST_PYCALL && PY_VERSION_HEX >= 0x030600B1)
#endif #endif
#if CYTHON_USE_PYLONG_INTERNALS #if CYTHON_USE_PYLONG_INTERNALS
#include "longintrepr.h" #if PY_MAJOR_VERSION < 3
#include "longintrepr.h"
#endif
#undef SHIFT #undef SHIFT
#undef BASE #undef BASE
#undef MASK #undef MASK
@ -310,9 +377,68 @@
#define __Pyx_DefaultClassType PyClass_Type #define __Pyx_DefaultClassType PyClass_Type
#else #else
#define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_BUILTIN_MODULE_NAME "builtins"
#if PY_VERSION_HEX >= 0x030800A4 && PY_VERSION_HEX < 0x030800B2 #define __Pyx_DefaultClassType PyType_Type
#define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ #if PY_VERSION_HEX >= 0x030B00A1
PyCode_New(a, 0, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) static CYTHON_INLINE PyCodeObject* __Pyx_PyCode_New(int a, int k, int l, int s, int f,
PyObject *code, PyObject *c, PyObject* n, PyObject *v,
PyObject *fv, PyObject *cell, PyObject* fn,
PyObject *name, int fline, PyObject *lnos) {
PyObject *kwds=NULL, *argcount=NULL, *posonlyargcount=NULL, *kwonlyargcount=NULL;
PyObject *nlocals=NULL, *stacksize=NULL, *flags=NULL, *replace=NULL, *call_result=NULL, *empty=NULL;
const char *fn_cstr=NULL;
const char *name_cstr=NULL;
PyCodeObject* co=NULL;
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
if (!(kwds=PyDict_New())) goto end;
if (!(argcount=PyLong_FromLong(a))) goto end;
if (PyDict_SetItemString(kwds, "co_argcount", argcount) != 0) goto end;
if (!(posonlyargcount=PyLong_FromLong(0))) goto end;
if (PyDict_SetItemString(kwds, "co_posonlyargcount", posonlyargcount) != 0) goto end;
if (!(kwonlyargcount=PyLong_FromLong(k))) goto end;
if (PyDict_SetItemString(kwds, "co_kwonlyargcount", kwonlyargcount) != 0) goto end;
if (!(nlocals=PyLong_FromLong(l))) goto end;
if (PyDict_SetItemString(kwds, "co_nlocals", nlocals) != 0) goto end;
if (!(stacksize=PyLong_FromLong(s))) goto end;
if (PyDict_SetItemString(kwds, "co_stacksize", stacksize) != 0) goto end;
if (!(flags=PyLong_FromLong(f))) goto end;
if (PyDict_SetItemString(kwds, "co_flags", flags) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_code", code) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_consts", c) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_names", n) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_varnames", v) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_freevars", fv) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_cellvars", cell) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_linetable", lnos) != 0) goto end;
if (!(fn_cstr=PyUnicode_AsUTF8AndSize(fn, NULL))) goto end;
if (!(name_cstr=PyUnicode_AsUTF8AndSize(name, NULL))) goto end;
if (!(co = PyCode_NewEmpty(fn_cstr, name_cstr, fline))) goto end;
if (!(replace = PyObject_GetAttrString((PyObject*)co, "replace"))) goto cleanup_code_too;
if (!(empty = PyTuple_New(0))) goto cleanup_code_too; // unfortunately __pyx_empty_tuple isn't available here
if (!(call_result = PyObject_Call(replace, empty, kwds))) goto cleanup_code_too;
Py_XDECREF((PyObject*)co);
co = (PyCodeObject*)call_result;
call_result = NULL;
if (0) {
cleanup_code_too:
Py_XDECREF((PyObject*)co);
co = NULL;
}
end:
Py_XDECREF(kwds);
Py_XDECREF(argcount);
Py_XDECREF(posonlyargcount);
Py_XDECREF(kwonlyargcount);
Py_XDECREF(nlocals);
Py_XDECREF(stacksize);
Py_XDECREF(replace);
Py_XDECREF(call_result);
Py_XDECREF(empty);
if (type) {
PyErr_Restore(type, value, traceback);
}
return co;
}
#else #else
#define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
@ -426,8 +552,12 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
#endif #endif
#if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND) #if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND)
#define CYTHON_PEP393_ENABLED 1 #define CYTHON_PEP393_ENABLED 1
#if defined(PyUnicode_IS_READY)
#define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\ #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\
0 : _PyUnicode_Ready((PyObject *)(op))) 0 : _PyUnicode_Ready((PyObject *)(op)))
#else
#define __Pyx_PyUnicode_READY(op) (0)
#endif
#define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u) #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u)
#define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i)
#define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u) #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u)
@ -436,7 +566,11 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
#define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i) #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i)
#define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, ch) #define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, ch)
#if defined(PyUnicode_IS_READY) && defined(PyUnicode_GET_SIZE) #if defined(PyUnicode_IS_READY) && defined(PyUnicode_GET_SIZE)
#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length))
#else
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u))) #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
#endif
#else #else
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u)) #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u))
#endif #endif
@ -542,10 +676,10 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
#if PY_VERSION_HEX < 0x030200A4 #if PY_VERSION_HEX < 0x030200A4
typedef long Py_hash_t; typedef long Py_hash_t;
#define __Pyx_PyInt_FromHash_t PyInt_FromLong #define __Pyx_PyInt_FromHash_t PyInt_FromLong
#define __Pyx_PyInt_AsHash_t PyInt_AsLong #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsHash_t
#else #else
#define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t #define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t
#define __Pyx_PyInt_AsHash_t PyInt_AsSsize_t #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsSsize_t
#endif #endif
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
#define __Pyx_PyMethod_New(func, self, klass) ((self) ? ((void)(klass), PyMethod_New(func, self)) : __Pyx_NewRef(func)) #define __Pyx_PyMethod_New(func, self, klass) ((self) ? ((void)(klass), PyMethod_New(func, self)) : __Pyx_NewRef(func))
@ -570,8 +704,10 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
} __Pyx_PyAsyncMethodsStruct; } __Pyx_PyAsyncMethodsStruct;
#endif #endif
#if defined(WIN32) || defined(MS_WINDOWS) #if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)
#define _USE_MATH_DEFINES #if !defined(_USE_MATH_DEFINES)
#define _USE_MATH_DEFINES
#endif
#endif #endif
#include <math.h> #include <math.h>
#ifdef NAN #ifdef NAN
@ -701,6 +837,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
(likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj)) (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj))
static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*);
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t);
static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*);
#if CYTHON_ASSUME_SAFE_MACROS #if CYTHON_ASSUME_SAFE_MACROS
#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) #define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
#else #else
@ -1011,13 +1148,21 @@ static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args,
#ifndef Py_MEMBER_SIZE #ifndef Py_MEMBER_SIZE
#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) #define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
#endif #endif
#if CYTHON_FAST_PYCALL
static size_t __pyx_pyframe_localsplus_offset = 0; static size_t __pyx_pyframe_localsplus_offset = 0;
#include "frameobject.h" #include "frameobject.h"
#if PY_VERSION_HEX >= 0x030b00a6
#ifndef Py_BUILD_CORE
#define Py_BUILD_CORE 1
#endif
#include "internal/pycore_frame.h"
#endif
#define __Pxy_PyFrame_Initialize_Offsets()\ #define __Pxy_PyFrame_Initialize_Offsets()\
((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)),\ ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)),\
(void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus))) (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))
#define __Pyx_PyFrame_GetLocalsplus(frame)\ #define __Pyx_PyFrame_GetLocalsplus(frame)\
(assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset)) (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset))
#endif // CYTHON_FAST_PYCALL
#endif #endif
/* PyObjectCall.proto */ /* PyObjectCall.proto */
@ -1119,6 +1264,12 @@ static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_ve
static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name); static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name);
#endif #endif
/* PySequenceContains.proto */
static CYTHON_INLINE int __Pyx_PySequence_ContainsTF(PyObject* item, PyObject* seq, int eq) {
int result = PySequence_Contains(seq, item);
return unlikely(result < 0) ? result : (result == (eq == Py_EQ));
}
/* Import.proto */ /* Import.proto */
static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level); static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level);
@ -1174,12 +1325,17 @@ static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object);
static void __Pyx_AddTraceback(const char *funcname, int c_line, static void __Pyx_AddTraceback(const char *funcname, int c_line,
int py_line, const char *filename); int py_line, const char *filename);
/* CIntToPy.proto */ /* GCCDiagnostics.proto */
static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value); #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#define __Pyx_HAS_GCC_DIAGNOSTIC
#endif
/* CIntFromPy.proto */ /* CIntFromPy.proto */
static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *); static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *);
/* CIntToPy.proto */
static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value);
/* CIntFromPy.proto */ /* CIntFromPy.proto */
static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *); static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *);
@ -1244,9 +1400,9 @@ static const char __pyx_k_aiohttp__helpers[] = "aiohttp._helpers";
static const char __pyx_k_cline_in_traceback[] = "cline_in_traceback"; static const char __pyx_k_cline_in_traceback[] = "cline_in_traceback";
static const char __pyx_k_pyx_unpickle_reify[] = "__pyx_unpickle_reify"; static const char __pyx_k_pyx_unpickle_reify[] = "__pyx_unpickle_reify";
static const char __pyx_k_reified_property_is_read_only[] = "reified property is read-only"; static const char __pyx_k_reified_property_is_read_only[] = "reified property is read-only";
static const char __pyx_k_Incompatible_checksums_s_vs_0x77[] = "Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))"; static const char __pyx_k_Incompatible_checksums_0x_x_vs_0[] = "Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))";
static PyObject *__pyx_n_s_AttributeError; static PyObject *__pyx_n_s_AttributeError;
static PyObject *__pyx_kp_s_Incompatible_checksums_s_vs_0x77; static PyObject *__pyx_kp_s_Incompatible_checksums_0x_x_vs_0;
static PyObject *__pyx_n_s_KeyError; static PyObject *__pyx_n_s_KeyError;
static PyObject *__pyx_n_s_PickleError; static PyObject *__pyx_n_s_PickleError;
static PyObject *__pyx_n_s_aiohttp__helpers; static PyObject *__pyx_n_s_aiohttp__helpers;
@ -1285,10 +1441,13 @@ static PyObject *__pyx_pf_7aiohttp_8_helpers_5reify_6__reduce_cython__(struct __
static PyObject *__pyx_pf_7aiohttp_8_helpers_5reify_8__setstate_cython__(struct __pyx_obj_7aiohttp_8_helpers_reify *__pyx_v_self, PyObject *__pyx_v___pyx_state); /* proto */ static PyObject *__pyx_pf_7aiohttp_8_helpers_5reify_8__setstate_cython__(struct __pyx_obj_7aiohttp_8_helpers_reify *__pyx_v_self, PyObject *__pyx_v___pyx_state); /* proto */
static PyObject *__pyx_pf_7aiohttp_8_helpers___pyx_unpickle_reify(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v___pyx_type, long __pyx_v___pyx_checksum, PyObject *__pyx_v___pyx_state); /* proto */ static PyObject *__pyx_pf_7aiohttp_8_helpers___pyx_unpickle_reify(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v___pyx_type, long __pyx_v___pyx_checksum, PyObject *__pyx_v___pyx_state); /* proto */
static PyObject *__pyx_tp_new_7aiohttp_8_helpers_reify(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ static PyObject *__pyx_tp_new_7aiohttp_8_helpers_reify(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
static PyObject *__pyx_int_88416349;
static PyObject *__pyx_int_124832655; static PyObject *__pyx_int_124832655;
static PyObject *__pyx_int_250410337;
static PyObject *__pyx_tuple_; static PyObject *__pyx_tuple_;
static PyObject *__pyx_tuple__2; static PyObject *__pyx_tuple__2;
static PyObject *__pyx_codeobj__3; static PyObject *__pyx_tuple__3;
static PyObject *__pyx_codeobj__4;
/* Late includes */ /* Late includes */
/* "aiohttp/_helpers.pyx":13 /* "aiohttp/_helpers.pyx":13
@ -2259,12 +2418,12 @@ static PyObject *__pyx_pf_7aiohttp_8_helpers___pyx_unpickle_reify(CYTHON_UNUSED
PyObject *__pyx_v___pyx_result = 0; PyObject *__pyx_v___pyx_result = 0;
PyObject *__pyx_r = NULL; PyObject *__pyx_r = NULL;
__Pyx_RefNannyDeclarations __Pyx_RefNannyDeclarations
int __pyx_t_1; PyObject *__pyx_t_1 = NULL;
PyObject *__pyx_t_2 = NULL; int __pyx_t_2;
PyObject *__pyx_t_3 = NULL; int __pyx_t_3;
PyObject *__pyx_t_4 = NULL; PyObject *__pyx_t_4 = NULL;
PyObject *__pyx_t_5 = NULL; PyObject *__pyx_t_5 = NULL;
int __pyx_t_6; PyObject *__pyx_t_6 = NULL;
int __pyx_lineno = 0; int __pyx_lineno = 0;
const char *__pyx_filename = NULL; const char *__pyx_filename = NULL;
int __pyx_clineno = 0; int __pyx_clineno = 0;
@ -2273,114 +2432,118 @@ static PyObject *__pyx_pf_7aiohttp_8_helpers___pyx_unpickle_reify(CYTHON_UNUSED
/* "(tree fragment)":4 /* "(tree fragment)":4
* cdef object __pyx_PickleError * cdef object __pyx_PickleError
* cdef object __pyx_result * cdef object __pyx_result
* if __pyx_checksum != 0x770cb8f: # <<<<<<<<<<<<<< * if __pyx_checksum not in (0x770cb8f, 0xeecf561, 0x545205d): # <<<<<<<<<<<<<<
* from pickle import PickleError as __pyx_PickleError * from pickle import PickleError as __pyx_PickleError
* raise __pyx_PickleError("Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))" % __pyx_checksum) * raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum)
*/ */
__pyx_t_1 = ((__pyx_v___pyx_checksum != 0x770cb8f) != 0); __pyx_t_1 = __Pyx_PyInt_From_long(__pyx_v___pyx_checksum); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 4, __pyx_L1_error)
if (__pyx_t_1) { __Pyx_GOTREF(__pyx_t_1);
__pyx_t_2 = (__Pyx_PySequence_ContainsTF(__pyx_t_1, __pyx_tuple__2, Py_NE)); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(1, 4, __pyx_L1_error)
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_t_3 = (__pyx_t_2 != 0);
if (__pyx_t_3) {
/* "(tree fragment)":5 /* "(tree fragment)":5
* cdef object __pyx_result * cdef object __pyx_result
* if __pyx_checksum != 0x770cb8f: * if __pyx_checksum not in (0x770cb8f, 0xeecf561, 0x545205d):
* from pickle import PickleError as __pyx_PickleError # <<<<<<<<<<<<<< * from pickle import PickleError as __pyx_PickleError # <<<<<<<<<<<<<<
* raise __pyx_PickleError("Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))" % __pyx_checksum) * raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum)
* __pyx_result = reify.__new__(__pyx_type) * __pyx_result = reify.__new__(__pyx_type)
*/ */
__pyx_t_2 = PyList_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 5, __pyx_L1_error) __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 5, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_2); __Pyx_GOTREF(__pyx_t_1);
__Pyx_INCREF(__pyx_n_s_PickleError); __Pyx_INCREF(__pyx_n_s_PickleError);
__Pyx_GIVEREF(__pyx_n_s_PickleError); __Pyx_GIVEREF(__pyx_n_s_PickleError);
PyList_SET_ITEM(__pyx_t_2, 0, __pyx_n_s_PickleError); PyList_SET_ITEM(__pyx_t_1, 0, __pyx_n_s_PickleError);
__pyx_t_3 = __Pyx_Import(__pyx_n_s_pickle, __pyx_t_2, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 5, __pyx_L1_error) __pyx_t_4 = __Pyx_Import(__pyx_n_s_pickle, __pyx_t_1, 0); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 5, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_3); __Pyx_GOTREF(__pyx_t_4);
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_t_2 = __Pyx_ImportFrom(__pyx_t_3, __pyx_n_s_PickleError); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 5, __pyx_L1_error) __pyx_t_1 = __Pyx_ImportFrom(__pyx_t_4, __pyx_n_s_PickleError); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 5, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_2); __Pyx_GOTREF(__pyx_t_1);
__Pyx_INCREF(__pyx_t_2); __Pyx_INCREF(__pyx_t_1);
__pyx_v___pyx_PickleError = __pyx_t_2; __pyx_v___pyx_PickleError = __pyx_t_1;
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
/* "(tree fragment)":6 /* "(tree fragment)":6
* if __pyx_checksum != 0x770cb8f: * if __pyx_checksum not in (0x770cb8f, 0xeecf561, 0x545205d):
* from pickle import PickleError as __pyx_PickleError * from pickle import PickleError as __pyx_PickleError
* raise __pyx_PickleError("Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))" % __pyx_checksum) # <<<<<<<<<<<<<< * raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum) # <<<<<<<<<<<<<<
* __pyx_result = reify.__new__(__pyx_type) * __pyx_result = reify.__new__(__pyx_type)
* if __pyx_state is not None: * if __pyx_state is not None:
*/ */
__pyx_t_2 = __Pyx_PyInt_From_long(__pyx_v___pyx_checksum); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 6, __pyx_L1_error) __pyx_t_1 = __Pyx_PyInt_From_long(__pyx_v___pyx_checksum); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_2); __Pyx_GOTREF(__pyx_t_1);
__pyx_t_4 = __Pyx_PyString_Format(__pyx_kp_s_Incompatible_checksums_s_vs_0x77, __pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 6, __pyx_L1_error) __pyx_t_5 = __Pyx_PyString_Format(__pyx_kp_s_Incompatible_checksums_0x_x_vs_0, __pyx_t_1); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_4); __Pyx_GOTREF(__pyx_t_5);
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__Pyx_INCREF(__pyx_v___pyx_PickleError); __Pyx_INCREF(__pyx_v___pyx_PickleError);
__pyx_t_2 = __pyx_v___pyx_PickleError; __pyx_t_5 = NULL; __pyx_t_1 = __pyx_v___pyx_PickleError; __pyx_t_6 = NULL;
if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_2))) { if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_1))) {
__pyx_t_5 = PyMethod_GET_SELF(__pyx_t_2); __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_1);
if (likely(__pyx_t_5)) { if (likely(__pyx_t_6)) {
PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2); PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_1);
__Pyx_INCREF(__pyx_t_5); __Pyx_INCREF(__pyx_t_6);
__Pyx_INCREF(function); __Pyx_INCREF(function);
__Pyx_DECREF_SET(__pyx_t_2, function); __Pyx_DECREF_SET(__pyx_t_1, function);
} }
} }
__pyx_t_3 = (__pyx_t_5) ? __Pyx_PyObject_Call2Args(__pyx_t_2, __pyx_t_5, __pyx_t_4) : __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_t_4); __pyx_t_4 = (__pyx_t_6) ? __Pyx_PyObject_Call2Args(__pyx_t_1, __pyx_t_6, __pyx_t_5) : __Pyx_PyObject_CallOneArg(__pyx_t_1, __pyx_t_5);
__Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0; __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
__Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_4);
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__Pyx_Raise(__pyx_t_4, 0, 0, 0);
__Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_3);
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
__Pyx_Raise(__pyx_t_3, 0, 0, 0);
__Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
__PYX_ERR(1, 6, __pyx_L1_error) __PYX_ERR(1, 6, __pyx_L1_error)
/* "(tree fragment)":4 /* "(tree fragment)":4
* cdef object __pyx_PickleError * cdef object __pyx_PickleError
* cdef object __pyx_result * cdef object __pyx_result
* if __pyx_checksum != 0x770cb8f: # <<<<<<<<<<<<<< * if __pyx_checksum not in (0x770cb8f, 0xeecf561, 0x545205d): # <<<<<<<<<<<<<<
* from pickle import PickleError as __pyx_PickleError * from pickle import PickleError as __pyx_PickleError
* raise __pyx_PickleError("Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))" % __pyx_checksum) * raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum)
*/ */
} }
/* "(tree fragment)":7 /* "(tree fragment)":7
* from pickle import PickleError as __pyx_PickleError * from pickle import PickleError as __pyx_PickleError
* raise __pyx_PickleError("Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))" % __pyx_checksum) * raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum)
* __pyx_result = reify.__new__(__pyx_type) # <<<<<<<<<<<<<< * __pyx_result = reify.__new__(__pyx_type) # <<<<<<<<<<<<<<
* if __pyx_state is not None: * if __pyx_state is not None:
* __pyx_unpickle_reify__set_state(<reify> __pyx_result, __pyx_state) * __pyx_unpickle_reify__set_state(<reify> __pyx_result, __pyx_state)
*/ */
__pyx_t_2 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_ptype_7aiohttp_8_helpers_reify), __pyx_n_s_new); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 7, __pyx_L1_error) __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_ptype_7aiohttp_8_helpers_reify), __pyx_n_s_new); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 7, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_2); __Pyx_GOTREF(__pyx_t_1);
__pyx_t_4 = NULL; __pyx_t_5 = NULL;
if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_2))) { if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_1))) {
__pyx_t_4 = PyMethod_GET_SELF(__pyx_t_2); __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_1);
if (likely(__pyx_t_4)) { if (likely(__pyx_t_5)) {
PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2); PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_1);
__Pyx_INCREF(__pyx_t_4); __Pyx_INCREF(__pyx_t_5);
__Pyx_INCREF(function); __Pyx_INCREF(function);
__Pyx_DECREF_SET(__pyx_t_2, function); __Pyx_DECREF_SET(__pyx_t_1, function);
} }
} }
__pyx_t_3 = (__pyx_t_4) ? __Pyx_PyObject_Call2Args(__pyx_t_2, __pyx_t_4, __pyx_v___pyx_type) : __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_v___pyx_type); __pyx_t_4 = (__pyx_t_5) ? __Pyx_PyObject_Call2Args(__pyx_t_1, __pyx_t_5, __pyx_v___pyx_type) : __Pyx_PyObject_CallOneArg(__pyx_t_1, __pyx_v___pyx_type);
__Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 7, __pyx_L1_error) if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 7, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_3); __Pyx_GOTREF(__pyx_t_4);
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_v___pyx_result = __pyx_t_3; __pyx_v___pyx_result = __pyx_t_4;
__pyx_t_3 = 0; __pyx_t_4 = 0;
/* "(tree fragment)":8 /* "(tree fragment)":8
* raise __pyx_PickleError("Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))" % __pyx_checksum) * raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum)
* __pyx_result = reify.__new__(__pyx_type) * __pyx_result = reify.__new__(__pyx_type)
* if __pyx_state is not None: # <<<<<<<<<<<<<< * if __pyx_state is not None: # <<<<<<<<<<<<<<
* __pyx_unpickle_reify__set_state(<reify> __pyx_result, __pyx_state) * __pyx_unpickle_reify__set_state(<reify> __pyx_result, __pyx_state)
* return __pyx_result * return __pyx_result
*/ */
__pyx_t_1 = (__pyx_v___pyx_state != Py_None); __pyx_t_3 = (__pyx_v___pyx_state != Py_None);
__pyx_t_6 = (__pyx_t_1 != 0); __pyx_t_2 = (__pyx_t_3 != 0);
if (__pyx_t_6) { if (__pyx_t_2) {
/* "(tree fragment)":9 /* "(tree fragment)":9
* __pyx_result = reify.__new__(__pyx_type) * __pyx_result = reify.__new__(__pyx_type)
@ -2390,12 +2553,12 @@ static PyObject *__pyx_pf_7aiohttp_8_helpers___pyx_unpickle_reify(CYTHON_UNUSED
* cdef __pyx_unpickle_reify__set_state(reify __pyx_result, tuple __pyx_state): * cdef __pyx_unpickle_reify__set_state(reify __pyx_result, tuple __pyx_state):
*/ */
if (!(likely(PyTuple_CheckExact(__pyx_v___pyx_state))||((__pyx_v___pyx_state) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected %.16s, got %.200s", "tuple", Py_TYPE(__pyx_v___pyx_state)->tp_name), 0))) __PYX_ERR(1, 9, __pyx_L1_error) if (!(likely(PyTuple_CheckExact(__pyx_v___pyx_state))||((__pyx_v___pyx_state) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected %.16s, got %.200s", "tuple", Py_TYPE(__pyx_v___pyx_state)->tp_name), 0))) __PYX_ERR(1, 9, __pyx_L1_error)
__pyx_t_3 = __pyx_f_7aiohttp_8_helpers___pyx_unpickle_reify__set_state(((struct __pyx_obj_7aiohttp_8_helpers_reify *)__pyx_v___pyx_result), ((PyObject*)__pyx_v___pyx_state)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 9, __pyx_L1_error) __pyx_t_4 = __pyx_f_7aiohttp_8_helpers___pyx_unpickle_reify__set_state(((struct __pyx_obj_7aiohttp_8_helpers_reify *)__pyx_v___pyx_result), ((PyObject*)__pyx_v___pyx_state)); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 9, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_3); __Pyx_GOTREF(__pyx_t_4);
__Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
/* "(tree fragment)":8 /* "(tree fragment)":8
* raise __pyx_PickleError("Incompatible checksums (%s vs 0x770cb8f = (name, wrapped))" % __pyx_checksum) * raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum)
* __pyx_result = reify.__new__(__pyx_type) * __pyx_result = reify.__new__(__pyx_type)
* if __pyx_state is not None: # <<<<<<<<<<<<<< * if __pyx_state is not None: # <<<<<<<<<<<<<<
* __pyx_unpickle_reify__set_state(<reify> __pyx_result, __pyx_state) * __pyx_unpickle_reify__set_state(<reify> __pyx_result, __pyx_state)
@ -2423,10 +2586,10 @@ static PyObject *__pyx_pf_7aiohttp_8_helpers___pyx_unpickle_reify(CYTHON_UNUSED
/* function exit code */ /* function exit code */
__pyx_L1_error:; __pyx_L1_error:;
__Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_1);
__Pyx_XDECREF(__pyx_t_3);
__Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_4);
__Pyx_XDECREF(__pyx_t_5); __Pyx_XDECREF(__pyx_t_5);
__Pyx_XDECREF(__pyx_t_6);
__Pyx_AddTraceback("aiohttp._helpers.__pyx_unpickle_reify", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_AddTraceback("aiohttp._helpers.__pyx_unpickle_reify", __pyx_clineno, __pyx_lineno, __pyx_filename);
__pyx_r = NULL; __pyx_r = NULL;
__pyx_L0:; __pyx_L0:;
@ -2726,12 +2889,15 @@ static PyTypeObject __pyx_type_7aiohttp_8_helpers_reify = {
#if PY_VERSION_HEX >= 0x030400a1 #if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/ 0, /*tp_finalize*/
#endif #endif
#if PY_VERSION_HEX >= 0x030800b1 #if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
0, /*tp_vectorcall*/ 0, /*tp_vectorcall*/
#endif #endif
#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000 #if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
0, /*tp_print*/ 0, /*tp_print*/
#endif #endif
#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
0, /*tp_pypy_flags*/
#endif
}; };
static PyMethodDef __pyx_methods[] = { static PyMethodDef __pyx_methods[] = {
@ -2781,7 +2947,7 @@ static struct PyModuleDef __pyx_moduledef = {
static __Pyx_StringTabEntry __pyx_string_tab[] = { static __Pyx_StringTabEntry __pyx_string_tab[] = {
{&__pyx_n_s_AttributeError, __pyx_k_AttributeError, sizeof(__pyx_k_AttributeError), 0, 0, 1, 1}, {&__pyx_n_s_AttributeError, __pyx_k_AttributeError, sizeof(__pyx_k_AttributeError), 0, 0, 1, 1},
{&__pyx_kp_s_Incompatible_checksums_s_vs_0x77, __pyx_k_Incompatible_checksums_s_vs_0x77, sizeof(__pyx_k_Incompatible_checksums_s_vs_0x77), 0, 0, 1, 0}, {&__pyx_kp_s_Incompatible_checksums_0x_x_vs_0, __pyx_k_Incompatible_checksums_0x_x_vs_0, sizeof(__pyx_k_Incompatible_checksums_0x_x_vs_0), 0, 0, 1, 0},
{&__pyx_n_s_KeyError, __pyx_k_KeyError, sizeof(__pyx_k_KeyError), 0, 0, 1, 1}, {&__pyx_n_s_KeyError, __pyx_k_KeyError, sizeof(__pyx_k_KeyError), 0, 0, 1, 1},
{&__pyx_n_s_PickleError, __pyx_k_PickleError, sizeof(__pyx_k_PickleError), 0, 0, 1, 1}, {&__pyx_n_s_PickleError, __pyx_k_PickleError, sizeof(__pyx_k_PickleError), 0, 0, 1, 1},
{&__pyx_n_s_aiohttp__helpers, __pyx_k_aiohttp__helpers, sizeof(__pyx_k_aiohttp__helpers), 0, 0, 1, 1}, {&__pyx_n_s_aiohttp__helpers, __pyx_k_aiohttp__helpers, sizeof(__pyx_k_aiohttp__helpers), 0, 0, 1, 1},
@ -2835,15 +3001,26 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
__Pyx_GOTREF(__pyx_tuple_); __Pyx_GOTREF(__pyx_tuple_);
__Pyx_GIVEREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_);
/* "(tree fragment)":4
* cdef object __pyx_PickleError
* cdef object __pyx_result
* if __pyx_checksum not in (0x770cb8f, 0xeecf561, 0x545205d): # <<<<<<<<<<<<<<
* from pickle import PickleError as __pyx_PickleError
* raise __pyx_PickleError("Incompatible checksums (0x%x vs (0x770cb8f, 0xeecf561, 0x545205d) = (name, wrapped))" % __pyx_checksum)
*/
__pyx_tuple__2 = PyTuple_Pack(3, __pyx_int_124832655, __pyx_int_250410337, __pyx_int_88416349); if (unlikely(!__pyx_tuple__2)) __PYX_ERR(1, 4, __pyx_L1_error)
__Pyx_GOTREF(__pyx_tuple__2);
__Pyx_GIVEREF(__pyx_tuple__2);
/* "(tree fragment)":1 /* "(tree fragment)":1
* def __pyx_unpickle_reify(__pyx_type, long __pyx_checksum, __pyx_state): # <<<<<<<<<<<<<< * def __pyx_unpickle_reify(__pyx_type, long __pyx_checksum, __pyx_state): # <<<<<<<<<<<<<<
* cdef object __pyx_PickleError * cdef object __pyx_PickleError
* cdef object __pyx_result * cdef object __pyx_result
*/ */
__pyx_tuple__2 = PyTuple_Pack(5, __pyx_n_s_pyx_type, __pyx_n_s_pyx_checksum, __pyx_n_s_pyx_state, __pyx_n_s_pyx_PickleError, __pyx_n_s_pyx_result); if (unlikely(!__pyx_tuple__2)) __PYX_ERR(1, 1, __pyx_L1_error) __pyx_tuple__3 = PyTuple_Pack(5, __pyx_n_s_pyx_type, __pyx_n_s_pyx_checksum, __pyx_n_s_pyx_state, __pyx_n_s_pyx_PickleError, __pyx_n_s_pyx_result); if (unlikely(!__pyx_tuple__3)) __PYX_ERR(1, 1, __pyx_L1_error)
__Pyx_GOTREF(__pyx_tuple__2); __Pyx_GOTREF(__pyx_tuple__3);
__Pyx_GIVEREF(__pyx_tuple__2); __Pyx_GIVEREF(__pyx_tuple__3);
__pyx_codeobj__3 = (PyObject*)__Pyx_PyCode_New(3, 0, 5, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__2, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_stringsource, __pyx_n_s_pyx_unpickle_reify, 1, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__3)) __PYX_ERR(1, 1, __pyx_L1_error) __pyx_codeobj__4 = (PyObject*)__Pyx_PyCode_New(3, 0, 5, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__3, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_stringsource, __pyx_n_s_pyx_unpickle_reify, 1, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__4)) __PYX_ERR(1, 1, __pyx_L1_error)
__Pyx_RefNannyFinishContext(); __Pyx_RefNannyFinishContext();
return 0; return 0;
__pyx_L1_error:; __pyx_L1_error:;
@ -2853,7 +3030,9 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) { static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) {
if (__Pyx_InitStrings(__pyx_string_tab) < 0) __PYX_ERR(0, 1, __pyx_L1_error); if (__Pyx_InitStrings(__pyx_string_tab) < 0) __PYX_ERR(0, 1, __pyx_L1_error);
__pyx_int_88416349 = PyInt_FromLong(88416349L); if (unlikely(!__pyx_int_88416349)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_124832655 = PyInt_FromLong(124832655L); if (unlikely(!__pyx_int_124832655)) __PYX_ERR(0, 1, __pyx_L1_error) __pyx_int_124832655 = PyInt_FromLong(124832655L); if (unlikely(!__pyx_int_124832655)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_250410337 = PyInt_FromLong(250410337L); if (unlikely(!__pyx_int_250410337)) __PYX_ERR(0, 1, __pyx_L1_error)
return 0; return 0;
__pyx_L1_error:; __pyx_L1_error:;
return -1; return -1;
@ -3084,11 +3263,9 @@ if (!__Pyx_RefNanny) {
#endif #endif
/*--- Library function declarations ---*/ /*--- Library function declarations ---*/
/*--- Threads initialization code ---*/ /*--- Threads initialization code ---*/
#if defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS #if defined(WITH_THREAD) && PY_VERSION_HEX < 0x030700F0 && defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS
#ifdef WITH_THREAD /* Python build with threading support? */
PyEval_InitThreads(); PyEval_InitThreads();
#endif #endif
#endif
/*--- Module creation code ---*/ /*--- Module creation code ---*/
#if CYTHON_PEP489_MULTI_PHASE_INIT #if CYTHON_PEP489_MULTI_PHASE_INIT
__pyx_m = __pyx_pyinit_module; __pyx_m = __pyx_pyinit_module;
@ -3790,7 +3967,7 @@ done:
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) {
PyObject *result; PyObject *result;
ternaryfunc call = func->ob_type->tp_call; ternaryfunc call = Py_TYPE(func)->tp_call;
if (unlikely(!call)) if (unlikely(!call))
return PyObject_Call(func, arg, kw); return PyObject_Call(func, arg, kw);
if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object")))
@ -3877,7 +4054,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObjec
if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) { if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) {
return __Pyx_PyObject_CallMethO(func, arg); return __Pyx_PyObject_CallMethO(func, arg);
#if CYTHON_FAST_PYCCALL #if CYTHON_FAST_PYCCALL
} else if (PyCFunction_GET_FLAGS(func) & METH_FASTCALL) { } else if (__Pyx_PyFastCFunction_Check(func)) {
return __Pyx_PyCFunction_FastCall(func, &arg, 1); return __Pyx_PyCFunction_FastCall(func, &arg, 1);
#endif #endif
} }
@ -4356,17 +4533,35 @@ static int __Pyx_setup_reduce_is_named(PyObject* meth, PyObject* name) {
static int __Pyx_setup_reduce(PyObject* type_obj) { static int __Pyx_setup_reduce(PyObject* type_obj) {
int ret = 0; int ret = 0;
PyObject *object_reduce = NULL; PyObject *object_reduce = NULL;
PyObject *object_getstate = NULL;
PyObject *object_reduce_ex = NULL; PyObject *object_reduce_ex = NULL;
PyObject *reduce = NULL; PyObject *reduce = NULL;
PyObject *reduce_ex = NULL; PyObject *reduce_ex = NULL;
PyObject *reduce_cython = NULL; PyObject *reduce_cython = NULL;
PyObject *setstate = NULL; PyObject *setstate = NULL;
PyObject *setstate_cython = NULL; PyObject *setstate_cython = NULL;
PyObject *getstate = NULL;
#if CYTHON_USE_PYTYPE_LOOKUP #if CYTHON_USE_PYTYPE_LOOKUP
if (_PyType_Lookup((PyTypeObject*)type_obj, __pyx_n_s_getstate)) goto __PYX_GOOD; getstate = _PyType_Lookup((PyTypeObject*)type_obj, __pyx_n_s_getstate);
#else #else
if (PyObject_HasAttr(type_obj, __pyx_n_s_getstate)) goto __PYX_GOOD; getstate = __Pyx_PyObject_GetAttrStrNoError(type_obj, __pyx_n_s_getstate);
if (!getstate && PyErr_Occurred()) {
goto __PYX_BAD;
}
#endif #endif
if (getstate) {
#if CYTHON_USE_PYTYPE_LOOKUP
object_getstate = _PyType_Lookup(&PyBaseObject_Type, __pyx_n_s_getstate);
#else
object_getstate = __Pyx_PyObject_GetAttrStrNoError((PyObject*)&PyBaseObject_Type, __pyx_n_s_getstate);
if (!object_getstate && PyErr_Occurred()) {
goto __PYX_BAD;
}
#endif
if (object_getstate != getstate) {
goto __PYX_GOOD;
}
}
#if CYTHON_USE_PYTYPE_LOOKUP #if CYTHON_USE_PYTYPE_LOOKUP
object_reduce_ex = _PyType_Lookup(&PyBaseObject_Type, __pyx_n_s_reduce_ex); if (!object_reduce_ex) goto __PYX_BAD; object_reduce_ex = _PyType_Lookup(&PyBaseObject_Type, __pyx_n_s_reduce_ex); if (!object_reduce_ex) goto __PYX_BAD;
#else #else
@ -4411,6 +4606,8 @@ __PYX_GOOD:
#if !CYTHON_USE_PYTYPE_LOOKUP #if !CYTHON_USE_PYTYPE_LOOKUP
Py_XDECREF(object_reduce); Py_XDECREF(object_reduce);
Py_XDECREF(object_reduce_ex); Py_XDECREF(object_reduce_ex);
Py_XDECREF(object_getstate);
Py_XDECREF(getstate);
#endif #endif
Py_XDECREF(reduce); Py_XDECREF(reduce);
Py_XDECREF(reduce_ex); Py_XDECREF(reduce_ex);
@ -4452,7 +4649,7 @@ static int __Pyx_CLineForTraceback(CYTHON_NCP_UNUSED PyThreadState *tstate, int
} }
if (!use_cline) { if (!use_cline) {
c_line = 0; c_line = 0;
PyObject_SetAttr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback, Py_False); (void) PyObject_SetAttr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback, Py_False);
} }
else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) { else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) {
c_line = 0; c_line = 0;
@ -4546,33 +4743,40 @@ static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) {
#include "compile.h" #include "compile.h"
#include "frameobject.h" #include "frameobject.h"
#include "traceback.h" #include "traceback.h"
#if PY_VERSION_HEX >= 0x030b00a6
#ifndef Py_BUILD_CORE
#define Py_BUILD_CORE 1
#endif
#include "internal/pycore_frame.h"
#endif
static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
const char *funcname, int c_line, const char *funcname, int c_line,
int py_line, const char *filename) { int py_line, const char *filename) {
PyCodeObject *py_code = 0; PyCodeObject *py_code = NULL;
PyObject *py_srcfile = 0; PyObject *py_funcname = NULL;
PyObject *py_funcname = 0;
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
PyObject *py_srcfile = NULL;
py_srcfile = PyString_FromString(filename); py_srcfile = PyString_FromString(filename);
#else
py_srcfile = PyUnicode_FromString(filename);
#endif
if (!py_srcfile) goto bad; if (!py_srcfile) goto bad;
#endif
if (c_line) { if (c_line) {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
if (!py_funcname) goto bad;
#else #else
py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
if (!py_funcname) goto bad;
funcname = PyUnicode_AsUTF8(py_funcname);
if (!funcname) goto bad;
#endif #endif
} }
else { else {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
py_funcname = PyString_FromString(funcname); py_funcname = PyString_FromString(funcname);
#else if (!py_funcname) goto bad;
py_funcname = PyUnicode_FromString(funcname);
#endif #endif
} }
if (!py_funcname) goto bad; #if PY_MAJOR_VERSION < 3
py_code = __Pyx_PyCode_New( py_code = __Pyx_PyCode_New(
0, 0,
0, 0,
@ -4591,11 +4795,16 @@ static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
__pyx_empty_bytes /*PyObject *lnotab*/ __pyx_empty_bytes /*PyObject *lnotab*/
); );
Py_DECREF(py_srcfile); Py_DECREF(py_srcfile);
Py_DECREF(py_funcname); #else
py_code = PyCode_NewEmpty(filename, funcname, py_line);
#endif
Py_XDECREF(py_funcname); // XDECREF since it's only set on Py3 if cline
return py_code; return py_code;
bad: bad:
Py_XDECREF(py_srcfile);
Py_XDECREF(py_funcname); Py_XDECREF(py_funcname);
#if PY_MAJOR_VERSION < 3
Py_XDECREF(py_srcfile);
#endif
return NULL; return NULL;
} }
static void __Pyx_AddTraceback(const char *funcname, int c_line, static void __Pyx_AddTraceback(const char *funcname, int c_line,
@ -4603,14 +4812,24 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line,
PyCodeObject *py_code = 0; PyCodeObject *py_code = 0;
PyFrameObject *py_frame = 0; PyFrameObject *py_frame = 0;
PyThreadState *tstate = __Pyx_PyThreadState_Current; PyThreadState *tstate = __Pyx_PyThreadState_Current;
PyObject *ptype, *pvalue, *ptraceback;
if (c_line) { if (c_line) {
c_line = __Pyx_CLineForTraceback(tstate, c_line); c_line = __Pyx_CLineForTraceback(tstate, c_line);
} }
py_code = __pyx_find_code_object(c_line ? -c_line : py_line); py_code = __pyx_find_code_object(c_line ? -c_line : py_line);
if (!py_code) { if (!py_code) {
__Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
py_code = __Pyx_CreateCodeObjectForTraceback( py_code = __Pyx_CreateCodeObjectForTraceback(
funcname, c_line, py_line, filename); funcname, c_line, py_line, filename);
if (!py_code) goto bad; if (!py_code) {
/* If the code object creation fails, then we should clear the
fetched exception references and propagate the new exception */
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptraceback);
goto bad;
}
__Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
__pyx_insert_code_object(c_line ? -c_line : py_line, py_code); __pyx_insert_code_object(c_line ? -c_line : py_line, py_code);
} }
py_frame = PyFrame_New( py_frame = PyFrame_New(
@ -4649,40 +4868,16 @@ bad:
return (target_type) value;\ return (target_type) value;\
} }
/* CIntToPy */
static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) {
const long neg_one = (long) ((long) 0 - (long) 1), const_zero = (long) 0;
const int is_unsigned = neg_one > const_zero;
if (is_unsigned) {
if (sizeof(long) < sizeof(long)) {
return PyInt_FromLong((long) value);
} else if (sizeof(long) <= sizeof(unsigned long)) {
return PyLong_FromUnsignedLong((unsigned long) value);
#ifdef HAVE_LONG_LONG
} else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) {
return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value);
#endif
}
} else {
if (sizeof(long) <= sizeof(long)) {
return PyInt_FromLong((long) value);
#ifdef HAVE_LONG_LONG
} else if (sizeof(long) <= sizeof(PY_LONG_LONG)) {
return PyLong_FromLongLong((PY_LONG_LONG) value);
#endif
}
}
{
int one = 1; int little = (int)*(unsigned char *)&one;
unsigned char *bytes = (unsigned char *)&value;
return _PyLong_FromByteArray(bytes, sizeof(long),
little, !is_unsigned);
}
}
/* CIntFromPy */ /* CIntFromPy */
static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) { static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) {
const long neg_one = (long) ((long) 0 - (long) 1), const_zero = (long) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
const long neg_one = (long) -1, const_zero = (long) 0;
#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
const int is_unsigned = neg_one > const_zero; const int is_unsigned = neg_one > const_zero;
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (likely(PyInt_Check(x))) { if (likely(PyInt_Check(x))) {
@ -4869,9 +5064,54 @@ raise_neg_overflow:
return (long) -1; return (long) -1;
} }
/* CIntToPy */
static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) {
#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
const long neg_one = (long) -1, const_zero = (long) 0;
#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
const int is_unsigned = neg_one > const_zero;
if (is_unsigned) {
if (sizeof(long) < sizeof(long)) {
return PyInt_FromLong((long) value);
} else if (sizeof(long) <= sizeof(unsigned long)) {
return PyLong_FromUnsignedLong((unsigned long) value);
#ifdef HAVE_LONG_LONG
} else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) {
return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value);
#endif
}
} else {
if (sizeof(long) <= sizeof(long)) {
return PyInt_FromLong((long) value);
#ifdef HAVE_LONG_LONG
} else if (sizeof(long) <= sizeof(PY_LONG_LONG)) {
return PyLong_FromLongLong((PY_LONG_LONG) value);
#endif
}
}
{
int one = 1; int little = (int)*(unsigned char *)&one;
unsigned char *bytes = (unsigned char *)&value;
return _PyLong_FromByteArray(bytes, sizeof(long),
little, !is_unsigned);
}
}
/* CIntFromPy */ /* CIntFromPy */
static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) { static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) {
const int neg_one = (int) ((int) 0 - (int) 1), const_zero = (int) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
const int neg_one = (int) -1, const_zero = (int) 0;
#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
const int is_unsigned = neg_one > const_zero; const int is_unsigned = neg_one > const_zero;
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (likely(PyInt_Check(x))) { if (likely(PyInt_Check(x))) {
@ -5160,11 +5400,33 @@ static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObj
/* CheckBinaryVersion */ /* CheckBinaryVersion */
static int __Pyx_check_binary_version(void) { static int __Pyx_check_binary_version(void) {
char ctversion[4], rtversion[4]; char ctversion[5];
PyOS_snprintf(ctversion, 4, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION); int same=1, i, found_dot;
PyOS_snprintf(rtversion, 4, "%s", Py_GetVersion()); const char* rt_from_call = Py_GetVersion();
if (ctversion[0] != rtversion[0] || ctversion[2] != rtversion[2]) { PyOS_snprintf(ctversion, 5, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION);
found_dot = 0;
for (i = 0; i < 4; i++) {
if (!ctversion[i]) {
same = (rt_from_call[i] < '0' || rt_from_call[i] > '9');
break;
}
if (rt_from_call[i] != ctversion[i]) {
same = 0;
break;
}
}
if (!same) {
char rtversion[5] = {'\0'};
char message[200]; char message[200];
for (i=0; i<4; ++i) {
if (rt_from_call[i] == '.') {
if (found_dot) break;
found_dot = 1;
} else if (rt_from_call[i] < '0' || rt_from_call[i] > '9') {
break;
}
rtversion[i] = rt_from_call[i];
}
PyOS_snprintf(message, sizeof(message), PyOS_snprintf(message, sizeof(message),
"compiletime version %s of module '%.100s' " "compiletime version %s of module '%.100s' "
"does not match runtime version %s", "does not match runtime version %s",
@ -5422,6 +5684,23 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
Py_DECREF(x); Py_DECREF(x);
return ival; return ival;
} }
static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) {
if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) {
return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o);
#if PY_MAJOR_VERSION < 3
} else if (likely(PyInt_CheckExact(o))) {
return PyInt_AS_LONG(o);
#endif
} else {
Py_ssize_t ival;
PyObject *x;
x = PyNumber_Index(o);
if (!x) return -1;
ival = PyInt_AsLong(x);
Py_DECREF(x);
return ival;
}
}
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) { static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False); return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False);
} }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -80,13 +80,13 @@ cdef inline object extend(object buf, const char* at, size_t length):
memcpy(ptr + s, at, length) memcpy(ptr + s, at, length)
DEF METHODS_COUNT = 34; DEF METHODS_COUNT = 46;
cdef list _http_method = [] cdef list _http_method = []
for i in range(METHODS_COUNT): for i in range(METHODS_COUNT):
_http_method.append( _http_method.append(
cparser.http_method_str(<cparser.http_method> i).decode('ascii')) cparser.llhttp_method_name(<cparser.llhttp_method_t> i).decode('ascii'))
cdef inline str http_method_str(int i): cdef inline str http_method_str(int i):
@ -272,8 +272,8 @@ cdef _new_response_message(object version,
cdef class HttpParser: cdef class HttpParser:
cdef: cdef:
cparser.http_parser* _cparser cparser.llhttp_t* _cparser
cparser.http_parser_settings* _csettings cparser.llhttp_settings_t* _csettings
bytearray _raw_name bytearray _raw_name
bytearray _raw_value bytearray _raw_value
@ -310,13 +310,13 @@ cdef class HttpParser:
Py_buffer py_buf Py_buffer py_buf
def __cinit__(self): def __cinit__(self):
self._cparser = <cparser.http_parser*> \ self._cparser = <cparser.llhttp_t*> \
PyMem_Malloc(sizeof(cparser.http_parser)) PyMem_Malloc(sizeof(cparser.llhttp_t))
if self._cparser is NULL: if self._cparser is NULL:
raise MemoryError() raise MemoryError()
self._csettings = <cparser.http_parser_settings*> \ self._csettings = <cparser.llhttp_settings_t*> \
PyMem_Malloc(sizeof(cparser.http_parser_settings)) PyMem_Malloc(sizeof(cparser.llhttp_settings_t))
if self._csettings is NULL: if self._csettings is NULL:
raise MemoryError() raise MemoryError()
@ -324,19 +324,20 @@ cdef class HttpParser:
PyMem_Free(self._cparser) PyMem_Free(self._cparser)
PyMem_Free(self._csettings) PyMem_Free(self._csettings)
cdef _init(self, cparser.http_parser_type mode, cdef _init(
object protocol, object loop, int limit, self, cparser.llhttp_type mode,
object timer=None, object protocol, object loop, int limit,
size_t max_line_size=8190, size_t max_headers=32768, object timer=None,
size_t max_field_size=8190, payload_exception=None, size_t max_line_size=8190, size_t max_headers=32768,
bint response_with_body=True, bint read_until_eof=False, size_t max_field_size=8190, payload_exception=None,
bint auto_decompress=True): bint response_with_body=True, bint read_until_eof=False,
cparser.http_parser_init(self._cparser, mode) bint auto_decompress=True,
):
cparser.llhttp_settings_init(self._csettings)
cparser.llhttp_init(self._cparser, mode, self._csettings)
self._cparser.data = <void*>self self._cparser.data = <void*>self
self._cparser.content_length = 0 self._cparser.content_length = 0
cparser.http_parser_settings_init(self._csettings)
self._protocol = protocol self._protocol = protocol
self._loop = loop self._loop = loop
self._timer = timer self._timer = timer
@ -417,14 +418,14 @@ cdef class HttpParser:
self._process_header() self._process_header()
method = http_method_str(self._cparser.method) method = http_method_str(self._cparser.method)
should_close = not cparser.http_should_keep_alive(self._cparser) should_close = not cparser.llhttp_should_keep_alive(self._cparser)
upgrade = self._cparser.upgrade upgrade = self._cparser.upgrade
chunked = self._cparser.flags & cparser.F_CHUNKED chunked = self._cparser.flags & cparser.F_CHUNKED
raw_headers = tuple(self._raw_headers) raw_headers = tuple(self._raw_headers)
headers = CIMultiDictProxy(self._headers) headers = CIMultiDictProxy(self._headers)
if upgrade or self._cparser.method == 5: # cparser.CONNECT: if upgrade or self._cparser.method == cparser.HTTP_CONNECT:
self._upgraded = True self._upgraded = True
# do not support old websocket spec # do not support old websocket spec
@ -450,11 +451,12 @@ cdef class HttpParser:
headers, raw_headers, should_close, encoding, headers, raw_headers, should_close, encoding,
upgrade, chunked) upgrade, chunked)
if (ULLONG_MAX > self._cparser.content_length > 0 or chunked or if (
self._cparser.method == 5 or # CONNECT: 5 ULLONG_MAX > self._cparser.content_length > 0 or chunked or
(self._cparser.status_code >= 199 and self._cparser.method == cparser.HTTP_CONNECT or
self._cparser.content_length == ULLONG_MAX and (self._cparser.status_code >= 199 and
self._read_until_eof) self._cparser.content_length == 0 and
self._read_until_eof)
): ):
payload = StreamReader( payload = StreamReader(
self._protocol, timer=self._timer, loop=self._loop, self._protocol, timer=self._timer, loop=self._loop,
@ -485,7 +487,7 @@ cdef class HttpParser:
pass pass
cdef inline http_version(self): cdef inline http_version(self):
cdef cparser.http_parser* parser = self._cparser cdef cparser.llhttp_t* parser = self._cparser
if parser.http_major == 1: if parser.http_major == 1:
if parser.http_minor == 0: if parser.http_minor == 0:
@ -504,12 +506,11 @@ cdef class HttpParser:
if self._cparser.flags & cparser.F_CHUNKED: if self._cparser.flags & cparser.F_CHUNKED:
raise TransferEncodingError( raise TransferEncodingError(
"Not enough data for satisfy transfer length header.") "Not enough data for satisfy transfer length header.")
elif self._cparser.flags & cparser.F_CONTENTLENGTH: elif self._cparser.flags & cparser.F_CONTENT_LENGTH:
raise ContentLengthError( raise ContentLengthError(
"Not enough data for satisfy content length header.") "Not enough data for satisfy content length header.")
elif self._cparser.http_errno != cparser.HPE_OK: elif cparser.llhttp_get_errno(self._cparser) != cparser.HPE_OK:
desc = cparser.http_errno_description( desc = cparser.llhttp_get_error_reason(self._cparser)
<cparser.http_errno> self._cparser.http_errno)
raise PayloadEncodingError(desc.decode('latin-1')) raise PayloadEncodingError(desc.decode('latin-1'))
else: else:
self._payload.feed_eof() self._payload.feed_eof()
@ -522,26 +523,36 @@ cdef class HttpParser:
cdef: cdef:
size_t data_len size_t data_len
size_t nb size_t nb
cdef cparser.llhttp_errno_t errno
PyObject_GetBuffer(data, &self.py_buf, PyBUF_SIMPLE) PyObject_GetBuffer(data, &self.py_buf, PyBUF_SIMPLE)
data_len = <size_t>self.py_buf.len data_len = <size_t>self.py_buf.len
nb = cparser.http_parser_execute( errno = cparser.llhttp_execute(
self._cparser, self._cparser,
self._csettings,
<char*>self.py_buf.buf, <char*>self.py_buf.buf,
data_len) data_len)
if errno is cparser.HPE_PAUSED_UPGRADE:
cparser.llhttp_resume_after_upgrade(self._cparser)
nb = cparser.llhttp_get_error_pos(self._cparser) - <char*>self.py_buf.buf
PyBuffer_Release(&self.py_buf) PyBuffer_Release(&self.py_buf)
if (self._cparser.http_errno != cparser.HPE_OK): if errno not in (cparser.HPE_OK, cparser.HPE_PAUSED_UPGRADE):
if self._payload_error == 0: if self._payload_error == 0:
if self._last_error is not None: if self._last_error is not None:
ex = self._last_error ex = self._last_error
self._last_error = None self._last_error = None
else: else:
ex = parser_error_from_errno( after = cparser.llhttp_get_error_pos(self._cparser)
<cparser.http_errno> self._cparser.http_errno) before = data[:after - <char*>self.py_buf.buf]
after_b = after.split(b"\n", 1)[0]
before = before.rsplit(b"\n", 1)[-1]
data = before + after_b
pointer = " " * (len(repr(before))-1) + "^"
ex = parser_error_from_errno(self._cparser, data, pointer)
self._payload = None self._payload = None
raise ex raise ex
@ -562,39 +573,76 @@ cdef class HttpParser:
cdef class HttpRequestParser(HttpParser): cdef class HttpRequestParser(HttpParser):
def __init__(self, protocol, loop, int limit, timer=None, def __init__(
size_t max_line_size=8190, size_t max_headers=32768, self, protocol, loop, int limit, timer=None,
size_t max_field_size=8190, payload_exception=None, size_t max_line_size=8190, size_t max_headers=32768,
bint response_with_body=True, bint read_until_eof=False, size_t max_field_size=8190, payload_exception=None,
bint response_with_body=True, bint read_until_eof=False,
bint auto_decompress=True,
): ):
self._init(cparser.HTTP_REQUEST, protocol, loop, limit, timer, self._init(cparser.HTTP_REQUEST, protocol, loop, limit, timer,
max_line_size, max_headers, max_field_size, max_line_size, max_headers, max_field_size,
payload_exception, response_with_body, read_until_eof) payload_exception, response_with_body, read_until_eof,
auto_decompress)
cdef object _on_status_complete(self): cdef object _on_status_complete(self):
cdef Py_buffer py_buf cdef int idx1, idx2
if not self._buf: if not self._buf:
return return
self._path = self._buf.decode('utf-8', 'surrogateescape') self._path = self._buf.decode('utf-8', 'surrogateescape')
if self._cparser.method == 5: # CONNECT try:
self._url = URL(self._path) idx3 = len(self._path)
else: if self._cparser.method == cparser.HTTP_CONNECT:
PyObject_GetBuffer(self._buf, &py_buf, PyBUF_SIMPLE) # authority-form,
try: # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3
self._url = _parse_url(<char*>py_buf.buf, self._url = URL.build(authority=self._path, encoded=True)
py_buf.len) elif idx3 > 1 and self._path[0] == '/':
finally: # origin-form,
PyBuffer_Release(&py_buf) # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1
PyByteArray_Resize(self._buf, 0) idx1 = self._path.find("?")
if idx1 == -1:
query = ""
idx2 = self._path.find("#")
if idx2 == -1:
path = self._path
fragment = ""
else:
path = self._path[0: idx2]
fragment = self._path[idx2+1:]
else:
path = self._path[0:idx1]
idx1 += 1
idx2 = self._path.find("#", idx1+1)
if idx2 == -1:
query = self._path[idx1:]
fragment = ""
else:
query = self._path[idx1: idx2]
fragment = self._path[idx2+1:]
self._url = URL.build(
path=path,
query_string=query,
fragment=fragment,
encoded=True,
)
else:
# absolute-form for proxy maybe,
# https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2
self._url = URL(self._path, encoded=True)
finally:
PyByteArray_Resize(self._buf, 0)
cdef class HttpResponseParser(HttpParser): cdef class HttpResponseParser(HttpParser):
def __init__(self, protocol, loop, int limit, timer=None, def __init__(
size_t max_line_size=8190, size_t max_headers=32768, self, protocol, loop, int limit, timer=None,
size_t max_field_size=8190, payload_exception=None, size_t max_line_size=8190, size_t max_headers=32768,
bint response_with_body=True, bint read_until_eof=False, size_t max_field_size=8190, payload_exception=None,
bint auto_decompress=True bint response_with_body=True, bint read_until_eof=False,
bint auto_decompress=True
): ):
self._init(cparser.HTTP_RESPONSE, protocol, loop, limit, timer, self._init(cparser.HTTP_RESPONSE, protocol, loop, limit, timer,
max_line_size, max_headers, max_field_size, max_line_size, max_headers, max_field_size,
@ -608,7 +656,7 @@ cdef class HttpResponseParser(HttpParser):
else: else:
self._reason = self._reason or '' self._reason = self._reason or ''
cdef int cb_on_message_begin(cparser.http_parser* parser) except -1: cdef int cb_on_message_begin(cparser.llhttp_t* parser) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
pyparser._started = True pyparser._started = True
@ -620,7 +668,7 @@ cdef int cb_on_message_begin(cparser.http_parser* parser) except -1:
return 0 return 0
cdef int cb_on_url(cparser.http_parser* parser, cdef int cb_on_url(cparser.llhttp_t* parser,
const char *at, size_t length) except -1: const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
try: try:
@ -635,7 +683,7 @@ cdef int cb_on_url(cparser.http_parser* parser,
return 0 return 0
cdef int cb_on_status(cparser.http_parser* parser, cdef int cb_on_status(cparser.llhttp_t* parser,
const char *at, size_t length) except -1: const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
cdef str reason cdef str reason
@ -651,7 +699,7 @@ cdef int cb_on_status(cparser.http_parser* parser,
return 0 return 0
cdef int cb_on_header_field(cparser.http_parser* parser, cdef int cb_on_header_field(cparser.llhttp_t* parser,
const char *at, size_t length) except -1: const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
cdef Py_ssize_t size cdef Py_ssize_t size
@ -669,7 +717,7 @@ cdef int cb_on_header_field(cparser.http_parser* parser,
return 0 return 0
cdef int cb_on_header_value(cparser.http_parser* parser, cdef int cb_on_header_value(cparser.llhttp_t* parser,
const char *at, size_t length) except -1: const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
cdef Py_ssize_t size cdef Py_ssize_t size
@ -686,7 +734,7 @@ cdef int cb_on_header_value(cparser.http_parser* parser,
return 0 return 0
cdef int cb_on_headers_complete(cparser.http_parser* parser) except -1: cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
try: try:
pyparser._on_status_complete() pyparser._on_status_complete()
@ -695,13 +743,16 @@ cdef int cb_on_headers_complete(cparser.http_parser* parser) except -1:
pyparser._last_error = exc pyparser._last_error = exc
return -1 return -1
else: else:
if pyparser._cparser.upgrade or pyparser._cparser.method == 5: # CONNECT if (
pyparser._cparser.upgrade or
pyparser._cparser.method == cparser.HTTP_CONNECT
):
return 2 return 2
else: else:
return 0 return 0
cdef int cb_on_body(cparser.http_parser* parser, cdef int cb_on_body(cparser.llhttp_t* parser,
const char *at, size_t length) except -1: const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
cdef bytes body = at[:length] cdef bytes body = at[:length]
@ -718,7 +769,7 @@ cdef int cb_on_body(cparser.http_parser* parser,
return 0 return 0
cdef int cb_on_message_complete(cparser.http_parser* parser) except -1: cdef int cb_on_message_complete(cparser.llhttp_t* parser) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
try: try:
pyparser._started = False pyparser._started = False
@ -730,7 +781,7 @@ cdef int cb_on_message_complete(cparser.http_parser* parser) except -1:
return 0 return 0
cdef int cb_on_chunk_header(cparser.http_parser* parser) except -1: cdef int cb_on_chunk_header(cparser.llhttp_t* parser) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
try: try:
pyparser._on_chunk_header() pyparser._on_chunk_header()
@ -741,7 +792,7 @@ cdef int cb_on_chunk_header(cparser.http_parser* parser) except -1:
return 0 return 0
cdef int cb_on_chunk_complete(cparser.http_parser* parser) except -1: cdef int cb_on_chunk_complete(cparser.llhttp_t* parser) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data cdef HttpParser pyparser = <HttpParser>parser.data
try: try:
pyparser._on_chunk_complete() pyparser._on_chunk_complete()
@ -752,19 +803,21 @@ cdef int cb_on_chunk_complete(cparser.http_parser* parser) except -1:
return 0 return 0
cdef parser_error_from_errno(cparser.http_errno errno): cdef parser_error_from_errno(cparser.llhttp_t* parser, data, pointer):
cdef bytes desc = cparser.http_errno_description(errno) cdef cparser.llhttp_errno_t errno = cparser.llhttp_get_errno(parser)
cdef bytes desc = cparser.llhttp_get_error_reason(parser)
if errno in (cparser.HPE_CB_message_begin, if errno in (cparser.HPE_CB_MESSAGE_BEGIN,
cparser.HPE_CB_url, cparser.HPE_CB_HEADERS_COMPLETE,
cparser.HPE_CB_header_field, cparser.HPE_CB_MESSAGE_COMPLETE,
cparser.HPE_CB_header_value, cparser.HPE_CB_CHUNK_HEADER,
cparser.HPE_CB_headers_complete, cparser.HPE_CB_CHUNK_COMPLETE,
cparser.HPE_CB_body, cparser.HPE_INVALID_CONSTANT,
cparser.HPE_CB_message_complete, cparser.HPE_INVALID_HEADER_TOKEN,
cparser.HPE_CB_status, cparser.HPE_INVALID_CONTENT_LENGTH,
cparser.HPE_CB_chunk_header, cparser.HPE_INVALID_CHUNK_SIZE,
cparser.HPE_CB_chunk_complete): cparser.HPE_INVALID_EOF_STATE,
cparser.HPE_INVALID_TRANSFER_ENCODING):
cls = BadHttpMessage cls = BadHttpMessage
elif errno == cparser.HPE_INVALID_STATUS: elif errno == cparser.HPE_INVALID_STATUS:
@ -773,103 +826,13 @@ cdef parser_error_from_errno(cparser.http_errno errno):
elif errno == cparser.HPE_INVALID_METHOD: elif errno == cparser.HPE_INVALID_METHOD:
cls = BadStatusLine cls = BadStatusLine
elif errno == cparser.HPE_INVALID_VERSION:
cls = BadStatusLine
elif errno == cparser.HPE_INVALID_URL: elif errno == cparser.HPE_INVALID_URL:
cls = InvalidURLError cls = InvalidURLError
else: else:
cls = BadHttpMessage cls = BadHttpMessage
return cls(desc.decode('latin-1')) return cls("{}:\n\n {!r}\n {}".format(desc.decode("latin-1"), data, pointer))
def parse_url(url):
cdef:
Py_buffer py_buf
char* buf_data
PyObject_GetBuffer(url, &py_buf, PyBUF_SIMPLE)
try:
buf_data = <char*>py_buf.buf
return _parse_url(buf_data, py_buf.len)
finally:
PyBuffer_Release(&py_buf)
cdef _parse_url(char* buf_data, size_t length):
cdef:
cparser.http_parser_url* parsed
int res
str schema = None
str host = None
object port = None
str path = None
str query = None
str fragment = None
str user = None
str password = None
str userinfo = None
object result = None
int off
int ln
parsed = <cparser.http_parser_url*> \
PyMem_Malloc(sizeof(cparser.http_parser_url))
if parsed is NULL:
raise MemoryError()
cparser.http_parser_url_init(parsed)
try:
res = cparser.http_parser_parse_url(buf_data, length, 0, parsed)
if res == 0:
if parsed.field_set & (1 << cparser.UF_SCHEMA):
off = parsed.field_data[<int>cparser.UF_SCHEMA].off
ln = parsed.field_data[<int>cparser.UF_SCHEMA].len
schema = buf_data[off:off+ln].decode('utf-8', 'surrogateescape')
else:
schema = ''
if parsed.field_set & (1 << cparser.UF_HOST):
off = parsed.field_data[<int>cparser.UF_HOST].off
ln = parsed.field_data[<int>cparser.UF_HOST].len
host = buf_data[off:off+ln].decode('utf-8', 'surrogateescape')
else:
host = ''
if parsed.field_set & (1 << cparser.UF_PORT):
port = parsed.port
if parsed.field_set & (1 << cparser.UF_PATH):
off = parsed.field_data[<int>cparser.UF_PATH].off
ln = parsed.field_data[<int>cparser.UF_PATH].len
path = buf_data[off:off+ln].decode('utf-8', 'surrogateescape')
else:
path = ''
if parsed.field_set & (1 << cparser.UF_QUERY):
off = parsed.field_data[<int>cparser.UF_QUERY].off
ln = parsed.field_data[<int>cparser.UF_QUERY].len
query = buf_data[off:off+ln].decode('utf-8', 'surrogateescape')
else:
query = ''
if parsed.field_set & (1 << cparser.UF_FRAGMENT):
off = parsed.field_data[<int>cparser.UF_FRAGMENT].off
ln = parsed.field_data[<int>cparser.UF_FRAGMENT].len
fragment = buf_data[off:off+ln].decode('utf-8', 'surrogateescape')
else:
fragment = ''
if parsed.field_set & (1 << cparser.UF_USERINFO):
off = parsed.field_data[<int>cparser.UF_USERINFO].off
ln = parsed.field_data[<int>cparser.UF_USERINFO].len
userinfo = buf_data[off:off+ln].decode('utf-8', 'surrogateescape')
user, sep, password = userinfo.partition(':')
return URL_build(scheme=schema,
user=user, password=password, host=host, port=port,
path=path, query_string=query, fragment=fragment, encoded=True)
else:
raise InvalidURLError("invalid url {!r}".format(buf_data))
finally:
PyMem_Free(parsed)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -111,6 +111,14 @@ cdef str to_str(object s):
return str(s) return str(s)
cdef void _safe_header(str string) except *:
if "\r" in string or "\n" in string:
raise ValueError(
"Newline or carriage return character detected in HTTP status message or "
"header. This is a potential security issue."
)
def _serialize_headers(str status_line, headers): def _serialize_headers(str status_line, headers):
cdef Writer writer cdef Writer writer
cdef object key cdef object key
@ -119,6 +127,10 @@ def _serialize_headers(str status_line, headers):
_init_writer(&writer) _init_writer(&writer)
for key, val in headers.items():
_safe_header(to_str(key))
_safe_header(to_str(val))
try: try:
if _write_str(&writer, status_line) < 0: if _write_str(&writer, status_line) < 0:
raise raise

Просмотреть файл

@ -1,14 +1,16 @@
/* Generated by Cython 0.29.21 */ /* Generated by Cython 0.29.32 */
#ifndef PY_SSIZE_T_CLEAN
#define PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN
#endif /* PY_SSIZE_T_CLEAN */
#include "Python.h" #include "Python.h"
#ifndef Py_PYTHON_H #ifndef Py_PYTHON_H
#error Python headers needed to compile C extensions, please install development version of Python. #error Python headers needed to compile C extensions, please install development version of Python.
#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000) #elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
#error Cython requires Python 2.6+ or Python 3.3+. #error Cython requires Python 2.6+ or Python 3.3+.
#else #else
#define CYTHON_ABI "0_29_21" #define CYTHON_ABI "0_29_32"
#define CYTHON_HEX_VERSION 0x001D15F0 #define CYTHON_HEX_VERSION 0x001D20F0
#define CYTHON_FUTURE_DIVISION 1 #define CYTHON_FUTURE_DIVISION 1
#include <stddef.h> #include <stddef.h>
#ifndef offsetof #ifndef offsetof
@ -47,6 +49,7 @@
#define CYTHON_COMPILING_IN_PYPY 1 #define CYTHON_COMPILING_IN_PYPY 1
#define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_PYSTON 0
#define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_CPYTHON 0
#define CYTHON_COMPILING_IN_NOGIL 0
#undef CYTHON_USE_TYPE_SLOTS #undef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 0 #define CYTHON_USE_TYPE_SLOTS 0
#undef CYTHON_USE_PYTYPE_LOOKUP #undef CYTHON_USE_PYTYPE_LOOKUP
@ -83,10 +86,14 @@
#define CYTHON_USE_DICT_VERSIONS 0 #define CYTHON_USE_DICT_VERSIONS 0
#undef CYTHON_USE_EXC_INFO_STACK #undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0 #define CYTHON_USE_EXC_INFO_STACK 0
#ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
#define CYTHON_UPDATE_DESCRIPTOR_DOC (PYPY_VERSION_HEX >= 0x07030900)
#endif
#elif defined(PYSTON_VERSION) #elif defined(PYSTON_VERSION)
#define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYPY 0
#define CYTHON_COMPILING_IN_PYSTON 1 #define CYTHON_COMPILING_IN_PYSTON 1
#define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_CPYTHON 0
#define CYTHON_COMPILING_IN_NOGIL 0
#ifndef CYTHON_USE_TYPE_SLOTS #ifndef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 1 #define CYTHON_USE_TYPE_SLOTS 1
#endif #endif
@ -124,10 +131,59 @@
#define CYTHON_USE_DICT_VERSIONS 0 #define CYTHON_USE_DICT_VERSIONS 0
#undef CYTHON_USE_EXC_INFO_STACK #undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0 #define CYTHON_USE_EXC_INFO_STACK 0
#ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
#define CYTHON_UPDATE_DESCRIPTOR_DOC 0
#endif
#elif defined(PY_NOGIL)
#define CYTHON_COMPILING_IN_PYPY 0
#define CYTHON_COMPILING_IN_PYSTON 0
#define CYTHON_COMPILING_IN_CPYTHON 0
#define CYTHON_COMPILING_IN_NOGIL 1
#ifndef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 1
#endif
#undef CYTHON_USE_PYTYPE_LOOKUP
#define CYTHON_USE_PYTYPE_LOOKUP 0
#ifndef CYTHON_USE_ASYNC_SLOTS
#define CYTHON_USE_ASYNC_SLOTS 1
#endif
#undef CYTHON_USE_PYLIST_INTERNALS
#define CYTHON_USE_PYLIST_INTERNALS 0
#ifndef CYTHON_USE_UNICODE_INTERNALS
#define CYTHON_USE_UNICODE_INTERNALS 1
#endif
#undef CYTHON_USE_UNICODE_WRITER
#define CYTHON_USE_UNICODE_WRITER 0
#undef CYTHON_USE_PYLONG_INTERNALS
#define CYTHON_USE_PYLONG_INTERNALS 0
#ifndef CYTHON_AVOID_BORROWED_REFS
#define CYTHON_AVOID_BORROWED_REFS 0
#endif
#ifndef CYTHON_ASSUME_SAFE_MACROS
#define CYTHON_ASSUME_SAFE_MACROS 1
#endif
#ifndef CYTHON_UNPACK_METHODS
#define CYTHON_UNPACK_METHODS 1
#endif
#undef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 0
#undef CYTHON_FAST_PYCALL
#define CYTHON_FAST_PYCALL 0
#ifndef CYTHON_PEP489_MULTI_PHASE_INIT
#define CYTHON_PEP489_MULTI_PHASE_INIT 1
#endif
#ifndef CYTHON_USE_TP_FINALIZE
#define CYTHON_USE_TP_FINALIZE 1
#endif
#undef CYTHON_USE_DICT_VERSIONS
#define CYTHON_USE_DICT_VERSIONS 0
#undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0
#else #else
#define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYPY 0
#define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_PYSTON 0
#define CYTHON_COMPILING_IN_CPYTHON 1 #define CYTHON_COMPILING_IN_CPYTHON 1
#define CYTHON_COMPILING_IN_NOGIL 0
#ifndef CYTHON_USE_TYPE_SLOTS #ifndef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 1 #define CYTHON_USE_TYPE_SLOTS 1
#endif #endif
@ -155,7 +211,7 @@
#ifndef CYTHON_USE_UNICODE_INTERNALS #ifndef CYTHON_USE_UNICODE_INTERNALS
#define CYTHON_USE_UNICODE_INTERNALS 1 #define CYTHON_USE_UNICODE_INTERNALS 1
#endif #endif
#if PY_VERSION_HEX < 0x030300F0 #if PY_VERSION_HEX < 0x030300F0 || PY_VERSION_HEX >= 0x030B00A2
#undef CYTHON_USE_UNICODE_WRITER #undef CYTHON_USE_UNICODE_WRITER
#define CYTHON_USE_UNICODE_WRITER 0 #define CYTHON_USE_UNICODE_WRITER 0
#elif !defined(CYTHON_USE_UNICODE_WRITER) #elif !defined(CYTHON_USE_UNICODE_WRITER)
@ -170,11 +226,14 @@
#ifndef CYTHON_UNPACK_METHODS #ifndef CYTHON_UNPACK_METHODS
#define CYTHON_UNPACK_METHODS 1 #define CYTHON_UNPACK_METHODS 1
#endif #endif
#ifndef CYTHON_FAST_THREAD_STATE #if PY_VERSION_HEX >= 0x030B00A4
#undef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 0
#elif !defined(CYTHON_FAST_THREAD_STATE)
#define CYTHON_FAST_THREAD_STATE 1 #define CYTHON_FAST_THREAD_STATE 1
#endif #endif
#ifndef CYTHON_FAST_PYCALL #ifndef CYTHON_FAST_PYCALL
#define CYTHON_FAST_PYCALL 1 #define CYTHON_FAST_PYCALL (PY_VERSION_HEX < 0x030A0000)
#endif #endif
#ifndef CYTHON_PEP489_MULTI_PHASE_INIT #ifndef CYTHON_PEP489_MULTI_PHASE_INIT
#define CYTHON_PEP489_MULTI_PHASE_INIT (PY_VERSION_HEX >= 0x03050000) #define CYTHON_PEP489_MULTI_PHASE_INIT (PY_VERSION_HEX >= 0x03050000)
@ -185,15 +244,23 @@
#ifndef CYTHON_USE_DICT_VERSIONS #ifndef CYTHON_USE_DICT_VERSIONS
#define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX >= 0x030600B1) #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX >= 0x030600B1)
#endif #endif
#ifndef CYTHON_USE_EXC_INFO_STACK #if PY_VERSION_HEX >= 0x030B00A4
#undef CYTHON_USE_EXC_INFO_STACK
#define CYTHON_USE_EXC_INFO_STACK 0
#elif !defined(CYTHON_USE_EXC_INFO_STACK)
#define CYTHON_USE_EXC_INFO_STACK (PY_VERSION_HEX >= 0x030700A3) #define CYTHON_USE_EXC_INFO_STACK (PY_VERSION_HEX >= 0x030700A3)
#endif #endif
#ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
#define CYTHON_UPDATE_DESCRIPTOR_DOC 1
#endif
#endif #endif
#if !defined(CYTHON_FAST_PYCCALL) #if !defined(CYTHON_FAST_PYCCALL)
#define CYTHON_FAST_PYCCALL (CYTHON_FAST_PYCALL && PY_VERSION_HEX >= 0x030600B1) #define CYTHON_FAST_PYCCALL (CYTHON_FAST_PYCALL && PY_VERSION_HEX >= 0x030600B1)
#endif #endif
#if CYTHON_USE_PYLONG_INTERNALS #if CYTHON_USE_PYLONG_INTERNALS
#include "longintrepr.h" #if PY_MAJOR_VERSION < 3
#include "longintrepr.h"
#endif
#undef SHIFT #undef SHIFT
#undef BASE #undef BASE
#undef MASK #undef MASK
@ -310,9 +377,68 @@
#define __Pyx_DefaultClassType PyClass_Type #define __Pyx_DefaultClassType PyClass_Type
#else #else
#define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_BUILTIN_MODULE_NAME "builtins"
#if PY_VERSION_HEX >= 0x030800A4 && PY_VERSION_HEX < 0x030800B2 #define __Pyx_DefaultClassType PyType_Type
#define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ #if PY_VERSION_HEX >= 0x030B00A1
PyCode_New(a, 0, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) static CYTHON_INLINE PyCodeObject* __Pyx_PyCode_New(int a, int k, int l, int s, int f,
PyObject *code, PyObject *c, PyObject* n, PyObject *v,
PyObject *fv, PyObject *cell, PyObject* fn,
PyObject *name, int fline, PyObject *lnos) {
PyObject *kwds=NULL, *argcount=NULL, *posonlyargcount=NULL, *kwonlyargcount=NULL;
PyObject *nlocals=NULL, *stacksize=NULL, *flags=NULL, *replace=NULL, *call_result=NULL, *empty=NULL;
const char *fn_cstr=NULL;
const char *name_cstr=NULL;
PyCodeObject* co=NULL;
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
if (!(kwds=PyDict_New())) goto end;
if (!(argcount=PyLong_FromLong(a))) goto end;
if (PyDict_SetItemString(kwds, "co_argcount", argcount) != 0) goto end;
if (!(posonlyargcount=PyLong_FromLong(0))) goto end;
if (PyDict_SetItemString(kwds, "co_posonlyargcount", posonlyargcount) != 0) goto end;
if (!(kwonlyargcount=PyLong_FromLong(k))) goto end;
if (PyDict_SetItemString(kwds, "co_kwonlyargcount", kwonlyargcount) != 0) goto end;
if (!(nlocals=PyLong_FromLong(l))) goto end;
if (PyDict_SetItemString(kwds, "co_nlocals", nlocals) != 0) goto end;
if (!(stacksize=PyLong_FromLong(s))) goto end;
if (PyDict_SetItemString(kwds, "co_stacksize", stacksize) != 0) goto end;
if (!(flags=PyLong_FromLong(f))) goto end;
if (PyDict_SetItemString(kwds, "co_flags", flags) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_code", code) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_consts", c) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_names", n) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_varnames", v) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_freevars", fv) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_cellvars", cell) != 0) goto end;
if (PyDict_SetItemString(kwds, "co_linetable", lnos) != 0) goto end;
if (!(fn_cstr=PyUnicode_AsUTF8AndSize(fn, NULL))) goto end;
if (!(name_cstr=PyUnicode_AsUTF8AndSize(name, NULL))) goto end;
if (!(co = PyCode_NewEmpty(fn_cstr, name_cstr, fline))) goto end;
if (!(replace = PyObject_GetAttrString((PyObject*)co, "replace"))) goto cleanup_code_too;
if (!(empty = PyTuple_New(0))) goto cleanup_code_too; // unfortunately __pyx_empty_tuple isn't available here
if (!(call_result = PyObject_Call(replace, empty, kwds))) goto cleanup_code_too;
Py_XDECREF((PyObject*)co);
co = (PyCodeObject*)call_result;
call_result = NULL;
if (0) {
cleanup_code_too:
Py_XDECREF((PyObject*)co);
co = NULL;
}
end:
Py_XDECREF(kwds);
Py_XDECREF(argcount);
Py_XDECREF(posonlyargcount);
Py_XDECREF(kwonlyargcount);
Py_XDECREF(nlocals);
Py_XDECREF(stacksize);
Py_XDECREF(replace);
Py_XDECREF(call_result);
Py_XDECREF(empty);
if (type) {
PyErr_Restore(type, value, traceback);
}
return co;
}
#else #else
#define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
@ -426,8 +552,12 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
#endif #endif
#if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND) #if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND)
#define CYTHON_PEP393_ENABLED 1 #define CYTHON_PEP393_ENABLED 1
#if defined(PyUnicode_IS_READY)
#define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\ #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\
0 : _PyUnicode_Ready((PyObject *)(op))) 0 : _PyUnicode_Ready((PyObject *)(op)))
#else
#define __Pyx_PyUnicode_READY(op) (0)
#endif
#define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u) #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u)
#define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i)
#define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u) #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u)
@ -436,7 +566,11 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
#define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i) #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i)
#define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, ch) #define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, ch)
#if defined(PyUnicode_IS_READY) && defined(PyUnicode_GET_SIZE) #if defined(PyUnicode_IS_READY) && defined(PyUnicode_GET_SIZE)
#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length))
#else
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u))) #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
#endif
#else #else
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u)) #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u))
#endif #endif
@ -542,10 +676,10 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
#if PY_VERSION_HEX < 0x030200A4 #if PY_VERSION_HEX < 0x030200A4
typedef long Py_hash_t; typedef long Py_hash_t;
#define __Pyx_PyInt_FromHash_t PyInt_FromLong #define __Pyx_PyInt_FromHash_t PyInt_FromLong
#define __Pyx_PyInt_AsHash_t PyInt_AsLong #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsHash_t
#else #else
#define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t #define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t
#define __Pyx_PyInt_AsHash_t PyInt_AsSsize_t #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsSsize_t
#endif #endif
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
#define __Pyx_PyMethod_New(func, self, klass) ((self) ? ((void)(klass), PyMethod_New(func, self)) : __Pyx_NewRef(func)) #define __Pyx_PyMethod_New(func, self, klass) ((self) ? ((void)(klass), PyMethod_New(func, self)) : __Pyx_NewRef(func))
@ -570,8 +704,10 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
} __Pyx_PyAsyncMethodsStruct; } __Pyx_PyAsyncMethodsStruct;
#endif #endif
#if defined(WIN32) || defined(MS_WINDOWS) #if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)
#define _USE_MATH_DEFINES #if !defined(_USE_MATH_DEFINES)
#define _USE_MATH_DEFINES
#endif
#endif #endif
#include <math.h> #include <math.h>
#ifdef NAN #ifdef NAN
@ -705,6 +841,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
(likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj)) (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj))
static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*);
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t);
static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*);
#if CYTHON_ASSUME_SAFE_MACROS #if CYTHON_ASSUME_SAFE_MACROS
#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) #define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
#else #else
@ -932,13 +1069,21 @@ static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args,
#ifndef Py_MEMBER_SIZE #ifndef Py_MEMBER_SIZE
#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) #define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
#endif #endif
#if CYTHON_FAST_PYCALL
static size_t __pyx_pyframe_localsplus_offset = 0; static size_t __pyx_pyframe_localsplus_offset = 0;
#include "frameobject.h" #include "frameobject.h"
#if PY_VERSION_HEX >= 0x030b00a6
#ifndef Py_BUILD_CORE
#define Py_BUILD_CORE 1
#endif
#include "internal/pycore_frame.h"
#endif
#define __Pxy_PyFrame_Initialize_Offsets()\ #define __Pxy_PyFrame_Initialize_Offsets()\
((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)),\ ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)),\
(void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus))) (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))
#define __Pyx_PyFrame_GetLocalsplus(frame)\ #define __Pyx_PyFrame_GetLocalsplus(frame)\
(assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset)) (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset))
#endif // CYTHON_FAST_PYCALL
#endif #endif
/* PyObjectCall.proto */ /* PyObjectCall.proto */
@ -1055,6 +1200,11 @@ static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object);
static void __Pyx_AddTraceback(const char *funcname, int c_line, static void __Pyx_AddTraceback(const char *funcname, int c_line,
int py_line, const char *filename); int py_line, const char *filename);
/* GCCDiagnostics.proto */
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#define __Pyx_HAS_GCC_DIAGNOSTIC
#endif
/* CIntToPy.proto */ /* CIntToPy.proto */
static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value); static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value);
@ -1951,11 +2101,9 @@ if (!__Pyx_RefNanny) {
#endif #endif
/*--- Library function declarations ---*/ /*--- Library function declarations ---*/
/*--- Threads initialization code ---*/ /*--- Threads initialization code ---*/
#if defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS #if defined(WITH_THREAD) && PY_VERSION_HEX < 0x030700F0 && defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS
#ifdef WITH_THREAD /* Python build with threading support? */
PyEval_InitThreads(); PyEval_InitThreads();
#endif #endif
#endif
/*--- Module creation code ---*/ /*--- Module creation code ---*/
#if CYTHON_PEP489_MULTI_PHASE_INIT #if CYTHON_PEP489_MULTI_PHASE_INIT
__pyx_m = __pyx_pyinit_module; __pyx_m = __pyx_pyinit_module;
@ -2388,7 +2536,7 @@ done:
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) {
PyObject *result; PyObject *result;
ternaryfunc call = func->ob_type->tp_call; ternaryfunc call = Py_TYPE(func)->tp_call;
if (unlikely(!call)) if (unlikely(!call))
return PyObject_Call(func, arg, kw); return PyObject_Call(func, arg, kw);
if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object")))
@ -2446,7 +2594,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObjec
if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) { if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) {
return __Pyx_PyObject_CallMethO(func, arg); return __Pyx_PyObject_CallMethO(func, arg);
#if CYTHON_FAST_PYCCALL #if CYTHON_FAST_PYCCALL
} else if (PyCFunction_GET_FLAGS(func) & METH_FASTCALL) { } else if (__Pyx_PyFastCFunction_Check(func)) {
return __Pyx_PyCFunction_FastCall(func, &arg, 1); return __Pyx_PyCFunction_FastCall(func, &arg, 1);
#endif #endif
} }
@ -2607,7 +2755,7 @@ static int __Pyx_CLineForTraceback(CYTHON_NCP_UNUSED PyThreadState *tstate, int
} }
if (!use_cline) { if (!use_cline) {
c_line = 0; c_line = 0;
PyObject_SetAttr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback, Py_False); (void) PyObject_SetAttr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback, Py_False);
} }
else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) { else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) {
c_line = 0; c_line = 0;
@ -2701,33 +2849,40 @@ static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) {
#include "compile.h" #include "compile.h"
#include "frameobject.h" #include "frameobject.h"
#include "traceback.h" #include "traceback.h"
#if PY_VERSION_HEX >= 0x030b00a6
#ifndef Py_BUILD_CORE
#define Py_BUILD_CORE 1
#endif
#include "internal/pycore_frame.h"
#endif
static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
const char *funcname, int c_line, const char *funcname, int c_line,
int py_line, const char *filename) { int py_line, const char *filename) {
PyCodeObject *py_code = 0; PyCodeObject *py_code = NULL;
PyObject *py_srcfile = 0; PyObject *py_funcname = NULL;
PyObject *py_funcname = 0;
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
PyObject *py_srcfile = NULL;
py_srcfile = PyString_FromString(filename); py_srcfile = PyString_FromString(filename);
#else
py_srcfile = PyUnicode_FromString(filename);
#endif
if (!py_srcfile) goto bad; if (!py_srcfile) goto bad;
#endif
if (c_line) { if (c_line) {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
if (!py_funcname) goto bad;
#else #else
py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
if (!py_funcname) goto bad;
funcname = PyUnicode_AsUTF8(py_funcname);
if (!funcname) goto bad;
#endif #endif
} }
else { else {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
py_funcname = PyString_FromString(funcname); py_funcname = PyString_FromString(funcname);
#else if (!py_funcname) goto bad;
py_funcname = PyUnicode_FromString(funcname);
#endif #endif
} }
if (!py_funcname) goto bad; #if PY_MAJOR_VERSION < 3
py_code = __Pyx_PyCode_New( py_code = __Pyx_PyCode_New(
0, 0,
0, 0,
@ -2746,11 +2901,16 @@ static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
__pyx_empty_bytes /*PyObject *lnotab*/ __pyx_empty_bytes /*PyObject *lnotab*/
); );
Py_DECREF(py_srcfile); Py_DECREF(py_srcfile);
Py_DECREF(py_funcname); #else
py_code = PyCode_NewEmpty(filename, funcname, py_line);
#endif
Py_XDECREF(py_funcname); // XDECREF since it's only set on Py3 if cline
return py_code; return py_code;
bad: bad:
Py_XDECREF(py_srcfile);
Py_XDECREF(py_funcname); Py_XDECREF(py_funcname);
#if PY_MAJOR_VERSION < 3
Py_XDECREF(py_srcfile);
#endif
return NULL; return NULL;
} }
static void __Pyx_AddTraceback(const char *funcname, int c_line, static void __Pyx_AddTraceback(const char *funcname, int c_line,
@ -2758,14 +2918,24 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line,
PyCodeObject *py_code = 0; PyCodeObject *py_code = 0;
PyFrameObject *py_frame = 0; PyFrameObject *py_frame = 0;
PyThreadState *tstate = __Pyx_PyThreadState_Current; PyThreadState *tstate = __Pyx_PyThreadState_Current;
PyObject *ptype, *pvalue, *ptraceback;
if (c_line) { if (c_line) {
c_line = __Pyx_CLineForTraceback(tstate, c_line); c_line = __Pyx_CLineForTraceback(tstate, c_line);
} }
py_code = __pyx_find_code_object(c_line ? -c_line : py_line); py_code = __pyx_find_code_object(c_line ? -c_line : py_line);
if (!py_code) { if (!py_code) {
__Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
py_code = __Pyx_CreateCodeObjectForTraceback( py_code = __Pyx_CreateCodeObjectForTraceback(
funcname, c_line, py_line, filename); funcname, c_line, py_line, filename);
if (!py_code) goto bad; if (!py_code) {
/* If the code object creation fails, then we should clear the
fetched exception references and propagate the new exception */
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptraceback);
goto bad;
}
__Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
__pyx_insert_code_object(c_line ? -c_line : py_line, py_code); __pyx_insert_code_object(c_line ? -c_line : py_line, py_code);
} }
py_frame = PyFrame_New( py_frame = PyFrame_New(
@ -2784,7 +2954,14 @@ bad:
/* CIntToPy */ /* CIntToPy */
static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) { static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) {
const long neg_one = (long) ((long) 0 - (long) 1), const_zero = (long) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
const long neg_one = (long) -1, const_zero = (long) 0;
#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
const int is_unsigned = neg_one > const_zero; const int is_unsigned = neg_one > const_zero;
if (is_unsigned) { if (is_unsigned) {
if (sizeof(long) < sizeof(long)) { if (sizeof(long) < sizeof(long)) {
@ -2837,7 +3014,14 @@ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) {
/* CIntFromPy */ /* CIntFromPy */
static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) { static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) {
const long neg_one = (long) ((long) 0 - (long) 1), const_zero = (long) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
const long neg_one = (long) -1, const_zero = (long) 0;
#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
const int is_unsigned = neg_one > const_zero; const int is_unsigned = neg_one > const_zero;
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (likely(PyInt_Check(x))) { if (likely(PyInt_Check(x))) {
@ -3026,7 +3210,14 @@ raise_neg_overflow:
/* CIntFromPy */ /* CIntFromPy */
static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) { static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) {
const int neg_one = (int) ((int) 0 - (int) 1), const_zero = (int) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
const int neg_one = (int) -1, const_zero = (int) 0;
#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
const int is_unsigned = neg_one > const_zero; const int is_unsigned = neg_one > const_zero;
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (likely(PyInt_Check(x))) { if (likely(PyInt_Check(x))) {
@ -3315,11 +3506,33 @@ static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObj
/* CheckBinaryVersion */ /* CheckBinaryVersion */
static int __Pyx_check_binary_version(void) { static int __Pyx_check_binary_version(void) {
char ctversion[4], rtversion[4]; char ctversion[5];
PyOS_snprintf(ctversion, 4, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION); int same=1, i, found_dot;
PyOS_snprintf(rtversion, 4, "%s", Py_GetVersion()); const char* rt_from_call = Py_GetVersion();
if (ctversion[0] != rtversion[0] || ctversion[2] != rtversion[2]) { PyOS_snprintf(ctversion, 5, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION);
found_dot = 0;
for (i = 0; i < 4; i++) {
if (!ctversion[i]) {
same = (rt_from_call[i] < '0' || rt_from_call[i] > '9');
break;
}
if (rt_from_call[i] != ctversion[i]) {
same = 0;
break;
}
}
if (!same) {
char rtversion[5] = {'\0'};
char message[200]; char message[200];
for (i=0; i<4; ++i) {
if (rt_from_call[i] == '.') {
if (found_dot) break;
found_dot = 1;
} else if (rt_from_call[i] < '0' || rt_from_call[i] > '9') {
break;
}
rtversion[i] = rt_from_call[i];
}
PyOS_snprintf(message, sizeof(message), PyOS_snprintf(message, sizeof(message),
"compiletime version %s of module '%.100s' " "compiletime version %s of module '%.100s' "
"does not match runtime version %s", "does not match runtime version %s",
@ -3577,6 +3790,23 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
Py_DECREF(x); Py_DECREF(x);
return ival; return ival;
} }
static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) {
if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) {
return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o);
#if PY_MAJOR_VERSION < 3
} else if (likely(PyInt_CheckExact(o))) {
return PyInt_AS_LONG(o);
#endif
} else {
Py_ssize_t ival;
PyObject *x;
x = PyNumber_Index(o);
if (!x) return -1;
ival = PyInt_AsLong(x);
Py_DECREF(x);
return ival;
}
}
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) { static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False); return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False);
} }

13
third_party/python/aiohttp/aiohttp/abc.py поставляемый
Просмотреть файл

@ -135,6 +135,9 @@ else:
IterableBase = Iterable IterableBase = Iterable
ClearCookiePredicate = Callable[["Morsel[str]"], bool]
class AbstractCookieJar(Sized, IterableBase): class AbstractCookieJar(Sized, IterableBase):
"""Abstract Cookie Jar.""" """Abstract Cookie Jar."""
@ -142,8 +145,12 @@ class AbstractCookieJar(Sized, IterableBase):
self._loop = get_running_loop(loop) self._loop = get_running_loop(loop)
@abstractmethod @abstractmethod
def clear(self) -> None: def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None:
"""Clear all cookies.""" """Clear all cookies if no predicate is passed."""
@abstractmethod
def clear_domain(self, domain: str) -> None:
"""Clear all cookies for domain and all subdomains."""
@abstractmethod @abstractmethod
def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None:
@ -159,7 +166,7 @@ class AbstractStreamWriter(ABC):
buffer_size = 0 buffer_size = 0
output_size = 0 output_size = 0
length = 0 # type: Optional[int] length: Optional[int] = 0
@abstractmethod @abstractmethod
async def write(self, chunk: bytes) -> None: async def write(self, chunk: bytes) -> None:

Просмотреть файл

@ -15,13 +15,17 @@ class BaseProtocol(asyncio.Protocol):
) )
def __init__(self, loop: asyncio.AbstractEventLoop) -> None: def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
self._loop = loop # type: asyncio.AbstractEventLoop self._loop: asyncio.AbstractEventLoop = loop
self._paused = False self._paused = False
self._drain_waiter = None # type: Optional[asyncio.Future[None]] self._drain_waiter: Optional[asyncio.Future[None]] = None
self._connection_lost = False
self._reading_paused = False self._reading_paused = False
self.transport = None # type: Optional[asyncio.Transport] self.transport: Optional[asyncio.Transport] = None
@property
def connected(self) -> bool:
"""Return True if the connection is open."""
return self.transport is not None
def pause_writing(self) -> None: def pause_writing(self) -> None:
assert not self._paused assert not self._paused
@ -59,7 +63,6 @@ class BaseProtocol(asyncio.Protocol):
self.transport = tr self.transport = tr
def connection_lost(self, exc: Optional[BaseException]) -> None: def connection_lost(self, exc: Optional[BaseException]) -> None:
self._connection_lost = True
# Wake up the writer if currently paused. # Wake up the writer if currently paused.
self.transport = None self.transport = None
if not self._paused: if not self._paused:
@ -76,12 +79,12 @@ class BaseProtocol(asyncio.Protocol):
waiter.set_exception(exc) waiter.set_exception(exc)
async def _drain_helper(self) -> None: async def _drain_helper(self) -> None:
if self._connection_lost: if not self.connected:
raise ConnectionResetError("Connection lost") raise ConnectionResetError("Connection lost")
if not self._paused: if not self._paused:
return return
waiter = self._drain_waiter waiter = self._drain_waiter
assert waiter is None or waiter.cancelled() if waiter is None:
waiter = self._loop.create_future() waiter = self._loop.create_future()
self._drain_waiter = waiter self._drain_waiter = waiter
await waiter await asyncio.shield(waiter)

116
third_party/python/aiohttp/aiohttp/client.py поставляемый
Просмотреть файл

@ -8,6 +8,7 @@ import os
import sys import sys
import traceback import traceback
import warnings import warnings
from contextlib import suppress
from types import SimpleNamespace, TracebackType from types import SimpleNamespace, TracebackType
from typing import ( from typing import (
Any, Any,
@ -74,10 +75,10 @@ from .helpers import (
DEBUG, DEBUG,
PY_36, PY_36,
BasicAuth, BasicAuth,
CeilTimeout,
TimeoutHandle, TimeoutHandle,
ceil_timeout,
get_env_proxy_for_url,
get_running_loop, get_running_loop,
proxies_from_env,
sentinel, sentinel,
strip_auth_from_url, strip_auth_from_url,
) )
@ -85,7 +86,7 @@ from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter
from .http_websocket import WSHandshakeError, WSMessage, ws_ext_gen, ws_ext_parse from .http_websocket import WSHandshakeError, WSMessage, ws_ext_gen, ws_ext_parse
from .streams import FlowControlDataQueue from .streams import FlowControlDataQueue
from .tracing import Trace, TraceConfig from .tracing import Trace, TraceConfig
from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, StrOrURL from .typedefs import Final, JSONEncoder, LooseCookies, LooseHeaders, StrOrURL
__all__ = ( __all__ = (
# client_exceptions # client_exceptions
@ -130,7 +131,7 @@ __all__ = (
try: try:
from ssl import SSLContext from ssl import SSLContext
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
SSLContext = object # type: ignore SSLContext = object # type: ignore[misc,assignment]
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
@ -155,7 +156,7 @@ class ClientTimeout:
# 5 Minute default read timeout # 5 Minute default read timeout
DEFAULT_TIMEOUT = ClientTimeout(total=5 * 60) DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60)
_RetType = TypeVar("_RetType") _RetType = TypeVar("_RetType")
@ -165,6 +166,7 @@ class ClientSession:
ATTRS = frozenset( ATTRS = frozenset(
[ [
"_base_url",
"_source_traceback", "_source_traceback",
"_connector", "_connector",
"requote_redirect_url", "requote_redirect_url",
@ -189,10 +191,12 @@ class ClientSession:
] ]
) )
_source_traceback = None _source_traceback = None # type: Optional[traceback.StackSummary]
_connector = None # type: Optional[BaseConnector]
def __init__( def __init__(
self, self,
base_url: Optional[StrOrURL] = None,
*, *,
connector: Optional[BaseConnector] = None, connector: Optional[BaseConnector] = None,
loop: Optional[asyncio.AbstractEventLoop] = None, loop: Optional[asyncio.AbstractEventLoop] = None,
@ -215,15 +219,22 @@ class ClientSession:
trust_env: bool = False, trust_env: bool = False,
requote_redirect_url: bool = True, requote_redirect_url: bool = True,
trace_configs: Optional[List[TraceConfig]] = None, trace_configs: Optional[List[TraceConfig]] = None,
read_bufsize: int = 2 ** 16, read_bufsize: int = 2**16,
) -> None: ) -> None:
if loop is None: if loop is None:
if connector is not None: if connector is not None:
loop = connector._loop loop = connector._loop
loop = get_running_loop(loop) loop = get_running_loop(loop)
if base_url is None or isinstance(base_url, URL):
self._base_url: Optional[URL] = base_url
else:
self._base_url = URL(base_url)
assert (
self._base_url.origin() == self._base_url
), "Only absolute URLs without path part are supported"
if connector is None: if connector is None:
connector = TCPConnector(loop=loop) connector = TCPConnector(loop=loop)
@ -242,7 +253,7 @@ class ClientSession:
if cookies is not None: if cookies is not None:
self._cookie_jar.update_cookies(cookies) self._cookie_jar.update_cookies(cookies)
self._connector = connector # type: Optional[BaseConnector] self._connector = connector
self._connector_owner = connector_owner self._connector_owner = connector_owner
self._default_auth = auth self._default_auth = auth
self._version = version self._version = version
@ -264,7 +275,7 @@ class ClientSession:
stacklevel=2, stacklevel=2,
) )
else: else:
self._timeout = timeout # type: ignore self._timeout = timeout # type: ignore[assignment]
if read_timeout is not sentinel: if read_timeout is not sentinel:
raise ValueError( raise ValueError(
"read_timeout and timeout parameters " "read_timeout and timeout parameters "
@ -285,12 +296,12 @@ class ClientSession:
# Convert to list of tuples # Convert to list of tuples
if headers: if headers:
real_headers = CIMultiDict(headers) # type: CIMultiDict[str] real_headers: CIMultiDict[str] = CIMultiDict(headers)
else: else:
real_headers = CIMultiDict() real_headers = CIMultiDict()
self._default_headers = real_headers # type: CIMultiDict[str] self._default_headers: CIMultiDict[str] = real_headers
if skip_auto_headers is not None: if skip_auto_headers is not None:
self._skip_auto_headers = frozenset([istr(i) for i in skip_auto_headers]) self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers)
else: else:
self._skip_auto_headers = frozenset() self._skip_auto_headers = frozenset()
@ -342,6 +353,14 @@ class ClientSession:
"""Perform HTTP request.""" """Perform HTTP request."""
return _RequestContextManager(self._request(method, url, **kwargs)) return _RequestContextManager(self._request(method, url, **kwargs))
def _build_url(self, str_or_url: StrOrURL) -> URL:
url = URL(str_or_url)
if self._base_url is None:
return url
else:
assert not url.is_absolute() and url.path.startswith("/")
return self._base_url.join(url)
async def _request( async def _request(
self, self,
method: str, method: str,
@ -401,7 +420,7 @@ class ClientSession:
proxy_headers = self._prepare_headers(proxy_headers) proxy_headers = self._prepare_headers(proxy_headers)
try: try:
url = URL(str_or_url) url = self._build_url(str_or_url)
except ValueError as e: except ValueError as e:
raise InvalidURL(str_or_url) from e raise InvalidURL(str_or_url) from e
@ -417,10 +436,10 @@ class ClientSession:
raise InvalidURL(proxy) from e raise InvalidURL(proxy) from e
if timeout is sentinel: if timeout is sentinel:
real_timeout = self._timeout # type: ClientTimeout real_timeout: ClientTimeout = self._timeout
else: else:
if not isinstance(timeout, ClientTimeout): if not isinstance(timeout, ClientTimeout):
real_timeout = ClientTimeout(total=timeout) # type: ignore real_timeout = ClientTimeout(total=timeout) # type: ignore[arg-type]
else: else:
real_timeout = timeout real_timeout = timeout
# timeout is cumulative for all request operations # timeout is cumulative for all request operations
@ -441,7 +460,7 @@ class ClientSession:
] ]
for trace in traces: for trace in traces:
await trace.send_request_start(method, url, headers) await trace.send_request_start(method, url.update_query(params), headers)
timer = tm.timer() timer = tm.timer()
try: try:
@ -483,11 +502,8 @@ class ClientSession:
if proxy is not None: if proxy is not None:
proxy = URL(proxy) proxy = URL(proxy)
elif self._trust_env: elif self._trust_env:
for scheme, proxy_info in proxies_from_env().items(): with suppress(LookupError):
if scheme == url.scheme: proxy, proxy_auth = get_env_proxy_for_url(url)
proxy = proxy_info.proxy
proxy_auth = proxy_info.proxy_auth
break
req = self._request_class( req = self._request_class(
method, method,
@ -515,7 +531,7 @@ class ClientSession:
# connection timeout # connection timeout
try: try:
with CeilTimeout(real_timeout.connect, loop=self._loop): async with ceil_timeout(real_timeout.connect):
assert self._connector is not None assert self._connector is not None
conn = await self._connector.connect( conn = await self._connector.connect(
req, traces=traces, timeout=real_timeout req, traces=traces, timeout=real_timeout
@ -551,6 +567,8 @@ class ClientSession:
except ClientError: except ClientError:
raise raise
except OSError as exc: except OSError as exc:
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise ClientOSError(*exc.args) from exc raise ClientOSError(*exc.args) from exc
self._cookie_jar.update_cookies(resp.cookies, resp.url) self._cookie_jar.update_cookies(resp.cookies, resp.url)
@ -560,7 +578,7 @@ class ClientSession:
for trace in traces: for trace in traces:
await trace.send_request_redirect( await trace.send_request_redirect(
method, url, headers, resp method, url.update_query(params), headers, resp
) )
redirects += 1 redirects += 1
@ -634,7 +652,9 @@ class ClientSession:
resp._history = tuple(history) resp._history = tuple(history)
for trace in traces: for trace in traces:
await trace.send_request_end(method, url, headers, resp) await trace.send_request_end(
method, url.update_query(params), headers, resp
)
return resp return resp
except BaseException as e: except BaseException as e:
@ -645,7 +665,9 @@ class ClientSession:
handle = None handle = None
for trace in traces: for trace in traces:
await trace.send_request_exception(method, url, headers, e) await trace.send_request_exception(
method, url.update_query(params), headers, e
)
raise raise
def ws_connect( def ws_connect(
@ -661,6 +683,7 @@ class ClientSession:
heartbeat: Optional[float] = None, heartbeat: Optional[float] = None,
auth: Optional[BasicAuth] = None, auth: Optional[BasicAuth] = None,
origin: Optional[str] = None, origin: Optional[str] = None,
params: Optional[Mapping[str, str]] = None,
headers: Optional[LooseHeaders] = None, headers: Optional[LooseHeaders] = None,
proxy: Optional[StrOrURL] = None, proxy: Optional[StrOrURL] = None,
proxy_auth: Optional[BasicAuth] = None, proxy_auth: Optional[BasicAuth] = None,
@ -685,6 +708,7 @@ class ClientSession:
heartbeat=heartbeat, heartbeat=heartbeat,
auth=auth, auth=auth,
origin=origin, origin=origin,
params=params,
headers=headers, headers=headers,
proxy=proxy, proxy=proxy,
proxy_auth=proxy_auth, proxy_auth=proxy_auth,
@ -711,6 +735,7 @@ class ClientSession:
heartbeat: Optional[float] = None, heartbeat: Optional[float] = None,
auth: Optional[BasicAuth] = None, auth: Optional[BasicAuth] = None,
origin: Optional[str] = None, origin: Optional[str] = None,
params: Optional[Mapping[str, str]] = None,
headers: Optional[LooseHeaders] = None, headers: Optional[LooseHeaders] = None,
proxy: Optional[StrOrURL] = None, proxy: Optional[StrOrURL] = None,
proxy_auth: Optional[BasicAuth] = None, proxy_auth: Optional[BasicAuth] = None,
@ -724,7 +749,7 @@ class ClientSession:
) -> ClientWebSocketResponse: ) -> ClientWebSocketResponse:
if headers is None: if headers is None:
real_headers = CIMultiDict() # type: CIMultiDict[str] real_headers: CIMultiDict[str] = CIMultiDict()
else: else:
real_headers = CIMultiDict(headers) real_headers = CIMultiDict(headers)
@ -754,6 +779,7 @@ class ClientSession:
resp = await self.request( resp = await self.request(
method, method,
url, url,
params=params,
headers=real_headers, headers=real_headers,
read_until_eof=False, read_until_eof=False,
auth=auth, auth=auth,
@ -842,9 +868,9 @@ class ClientSession:
assert conn_proto is not None assert conn_proto is not None
transport = conn.transport transport = conn.transport
assert transport is not None assert transport is not None
reader = FlowControlDataQueue( reader: FlowControlDataQueue[WSMessage] = FlowControlDataQueue(
conn_proto, 2 ** 16, loop=self._loop conn_proto, 2**16, loop=self._loop
) # type: FlowControlDataQueue[WSMessage] )
conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader) conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader)
writer = WebSocketWriter( writer = WebSocketWriter(
conn_proto, conn_proto,
@ -879,7 +905,7 @@ class ClientSession:
if headers: if headers:
if not isinstance(headers, (MultiDictProxy, MultiDict)): if not isinstance(headers, (MultiDictProxy, MultiDict)):
headers = CIMultiDict(headers) headers = CIMultiDict(headers)
added_names = set() # type: Set[str] added_names: Set[str] = set()
for key, value in headers.items(): for key, value in headers.items():
if key in added_names: if key in added_names:
result.add(key, value) result.add(key, value)
@ -1001,7 +1027,7 @@ class ClientSession:
return self._loop return self._loop
@property @property
def timeout(self) -> Union[object, ClientTimeout]: def timeout(self) -> ClientTimeout:
"""Timeout for the session.""" """Timeout for the session."""
return self._timeout return self._timeout
@ -1034,23 +1060,21 @@ class ClientSession:
def raise_for_status( def raise_for_status(
self, self,
) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]: ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]:
""" """Should `ClientResponse.raise_for_status()` be called for each response."""
Should `ClientResponse.raise_for_status()`
be called for each response
"""
return self._raise_for_status return self._raise_for_status
@property @property
def auto_decompress(self) -> bool: def auto_decompress(self) -> bool:
"""Should the body response be automatically decompressed""" """Should the body response be automatically decompressed."""
return self._auto_decompress return self._auto_decompress
@property @property
def trust_env(self) -> bool: def trust_env(self) -> bool:
""" """
Should get proxies information Should proxies information from environment or netrc be trusted.
from HTTP_PROXY / HTTPS_PROXY environment variables
or ~/.netrc file if present Information is from HTTP_PROXY / HTTPS_PROXY environment variables
or ~/.netrc file if present.
""" """
return self._trust_env return self._trust_env
@ -1100,7 +1124,7 @@ class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType
def send(self, arg: None) -> "asyncio.Future[Any]": def send(self, arg: None) -> "asyncio.Future[Any]":
return self._coro.send(arg) return self._coro.send(arg)
def throw(self, arg: BaseException) -> None: # type: ignore def throw(self, arg: BaseException) -> None: # type: ignore[arg-type,override]
self._coro.throw(arg) self._coro.throw(arg)
def close(self) -> None: def close(self) -> None:
@ -1119,6 +1143,8 @@ class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType
class _RequestContextManager(_BaseRequestContextManager[ClientResponse]): class _RequestContextManager(_BaseRequestContextManager[ClientResponse]):
__slots__ = ()
async def __aexit__( async def __aexit__(
self, self,
exc_type: Optional[Type[BaseException]], exc_type: Optional[Type[BaseException]],
@ -1134,6 +1160,8 @@ class _RequestContextManager(_BaseRequestContextManager[ClientResponse]):
class _WSRequestContextManager(_BaseRequestContextManager[ClientWebSocketResponse]): class _WSRequestContextManager(_BaseRequestContextManager[ClientWebSocketResponse]):
__slots__ = ()
async def __aexit__( async def __aexit__(
self, self,
exc_type: Optional[Type[BaseException]], exc_type: Optional[Type[BaseException]],
@ -1153,7 +1181,7 @@ class _SessionRequestContextManager:
session: ClientSession, session: ClientSession,
) -> None: ) -> None:
self._coro = coro self._coro = coro
self._resp = None # type: Optional[ClientResponse] self._resp: Optional[ClientResponse] = None
self._session = session self._session = session
async def __aenter__(self) -> ClientResponse: async def __aenter__(self) -> ClientResponse:
@ -1202,7 +1230,9 @@ def request(
read_bufsize: Optional[int] = None, read_bufsize: Optional[int] = None,
loop: Optional[asyncio.AbstractEventLoop] = None, loop: Optional[asyncio.AbstractEventLoop] = None,
) -> _SessionRequestContextManager: ) -> _SessionRequestContextManager:
"""Constructs and sends a request. Returns response object. """Constructs and sends a request.
Returns response object.
method - HTTP method method - HTTP method
url - request url url - request url
params - (optional) Dictionary or bytes to be sent in the query params - (optional) Dictionary or bytes to be sent in the query

Просмотреть файл

@ -4,6 +4,7 @@ import asyncio
import warnings import warnings
from typing import TYPE_CHECKING, Any, Optional, Tuple, Union from typing import TYPE_CHECKING, Any, Optional, Tuple, Union
from .http_parser import RawResponseMessage
from .typedefs import LooseHeaders from .typedefs import LooseHeaders
try: try:
@ -11,7 +12,7 @@ try:
SSLContext = ssl.SSLContext SSLContext = ssl.SSLContext
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
ssl = SSLContext = None # type: ignore ssl = SSLContext = None # type: ignore[assignment]
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
@ -99,7 +100,7 @@ class ClientResponseError(ClientError):
args += f", message={self.message!r}" args += f", message={self.message!r}"
if self.headers is not None: if self.headers is not None:
args += f", headers={self.headers!r}" args += f", headers={self.headers!r}"
return "{}({})".format(type(self).__name__, args) return f"{type(self).__name__}({args})"
@property @property
def code(self) -> int: def code(self) -> int:
@ -153,7 +154,7 @@ class ClientConnectorError(ClientOSError):
"""Client connector error. """Client connector error.
Raised in :class:`aiohttp.connector.TCPConnector` if Raised in :class:`aiohttp.connector.TCPConnector` if
connection to proxy can not be established. a connection can not be established.
""" """
def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None: def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None:
@ -195,6 +196,29 @@ class ClientProxyConnectionError(ClientConnectorError):
""" """
class UnixClientConnectorError(ClientConnectorError):
"""Unix connector error.
Raised in :py:class:`aiohttp.connector.UnixConnector`
if connection to unix socket can not be established.
"""
def __init__(
self, path: str, connection_key: ConnectionKey, os_error: OSError
) -> None:
self._path = path
super().__init__(connection_key, os_error)
@property
def path(self) -> str:
return self._path
def __str__(self) -> str:
return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format(
self, self.ssl if self.ssl is not None else "default", self.strerror
)
class ServerConnectionError(ClientConnectionError): class ServerConnectionError(ClientConnectionError):
"""Server connection errors.""" """Server connection errors."""
@ -202,7 +226,7 @@ class ServerConnectionError(ClientConnectionError):
class ServerDisconnectedError(ServerConnectionError): class ServerDisconnectedError(ServerConnectionError):
"""Server disconnected.""" """Server disconnected."""
def __init__(self, message: Optional[str] = None) -> None: def __init__(self, message: Union[RawResponseMessage, str, None] = None) -> None:
if message is None: if message is None:
message = "Server disconnected" message = "Server disconnected"
@ -238,7 +262,8 @@ class InvalidURL(ClientError, ValueError):
"""Invalid URL. """Invalid URL.
URL used for fetching is malformed, e.g. it doesn't contains host URL used for fetching is malformed, e.g. it doesn't contains host
part.""" part.
"""
# Derive from ValueError for backward compatibility # Derive from ValueError for backward compatibility
@ -279,11 +304,11 @@ else: # pragma: no cover
ssl_error_bases = (ClientSSLError,) ssl_error_bases = (ClientSSLError,)
class ClientConnectorSSLError(*ssl_error_bases): # type: ignore class ClientConnectorSSLError(*ssl_error_bases): # type: ignore[misc]
"""Response ssl error.""" """Response ssl error."""
class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc]
"""Response certificate error.""" """Response certificate error."""
def __init__( def __init__(

Просмотреть файл

@ -23,7 +23,7 @@ class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamRe
self._should_close = False self._should_close = False
self._payload = None self._payload: Optional[StreamReader] = None
self._skip_payload = False self._skip_payload = False
self._payload_parser = None self._payload_parser = None
@ -31,10 +31,10 @@ class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamRe
self._tail = b"" self._tail = b""
self._upgraded = False self._upgraded = False
self._parser = None # type: Optional[HttpResponseParser] self._parser: Optional[HttpResponseParser] = None
self._read_timeout = None # type: Optional[float] self._read_timeout: Optional[float] = None
self._read_timeout_handle = None # type: Optional[asyncio.TimerHandle] self._read_timeout_handle: Optional[asyncio.TimerHandle] = None
@property @property
def upgraded(self) -> bool: def upgraded(self) -> bool:
@ -142,7 +142,7 @@ class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamRe
read_until_eof: bool = False, read_until_eof: bool = False,
auto_decompress: bool = True, auto_decompress: bool = True,
read_timeout: Optional[float] = None, read_timeout: Optional[float] = None,
read_bufsize: int = 2 ** 16 read_bufsize: int = 2**16,
) -> None: ) -> None:
self._skip_payload = skip_payload self._skip_payload = skip_payload
@ -223,7 +223,7 @@ class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamRe
self._upgraded = upgraded self._upgraded = upgraded
payload = None payload: Optional[StreamReader] = None
for message, payload in messages: for message, payload in messages:
if message.should_close: if message.should_close:
self._should_close = True self._should_close = True
@ -231,7 +231,7 @@ class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamRe
self._payload = payload self._payload = payload
if self._skip_payload or message.code in (204, 304): if self._skip_payload or message.code in (204, 304):
self.feed_data((message, EMPTY_PAYLOAD), 0) # type: ignore self.feed_data((message, EMPTY_PAYLOAD), 0)
else: else:
self.feed_data((message, payload), 0) self.feed_data((message, payload), 0)
if payload is not None: if payload is not None:

Просмотреть файл

@ -63,13 +63,13 @@ try:
import ssl import ssl
from ssl import SSLContext from ssl import SSLContext
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
ssl = None # type: ignore ssl = None # type: ignore[assignment]
SSLContext = object # type: ignore SSLContext = object # type: ignore[misc,assignment]
try: try:
import cchardet as chardet import cchardet as chardet
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
import chardet # type: ignore import charset_normalizer as chardet # type: ignore[no-redef]
__all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint") __all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint")
@ -294,7 +294,7 @@ class ClientRequest:
real_response_class = ClientResponse real_response_class = ClientResponse
else: else:
real_response_class = response_class real_response_class = response_class
self.response_class = real_response_class # type: Type[ClientResponse] self.response_class: Type[ClientResponse] = real_response_class
self._timer = timer if timer is not None else TimerNoop() self._timer = timer if timer is not None else TimerNoop()
self._ssl = ssl self._ssl = ssl
@ -311,7 +311,7 @@ class ClientRequest:
self.update_proxy(proxy, proxy_auth, proxy_headers) self.update_proxy(proxy, proxy_auth, proxy_headers)
self.update_body_from_data(data) self.update_body_from_data(data)
if data or self.method not in self.GET_METHODS: if data is not None or self.method not in self.GET_METHODS:
self.update_transfer_encoding() self.update_transfer_encoding()
self.update_expect_continue(expect100) self.update_expect_continue(expect100)
if traces is None: if traces is None:
@ -329,9 +329,7 @@ class ClientRequest:
def connection_key(self) -> ConnectionKey: def connection_key(self) -> ConnectionKey:
proxy_headers = self.proxy_headers proxy_headers = self.proxy_headers
if proxy_headers: if proxy_headers:
h = hash( h: Optional[int] = hash(tuple((k, v) for k, v in proxy_headers.items()))
tuple((k, v) for k, v in proxy_headers.items())
) # type: Optional[int]
else: else:
h = None h = None
return ConnectionKey( return ConnectionKey(
@ -356,7 +354,7 @@ class ClientRequest:
@property @property
def request_info(self) -> RequestInfo: def request_info(self) -> RequestInfo:
headers = CIMultiDictProxy(self.headers) # type: CIMultiDictProxy[str] headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers)
return RequestInfo(self.url, self.method, headers, self.original_url) return RequestInfo(self.url, self.method, headers, self.original_url)
def update_host(self, url: URL) -> None: def update_host(self, url: URL) -> None:
@ -387,7 +385,7 @@ class ClientRequest:
def update_headers(self, headers: Optional[LooseHeaders]) -> None: def update_headers(self, headers: Optional[LooseHeaders]) -> None:
"""Update request headers.""" """Update request headers."""
self.headers = CIMultiDict() # type: CIMultiDict[str] self.headers: CIMultiDict[str] = CIMultiDict()
# add host # add host
netloc = cast(str, self.url.raw_host) netloc = cast(str, self.url.raw_host)
@ -399,9 +397,9 @@ class ClientRequest:
if headers: if headers:
if isinstance(headers, (dict, MultiDictProxy, MultiDict)): if isinstance(headers, (dict, MultiDictProxy, MultiDict)):
headers = headers.items() # type: ignore headers = headers.items() # type: ignore[assignment]
for key, value in headers: # type: ignore for key, value in headers: # type: ignore[misc]
# A special case for Host header # A special case for Host header
if key.lower() == "host": if key.lower() == "host":
self.headers[key] = value self.headers[key] = value
@ -413,7 +411,7 @@ class ClientRequest:
(hdr, None) for hdr in sorted(skip_auto_headers) (hdr, None) for hdr in sorted(skip_auto_headers)
) )
used_headers = self.headers.copy() used_headers = self.headers.copy()
used_headers.extend(self.skip_auto_headers) # type: ignore used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type]
for hdr, val in self.DEFAULT_HEADERS.items(): for hdr, val in self.DEFAULT_HEADERS.items():
if hdr not in used_headers: if hdr not in used_headers:
@ -427,7 +425,7 @@ class ClientRequest:
if not cookies: if not cookies:
return return
c = SimpleCookie() # type: SimpleCookie[str] c: SimpleCookie[str] = SimpleCookie()
if hdrs.COOKIE in self.headers: if hdrs.COOKIE in self.headers:
c.load(self.headers.get(hdrs.COOKIE, "")) c.load(self.headers.get(hdrs.COOKIE, ""))
del self.headers[hdrs.COOKIE] del self.headers[hdrs.COOKIE]
@ -435,7 +433,7 @@ class ClientRequest:
if isinstance(cookies, Mapping): if isinstance(cookies, Mapping):
iter_cookies = cookies.items() iter_cookies = cookies.items()
else: else:
iter_cookies = cookies # type: ignore iter_cookies = cookies # type: ignore[assignment]
for name, value in iter_cookies: for name, value in iter_cookies:
if isinstance(value, Morsel): if isinstance(value, Morsel):
# Preserve coded_value # Preserve coded_value
@ -443,13 +441,13 @@ class ClientRequest:
mrsl_val.set(value.key, value.value, value.coded_value) mrsl_val.set(value.key, value.value, value.coded_value)
c[name] = mrsl_val c[name] = mrsl_val
else: else:
c[name] = value # type: ignore c[name] = value # type: ignore[assignment]
self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip() self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip()
def update_content_encoding(self, data: Any) -> None: def update_content_encoding(self, data: Any) -> None:
"""Set request content encoding.""" """Set request content encoding."""
if not data: if data is None:
return return
enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower() enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower()
@ -499,7 +497,7 @@ class ClientRequest:
self.headers[hdrs.AUTHORIZATION] = auth.encode() self.headers[hdrs.AUTHORIZATION] = auth.encode()
def update_body_from_data(self, body: Any) -> None: def update_body_from_data(self, body: Any) -> None:
if not body: if body is None:
return return
# FormData # FormData
@ -547,8 +545,6 @@ class ClientRequest:
proxy_auth: Optional[BasicAuth], proxy_auth: Optional[BasicAuth],
proxy_headers: Optional[LooseHeaders], proxy_headers: Optional[LooseHeaders],
) -> None: ) -> None:
if proxy and not proxy.scheme == "http":
raise ValueError("Only http proxies are supported")
if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth): if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):
raise ValueError("proxy_auth must be None or BasicAuth() tuple") raise ValueError("proxy_auth must be None or BasicAuth() tuple")
self.proxy = proxy self.proxy = proxy
@ -585,19 +581,22 @@ class ClientRequest:
await self.body.write(writer) await self.body.write(writer)
else: else:
if isinstance(self.body, (bytes, bytearray)): if isinstance(self.body, (bytes, bytearray)):
self.body = (self.body,) # type: ignore self.body = (self.body,) # type: ignore[assignment]
for chunk in self.body: for chunk in self.body:
await writer.write(chunk) # type: ignore await writer.write(chunk) # type: ignore[arg-type]
await writer.write_eof() await writer.write_eof()
except OSError as exc: except OSError as exc:
new_exc = ClientOSError( if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
exc.errno, "Can not write request body for %s" % self.url protocol.set_exception(exc)
) else:
new_exc.__context__ = exc new_exc = ClientOSError(
new_exc.__cause__ = exc exc.errno, "Can not write request body for %s" % self.url
protocol.set_exception(new_exc) )
new_exc.__context__ = exc
new_exc.__cause__ = exc
protocol.set_exception(new_exc)
except asyncio.CancelledError as exc: except asyncio.CancelledError as exc:
if not conn.closed: if not conn.closed:
protocol.set_exception(exc) protocol.set_exception(exc)
@ -632,6 +631,9 @@ class ClientRequest:
on_chunk_sent=functools.partial( on_chunk_sent=functools.partial(
self._on_chunk_request_sent, self.method, self.url self._on_chunk_request_sent, self.method, self.url
), ),
on_headers_sent=functools.partial(
self._on_headers_request_sent, self.method, self.url
),
) )
if self.compress: if self.compress:
@ -701,17 +703,23 @@ class ClientRequest:
for trace in self._traces: for trace in self._traces:
await trace.send_request_chunk_sent(method, url, chunk) await trace.send_request_chunk_sent(method, url, chunk)
async def _on_headers_request_sent(
self, method: str, url: URL, headers: "CIMultiDict[str]"
) -> None:
for trace in self._traces:
await trace.send_request_headers(method, url, headers)
class ClientResponse(HeadersMixin): class ClientResponse(HeadersMixin):
# from the Status-Line of the response # from the Status-Line of the response
version = None # HTTP-Version version = None # HTTP-Version
status = None # type: int # Status-Code status: int = None # type: ignore[assignment] # Status-Code
reason = None # Reason-Phrase reason = None # Reason-Phrase
content = None # type: StreamReader # Payload stream content: StreamReader = None # type: ignore[assignment] # Payload stream
_headers = None # type: CIMultiDictProxy[str] # Response headers _headers: "CIMultiDictProxy[str]" = None # type: ignore[assignment]
_raw_headers = None # type: RawHeaders # Response raw headers _raw_headers: RawHeaders = None # type: ignore[assignment] # Response raw headers
_connection = None # current connection _connection = None # current connection
_source_traceback = None _source_traceback = None
@ -736,22 +744,22 @@ class ClientResponse(HeadersMixin):
assert isinstance(url, URL) assert isinstance(url, URL)
self.method = method self.method = method
self.cookies = SimpleCookie() # type: SimpleCookie[str] self.cookies: SimpleCookie[str] = SimpleCookie()
self._real_url = url self._real_url = url
self._url = url.with_fragment(None) self._url = url.with_fragment(None)
self._body = None # type: Any self._body: Any = None
self._writer = writer # type: Optional[asyncio.Task[None]] self._writer: Optional[asyncio.Task[None]] = writer
self._continue = continue100 # None by default self._continue = continue100 # None by default
self._closed = True self._closed = True
self._history = () # type: Tuple[ClientResponse, ...] self._history: Tuple[ClientResponse, ...] = ()
self._request_info = request_info self._request_info = request_info
self._timer = timer if timer is not None else TimerNoop() self._timer = timer if timer is not None else TimerNoop()
self._cache = {} # type: Dict[str, Any] self._cache: Dict[str, Any] = {}
self._traces = traces self._traces = traces
self._loop = loop self._loop = loop
# store a reference to session #1985 # store a reference to session #1985
self._session = session # type: Optional[ClientSession] self._session: Optional[ClientSession] = session
if loop.get_debug(): if loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1)) self._source_traceback = traceback.extract_stack(sys._getframe(1))
@ -848,7 +856,7 @@ class ClientResponse(HeadersMixin):
if not links_str: if not links_str:
return MultiDictProxy(MultiDict()) return MultiDictProxy(MultiDict())
links = MultiDict() # type: MultiDict[MultiDictProxy[Union[str, URL]]] links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()
for val in re.split(r",(?=\s*<)", links_str): for val in re.split(r",(?=\s*<)", links_str):
match = re.match(r"\s*<(.*)>(.*)", val) match = re.match(r"\s*<(.*)>(.*)", val)
@ -858,7 +866,7 @@ class ClientResponse(HeadersMixin):
url, params_str = match.groups() url, params_str = match.groups()
params = params_str.split(";")[1:] params = params_str.split(";")[1:]
link = MultiDict() # type: MultiDict[Union[str, URL]] link: MultiDict[Union[str, URL]] = MultiDict()
for param in params: for param in params:
match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M) match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M)
@ -869,7 +877,7 @@ class ClientResponse(HeadersMixin):
link.add(key, value) link.add(key, value)
key = link.get("rel", url) # type: ignore key = link.get("rel", url) # type: ignore[assignment]
link.add("url", self.url.join(URL(url))) link.add("url", self.url.join(URL(url)))
@ -887,7 +895,8 @@ class ClientResponse(HeadersMixin):
while True: while True:
# read response # read response
try: try:
message, payload = await self._protocol.read() # type: ignore protocol = self._protocol
message, payload = await protocol.read() # type: ignore[union-attr]
except http.HttpProcessingError as exc: except http.HttpProcessingError as exc:
raise ClientResponseError( raise ClientResponseError(
self.request_info, self.request_info,
@ -986,14 +995,10 @@ class ClientResponse(HeadersMixin):
This is **not** a check for ``200 OK`` but a check that the response This is **not** a check for ``200 OK`` but a check that the response
status is under 400. status is under 400.
""" """
try: return 400 > self.status
self.raise_for_status()
except ClientResponseError:
return False
return True
def raise_for_status(self) -> None: def raise_for_status(self) -> None:
if 400 <= self.status: if not self.ok:
# reason should always be not None for a started response # reason should always be not None for a started response
assert self.reason is not None assert self.reason is not None
self.release() self.release()
@ -1040,7 +1045,7 @@ class ClientResponse(HeadersMixin):
elif self._released: elif self._released:
raise ClientConnectionError("Connection closed") raise ClientConnectionError("Connection closed")
return self._body return self._body # type: ignore[no-any-return]
def get_encoding(self) -> str: def get_encoding(self) -> str:
ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()
@ -1078,7 +1083,9 @@ class ClientResponse(HeadersMixin):
if encoding is None: if encoding is None:
encoding = self.get_encoding() encoding = self.get_encoding()
return self._body.decode(encoding, errors=errors) # type: ignore return self._body.decode( # type: ignore[no-any-return,union-attr]
encoding, errors=errors
)
async def json( async def json(
self, self,
@ -1103,7 +1110,7 @@ class ClientResponse(HeadersMixin):
headers=self.headers, headers=self.headers,
) )
stripped = self._body.strip() # type: ignore stripped = self._body.strip() # type: ignore[union-attr]
if not stripped: if not stripped:
return None return None

Просмотреть файл

@ -1,7 +1,7 @@
"""WebSocket client for asyncio.""" """WebSocket client for asyncio."""
import asyncio import asyncio
from typing import Any, Optional from typing import Any, Optional, cast
import async_timeout import async_timeout
@ -12,6 +12,7 @@ from .http import (
WS_CLOSED_MESSAGE, WS_CLOSED_MESSAGE,
WS_CLOSING_MESSAGE, WS_CLOSING_MESSAGE,
WebSocketError, WebSocketError,
WSCloseCode,
WSMessage, WSMessage,
WSMsgType, WSMsgType,
) )
@ -50,19 +51,19 @@ class ClientWebSocketResponse:
self._protocol = protocol self._protocol = protocol
self._closed = False self._closed = False
self._closing = False self._closing = False
self._close_code = None # type: Optional[int] self._close_code: Optional[int] = None
self._timeout = timeout self._timeout = timeout
self._receive_timeout = receive_timeout self._receive_timeout = receive_timeout
self._autoclose = autoclose self._autoclose = autoclose
self._autoping = autoping self._autoping = autoping
self._heartbeat = heartbeat self._heartbeat = heartbeat
self._heartbeat_cb = None self._heartbeat_cb: Optional[asyncio.TimerHandle] = None
if heartbeat is not None: if heartbeat is not None:
self._pong_heartbeat = heartbeat / 2.0 self._pong_heartbeat = heartbeat / 2.0
self._pong_response_cb = None self._pong_response_cb: Optional[asyncio.TimerHandle] = None
self._loop = loop self._loop = loop
self._waiting = None # type: Optional[asyncio.Future[bool]] self._waiting: Optional[asyncio.Future[bool]] = None
self._exception = None # type: Optional[BaseException] self._exception: Optional[BaseException] = None
self._compress = compress self._compress = compress
self._client_notakeover = client_notakeover self._client_notakeover = client_notakeover
@ -101,7 +102,7 @@ class ClientWebSocketResponse:
def _pong_not_received(self) -> None: def _pong_not_received(self) -> None:
if not self._closed: if not self._closed:
self._closed = True self._closed = True
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = asyncio.TimeoutError() self._exception = asyncio.TimeoutError()
self._response.close() self._response.close()
@ -163,7 +164,7 @@ class ClientWebSocketResponse:
) -> None: ) -> None:
await self.send_str(dumps(data), compress=compress) await self.send_str(dumps(data), compress=compress)
async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool:
# we need to break `receive()` cycle first, # we need to break `receive()` cycle first,
# `close()` may be called from different task # `close()` may be called from different task
if self._waiting is not None and not self._closed: if self._waiting is not None and not self._closed:
@ -176,11 +177,11 @@ class ClientWebSocketResponse:
try: try:
await self._writer.close(code, message) await self._writer.close(code, message)
except asyncio.CancelledError: except asyncio.CancelledError:
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._response.close() self._response.close()
raise raise
except Exception as exc: except Exception as exc:
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = exc self._exception = exc
self._response.close() self._response.close()
return True return True
@ -191,14 +192,14 @@ class ClientWebSocketResponse:
while True: while True:
try: try:
with async_timeout.timeout(self._timeout, loop=self._loop): async with async_timeout.timeout(self._timeout):
msg = await self._reader.read() msg = await self._reader.read()
except asyncio.CancelledError: except asyncio.CancelledError:
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._response.close() self._response.close()
raise raise
except Exception as exc: except Exception as exc:
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = exc self._exception = exc
self._response.close() self._response.close()
return True return True
@ -224,9 +225,7 @@ class ClientWebSocketResponse:
try: try:
self._waiting = self._loop.create_future() self._waiting = self._loop.create_future()
try: try:
with async_timeout.timeout( async with async_timeout.timeout(timeout or self._receive_timeout):
timeout or self._receive_timeout, loop=self._loop
):
msg = await self._reader.read() msg = await self._reader.read()
self._reset_heartbeat() self._reset_heartbeat()
finally: finally:
@ -234,15 +233,15 @@ class ClientWebSocketResponse:
self._waiting = None self._waiting = None
set_result(waiter, True) set_result(waiter, True)
except (asyncio.CancelledError, asyncio.TimeoutError): except (asyncio.CancelledError, asyncio.TimeoutError):
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
raise raise
except EofStream: except EofStream:
self._close_code = 1000 self._close_code = WSCloseCode.OK
await self.close() await self.close()
return WSMessage(WSMsgType.CLOSED, None, None) return WSMessage(WSMsgType.CLOSED, None, None)
except ClientError: except ClientError:
self._closed = True self._closed = True
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
return WS_CLOSED_MESSAGE return WS_CLOSED_MESSAGE
except WebSocketError as exc: except WebSocketError as exc:
self._close_code = exc.code self._close_code = exc.code
@ -251,7 +250,7 @@ class ClientWebSocketResponse:
except Exception as exc: except Exception as exc:
self._exception = exc self._exception = exc
self._closing = True self._closing = True
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
await self.close() await self.close()
return WSMessage(WSMsgType.ERROR, exc, None) return WSMessage(WSMsgType.ERROR, exc, None)
@ -274,13 +273,13 @@ class ClientWebSocketResponse:
msg = await self.receive(timeout) msg = await self.receive(timeout)
if msg.type != WSMsgType.TEXT: if msg.type != WSMsgType.TEXT:
raise TypeError(f"Received message {msg.type}:{msg.data!r} is not str") raise TypeError(f"Received message {msg.type}:{msg.data!r} is not str")
return msg.data return cast(str, msg.data)
async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:
msg = await self.receive(timeout) msg = await self.receive(timeout)
if msg.type != WSMsgType.BINARY: if msg.type != WSMsgType.BINARY:
raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes") raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes")
return msg.data return cast(bytes, msg.data)
async def receive_json( async def receive_json(
self, self,

Просмотреть файл

@ -39,12 +39,20 @@ from .client_exceptions import (
ClientHttpProxyError, ClientHttpProxyError,
ClientProxyConnectionError, ClientProxyConnectionError,
ServerFingerprintMismatch, ServerFingerprintMismatch,
UnixClientConnectorError,
cert_errors, cert_errors,
ssl_errors, ssl_errors,
) )
from .client_proto import ResponseHandler from .client_proto import ResponseHandler
from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params
from .helpers import PY_36, CeilTimeout, get_running_loop, is_ip_address, noop, sentinel from .helpers import (
PY_36,
ceil_timeout,
get_running_loop,
is_ip_address,
noop,
sentinel,
)
from .http import RESPONSES from .http import RESPONSES
from .locks import EventResultOrError from .locks import EventResultOrError
from .resolver import DefaultResolver from .resolver import DefaultResolver
@ -54,8 +62,8 @@ try:
SSLContext = ssl.SSLContext SSLContext = ssl.SSLContext
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
ssl = None # type: ignore ssl = None # type: ignore[assignment]
SSLContext = object # type: ignore SSLContext = object # type: ignore[misc,assignment]
__all__ = ("BaseConnector", "TCPConnector", "UnixConnector", "NamedPipeConnector") __all__ = ("BaseConnector", "TCPConnector", "UnixConnector", "NamedPipeConnector")
@ -102,8 +110,8 @@ class Connection:
self._key = key self._key = key
self._connector = connector self._connector = connector
self._loop = loop self._loop = loop
self._protocol = protocol # type: Optional[ResponseHandler] self._protocol: Optional[ResponseHandler] = protocol
self._callbacks = [] # type: List[Callable[[], None]] self._callbacks: List[Callable[[], None]] = []
if loop.get_debug(): if loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1)) self._source_traceback = traceback.extract_stack(sys._getframe(1))
@ -178,7 +186,7 @@ class Connection:
class _TransportPlaceholder: class _TransportPlaceholder:
""" placeholder for BaseConnector.connect function """ """placeholder for BaseConnector.connect function"""
def close(self) -> None: def close(self) -> None:
pass pass
@ -229,33 +237,31 @@ class BaseConnector:
if loop.get_debug(): if loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1)) self._source_traceback = traceback.extract_stack(sys._getframe(1))
self._conns = ( self._conns: Dict[ConnectionKey, List[Tuple[ResponseHandler, float]]] = {}
{}
) # type: Dict[ConnectionKey, List[Tuple[ResponseHandler, float]]]
self._limit = limit self._limit = limit
self._limit_per_host = limit_per_host self._limit_per_host = limit_per_host
self._acquired = set() # type: Set[ResponseHandler] self._acquired: Set[ResponseHandler] = set()
self._acquired_per_host = defaultdict( self._acquired_per_host: DefaultDict[
set ConnectionKey, Set[ResponseHandler]
) # type: DefaultDict[ConnectionKey, Set[ResponseHandler]] ] = defaultdict(set)
self._keepalive_timeout = cast(float, keepalive_timeout) self._keepalive_timeout = cast(float, keepalive_timeout)
self._force_close = force_close self._force_close = force_close
# {host_key: FIFO list of waiters} # {host_key: FIFO list of waiters}
self._waiters = defaultdict(deque) # type: ignore self._waiters = defaultdict(deque) # type: ignore[var-annotated]
self._loop = loop self._loop = loop
self._factory = functools.partial(ResponseHandler, loop=loop) self._factory = functools.partial(ResponseHandler, loop=loop)
self.cookies = SimpleCookie() # type: SimpleCookie[str] self.cookies: SimpleCookie[str] = SimpleCookie()
# start keep-alive connection cleanup task # start keep-alive connection cleanup task
self._cleanup_handle = None self._cleanup_handle: Optional[asyncio.TimerHandle] = None
# start cleanup closed transports task # start cleanup closed transports task
self._cleanup_closed_handle = None self._cleanup_closed_handle: Optional[asyncio.TimerHandle] = None
self._cleanup_closed_disabled = not enable_cleanup_closed self._cleanup_closed_disabled = not enable_cleanup_closed
self._cleanup_closed_transports = [] # type: List[Optional[asyncio.Transport]] self._cleanup_closed_transports: List[Optional[asyncio.Transport]] = []
self._cleanup_closed() self._cleanup_closed()
def __del__(self, _warnings: Any = warnings) -> None: def __del__(self, _warnings: Any = warnings) -> None:
@ -284,14 +290,14 @@ class BaseConnector:
def __enter__(self) -> "BaseConnector": def __enter__(self) -> "BaseConnector":
warnings.warn( warnings.warn(
'"witn Connector():" is deprecated, ' '"with Connector():" is deprecated, '
'use "async with Connector():" instead', 'use "async with Connector():" instead',
DeprecationWarning, DeprecationWarning,
) )
return self return self
def __exit__(self, *exc: Any) -> None: def __exit__(self, *exc: Any) -> None:
self.close() self._close()
async def __aenter__(self) -> "BaseConnector": async def __aenter__(self) -> "BaseConnector":
return self return self
@ -320,12 +326,10 @@ class BaseConnector:
@property @property
def limit_per_host(self) -> int: def limit_per_host(self) -> int:
"""The limit_per_host for simultaneous connections """The limit for simultaneous connections to the same endpoint.
to the same endpoint.
Endpoints are the same if they are have equal Endpoints are the same if they are have equal
(host, port, is_ssl) triple. (host, port, is_ssl) triple.
""" """
return self._limit_per_host return self._limit_per_host
@ -383,6 +387,7 @@ class BaseConnector:
def _cleanup_closed(self) -> None: def _cleanup_closed(self) -> None:
"""Double confirmation for transport close. """Double confirmation for transport close.
Some broken ssl servers may leave socket open without proper close. Some broken ssl servers may leave socket open without proper close.
""" """
if self._cleanup_closed_handle: if self._cleanup_closed_handle:
@ -451,13 +456,13 @@ class BaseConnector:
def _available_connections(self, key: "ConnectionKey") -> int: def _available_connections(self, key: "ConnectionKey") -> int:
""" """
Return number of available connections taking into account Return number of available connections.
the limit, limit_per_host and the connection key.
If it returns less than 1 means that there is no connections The limit, limit_per_host and the connection key are taken into account.
availables.
If it returns less than 1 means that there are no connections
available.
""" """
if self._limit: if self._limit:
# total calc available connections # total calc available connections
available = self._limit - len(self._acquired) available = self._limit - len(self._acquired)
@ -552,8 +557,14 @@ class BaseConnector:
await trace.send_connection_create_end() await trace.send_connection_create_end()
else: else:
if traces: if traces:
# Acquire the connection to prevent race conditions with limits
placeholder = cast(ResponseHandler, _TransportPlaceholder())
self._acquired.add(placeholder)
self._acquired_per_host[key].add(placeholder)
for trace in traces: for trace in traces:
await trace.send_connection_reuseconn() await trace.send_connection_reuseconn()
self._acquired.remove(placeholder)
self._drop_acquired_per_host(key, placeholder)
self._acquired.add(proto) self._acquired.add(proto)
self._acquired_per_host[key].add(proto) self._acquired_per_host[key].add(proto)
@ -592,7 +603,9 @@ class BaseConnector:
def _release_waiter(self) -> None: def _release_waiter(self) -> None:
""" """
Iterates over all waiters till found one that is not finsihed and Iterates over all waiters until one to be released is found.
The one to be released is not finsihed and
belongs to a host that has available connections. belongs to a host that has available connections.
""" """
if not self._waiters: if not self._waiters:
@ -670,10 +683,8 @@ class BaseConnector:
class _DNSCacheTable: class _DNSCacheTable:
def __init__(self, ttl: Optional[float] = None) -> None: def __init__(self, ttl: Optional[float] = None) -> None:
self._addrs_rr = ( self._addrs_rr: Dict[Tuple[str, int], Tuple[Iterator[Dict[str, Any]], int]] = {}
{} self._timestamps: Dict[Tuple[str, int], float] = {}
) # type: Dict[Tuple[str, int], Tuple[Iterator[Dict[str, Any]], int]]
self._timestamps = {} # type: Dict[Tuple[str, int], float]
self._ttl = ttl self._ttl = ttl
def __contains__(self, host: object) -> bool: def __contains__(self, host: object) -> bool:
@ -769,9 +780,7 @@ class TCPConnector(BaseConnector):
self._use_dns_cache = use_dns_cache self._use_dns_cache = use_dns_cache
self._cached_hosts = _DNSCacheTable(ttl=ttl_dns_cache) self._cached_hosts = _DNSCacheTable(ttl=ttl_dns_cache)
self._throttle_dns_events = ( self._throttle_dns_events: Dict[Tuple[str, int], EventResultOrError] = {}
{}
) # type: Dict[Tuple[str, int], EventResultOrError]
self._family = family self._family = family
self._local_addr = local_addr self._local_addr = local_addr
@ -899,9 +908,11 @@ class TCPConnector(BaseConnector):
if verified: if verified:
return ssl.create_default_context() return ssl.create_default_context()
else: else:
sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslcontext.options |= ssl.OP_NO_SSLv2 sslcontext.options |= ssl.OP_NO_SSLv2
sslcontext.options |= ssl.OP_NO_SSLv3 sslcontext.options |= ssl.OP_NO_SSLv3
sslcontext.check_hostname = False
sslcontext.verify_mode = ssl.CERT_NONE
try: try:
sslcontext.options |= ssl.OP_NO_COMPRESSION sslcontext.options |= ssl.OP_NO_COMPRESSION
except AttributeError as attr_err: except AttributeError as attr_err:
@ -965,15 +976,160 @@ class TCPConnector(BaseConnector):
**kwargs: Any, **kwargs: Any,
) -> Tuple[asyncio.Transport, ResponseHandler]: ) -> Tuple[asyncio.Transport, ResponseHandler]:
try: try:
with CeilTimeout(timeout.sock_connect): async with ceil_timeout(timeout.sock_connect):
return await self._loop.create_connection(*args, **kwargs) # type: ignore # noqa return await self._loop.create_connection(*args, **kwargs) # type: ignore[return-value] # noqa
except cert_errors as exc: except cert_errors as exc:
raise ClientConnectorCertificateError(req.connection_key, exc) from exc raise ClientConnectorCertificateError(req.connection_key, exc) from exc
except ssl_errors as exc: except ssl_errors as exc:
raise ClientConnectorSSLError(req.connection_key, exc) from exc raise ClientConnectorSSLError(req.connection_key, exc) from exc
except OSError as exc: except OSError as exc:
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise client_error(req.connection_key, exc) from exc raise client_error(req.connection_key, exc) from exc
def _fail_on_no_start_tls(self, req: "ClientRequest") -> None:
"""Raise a :py:exc:`RuntimeError` on missing ``start_tls()``.
One case is that :py:meth:`asyncio.loop.start_tls` is not yet
implemented under Python 3.6. It is necessary for TLS-in-TLS so
that it is possible to send HTTPS queries through HTTPS proxies.
This doesn't affect regular HTTP requests, though.
"""
if not req.is_ssl():
return
proxy_url = req.proxy
assert proxy_url is not None
if proxy_url.scheme != "https":
return
self._check_loop_for_start_tls()
def _check_loop_for_start_tls(self) -> None:
try:
self._loop.start_tls
except AttributeError as attr_exc:
raise RuntimeError(
"An HTTPS request is being sent through an HTTPS proxy. "
"This needs support for TLS in TLS but it is not implemented "
"in your runtime for the stdlib asyncio.\n\n"
"Please upgrade to Python 3.7 or higher. For more details, "
"please see:\n"
"* https://bugs.python.org/issue37179\n"
"* https://github.com/python/cpython/pull/28073\n"
"* https://docs.aiohttp.org/en/stable/"
"client_advanced.html#proxy-support\n"
"* https://github.com/aio-libs/aiohttp/discussions/6044\n",
) from attr_exc
def _loop_supports_start_tls(self) -> bool:
try:
self._check_loop_for_start_tls()
except RuntimeError:
return False
else:
return True
def _warn_about_tls_in_tls(
self,
underlying_transport: asyncio.Transport,
req: "ClientRequest",
) -> None:
"""Issue a warning if the requested URL has HTTPS scheme."""
if req.request_info.url.scheme != "https":
return
asyncio_supports_tls_in_tls = getattr(
underlying_transport,
"_start_tls_compatible",
False,
)
if asyncio_supports_tls_in_tls:
return
warnings.warn(
"An HTTPS request is being sent through an HTTPS proxy. "
"This support for TLS in TLS is known to be disabled "
"in the stdlib asyncio. This is why you'll probably see "
"an error in the log below.\n\n"
"It is possible to enable it via monkeypatching under "
"Python 3.7 or higher. For more details, see:\n"
"* https://bugs.python.org/issue37179\n"
"* https://github.com/python/cpython/pull/28073\n\n"
"You can temporarily patch this as follows:\n"
"* https://docs.aiohttp.org/en/stable/client_advanced.html#proxy-support\n"
"* https://github.com/aio-libs/aiohttp/discussions/6044\n",
RuntimeWarning,
source=self,
# Why `4`? At least 3 of the calls in the stack originate
# from the methods in this class.
stacklevel=3,
)
async def _start_tls_connection(
self,
underlying_transport: asyncio.Transport,
req: "ClientRequest",
timeout: "ClientTimeout",
client_error: Type[Exception] = ClientConnectorError,
) -> Tuple[asyncio.BaseTransport, ResponseHandler]:
"""Wrap the raw TCP transport with TLS."""
tls_proto = self._factory() # Create a brand new proto for TLS
# Safety of the `cast()` call here is based on the fact that
# internally `_get_ssl_context()` only returns `None` when
# `req.is_ssl()` evaluates to `False` which is never gonna happen
# in this code path. Of course, it's rather fragile
# maintainability-wise but this is to be solved separately.
sslcontext = cast(ssl.SSLContext, self._get_ssl_context(req))
try:
async with ceil_timeout(timeout.sock_connect):
try:
tls_transport = await self._loop.start_tls(
underlying_transport,
tls_proto,
sslcontext,
server_hostname=req.host,
ssl_handshake_timeout=timeout.total,
)
except BaseException:
# We need to close the underlying transport since
# `start_tls()` probably failed before it had a
# chance to do this:
underlying_transport.close()
raise
except cert_errors as exc:
raise ClientConnectorCertificateError(req.connection_key, exc) from exc
except ssl_errors as exc:
raise ClientConnectorSSLError(req.connection_key, exc) from exc
except OSError as exc:
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise client_error(req.connection_key, exc) from exc
except TypeError as type_err:
# Example cause looks like this:
# TypeError: transport <asyncio.sslproto._SSLProtocolTransport
# object at 0x7f760615e460> is not supported by start_tls()
raise ClientConnectionError(
"Cannot initialize a TLS-in-TLS connection to host "
f"{req.host!s}:{req.port:d} through an underlying connection "
f"to an HTTPS proxy {req.proxy!s} ssl:{req.ssl or 'default'} "
f"[{type_err!s}]"
) from type_err
else:
if tls_transport is None:
msg = "Failed to start TLS (possibly caused by closing transport)"
raise client_error(req.connection_key, OSError(msg))
tls_proto.connection_made(
tls_transport
) # Kick the state machine of the new TLS protocol
return tls_transport, tls_proto
async def _create_direct_connection( async def _create_direct_connection(
self, self,
req: "ClientRequest", req: "ClientRequest",
@ -1006,11 +1162,13 @@ class TCPConnector(BaseConnector):
host_resolved.add_done_callback(drop_exception) host_resolved.add_done_callback(drop_exception)
raise raise
except OSError as exc: except OSError as exc:
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
# in case of proxy it is not ClientProxyConnectionError # in case of proxy it is not ClientProxyConnectionError
# it is problem of resolving proxy ip itself # it is problem of resolving proxy ip itself
raise ClientConnectorError(req.connection_key, exc) from exc raise ClientConnectorError(req.connection_key, exc) from exc
last_exc = None # type: Optional[Exception] last_exc: Optional[Exception] = None
for hinfo in hosts: for hinfo in hosts:
host = hinfo["host"] host = hinfo["host"]
@ -1052,10 +1210,13 @@ class TCPConnector(BaseConnector):
async def _create_proxy_connection( async def _create_proxy_connection(
self, req: "ClientRequest", traces: List["Trace"], timeout: "ClientTimeout" self, req: "ClientRequest", traces: List["Trace"], timeout: "ClientTimeout"
) -> Tuple[asyncio.Transport, ResponseHandler]: ) -> Tuple[asyncio.BaseTransport, ResponseHandler]:
headers = {} # type: Dict[str, str] self._fail_on_no_start_tls(req)
runtime_has_start_tls = self._loop_supports_start_tls()
headers: Dict[str, str] = {}
if req.proxy_headers is not None: if req.proxy_headers is not None:
headers = req.proxy_headers # type: ignore headers = req.proxy_headers # type: ignore[assignment]
headers[hdrs.HOST] = req.headers[hdrs.HOST] headers[hdrs.HOST] = req.headers[hdrs.HOST]
url = req.proxy url = req.proxy
@ -1087,7 +1248,9 @@ class TCPConnector(BaseConnector):
proxy_req.headers[hdrs.PROXY_AUTHORIZATION] = auth proxy_req.headers[hdrs.PROXY_AUTHORIZATION] = auth
if req.is_ssl(): if req.is_ssl():
sslcontext = self._get_ssl_context(req) if runtime_has_start_tls:
self._warn_about_tls_in_tls(transport, req)
# For HTTPS requests over HTTP proxy # For HTTPS requests over HTTP proxy
# we must notify proxy to tunnel connection # we must notify proxy to tunnel connection
# so we send CONNECT command: # so we send CONNECT command:
@ -1107,7 +1270,11 @@ class TCPConnector(BaseConnector):
try: try:
protocol = conn._protocol protocol = conn._protocol
assert protocol is not None assert protocol is not None
protocol.set_response_params()
# read_until_eof=True will ensure the connection isn't closed
# once the response is received and processed allowing
# START_TLS to work on the connection below.
protocol.set_response_params(read_until_eof=runtime_has_start_tls)
resp = await proxy_resp.start(conn) resp = await proxy_resp.start(conn)
except BaseException: except BaseException:
proxy_resp.close() proxy_resp.close()
@ -1128,21 +1295,42 @@ class TCPConnector(BaseConnector):
message=message, message=message,
headers=resp.headers, headers=resp.headers,
) )
rawsock = transport.get_extra_info("socket", default=None) if not runtime_has_start_tls:
if rawsock is None: rawsock = transport.get_extra_info("socket", default=None)
raise RuntimeError("Transport does not expose socket instance") if rawsock is None:
# Duplicate the socket, so now we can close proxy transport raise RuntimeError(
rawsock = rawsock.dup() "Transport does not expose socket instance"
finally: )
# Duplicate the socket, so now we can close proxy transport
rawsock = rawsock.dup()
except BaseException:
# It shouldn't be closed in `finally` because it's fed to
# `loop.start_tls()` and the docs say not to touch it after
# passing there.
transport.close() transport.close()
raise
finally:
if not runtime_has_start_tls:
transport.close()
transport, proto = await self._wrap_create_connection( if not runtime_has_start_tls:
self._factory, # HTTP proxy with support for upgrade to HTTPS
timeout=timeout, sslcontext = self._get_ssl_context(req)
ssl=sslcontext, return await self._wrap_create_connection(
sock=rawsock, self._factory,
server_hostname=req.host, timeout=timeout,
ssl=sslcontext,
sock=rawsock,
server_hostname=req.host,
req=req,
)
return await self._start_tls_connection(
# Access the old transport for the last time before it's
# closed and forgotten forever:
transport,
req=req, req=req,
timeout=timeout,
) )
finally: finally:
proxy_resp.close() proxy_resp.close()
@ -1189,12 +1377,14 @@ class UnixConnector(BaseConnector):
self, req: "ClientRequest", traces: List["Trace"], timeout: "ClientTimeout" self, req: "ClientRequest", traces: List["Trace"], timeout: "ClientTimeout"
) -> ResponseHandler: ) -> ResponseHandler:
try: try:
with CeilTimeout(timeout.sock_connect): async with ceil_timeout(timeout.sock_connect):
_, proto = await self._loop.create_unix_connection( _, proto = await self._loop.create_unix_connection(
self._factory, self._path self._factory, self._path
) )
except OSError as exc: except OSError as exc:
raise ClientConnectorError(req.connection_key, exc) from exc if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise UnixClientConnectorError(self.path, req.connection_key, exc) from exc
return cast(ResponseHandler, proto) return cast(ResponseHandler, proto)
@ -1230,7 +1420,9 @@ class NamedPipeConnector(BaseConnector):
limit_per_host=limit_per_host, limit_per_host=limit_per_host,
loop=loop, loop=loop,
) )
if not isinstance(self._loop, asyncio.ProactorEventLoop): # type: ignore if not isinstance(
self._loop, asyncio.ProactorEventLoop # type: ignore[attr-defined]
):
raise RuntimeError( raise RuntimeError(
"Named Pipes only available in proactor " "loop under windows" "Named Pipes only available in proactor " "loop under windows"
) )
@ -1245,8 +1437,8 @@ class NamedPipeConnector(BaseConnector):
self, req: "ClientRequest", traces: List["Trace"], timeout: "ClientTimeout" self, req: "ClientRequest", traces: List["Trace"], timeout: "ClientTimeout"
) -> ResponseHandler: ) -> ResponseHandler:
try: try:
with CeilTimeout(timeout.sock_connect): async with ceil_timeout(timeout.sock_connect):
_, proto = await self._loop.create_pipe_connection( # type: ignore _, proto = await self._loop.create_pipe_connection( # type: ignore[attr-defined] # noqa: E501
self._factory, self._path self._factory, self._path
) )
# the drain is required so that the connection_made is called # the drain is required so that the connection_made is called
@ -1257,6 +1449,8 @@ class NamedPipeConnector(BaseConnector):
# other option is to manually set transport like # other option is to manually set transport like
# `proto.transport = trans` # `proto.transport = trans`
except OSError as exc: except OSError as exc:
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise ClientConnectorError(req.connection_key, exc) from exc raise ClientConnectorError(req.connection_key, exc) from exc
return cast(ResponseHandler, proto) return cast(ResponseHandler, proto)

Просмотреть файл

@ -1,4 +1,5 @@
import asyncio import asyncio
import contextlib
import datetime import datetime
import os # noqa import os # noqa
import pathlib import pathlib
@ -11,6 +12,7 @@ from typing import ( # noqa
Dict, Dict,
Iterable, Iterable,
Iterator, Iterator,
List,
Mapping, Mapping,
Optional, Optional,
Set, Set,
@ -21,9 +23,9 @@ from typing import ( # noqa
from yarl import URL from yarl import URL
from .abc import AbstractCookieJar from .abc import AbstractCookieJar, ClearCookiePredicate
from .helpers import is_ip_address, next_whole_second from .helpers import is_ip_address, next_whole_second
from .typedefs import LooseCookies, PathLike from .typedefs import LooseCookies, PathLike, StrOrURL
__all__ = ("CookieJar", "DummyCookieJar") __all__ = ("CookieJar", "DummyCookieJar")
@ -52,24 +54,37 @@ class CookieJar(AbstractCookieJar):
MAX_TIME = datetime.datetime.max.replace(tzinfo=datetime.timezone.utc) MAX_TIME = datetime.datetime.max.replace(tzinfo=datetime.timezone.utc)
MAX_32BIT_TIME = datetime.datetime.utcfromtimestamp(2 ** 31 - 1) MAX_32BIT_TIME = datetime.datetime.utcfromtimestamp(2**31 - 1)
def __init__( def __init__(
self, self,
*, *,
unsafe: bool = False, unsafe: bool = False,
quote_cookie: bool = True, quote_cookie: bool = True,
loop: Optional[asyncio.AbstractEventLoop] = None treat_as_secure_origin: Union[StrOrURL, List[StrOrURL], None] = None,
loop: Optional[asyncio.AbstractEventLoop] = None,
) -> None: ) -> None:
super().__init__(loop=loop) super().__init__(loop=loop)
self._cookies = defaultdict( self._cookies: DefaultDict[Tuple[str, str], SimpleCookie[str]] = defaultdict(
SimpleCookie SimpleCookie
) # type: DefaultDict[str, SimpleCookie[str]] )
self._host_only_cookies = set() # type: Set[Tuple[str, str]] self._host_only_cookies: Set[Tuple[str, str]] = set()
self._unsafe = unsafe self._unsafe = unsafe
self._quote_cookie = quote_cookie self._quote_cookie = quote_cookie
if treat_as_secure_origin is None:
treat_as_secure_origin = []
elif isinstance(treat_as_secure_origin, URL):
treat_as_secure_origin = [treat_as_secure_origin.origin()]
elif isinstance(treat_as_secure_origin, str):
treat_as_secure_origin = [URL(treat_as_secure_origin).origin()]
else:
treat_as_secure_origin = [
URL(url).origin() if isinstance(url, str) else url.origin()
for url in treat_as_secure_origin
]
self._treat_as_secure_origin = treat_as_secure_origin
self._next_expiration = next_whole_second() self._next_expiration = next_whole_second()
self._expirations = {} # type: Dict[Tuple[str, str], datetime.datetime] self._expirations: Dict[Tuple[str, str, str], datetime.datetime] = {}
# #4515: datetime.max may not be representable on 32-bit platforms # #4515: datetime.max may not be representable on 32-bit platforms
self._max_time = self.MAX_TIME self._max_time = self.MAX_TIME
try: try:
@ -87,11 +102,41 @@ class CookieJar(AbstractCookieJar):
with file_path.open(mode="rb") as f: with file_path.open(mode="rb") as f:
self._cookies = pickle.load(f) self._cookies = pickle.load(f)
def clear(self) -> None: def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None:
self._cookies.clear() if predicate is None:
self._host_only_cookies.clear() self._next_expiration = next_whole_second()
self._next_expiration = next_whole_second() self._cookies.clear()
self._expirations.clear() self._host_only_cookies.clear()
self._expirations.clear()
return
to_del = []
now = datetime.datetime.now(datetime.timezone.utc)
for (domain, path), cookie in self._cookies.items():
for name, morsel in cookie.items():
key = (domain, path, name)
if (
key in self._expirations and self._expirations[key] <= now
) or predicate(morsel):
to_del.append(key)
for domain, path, name in to_del:
self._host_only_cookies.discard((domain, name))
key = (domain, path, name)
if key in self._expirations:
del self._expirations[(domain, path, name)]
self._cookies[(domain, path)].pop(name, None)
next_expiration = min(self._expirations.values(), default=self._max_time)
try:
self._next_expiration = next_expiration.replace(
microsecond=0
) + datetime.timedelta(seconds=1)
except OverflowError:
self._next_expiration = self._max_time
def clear_domain(self, domain: str) -> None:
self.clear(lambda x: self._is_domain_match(domain, x["domain"]))
def __iter__(self) -> "Iterator[Morsel[str]]": def __iter__(self) -> "Iterator[Morsel[str]]":
self._do_expiration() self._do_expiration()
@ -102,35 +147,13 @@ class CookieJar(AbstractCookieJar):
return sum(1 for i in self) return sum(1 for i in self)
def _do_expiration(self) -> None: def _do_expiration(self) -> None:
now = datetime.datetime.now(datetime.timezone.utc) self.clear(lambda x: False)
if self._next_expiration > now:
return
if not self._expirations:
return
next_expiration = self._max_time
to_del = []
cookies = self._cookies
expirations = self._expirations
for (domain, name), when in expirations.items():
if when <= now:
cookies[domain].pop(name, None)
to_del.append((domain, name))
self._host_only_cookies.discard((domain, name))
else:
next_expiration = min(next_expiration, when)
for key in to_del:
del expirations[key]
try: def _expire_cookie(
self._next_expiration = next_expiration.replace( self, when: datetime.datetime, domain: str, path: str, name: str
microsecond=0 ) -> None:
) + datetime.timedelta(seconds=1)
except OverflowError:
self._next_expiration = self._max_time
def _expire_cookie(self, when: datetime.datetime, domain: str, name: str) -> None:
self._next_expiration = min(self._next_expiration, when) self._next_expiration = min(self._next_expiration, when)
self._expirations[(domain, name)] = when self._expirations[(domain, path, name)] = when
def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None:
"""Update cookies.""" """Update cookies."""
@ -145,8 +168,8 @@ class CookieJar(AbstractCookieJar):
for name, cookie in cookies: for name, cookie in cookies:
if not isinstance(cookie, Morsel): if not isinstance(cookie, Morsel):
tmp = SimpleCookie() # type: SimpleCookie[str] tmp: SimpleCookie[str] = SimpleCookie()
tmp[name] = cookie # type: ignore tmp[name] = cookie # type: ignore[assignment]
cookie = tmp[name] cookie = tmp[name]
domain = cookie["domain"] domain = cookie["domain"]
@ -192,7 +215,7 @@ class CookieJar(AbstractCookieJar):
) + datetime.timedelta(seconds=delta_seconds) ) + datetime.timedelta(seconds=delta_seconds)
except OverflowError: except OverflowError:
max_age_expiration = self._max_time max_age_expiration = self._max_time
self._expire_cookie(max_age_expiration, domain, name) self._expire_cookie(max_age_expiration, domain, path, name)
except ValueError: except ValueError:
cookie["max-age"] = "" cookie["max-age"] = ""
@ -201,11 +224,11 @@ class CookieJar(AbstractCookieJar):
if expires: if expires:
expire_time = self._parse_date(expires) expire_time = self._parse_date(expires)
if expire_time: if expire_time:
self._expire_cookie(expire_time, domain, name) self._expire_cookie(expire_time, domain, path, name)
else: else:
cookie["expires"] = "" cookie["expires"] = ""
self._cookies[domain][name] = cookie self._cookies[(domain, path)][name] = cookie
self._do_expiration() self._do_expiration()
@ -219,7 +242,14 @@ class CookieJar(AbstractCookieJar):
SimpleCookie() if self._quote_cookie else BaseCookie() SimpleCookie() if self._quote_cookie else BaseCookie()
) )
hostname = request_url.raw_host or "" hostname = request_url.raw_host or ""
is_not_secure = request_url.scheme not in ("https", "wss") request_origin = URL()
with contextlib.suppress(ValueError):
request_origin = request_url.origin()
is_not_secure = (
request_url.scheme not in ("https", "wss")
and request_origin not in self._treat_as_secure_origin
)
for cookie in self: for cookie in self:
name = cookie.key name = cookie.key
@ -312,7 +342,7 @@ class CookieJar(AbstractCookieJar):
time_match = cls.DATE_HMS_TIME_RE.match(token) time_match = cls.DATE_HMS_TIME_RE.match(token)
if time_match: if time_match:
found_time = True found_time = True
hour, minute, second = [int(s) for s in time_match.groups()] hour, minute, second = (int(s) for s in time_match.groups())
continue continue
if not found_day: if not found_day:
@ -372,7 +402,10 @@ class DummyCookieJar(AbstractCookieJar):
def __len__(self) -> int: def __len__(self) -> int:
return 0 return 0
def clear(self) -> None: def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None:
pass
def clear_domain(self, domain: str) -> None:
pass pass
def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None:

Просмотреть файл

@ -12,8 +12,10 @@ __all__ = ("FormData",)
class FormData: class FormData:
"""Helper class for multipart/form-data and """Helper class for form body generation.
application/x-www-form-urlencoded body generation."""
Supports multipart/form-data and application/x-www-form-urlencoded.
"""
def __init__( def __init__(
self, self,
@ -22,7 +24,7 @@ class FormData:
charset: Optional[str] = None, charset: Optional[str] = None,
) -> None: ) -> None:
self._writer = multipart.MultipartWriter("form-data") self._writer = multipart.MultipartWriter("form-data")
self._fields = [] # type: List[Any] self._fields: List[Any] = []
self._is_multipart = False self._is_multipart = False
self._is_processed = False self._is_processed = False
self._quote_fields = quote_fields self._quote_fields = quote_fields
@ -45,7 +47,7 @@ class FormData:
*, *,
content_type: Optional[str] = None, content_type: Optional[str] = None,
filename: Optional[str] = None, filename: Optional[str] = None,
content_transfer_encoding: Optional[str] = None content_transfer_encoding: Optional[str] = None,
) -> None: ) -> None:
if isinstance(value, io.IOBase): if isinstance(value, io.IOBase):
@ -54,7 +56,7 @@ class FormData:
if filename is None and content_transfer_encoding is None: if filename is None and content_transfer_encoding is None:
filename = name filename = name
type_options = MultiDict({"name": name}) # type: MultiDict[str] type_options: MultiDict[str] = MultiDict({"name": name})
if filename is not None and not isinstance(filename, str): if filename is not None and not isinstance(filename, str):
raise TypeError( raise TypeError(
"filename must be an instance of str. " "Got: %s" % filename "filename must be an instance of str. " "Got: %s" % filename
@ -92,14 +94,14 @@ class FormData:
if isinstance(rec, io.IOBase): if isinstance(rec, io.IOBase):
k = guess_filename(rec, "unknown") k = guess_filename(rec, "unknown")
self.add_field(k, rec) # type: ignore self.add_field(k, rec) # type: ignore[arg-type]
elif isinstance(rec, (MultiDictProxy, MultiDict)): elif isinstance(rec, (MultiDictProxy, MultiDict)):
to_add.extend(rec.items()) to_add.extend(rec.items())
elif isinstance(rec, (list, tuple)) and len(rec) == 2: elif isinstance(rec, (list, tuple)) and len(rec) == 2:
k, fp = rec k, fp = rec
self.add_field(k, fp) # type: ignore self.add_field(k, fp) # type: ignore[arg-type]
else: else:
raise TypeError( raise TypeError(

184
third_party/python/aiohttp/aiohttp/hdrs.py поставляемый
Просмотреть файл

@ -2,21 +2,28 @@
# After changing the file content call ./tools/gen.py # After changing the file content call ./tools/gen.py
# to regenerate the headers parser # to regenerate the headers parser
import sys
from typing import Set
from multidict import istr from multidict import istr
METH_ANY = "*" if sys.version_info >= (3, 8):
METH_CONNECT = "CONNECT" from typing import Final
METH_HEAD = "HEAD" else:
METH_GET = "GET" from typing_extensions import Final
METH_DELETE = "DELETE"
METH_OPTIONS = "OPTIONS"
METH_PATCH = "PATCH"
METH_POST = "POST"
METH_PUT = "PUT"
METH_TRACE = "TRACE"
METH_ALL = { METH_ANY: Final[str] = "*"
METH_CONNECT: Final[str] = "CONNECT"
METH_HEAD: Final[str] = "HEAD"
METH_GET: Final[str] = "GET"
METH_DELETE: Final[str] = "DELETE"
METH_OPTIONS: Final[str] = "OPTIONS"
METH_PATCH: Final[str] = "PATCH"
METH_POST: Final[str] = "POST"
METH_PUT: Final[str] = "PUT"
METH_TRACE: Final[str] = "TRACE"
METH_ALL: Final[Set[str]] = {
METH_CONNECT, METH_CONNECT,
METH_HEAD, METH_HEAD,
METH_GET, METH_GET,
@ -28,81 +35,80 @@ METH_ALL = {
METH_TRACE, METH_TRACE,
} }
ACCEPT: Final[istr] = istr("Accept")
ACCEPT = istr("Accept") ACCEPT_CHARSET: Final[istr] = istr("Accept-Charset")
ACCEPT_CHARSET = istr("Accept-Charset") ACCEPT_ENCODING: Final[istr] = istr("Accept-Encoding")
ACCEPT_ENCODING = istr("Accept-Encoding") ACCEPT_LANGUAGE: Final[istr] = istr("Accept-Language")
ACCEPT_LANGUAGE = istr("Accept-Language") ACCEPT_RANGES: Final[istr] = istr("Accept-Ranges")
ACCEPT_RANGES = istr("Accept-Ranges") ACCESS_CONTROL_MAX_AGE: Final[istr] = istr("Access-Control-Max-Age")
ACCESS_CONTROL_MAX_AGE = istr("Access-Control-Max-Age") ACCESS_CONTROL_ALLOW_CREDENTIALS: Final[istr] = istr("Access-Control-Allow-Credentials")
ACCESS_CONTROL_ALLOW_CREDENTIALS = istr("Access-Control-Allow-Credentials") ACCESS_CONTROL_ALLOW_HEADERS: Final[istr] = istr("Access-Control-Allow-Headers")
ACCESS_CONTROL_ALLOW_HEADERS = istr("Access-Control-Allow-Headers") ACCESS_CONTROL_ALLOW_METHODS: Final[istr] = istr("Access-Control-Allow-Methods")
ACCESS_CONTROL_ALLOW_METHODS = istr("Access-Control-Allow-Methods") ACCESS_CONTROL_ALLOW_ORIGIN: Final[istr] = istr("Access-Control-Allow-Origin")
ACCESS_CONTROL_ALLOW_ORIGIN = istr("Access-Control-Allow-Origin") ACCESS_CONTROL_EXPOSE_HEADERS: Final[istr] = istr("Access-Control-Expose-Headers")
ACCESS_CONTROL_EXPOSE_HEADERS = istr("Access-Control-Expose-Headers") ACCESS_CONTROL_REQUEST_HEADERS: Final[istr] = istr("Access-Control-Request-Headers")
ACCESS_CONTROL_REQUEST_HEADERS = istr("Access-Control-Request-Headers") ACCESS_CONTROL_REQUEST_METHOD: Final[istr] = istr("Access-Control-Request-Method")
ACCESS_CONTROL_REQUEST_METHOD = istr("Access-Control-Request-Method") AGE: Final[istr] = istr("Age")
AGE = istr("Age") ALLOW: Final[istr] = istr("Allow")
ALLOW = istr("Allow") AUTHORIZATION: Final[istr] = istr("Authorization")
AUTHORIZATION = istr("Authorization") CACHE_CONTROL: Final[istr] = istr("Cache-Control")
CACHE_CONTROL = istr("Cache-Control") CONNECTION: Final[istr] = istr("Connection")
CONNECTION = istr("Connection") CONTENT_DISPOSITION: Final[istr] = istr("Content-Disposition")
CONTENT_DISPOSITION = istr("Content-Disposition") CONTENT_ENCODING: Final[istr] = istr("Content-Encoding")
CONTENT_ENCODING = istr("Content-Encoding") CONTENT_LANGUAGE: Final[istr] = istr("Content-Language")
CONTENT_LANGUAGE = istr("Content-Language") CONTENT_LENGTH: Final[istr] = istr("Content-Length")
CONTENT_LENGTH = istr("Content-Length") CONTENT_LOCATION: Final[istr] = istr("Content-Location")
CONTENT_LOCATION = istr("Content-Location") CONTENT_MD5: Final[istr] = istr("Content-MD5")
CONTENT_MD5 = istr("Content-MD5") CONTENT_RANGE: Final[istr] = istr("Content-Range")
CONTENT_RANGE = istr("Content-Range") CONTENT_TRANSFER_ENCODING: Final[istr] = istr("Content-Transfer-Encoding")
CONTENT_TRANSFER_ENCODING = istr("Content-Transfer-Encoding") CONTENT_TYPE: Final[istr] = istr("Content-Type")
CONTENT_TYPE = istr("Content-Type") COOKIE: Final[istr] = istr("Cookie")
COOKIE = istr("Cookie") DATE: Final[istr] = istr("Date")
DATE = istr("Date") DESTINATION: Final[istr] = istr("Destination")
DESTINATION = istr("Destination") DIGEST: Final[istr] = istr("Digest")
DIGEST = istr("Digest") ETAG: Final[istr] = istr("Etag")
ETAG = istr("Etag") EXPECT: Final[istr] = istr("Expect")
EXPECT = istr("Expect") EXPIRES: Final[istr] = istr("Expires")
EXPIRES = istr("Expires") FORWARDED: Final[istr] = istr("Forwarded")
FORWARDED = istr("Forwarded") FROM: Final[istr] = istr("From")
FROM = istr("From") HOST: Final[istr] = istr("Host")
HOST = istr("Host") IF_MATCH: Final[istr] = istr("If-Match")
IF_MATCH = istr("If-Match") IF_MODIFIED_SINCE: Final[istr] = istr("If-Modified-Since")
IF_MODIFIED_SINCE = istr("If-Modified-Since") IF_NONE_MATCH: Final[istr] = istr("If-None-Match")
IF_NONE_MATCH = istr("If-None-Match") IF_RANGE: Final[istr] = istr("If-Range")
IF_RANGE = istr("If-Range") IF_UNMODIFIED_SINCE: Final[istr] = istr("If-Unmodified-Since")
IF_UNMODIFIED_SINCE = istr("If-Unmodified-Since") KEEP_ALIVE: Final[istr] = istr("Keep-Alive")
KEEP_ALIVE = istr("Keep-Alive") LAST_EVENT_ID: Final[istr] = istr("Last-Event-ID")
LAST_EVENT_ID = istr("Last-Event-ID") LAST_MODIFIED: Final[istr] = istr("Last-Modified")
LAST_MODIFIED = istr("Last-Modified") LINK: Final[istr] = istr("Link")
LINK = istr("Link") LOCATION: Final[istr] = istr("Location")
LOCATION = istr("Location") MAX_FORWARDS: Final[istr] = istr("Max-Forwards")
MAX_FORWARDS = istr("Max-Forwards") ORIGIN: Final[istr] = istr("Origin")
ORIGIN = istr("Origin") PRAGMA: Final[istr] = istr("Pragma")
PRAGMA = istr("Pragma") PROXY_AUTHENTICATE: Final[istr] = istr("Proxy-Authenticate")
PROXY_AUTHENTICATE = istr("Proxy-Authenticate") PROXY_AUTHORIZATION: Final[istr] = istr("Proxy-Authorization")
PROXY_AUTHORIZATION = istr("Proxy-Authorization") RANGE: Final[istr] = istr("Range")
RANGE = istr("Range") REFERER: Final[istr] = istr("Referer")
REFERER = istr("Referer") RETRY_AFTER: Final[istr] = istr("Retry-After")
RETRY_AFTER = istr("Retry-After") SEC_WEBSOCKET_ACCEPT: Final[istr] = istr("Sec-WebSocket-Accept")
SEC_WEBSOCKET_ACCEPT = istr("Sec-WebSocket-Accept") SEC_WEBSOCKET_VERSION: Final[istr] = istr("Sec-WebSocket-Version")
SEC_WEBSOCKET_VERSION = istr("Sec-WebSocket-Version") SEC_WEBSOCKET_PROTOCOL: Final[istr] = istr("Sec-WebSocket-Protocol")
SEC_WEBSOCKET_PROTOCOL = istr("Sec-WebSocket-Protocol") SEC_WEBSOCKET_EXTENSIONS: Final[istr] = istr("Sec-WebSocket-Extensions")
SEC_WEBSOCKET_EXTENSIONS = istr("Sec-WebSocket-Extensions") SEC_WEBSOCKET_KEY: Final[istr] = istr("Sec-WebSocket-Key")
SEC_WEBSOCKET_KEY = istr("Sec-WebSocket-Key") SEC_WEBSOCKET_KEY1: Final[istr] = istr("Sec-WebSocket-Key1")
SEC_WEBSOCKET_KEY1 = istr("Sec-WebSocket-Key1") SERVER: Final[istr] = istr("Server")
SERVER = istr("Server") SET_COOKIE: Final[istr] = istr("Set-Cookie")
SET_COOKIE = istr("Set-Cookie") TE: Final[istr] = istr("TE")
TE = istr("TE") TRAILER: Final[istr] = istr("Trailer")
TRAILER = istr("Trailer") TRANSFER_ENCODING: Final[istr] = istr("Transfer-Encoding")
TRANSFER_ENCODING = istr("Transfer-Encoding") UPGRADE: Final[istr] = istr("Upgrade")
UPGRADE = istr("Upgrade") URI: Final[istr] = istr("URI")
URI = istr("URI") USER_AGENT: Final[istr] = istr("User-Agent")
USER_AGENT = istr("User-Agent") VARY: Final[istr] = istr("Vary")
VARY = istr("Vary") VIA: Final[istr] = istr("Via")
VIA = istr("Via") WANT_DIGEST: Final[istr] = istr("Want-Digest")
WANT_DIGEST = istr("Want-Digest") WARNING: Final[istr] = istr("Warning")
WARNING = istr("Warning") WWW_AUTHENTICATE: Final[istr] = istr("WWW-Authenticate")
WWW_AUTHENTICATE = istr("WWW-Authenticate") X_FORWARDED_FOR: Final[istr] = istr("X-Forwarded-For")
X_FORWARDED_FOR = istr("X-Forwarded-For") X_FORWARDED_HOST: Final[istr] = istr("X-Forwarded-Host")
X_FORWARDED_HOST = istr("X-Forwarded-Host") X_FORWARDED_PROTO: Final[istr] = istr("X-Forwarded-Proto")
X_FORWARDED_PROTO = istr("X-Forwarded-Proto")

266
third_party/python/aiohttp/aiohttp/helpers.py поставляемый
Просмотреть файл

@ -3,7 +3,6 @@
import asyncio import asyncio
import base64 import base64
import binascii import binascii
import cgi
import datetime import datetime
import functools import functools
import inspect import inspect
@ -17,12 +16,15 @@ import warnings
import weakref import weakref
from collections import namedtuple from collections import namedtuple
from contextlib import suppress from contextlib import suppress
from email.parser import HeaderParser
from email.utils import parsedate
from math import ceil from math import ceil
from pathlib import Path from pathlib import Path
from types import TracebackType from types import TracebackType
from typing import ( from typing import (
Any, Any,
Callable, Callable,
ContextManager,
Dict, Dict,
Generator, Generator,
Generic, Generic,
@ -40,58 +42,55 @@ from typing import (
cast, cast,
) )
from urllib.parse import quote from urllib.parse import quote
from urllib.request import getproxies from urllib.request import getproxies, proxy_bypass
import async_timeout import async_timeout
import attr import attr
from multidict import MultiDict, MultiDictProxy from multidict import MultiDict, MultiDictProxy
from typing_extensions import Protocol
from yarl import URL from yarl import URL
from . import hdrs from . import hdrs
from .log import client_logger, internal_logger from .log import client_logger, internal_logger
from .typedefs import PathLike # noqa from .typedefs import PathLike, Protocol # noqa
__all__ = ("BasicAuth", "ChainMapProxy") __all__ = ("BasicAuth", "ChainMapProxy", "ETag")
IS_MACOS = platform.system() == "Darwin"
IS_WINDOWS = platform.system() == "Windows"
PY_36 = sys.version_info >= (3, 6) PY_36 = sys.version_info >= (3, 6)
PY_37 = sys.version_info >= (3, 7) PY_37 = sys.version_info >= (3, 7)
PY_38 = sys.version_info >= (3, 8) PY_38 = sys.version_info >= (3, 8)
PY_310 = sys.version_info >= (3, 10)
PY_311 = sys.version_info >= (3, 11)
if not PY_37: if sys.version_info < (3, 7):
import idna_ssl import idna_ssl
idna_ssl.patch_match_hostname() idna_ssl.patch_match_hostname()
try: def all_tasks(
from typing import ContextManager loop: Optional[asyncio.AbstractEventLoop] = None,
except ImportError: ) -> Set["asyncio.Task[Any]"]:
from typing_extensions import ContextManager tasks = list(asyncio.Task.all_tasks(loop))
return {t for t in tasks if not t.done()}
else:
def all_tasks( all_tasks = asyncio.all_tasks
loop: Optional[asyncio.AbstractEventLoop] = None,
) -> Set["asyncio.Task[Any]"]:
tasks = list(asyncio.Task.all_tasks(loop))
return {t for t in tasks if not t.done()}
if PY_37:
all_tasks = getattr(asyncio, "all_tasks")
_T = TypeVar("_T") _T = TypeVar("_T")
_S = TypeVar("_S") _S = TypeVar("_S")
sentinel = object() # type: Any sentinel: Any = object()
NO_EXTENSIONS = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS")) # type: bool NO_EXTENSIONS: bool = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS"))
# N.B. sys.flags.dev_mode is available on Python 3.7+, use getattr # N.B. sys.flags.dev_mode is available on Python 3.7+, use getattr
# for compatibility with older versions # for compatibility with older versions
DEBUG = getattr(sys.flags, "dev_mode", False) or ( DEBUG: bool = getattr(sys.flags, "dev_mode", False) or (
not sys.flags.ignore_environment and bool(os.environ.get("PYTHONASYNCIODEBUG")) not sys.flags.ignore_environment and bool(os.environ.get("PYTHONASYNCIODEBUG"))
) # type: bool )
CHAR = {chr(i) for i in range(0, 128)} CHAR = {chr(i) for i in range(0, 128)}
@ -197,7 +196,9 @@ def strip_auth_from_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]:
def netrc_from_env() -> Optional[netrc.netrc]: def netrc_from_env() -> Optional[netrc.netrc]:
"""Attempt to load the netrc file from the path specified by the env-var """Load netrc from file.
Attempt to load it from the path specified by the env-var
NETRC or in the default location in the user's home directory. NETRC or in the default location in the user's home directory.
Returns None if it couldn't be found or fails to parse. Returns None if it couldn't be found or fails to parse.
@ -218,9 +219,7 @@ def netrc_from_env() -> Optional[netrc.netrc]:
) )
return None return None
netrc_path = home_dir / ( netrc_path = home_dir / ("_netrc" if IS_WINDOWS else ".netrc")
"_netrc" if platform.system() == "Windows" else ".netrc"
)
try: try:
return netrc.netrc(str(netrc_path)) return netrc.netrc(str(netrc_path))
@ -243,14 +242,20 @@ class ProxyInfo:
def proxies_from_env() -> Dict[str, ProxyInfo]: def proxies_from_env() -> Dict[str, ProxyInfo]:
proxy_urls = {k: URL(v) for k, v in getproxies().items() if k in ("http", "https")} proxy_urls = {
k: URL(v)
for k, v in getproxies().items()
if k in ("http", "https", "ws", "wss")
}
netrc_obj = netrc_from_env() netrc_obj = netrc_from_env()
stripped = {k: strip_auth_from_url(v) for k, v in proxy_urls.items()} stripped = {k: strip_auth_from_url(v) for k, v in proxy_urls.items()}
ret = {} ret = {}
for proto, val in stripped.items(): for proto, val in stripped.items():
proxy, auth = val proxy, auth = val
if proxy.scheme == "https": if proxy.scheme in ("https", "wss"):
client_logger.warning("HTTPS proxies %s are not supported, ignoring", proxy) client_logger.warning(
"%s proxies %s are not supported, ignoring", proxy.scheme.upper(), proxy
)
continue continue
if netrc_obj and auth is None: if netrc_obj and auth is None:
auth_from_netrc = None auth_from_netrc = None
@ -270,7 +275,7 @@ def proxies_from_env() -> Dict[str, ProxyInfo]:
def current_task( def current_task(
loop: Optional[asyncio.AbstractEventLoop] = None, loop: Optional[asyncio.AbstractEventLoop] = None,
) -> "Optional[asyncio.Task[Any]]": ) -> "Optional[asyncio.Task[Any]]":
if PY_37: if sys.version_info >= (3, 7):
return asyncio.current_task(loop=loop) return asyncio.current_task(loop=loop)
else: else:
return asyncio.Task.current_task(loop=loop) return asyncio.Task.current_task(loop=loop)
@ -297,11 +302,25 @@ def get_running_loop(
def isasyncgenfunction(obj: Any) -> bool: def isasyncgenfunction(obj: Any) -> bool:
func = getattr(inspect, "isasyncgenfunction", None) func = getattr(inspect, "isasyncgenfunction", None)
if func is not None: if func is not None:
return func(obj) return func(obj) # type: ignore[no-any-return]
else: else:
return False return False
def get_env_proxy_for_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]:
"""Get a permitted proxy for the given URL from the env."""
if url.host is not None and proxy_bypass(url.host):
raise LookupError(f"Proxying is disallowed for `{url.host!r}`")
proxies_in_env = proxies_from_env()
try:
proxy_info = proxies_in_env[url.scheme]
except KeyError:
raise LookupError(f"No proxies found for `{url!s}` in the env")
else:
return proxy_info.proxy, proxy_info.proxy_auth
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class MimeType: class MimeType:
type: str type: str
@ -331,7 +350,7 @@ def parse_mimetype(mimetype: str) -> MimeType:
) )
parts = mimetype.split(";") parts = mimetype.split(";")
params = MultiDict() # type: MultiDict[str] params: MultiDict[str] = MultiDict()
for item in parts[1:]: for item in parts[1:]:
if not item: if not item:
continue continue
@ -365,14 +384,41 @@ def guess_filename(obj: Any, default: Optional[str] = None) -> Optional[str]:
return default return default
not_qtext_re = re.compile(r"[^\041\043-\133\135-\176]")
QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {"\t"}
def quoted_string(content: str) -> str:
"""Return 7-bit content as quoted-string.
Format content into a quoted-string as defined in RFC5322 for
Internet Message Format. Notice that this is not the 8-bit HTTP
format, but the 7-bit email format. Content must be in usascii or
a ValueError is raised.
"""
if not (QCONTENT > set(content)):
raise ValueError(f"bad content for quoted-string {content!r}")
return not_qtext_re.sub(lambda x: "\\" + x.group(0), content)
def content_disposition_header( def content_disposition_header(
disptype: str, quote_fields: bool = True, **params: str disptype: str, quote_fields: bool = True, _charset: str = "utf-8", **params: str
) -> str: ) -> str:
"""Sets ``Content-Disposition`` header. """Sets ``Content-Disposition`` header for MIME.
This is the MIME payload Content-Disposition header from RFC 2183
and RFC 7579 section 4.2, not the HTTP Content-Disposition from
RFC 6266.
disptype is a disposition type: inline, attachment, form-data. disptype is a disposition type: inline, attachment, form-data.
Should be valid extension token (see RFC 2183) Should be valid extension token (see RFC 2183)
quote_fields performs value quoting to 7-bit MIME headers
according to RFC 7578. Set to quote_fields to False if recipient
can take 8-bit file names and field values.
_charset specifies the charset to use when quote_fields is True.
params is a dict with disposition params. params is a dict with disposition params.
""" """
if not disptype or not (TOKEN > set(disptype)): if not disptype or not (TOKEN > set(disptype)):
@ -386,26 +432,40 @@ def content_disposition_header(
raise ValueError( raise ValueError(
"bad content disposition parameter" " {!r}={!r}".format(key, val) "bad content disposition parameter" " {!r}={!r}".format(key, val)
) )
qval = quote(val, "") if quote_fields else val if quote_fields:
lparams.append((key, '"%s"' % qval)) if key.lower() == "filename":
if key == "filename": qval = quote(val, "", encoding=_charset)
lparams.append(("filename*", "utf-8''" + qval)) lparams.append((key, '"%s"' % qval))
else:
try:
qval = quoted_string(val)
except ValueError:
qval = "".join(
(_charset, "''", quote(val, "", encoding=_charset))
)
lparams.append((key + "*", qval))
else:
lparams.append((key, '"%s"' % qval))
else:
qval = val.replace("\\", "\\\\").replace('"', '\\"')
lparams.append((key, '"%s"' % qval))
sparams = "; ".join("=".join(pair) for pair in lparams) sparams = "; ".join("=".join(pair) for pair in lparams)
value = "; ".join((value, sparams)) value = "; ".join((value, sparams))
return value return value
class _TSelf(Protocol): class _TSelf(Protocol, Generic[_T]):
_cache: Dict[str, Any] _cache: Dict[str, _T]
class reify(Generic[_T]): class reify(Generic[_T]):
"""Use as a class method decorator. It operates almost exactly like """Use as a class method decorator.
It operates almost exactly like
the Python `@property` decorator, but it puts the result of the the Python `@property` decorator, but it puts the result of the
method it decorates into the instance dict after the first call, method it decorates into the instance dict after the first call,
effectively replacing the function it decorates with an instance effectively replacing the function it decorates with an instance
variable. It is, in Python parlance, a data descriptor. variable. It is, in Python parlance, a data descriptor.
""" """
def __init__(self, wrapped: Callable[..., _T]) -> None: def __init__(self, wrapped: Callable[..., _T]) -> None:
@ -413,7 +473,7 @@ class reify(Generic[_T]):
self.__doc__ = wrapped.__doc__ self.__doc__ = wrapped.__doc__
self.name = wrapped.__name__ self.name = wrapped.__name__
def __get__(self, inst: _TSelf, owner: Optional[Type[Any]] = None) -> _T: def __get__(self, inst: _TSelf[_T], owner: Optional[Type[Any]] = None) -> _T:
try: try:
try: try:
return inst._cache[self.name] return inst._cache[self.name]
@ -426,7 +486,7 @@ class reify(Generic[_T]):
return self return self
raise raise
def __set__(self, inst: _TSelf, value: _T) -> None: def __set__(self, inst: _TSelf[_T], value: _T) -> None:
raise AttributeError("reified property is read-only") raise AttributeError("reified property is read-only")
@ -436,7 +496,7 @@ try:
from ._helpers import reify as reify_c from ._helpers import reify as reify_c
if not NO_EXTENSIONS: if not NO_EXTENSIONS:
reify = reify_c # type: ignore reify = reify_c # type: ignore[misc,assignment]
except ImportError: except ImportError:
pass pass
@ -470,7 +530,7 @@ def _is_ip_address(
elif isinstance(host, (bytes, bytearray, memoryview)): elif isinstance(host, (bytes, bytearray, memoryview)):
return bool(regexb.match(host)) return bool(regexb.match(host))
else: else:
raise TypeError("{} [{}] is not a str or bytes".format(host, type(host))) raise TypeError(f"{host} [{type(host)}] is not a str or bytes")
is_ipv4_address = functools.partial(_is_ip_address, _ipv4_regex, _ipv4_regexb) is_ipv4_address = functools.partial(_is_ip_address, _ipv4_regex, _ipv4_regexb)
@ -488,7 +548,7 @@ def next_whole_second() -> datetime.datetime:
) + datetime.timedelta(seconds=0) ) + datetime.timedelta(seconds=0)
_cached_current_datetime = None # type: Optional[int] _cached_current_datetime: Optional[int] = None
_cached_formatted_datetime = "" _cached_formatted_datetime = ""
@ -532,7 +592,7 @@ def rfc822_formatted_time() -> str:
return _cached_formatted_datetime return _cached_formatted_datetime
def _weakref_handle(info): # type: ignore def _weakref_handle(info: "Tuple[weakref.ref[object], str]") -> None:
ref, name = info ref, name = info
ob = ref() ob = ref()
if ob is not None: if ob is not None:
@ -540,34 +600,40 @@ def _weakref_handle(info): # type: ignore
getattr(ob, name)() getattr(ob, name)()
def weakref_handle(ob, name, timeout, loop): # type: ignore def weakref_handle(
ob: object, name: str, timeout: float, loop: asyncio.AbstractEventLoop
) -> Optional[asyncio.TimerHandle]:
if timeout is not None and timeout > 0: if timeout is not None and timeout > 0:
when = loop.time() + timeout when = loop.time() + timeout
if timeout >= 5: if timeout >= 5:
when = ceil(when) when = ceil(when)
return loop.call_at(when, _weakref_handle, (weakref.ref(ob), name)) return loop.call_at(when, _weakref_handle, (weakref.ref(ob), name))
return None
def call_later(cb, timeout, loop): # type: ignore def call_later(
cb: Callable[[], Any], timeout: float, loop: asyncio.AbstractEventLoop
) -> Optional[asyncio.TimerHandle]:
if timeout is not None and timeout > 0: if timeout is not None and timeout > 0:
when = loop.time() + timeout when = loop.time() + timeout
if timeout > 5: if timeout > 5:
when = ceil(when) when = ceil(when)
return loop.call_at(when, cb) return loop.call_at(when, cb)
return None
class TimeoutHandle: class TimeoutHandle:
""" Timeout handle """ """Timeout handle"""
def __init__( def __init__(
self, loop: asyncio.AbstractEventLoop, timeout: Optional[float] self, loop: asyncio.AbstractEventLoop, timeout: Optional[float]
) -> None: ) -> None:
self._timeout = timeout self._timeout = timeout
self._loop = loop self._loop = loop
self._callbacks = ( self._callbacks: List[
[] Tuple[Callable[..., None], Tuple[Any, ...], Dict[str, Any]]
) # type: List[Tuple[Callable[..., None], Tuple[Any, ...], Dict[str, Any]]] ] = []
def register( def register(
self, callback: Callable[..., None], *args: Any, **kwargs: Any self, callback: Callable[..., None], *args: Any, **kwargs: Any
@ -621,11 +687,11 @@ class TimerNoop(BaseTimerContext):
class TimerContext(BaseTimerContext): class TimerContext(BaseTimerContext):
""" Low resolution timeout context manager """ """Low resolution timeout context manager"""
def __init__(self, loop: asyncio.AbstractEventLoop) -> None: def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
self._loop = loop self._loop = loop
self._tasks = [] # type: List[asyncio.Task[Any]] self._tasks: List[asyncio.Task[Any]] = []
self._cancelled = False self._cancelled = False
def __enter__(self) -> BaseTimerContext: def __enter__(self) -> BaseTimerContext:
@ -637,7 +703,6 @@ class TimerContext(BaseTimerContext):
) )
if self._cancelled: if self._cancelled:
task.cancel()
raise asyncio.TimeoutError from None raise asyncio.TimeoutError from None
self._tasks.append(task) self._tasks.append(task)
@ -664,29 +729,24 @@ class TimerContext(BaseTimerContext):
self._cancelled = True self._cancelled = True
class CeilTimeout(async_timeout.timeout): def ceil_timeout(delay: Optional[float]) -> async_timeout.Timeout:
def __enter__(self) -> async_timeout.timeout: if delay is None or delay <= 0:
if self._timeout is not None: return async_timeout.timeout(None)
self._task = current_task(loop=self._loop)
if self._task is None: loop = get_running_loop()
raise RuntimeError( now = loop.time()
"Timeout context manager should be used inside a task" when = now + delay
) if delay > 5:
now = self._loop.time() when = ceil(when)
delay = self._timeout return async_timeout.timeout_at(when)
when = now + delay
if delay > 5:
when = ceil(when)
self._cancel_handler = self._loop.call_at(when, self._cancel_task)
return self
class HeadersMixin: class HeadersMixin:
ATTRS = frozenset(["_content_type", "_content_dict", "_stored_content_type"]) ATTRS = frozenset(["_content_type", "_content_dict", "_stored_content_type"])
_content_type = None # type: Optional[str] _content_type: Optional[str] = None
_content_dict = None # type: Optional[Dict[str, str]] _content_dict: Optional[Dict[str, str]] = None
_stored_content_type = sentinel _stored_content_type = sentinel
def _parse_content_type(self, raw: str) -> None: def _parse_content_type(self, raw: str) -> None:
@ -696,28 +756,33 @@ class HeadersMixin:
self._content_type = "application/octet-stream" self._content_type = "application/octet-stream"
self._content_dict = {} self._content_dict = {}
else: else:
self._content_type, self._content_dict = cgi.parse_header(raw) msg = HeaderParser().parsestr("Content-Type: " + raw)
self._content_type = msg.get_content_type()
params = msg.get_params()
self._content_dict = dict(params[1:]) # First element is content type again
@property @property
def content_type(self) -> str: def content_type(self) -> str:
"""The value of content part for Content-Type HTTP header.""" """The value of content part for Content-Type HTTP header."""
raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore[attr-defined]
if self._stored_content_type != raw: if self._stored_content_type != raw:
self._parse_content_type(raw) self._parse_content_type(raw)
return self._content_type # type: ignore return self._content_type # type: ignore[return-value]
@property @property
def charset(self) -> Optional[str]: def charset(self) -> Optional[str]:
"""The value of charset part for Content-Type HTTP header.""" """The value of charset part for Content-Type HTTP header."""
raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore[attr-defined]
if self._stored_content_type != raw: if self._stored_content_type != raw:
self._parse_content_type(raw) self._parse_content_type(raw)
return self._content_dict.get("charset") # type: ignore return self._content_dict.get("charset") # type: ignore[union-attr]
@property @property
def content_length(self) -> Optional[int]: def content_length(self) -> Optional[int]:
"""The value of Content-Length HTTP header.""" """The value of Content-Length HTTP header."""
content_length = self._headers.get(hdrs.CONTENT_LENGTH) # type: ignore content_length = self._headers.get( # type: ignore[attr-defined]
hdrs.CONTENT_LENGTH
)
if content_length is not None: if content_length is not None:
return int(content_length) return int(content_length)
@ -760,10 +825,10 @@ class ChainMapProxy(Mapping[str, Any]):
def __len__(self) -> int: def __len__(self) -> int:
# reuses stored hash values if possible # reuses stored hash values if possible
return len(set().union(*self._maps)) # type: ignore return len(set().union(*self._maps)) # type: ignore[arg-type]
def __iter__(self) -> Iterator[str]: def __iter__(self) -> Iterator[str]:
d = {} # type: Dict[str, Any] d: Dict[str, Any] = {}
for mapping in reversed(self._maps): for mapping in reversed(self._maps):
# reuses stored hash values if possible # reuses stored hash values if possible
d.update(mapping) d.update(mapping)
@ -778,3 +843,36 @@ class ChainMapProxy(Mapping[str, Any]):
def __repr__(self) -> str: def __repr__(self) -> str:
content = ", ".join(map(repr, self._maps)) content = ", ".join(map(repr, self._maps))
return f"ChainMapProxy({content})" return f"ChainMapProxy({content})"
# https://tools.ietf.org/html/rfc7232#section-2.3
_ETAGC = r"[!#-}\x80-\xff]+"
_ETAGC_RE = re.compile(_ETAGC)
_QUOTED_ETAG = rf'(W/)?"({_ETAGC})"'
QUOTED_ETAG_RE = re.compile(_QUOTED_ETAG)
LIST_QUOTED_ETAG_RE = re.compile(rf"({_QUOTED_ETAG})(?:\s*,\s*|$)|(.)")
ETAG_ANY = "*"
@attr.s(auto_attribs=True, frozen=True, slots=True)
class ETag:
value: str
is_weak: bool = False
def validate_etag_value(value: str) -> None:
if value != ETAG_ANY and not _ETAGC_RE.fullmatch(value):
raise ValueError(
f"Value {value!r} is not a valid etag. Maybe it contains '\"'?"
)
def parse_http_date(date_str: Optional[str]) -> Optional[datetime.datetime]:
"""Process a date string, return a datetime object"""
if date_str is not None:
timetuple = parsedate(date_str)
if timetuple is not None:
with suppress(ValueError):
return datetime.datetime(*timetuple[:6], tzinfo=datetime.timezone.utc)
return None

8
third_party/python/aiohttp/aiohttp/http.py поставляемый
Просмотреть файл

@ -63,10 +63,8 @@ __all__ = (
) )
SERVER_SOFTWARE = "Python/{0[0]}.{0[1]} aiohttp/{1}".format( SERVER_SOFTWARE: str = "Python/{0[0]}.{0[1]} aiohttp/{1}".format(
sys.version_info, __version__ sys.version_info, __version__
) # type: str )
RESPONSES = ( RESPONSES: Mapping[int, Tuple[str, str]] = http.server.BaseHTTPRequestHandler.responses
http.server.BaseHTTPRequestHandler.responses
) # type: Mapping[int, Tuple[str, str]]

Просмотреть файл

@ -1,6 +1,7 @@
"""Low-level http related exceptions.""" """Low-level http related exceptions."""
from textwrap import indent
from typing import Optional, Union from typing import Optional, Union
from .typedefs import _CIMultiDict from .typedefs import _CIMultiDict
@ -35,10 +36,11 @@ class HttpProcessingError(Exception):
self.message = message self.message = message
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.code}, message={self.message!r}" msg = indent(self.message, " ")
return f"{self.code}, message:\n{msg}"
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<{self.__class__.__name__}: {self}>" return f"<{self.__class__.__name__}: {self.code}, message={self.message!r}>"
class BadHttpMessage(HttpProcessingError): class BadHttpMessage(HttpProcessingError):

Просмотреть файл

@ -4,8 +4,22 @@ import collections
import re import re
import string import string
import zlib import zlib
from contextlib import suppress
from enum import IntEnum from enum import IntEnum
from typing import Any, List, Optional, Tuple, Type, Union from typing import (
Any,
Generic,
List,
NamedTuple,
Optional,
Pattern,
Set,
Tuple,
Type,
TypeVar,
Union,
cast,
)
from multidict import CIMultiDict, CIMultiDictProxy, istr from multidict import CIMultiDict, CIMultiDictProxy, istr
from yarl import URL from yarl import URL
@ -14,6 +28,7 @@ from . import hdrs
from .base_protocol import BaseProtocol from .base_protocol import BaseProtocol
from .helpers import NO_EXTENSIONS, BaseTimerContext from .helpers import NO_EXTENSIONS, BaseTimerContext
from .http_exceptions import ( from .http_exceptions import (
BadHttpMessage,
BadStatusLine, BadStatusLine,
ContentEncodingError, ContentEncodingError,
ContentLengthError, ContentLengthError,
@ -24,7 +39,7 @@ from .http_exceptions import (
from .http_writer import HttpVersion, HttpVersion10 from .http_writer import HttpVersion, HttpVersion10
from .log import internal_logger from .log import internal_logger
from .streams import EMPTY_PAYLOAD, StreamReader from .streams import EMPTY_PAYLOAD, StreamReader
from .typedefs import RawHeaders from .typedefs import Final, RawHeaders
try: try:
import brotli import brotli
@ -43,7 +58,7 @@ __all__ = (
"RawResponseMessage", "RawResponseMessage",
) )
ASCIISET = set(string.printable) ASCIISET: Final[Set[str]] = set(string.printable)
# See https://tools.ietf.org/html/rfc7230#section-3.1.1 # See https://tools.ietf.org/html/rfc7230#section-3.1.1
# and https://tools.ietf.org/html/rfc7230#appendix-B # and https://tools.ietf.org/html/rfc7230#appendix-B
@ -52,25 +67,23 @@ ASCIISET = set(string.printable)
# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
# "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA # "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
# token = 1*tchar # token = 1*tchar
METHRE = re.compile(r"[!#$%&'*+\-.^_`|~0-9A-Za-z]+") METHRE: Final[Pattern[str]] = re.compile(r"[!#$%&'*+\-.^_`|~0-9A-Za-z]+")
VERSRE = re.compile(r"HTTP/(\d+).(\d+)") VERSRE: Final[Pattern[str]] = re.compile(r"HTTP/(\d+).(\d+)")
HDRRE = re.compile(rb"[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]") HDRRE: Final[Pattern[bytes]] = re.compile(rb"[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]")
class RawRequestMessage(NamedTuple):
method: str
path: str
version: HttpVersion
headers: "CIMultiDictProxy[str]"
raw_headers: RawHeaders
should_close: bool
compression: Optional[str]
upgrade: bool
chunked: bool
url: URL
RawRequestMessage = collections.namedtuple(
"RawRequestMessage",
[
"method",
"path",
"version",
"headers",
"raw_headers",
"should_close",
"compression",
"upgrade",
"chunked",
"url",
],
)
RawResponseMessage = collections.namedtuple( RawResponseMessage = collections.namedtuple(
"RawResponseMessage", "RawResponseMessage",
@ -88,6 +101,9 @@ RawResponseMessage = collections.namedtuple(
) )
_MsgT = TypeVar("_MsgT", RawRequestMessage, RawResponseMessage)
class ParseState(IntEnum): class ParseState(IntEnum):
PARSE_NONE = 0 PARSE_NONE = 0
@ -118,7 +134,7 @@ class HeadersParser:
def parse_headers( def parse_headers(
self, lines: List[bytes] self, lines: List[bytes]
) -> Tuple["CIMultiDictProxy[str]", RawHeaders]: ) -> Tuple["CIMultiDictProxy[str]", RawHeaders]:
headers = CIMultiDict() # type: CIMultiDict[str] headers: CIMultiDict[str] = CIMultiDict()
raw_headers = [] raw_headers = []
lines_idx = 1 lines_idx = 1
@ -198,12 +214,12 @@ class HeadersParser:
return (CIMultiDictProxy(headers), tuple(raw_headers)) return (CIMultiDictProxy(headers), tuple(raw_headers))
class HttpParser(abc.ABC): class HttpParser(abc.ABC, Generic[_MsgT]):
def __init__( def __init__(
self, self,
protocol: Optional[BaseProtocol] = None, protocol: Optional[BaseProtocol] = None,
loop: Optional[asyncio.AbstractEventLoop] = None, loop: Optional[asyncio.AbstractEventLoop] = None,
limit: int = 2 ** 16, limit: int = 2**16,
max_line_size: int = 8190, max_line_size: int = 8190,
max_headers: int = 32768, max_headers: int = 32768,
max_field_size: int = 8190, max_field_size: int = 8190,
@ -229,20 +245,20 @@ class HttpParser(abc.ABC):
self.response_with_body = response_with_body self.response_with_body = response_with_body
self.read_until_eof = read_until_eof self.read_until_eof = read_until_eof
self._lines = [] # type: List[bytes] self._lines: List[bytes] = []
self._tail = b"" self._tail = b""
self._upgraded = False self._upgraded = False
self._payload = None self._payload = None
self._payload_parser = None # type: Optional[HttpPayloadParser] self._payload_parser: Optional[HttpPayloadParser] = None
self._auto_decompress = auto_decompress self._auto_decompress = auto_decompress
self._limit = limit self._limit = limit
self._headers_parser = HeadersParser(max_line_size, max_headers, max_field_size) self._headers_parser = HeadersParser(max_line_size, max_headers, max_field_size)
@abc.abstractmethod @abc.abstractmethod
def parse_message(self, lines: List[bytes]) -> Any: def parse_message(self, lines: List[bytes]) -> _MsgT:
pass pass
def feed_eof(self) -> Any: def feed_eof(self) -> Optional[_MsgT]:
if self._payload_parser is not None: if self._payload_parser is not None:
self._payload_parser.feed_eof() self._payload_parser.feed_eof()
self._payload_parser = None self._payload_parser = None
@ -254,10 +270,9 @@ class HttpParser(abc.ABC):
if self._lines: if self._lines:
if self._lines[-1] != "\r\n": if self._lines[-1] != "\r\n":
self._lines.append(b"") self._lines.append(b"")
try: with suppress(Exception):
return self.parse_message(self._lines) return self.parse_message(self._lines)
except Exception: return None
return None
def feed_data( def feed_data(
self, self,
@ -267,7 +282,7 @@ class HttpParser(abc.ABC):
CONTENT_LENGTH: istr = hdrs.CONTENT_LENGTH, CONTENT_LENGTH: istr = hdrs.CONTENT_LENGTH,
METH_CONNECT: str = hdrs.METH_CONNECT, METH_CONNECT: str = hdrs.METH_CONNECT,
SEC_WEBSOCKET_KEY1: istr = hdrs.SEC_WEBSOCKET_KEY1, SEC_WEBSOCKET_KEY1: istr = hdrs.SEC_WEBSOCKET_KEY1,
) -> Tuple[List[Any], bool, bytes]: ) -> Tuple[List[Tuple[_MsgT, StreamReader]], bool, bytes]:
messages = [] messages = []
@ -297,20 +312,27 @@ class HttpParser(abc.ABC):
# \r\n\r\n found # \r\n\r\n found
if self._lines[-1] == EMPTY: if self._lines[-1] == EMPTY:
try: try:
msg = self.parse_message(self._lines) msg: _MsgT = self.parse_message(self._lines)
finally: finally:
self._lines.clear() self._lines.clear()
# payload length def get_content_length() -> Optional[int]:
length = msg.headers.get(CONTENT_LENGTH) # payload length
if length is not None: length_hdr = msg.headers.get(CONTENT_LENGTH)
if length_hdr is None:
return None
try: try:
length = int(length) length = int(length_hdr)
except ValueError: except ValueError:
raise InvalidHeader(CONTENT_LENGTH) raise InvalidHeader(CONTENT_LENGTH)
if length < 0: if length < 0:
raise InvalidHeader(CONTENT_LENGTH) raise InvalidHeader(CONTENT_LENGTH)
return length
length = get_content_length()
# do not support old websocket spec # do not support old websocket spec
if SEC_WEBSOCKET_KEY1 in msg.headers: if SEC_WEBSOCKET_KEY1 in msg.headers:
raise InvalidHeader(SEC_WEBSOCKET_KEY1) raise InvalidHeader(SEC_WEBSOCKET_KEY1)
@ -346,6 +368,7 @@ class HttpParser(abc.ABC):
if not payload_parser.done: if not payload_parser.done:
self._payload_parser = payload_parser self._payload_parser = payload_parser
elif method == METH_CONNECT: elif method == METH_CONNECT:
assert isinstance(msg, RawRequestMessage)
payload = StreamReader( payload = StreamReader(
self.protocol, self.protocol,
timer=self.timer, timer=self.timer,
@ -386,7 +409,7 @@ class HttpParser(abc.ABC):
if not payload_parser.done: if not payload_parser.done:
self._payload_parser = payload_parser self._payload_parser = payload_parser
else: else:
payload = EMPTY_PAYLOAD # type: ignore payload = EMPTY_PAYLOAD
messages.append((msg, payload)) messages.append((msg, payload))
else: else:
@ -467,25 +490,36 @@ class HttpParser(abc.ABC):
# chunking # chunking
te = headers.get(hdrs.TRANSFER_ENCODING) te = headers.get(hdrs.TRANSFER_ENCODING)
if te and "chunked" in te.lower(): if te is not None:
chunked = True if "chunked" == te.lower():
chunked = True
else:
raise BadHttpMessage("Request has invalid `Transfer-Encoding`")
if hdrs.CONTENT_LENGTH in headers:
raise BadHttpMessage(
"Content-Length can't be present with Transfer-Encoding",
)
return (headers, raw_headers, close_conn, encoding, upgrade, chunked) return (headers, raw_headers, close_conn, encoding, upgrade, chunked)
def set_upgraded(self, val: bool) -> None: def set_upgraded(self, val: bool) -> None:
"""Set connection upgraded (to websocket) mode. """Set connection upgraded (to websocket) mode.
:param bool val: new state. :param bool val: new state.
""" """
self._upgraded = val self._upgraded = val
class HttpRequestParser(HttpParser): class HttpRequestParser(HttpParser[RawRequestMessage]):
"""Read request status line. Exception .http_exceptions.BadStatusLine """Read request status line.
Exception .http_exceptions.BadStatusLine
could be raised in case of any errors in status line. could be raised in case of any errors in status line.
Returns RawRequestMessage. Returns RawRequestMessage.
""" """
def parse_message(self, lines: List[bytes]) -> Any: def parse_message(self, lines: List[bytes]) -> RawRequestMessage:
# request line # request line
line = lines[0].decode("utf-8", "surrogateescape") line = lines[0].decode("utf-8", "surrogateescape")
try: try:
@ -498,9 +532,6 @@ class HttpRequestParser(HttpParser):
"Status line is too long", str(self.max_line_size), str(len(path)) "Status line is too long", str(self.max_line_size), str(len(path))
) )
path_part, _hash_separator, url_fragment = path.partition("#")
path_part, _question_mark_separator, qs_part = path_part.partition("?")
# method # method
if not METHRE.match(method): if not METHRE.match(method):
raise BadStatusLine(method) raise BadStatusLine(method)
@ -515,6 +546,31 @@ class HttpRequestParser(HttpParser):
except Exception: except Exception:
raise BadStatusLine(version) raise BadStatusLine(version)
if method == "CONNECT":
# authority-form,
# https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3
url = URL.build(authority=path, encoded=True)
elif path.startswith("/"):
# origin-form,
# https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1
path_part, _hash_separator, url_fragment = path.partition("#")
path_part, _question_mark_separator, qs_part = path_part.partition("?")
# NOTE: `yarl.URL.build()` is used to mimic what the Cython-based
# NOTE: parser does, otherwise it results into the same
# NOTE: HTTP Request-Line input producing different
# NOTE: `yarl.URL()` objects
url = URL.build(
path=path_part,
query_string=qs_part,
fragment=url_fragment,
encoded=True,
)
else:
# absolute-form for proxy maybe,
# https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2
url = URL(path, encoded=True)
# read headers # read headers
( (
headers, headers,
@ -541,26 +597,18 @@ class HttpRequestParser(HttpParser):
compression, compression,
upgrade, upgrade,
chunked, chunked,
# NOTE: `yarl.URL.build()` is used to mimic what the Cython-based url,
# NOTE: parser does, otherwise it results into the same
# NOTE: HTTP Request-Line input producing different
# NOTE: `yarl.URL()` objects
URL.build(
path=path_part,
query_string=qs_part,
fragment=url_fragment,
encoded=True,
),
) )
class HttpResponseParser(HttpParser): class HttpResponseParser(HttpParser[RawResponseMessage]):
"""Read response status line and headers. """Read response status line and headers.
BadStatusLine could be raised in case of any errors in status line. BadStatusLine could be raised in case of any errors in status line.
Returns RawResponseMessage""" Returns RawResponseMessage.
"""
def parse_message(self, lines: List[bytes]) -> Any: def parse_message(self, lines: List[bytes]) -> RawResponseMessage:
line = lines[0].decode("utf-8", "surrogateescape") line = lines[0].decode("utf-8", "surrogateescape")
try: try:
version, status = line.split(None, 1) version, status = line.split(None, 1)
@ -641,9 +689,9 @@ class HttpPayloadParser:
# payload decompression wrapper # payload decompression wrapper
if response_with_body and compression and self._auto_decompress: if response_with_body and compression and self._auto_decompress:
real_payload = DeflateBuffer( real_payload: Union[StreamReader, DeflateBuffer] = DeflateBuffer(
payload, compression payload, compression
) # type: Union[StreamReader, DeflateBuffer] )
else: else:
real_payload = payload real_payload = payload
@ -812,6 +860,8 @@ class HttpPayloadParser:
class DeflateBuffer: class DeflateBuffer:
"""DeflateStream decompress stream and feed data into specified stream.""" """DeflateStream decompress stream and feed data into specified stream."""
decompressor: Any
def __init__(self, out: StreamReader, encoding: Optional[str]) -> None: def __init__(self, out: StreamReader, encoding: Optional[str]) -> None:
self.out = out self.out = out
self.size = 0 self.size = 0
@ -822,9 +872,27 @@ class DeflateBuffer:
if not HAS_BROTLI: # pragma: no cover if not HAS_BROTLI: # pragma: no cover
raise ContentEncodingError( raise ContentEncodingError(
"Can not decode content-encoding: brotli (br). " "Can not decode content-encoding: brotli (br). "
"Please install `brotlipy`" "Please install `Brotli`"
) )
self.decompressor = brotli.Decompressor()
class BrotliDecoder:
# Supports both 'brotlipy' and 'Brotli' packages
# since they share an import name. The top branches
# are for 'brotlipy' and bottom branches for 'Brotli'
def __init__(self) -> None:
self._obj = brotli.Decompressor()
def decompress(self, data: bytes) -> bytes:
if hasattr(self._obj, "decompress"):
return cast(bytes, self._obj.decompress(data))
return cast(bytes, self._obj.process(data))
def flush(self) -> bytes:
if hasattr(self._obj, "flush"):
return cast(bytes, self._obj.flush())
return b""
self.decompressor = BrotliDecoder()
else: else:
zlib_mode = 16 + zlib.MAX_WBITS if encoding == "gzip" else zlib.MAX_WBITS zlib_mode = 16 + zlib.MAX_WBITS if encoding == "gzip" else zlib.MAX_WBITS
self.decompressor = zlib.decompressobj(wbits=zlib_mode) self.decompressor = zlib.decompressobj(wbits=zlib_mode)
@ -886,7 +954,7 @@ RawResponseMessagePy = RawResponseMessage
try: try:
if not NO_EXTENSIONS: if not NO_EXTENSIONS:
from ._http_parser import ( # type: ignore from ._http_parser import ( # type: ignore[import,no-redef]
HttpRequestParser, HttpRequestParser,
HttpResponseParser, HttpResponseParser,
RawRequestMessage, RawRequestMessage,

Просмотреть файл

@ -9,11 +9,12 @@ import sys
import zlib import zlib
from enum import IntEnum from enum import IntEnum
from struct import Struct from struct import Struct
from typing import Any, Callable, List, Optional, Tuple, Union from typing import Any, Callable, List, Optional, Pattern, Set, Tuple, Union, cast
from .base_protocol import BaseProtocol from .base_protocol import BaseProtocol
from .helpers import NO_EXTENSIONS from .helpers import NO_EXTENSIONS
from .streams import DataQueue from .streams import DataQueue
from .typedefs import Final
__all__ = ( __all__ = (
"WS_CLOSED_MESSAGE", "WS_CLOSED_MESSAGE",
@ -33,6 +34,7 @@ class WSCloseCode(IntEnum):
GOING_AWAY = 1001 GOING_AWAY = 1001
PROTOCOL_ERROR = 1002 PROTOCOL_ERROR = 1002
UNSUPPORTED_DATA = 1003 UNSUPPORTED_DATA = 1003
ABNORMAL_CLOSURE = 1006
INVALID_TEXT = 1007 INVALID_TEXT = 1007
POLICY_VIOLATION = 1008 POLICY_VIOLATION = 1008
MESSAGE_TOO_BIG = 1009 MESSAGE_TOO_BIG = 1009
@ -40,9 +42,10 @@ class WSCloseCode(IntEnum):
INTERNAL_ERROR = 1011 INTERNAL_ERROR = 1011
SERVICE_RESTART = 1012 SERVICE_RESTART = 1012
TRY_AGAIN_LATER = 1013 TRY_AGAIN_LATER = 1013
BAD_GATEWAY = 1014
ALLOWED_CLOSE_CODES = {int(i) for i in WSCloseCode} ALLOWED_CLOSE_CODES: Final[Set[int]] = {int(i) for i in WSCloseCode}
class WSMsgType(IntEnum): class WSMsgType(IntEnum):
@ -69,7 +72,7 @@ class WSMsgType(IntEnum):
error = ERROR error = ERROR
WS_KEY = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" WS_KEY: Final[bytes] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
UNPACK_LEN2 = Struct("!H").unpack_from UNPACK_LEN2 = Struct("!H").unpack_from
@ -79,8 +82,8 @@ PACK_LEN1 = Struct("!BB").pack
PACK_LEN2 = Struct("!BBH").pack PACK_LEN2 = Struct("!BBH").pack
PACK_LEN3 = Struct("!BBQ").pack PACK_LEN3 = Struct("!BBQ").pack
PACK_CLOSE_CODE = Struct("!H").pack PACK_CLOSE_CODE = Struct("!H").pack
MSG_SIZE = 2 ** 14 MSG_SIZE: Final[int] = 2**14
DEFAULT_LIMIT = 2 ** 16 DEFAULT_LIMIT: Final[int] = 2**16
_WSMessageBase = collections.namedtuple("_WSMessageBase", ["type", "data", "extra"]) _WSMessageBase = collections.namedtuple("_WSMessageBase", ["type", "data", "extra"])
@ -107,18 +110,18 @@ class WebSocketError(Exception):
super().__init__(code, message) super().__init__(code, message)
def __str__(self) -> str: def __str__(self) -> str:
return self.args[1] return cast(str, self.args[1])
class WSHandshakeError(Exception): class WSHandshakeError(Exception):
"""WebSocket protocol handshake error.""" """WebSocket protocol handshake error."""
native_byteorder = sys.byteorder native_byteorder: Final[str] = sys.byteorder
# Used by _websocket_mask_python # Used by _websocket_mask_python
_XOR_TABLE = [bytes(a ^ b for a in range(256)) for b in range(256)] _XOR_TABLE: Final[List[bytes]] = [bytes(a ^ b for a in range(256)) for b in range(256)]
def _websocket_mask_python(mask: bytes, data: bytearray) -> None: def _websocket_mask_python(mask: bytes, data: bytearray) -> None:
@ -149,16 +152,16 @@ if NO_EXTENSIONS: # pragma: no cover
_websocket_mask = _websocket_mask_python _websocket_mask = _websocket_mask_python
else: else:
try: try:
from ._websocket import _websocket_mask_cython # type: ignore from ._websocket import _websocket_mask_cython # type: ignore[import]
_websocket_mask = _websocket_mask_cython _websocket_mask = _websocket_mask_cython
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
_websocket_mask = _websocket_mask_python _websocket_mask = _websocket_mask_python
_WS_DEFLATE_TRAILING = bytes([0x00, 0x00, 0xFF, 0xFF]) _WS_DEFLATE_TRAILING: Final[bytes] = bytes([0x00, 0x00, 0xFF, 0xFF])
_WS_EXT_RE = re.compile( _WS_EXT_RE: Final[Pattern[str]] = re.compile(
r"^(?:;\s*(?:" r"^(?:;\s*(?:"
r"(server_no_context_takeover)|" r"(server_no_context_takeover)|"
r"(client_no_context_takeover)|" r"(client_no_context_takeover)|"
@ -166,7 +169,7 @@ _WS_EXT_RE = re.compile(
r"(client_max_window_bits(?:=(\d+))?)))*$" r"(client_max_window_bits(?:=(\d+))?)))*$"
) )
_WS_EXT_RE_SPLIT = re.compile(r"permessage-deflate([^,]+)?") _WS_EXT_RE_SPLIT: Final[Pattern[str]] = re.compile(r"permessage-deflate([^,]+)?")
def ws_ext_parse(extstr: Optional[str], isserver: bool = False) -> Tuple[int, bool]: def ws_ext_parse(extstr: Optional[str], isserver: bool = False) -> Tuple[int, bool]:
@ -256,22 +259,22 @@ class WebSocketReader:
self.queue = queue self.queue = queue
self._max_msg_size = max_msg_size self._max_msg_size = max_msg_size
self._exc = None # type: Optional[BaseException] self._exc: Optional[BaseException] = None
self._partial = bytearray() self._partial = bytearray()
self._state = WSParserState.READ_HEADER self._state = WSParserState.READ_HEADER
self._opcode = None # type: Optional[int] self._opcode: Optional[int] = None
self._frame_fin = False self._frame_fin = False
self._frame_opcode = None # type: Optional[int] self._frame_opcode: Optional[int] = None
self._frame_payload = bytearray() self._frame_payload = bytearray()
self._tail = b"" self._tail = b""
self._has_mask = False self._has_mask = False
self._frame_mask = None # type: Optional[bytes] self._frame_mask: Optional[bytes] = None
self._payload_length = 0 self._payload_length = 0
self._payload_length_flag = 0 self._payload_length_flag = 0
self._compressed = None # type: Optional[bool] self._compressed: Optional[bool] = None
self._decompressobj = None # type: Any # zlib.decompressobj actually self._decompressobj: Any = None # zlib.decompressobj actually
self._compress = compress self._compress = compress
def feed_eof(self) -> None: def feed_eof(self) -> None:
@ -588,7 +591,7 @@ class WebSocketWriter:
self._closing = False self._closing = False
self._limit = limit self._limit = limit
self._output_size = 0 self._output_size = 0
self._compressobj = None # type: Any # actually compressobj self._compressobj: Any = None # actually compressobj
async def _send_frame( async def _send_frame(
self, message: bytes, opcode: int, compress: Optional[int] = None self, message: bytes, opcode: int, compress: Optional[int] = None

Просмотреть файл

@ -1,9 +1,8 @@
"""Http related parsers and protocol.""" """Http related parsers and protocol."""
import asyncio import asyncio
import collections
import zlib import zlib
from typing import Any, Awaitable, Callable, Optional, Union # noqa from typing import Any, Awaitable, Callable, NamedTuple, Optional, Union # noqa
from multidict import CIMultiDict from multidict import CIMultiDict
@ -13,12 +12,18 @@ from .helpers import NO_EXTENSIONS
__all__ = ("StreamWriter", "HttpVersion", "HttpVersion10", "HttpVersion11") __all__ = ("StreamWriter", "HttpVersion", "HttpVersion10", "HttpVersion11")
HttpVersion = collections.namedtuple("HttpVersion", ["major", "minor"])
class HttpVersion(NamedTuple):
major: int
minor: int
HttpVersion10 = HttpVersion(1, 0) HttpVersion10 = HttpVersion(1, 0)
HttpVersion11 = HttpVersion(1, 1) HttpVersion11 = HttpVersion(1, 1)
_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] _T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]]
_T_OnHeadersSent = Optional[Callable[["CIMultiDict[str]"], Awaitable[None]]]
class StreamWriter(AbstractStreamWriter): class StreamWriter(AbstractStreamWriter):
@ -27,9 +32,9 @@ class StreamWriter(AbstractStreamWriter):
protocol: BaseProtocol, protocol: BaseProtocol,
loop: asyncio.AbstractEventLoop, loop: asyncio.AbstractEventLoop,
on_chunk_sent: _T_OnChunkSent = None, on_chunk_sent: _T_OnChunkSent = None,
on_headers_sent: _T_OnHeadersSent = None,
) -> None: ) -> None:
self._protocol = protocol self._protocol = protocol
self._transport = protocol.transport
self.loop = loop self.loop = loop
self.length = None self.length = None
@ -38,14 +43,15 @@ class StreamWriter(AbstractStreamWriter):
self.output_size = 0 self.output_size = 0
self._eof = False self._eof = False
self._compress = None # type: Any self._compress: Any = None
self._drain_waiter = None self._drain_waiter = None
self._on_chunk_sent = on_chunk_sent # type: _T_OnChunkSent self._on_chunk_sent: _T_OnChunkSent = on_chunk_sent
self._on_headers_sent: _T_OnHeadersSent = on_headers_sent
@property @property
def transport(self) -> Optional[asyncio.Transport]: def transport(self) -> Optional[asyncio.Transport]:
return self._transport return self._protocol.transport
@property @property
def protocol(self) -> BaseProtocol: def protocol(self) -> BaseProtocol:
@ -54,18 +60,20 @@ class StreamWriter(AbstractStreamWriter):
def enable_chunking(self) -> None: def enable_chunking(self) -> None:
self.chunked = True self.chunked = True
def enable_compression(self, encoding: str = "deflate") -> None: def enable_compression(
self, encoding: str = "deflate", strategy: int = zlib.Z_DEFAULT_STRATEGY
) -> None:
zlib_mode = 16 + zlib.MAX_WBITS if encoding == "gzip" else zlib.MAX_WBITS zlib_mode = 16 + zlib.MAX_WBITS if encoding == "gzip" else zlib.MAX_WBITS
self._compress = zlib.compressobj(wbits=zlib_mode) self._compress = zlib.compressobj(wbits=zlib_mode, strategy=strategy)
def _write(self, chunk: bytes) -> None: def _write(self, chunk: bytes) -> None:
size = len(chunk) size = len(chunk)
self.buffer_size += size self.buffer_size += size
self.output_size += size self.output_size += size
transport = self.transport
if self._transport is None or self._transport.is_closing(): if not self._protocol.connected or transport is None or transport.is_closing():
raise ConnectionResetError("Cannot write to closing transport") raise ConnectionResetError("Cannot write to closing transport")
self._transport.write(chunk) transport.write(chunk)
async def write( async def write(
self, chunk: bytes, *, drain: bool = True, LIMIT: int = 0x10000 self, chunk: bytes, *, drain: bool = True, LIMIT: int = 0x10000
@ -114,6 +122,9 @@ class StreamWriter(AbstractStreamWriter):
self, status_line: str, headers: "CIMultiDict[str]" self, status_line: str, headers: "CIMultiDict[str]"
) -> None: ) -> None:
"""Write request/response status and headers.""" """Write request/response status and headers."""
if self._on_headers_sent is not None:
await self._on_headers_sent(headers)
# status + headers # status + headers
buf = _serialize_headers(status_line, headers) buf = _serialize_headers(status_line, headers)
self._write(buf) self._write(buf)
@ -147,7 +158,6 @@ class StreamWriter(AbstractStreamWriter):
await self.drain() await self.drain()
self._eof = True self._eof = True
self._transport = None
async def drain(self) -> None: async def drain(self) -> None:
"""Flush the write buffer. """Flush the write buffer.
@ -161,19 +171,25 @@ class StreamWriter(AbstractStreamWriter):
await self._protocol._drain_helper() await self._protocol._drain_helper()
def _safe_header(string: str) -> str:
if "\r" in string or "\n" in string:
raise ValueError(
"Newline or carriage return detected in headers. "
"Potential header injection attack."
)
return string
def _py_serialize_headers(status_line: str, headers: "CIMultiDict[str]") -> bytes: def _py_serialize_headers(status_line: str, headers: "CIMultiDict[str]") -> bytes:
line = ( headers_gen = (_safe_header(k) + ": " + _safe_header(v) for k, v in headers.items())
status_line line = status_line + "\r\n" + "\r\n".join(headers_gen) + "\r\n\r\n"
+ "\r\n" return line.encode("utf-8")
+ "".join([k + ": " + v + "\r\n" for k, v in headers.items()])
)
return line.encode("utf-8") + b"\r\n"
_serialize_headers = _py_serialize_headers _serialize_headers = _py_serialize_headers
try: try:
import aiohttp._http_writer as _http_writer # type: ignore import aiohttp._http_writer as _http_writer # type: ignore[import]
_c_serialize_headers = _http_writer._serialize_headers _c_serialize_headers = _http_writer._serialize_headers
if not NO_EXTENSIONS: if not NO_EXTENSIONS:

18
third_party/python/aiohttp/aiohttp/locks.py поставляемый
Просмотреть файл

@ -1,16 +1,12 @@
import asyncio import asyncio
import collections import collections
from typing import Any, Optional from typing import Any, Deque, Optional
try:
from typing import Deque
except ImportError:
from typing_extensions import Deque
class EventResultOrError: class EventResultOrError:
""" """Event asyncio lock helper class.
This class wrappers the Event asyncio lock allowing either awake the
Wraps the Event asyncio lock allowing either to awake the
locked Tasks without any error or raising an exception. locked Tasks without any error or raising an exception.
thanks to @vorpalsmith for the simple design. thanks to @vorpalsmith for the simple design.
@ -18,9 +14,9 @@ class EventResultOrError:
def __init__(self, loop: asyncio.AbstractEventLoop) -> None: def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
self._loop = loop self._loop = loop
self._exc = None # type: Optional[BaseException] self._exc: Optional[BaseException] = None
self._event = asyncio.Event() self._event = asyncio.Event()
self._waiters = collections.deque() # type: Deque[asyncio.Future[Any]] self._waiters: Deque[asyncio.Future[Any]] = collections.deque()
def set(self, exc: Optional[BaseException] = None) -> None: def set(self, exc: Optional[BaseException] = None) -> None:
self._exc = exc self._exc = exc
@ -40,6 +36,6 @@ class EventResultOrError:
return val return val
def cancel(self) -> None: def cancel(self) -> None:
""" Cancel all waiters """ """Cancel all waiters"""
for waiter in self._waiters: for waiter in self._waiters:
waiter.cancel() waiter.cancel()

Просмотреть файл

@ -11,6 +11,7 @@ from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
AsyncIterator, AsyncIterator,
Deque,
Dict, Dict,
Iterator, Iterator,
List, List,
@ -20,6 +21,7 @@ from typing import (
Tuple, Tuple,
Type, Type,
Union, Union,
cast,
) )
from urllib.parse import parse_qsl, unquote, urlencode from urllib.parse import parse_qsl, unquote, urlencode
@ -101,7 +103,7 @@ def parse_content_disposition(
warnings.warn(BadContentDispositionHeader(header)) warnings.warn(BadContentDispositionHeader(header))
return None, {} return None, {}
params = {} # type: Dict[str, str] params: Dict[str, str] = {}
while parts: while parts:
item = parts.pop(0) item = parts.pop(0)
@ -152,7 +154,7 @@ def parse_content_disposition(
elif parts: elif parts:
# maybe just ; in filename, in any case this is just # maybe just ; in filename, in any case this is just
# one case fix, for proper fix we need to redesign parser # one case fix, for proper fix we need to redesign parser
_value = "{};{}".format(value, parts[0]) _value = f"{value};{parts[0]}"
if is_quoted(_value): if is_quoted(_value):
parts.pop(0) parts.pop(0)
value = unescape(_value[1:-1].lstrip("\\/")) value = unescape(_value[1:-1].lstrip("\\/"))
@ -240,8 +242,10 @@ class MultipartResponseWrapper:
return item return item
async def release(self) -> None: async def release(self) -> None:
"""Releases the connection gracefully, reading all the content """Release the connection gracefully.
to the void."""
All remaining content is read to the void.
"""
await self.resp.release() await self.resp.release()
@ -261,13 +265,13 @@ class BodyPartReader:
self._length = int(length) if length is not None else None self._length = int(length) if length is not None else None
self._read_bytes = 0 self._read_bytes = 0
# TODO: typeing.Deque is not supported by Python 3.5 # TODO: typeing.Deque is not supported by Python 3.5
self._unread = deque() # type: Any self._unread: Deque[bytes] = deque()
self._prev_chunk = None # type: Optional[bytes] self._prev_chunk: Optional[bytes] = None
self._content_eof = 0 self._content_eof = 0
self._cache = {} # type: Dict[str, Any] self._cache: Dict[str, Any] = {}
def __aiter__(self) -> AsyncIterator["BodyPartReader"]: def __aiter__(self) -> AsyncIterator["BodyPartReader"]:
return self # type: ignore return self # type: ignore[return-value]
async def __anext__(self) -> bytes: async def __anext__(self) -> bytes:
part = await self.next() part = await self.next()
@ -411,12 +415,10 @@ class BodyPartReader:
if not data: if not data:
return None return None
encoding = encoding or self.get_charset(default="utf-8") encoding = encoding or self.get_charset(default="utf-8")
return json.loads(data.decode(encoding)) return cast(Dict[str, Any], json.loads(data.decode(encoding)))
async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]: async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]:
"""Like read(), but assumes that body parts contains form """Like read(), but assumes that body parts contain form urlencoded data."""
urlencoded data.
"""
data = await self.read(decode=True) data = await self.read(decode=True)
if not data: if not data:
return [] return []
@ -435,7 +437,9 @@ class BodyPartReader:
return self._at_eof return self._at_eof
def decode(self, data: bytes) -> bytes: def decode(self, data: bytes) -> bytes:
"""Decodes data according the specified Content-Encoding """Decodes data.
Decoding is done according the specified Content-Encoding
or Content-Transfer-Encoding headers value. or Content-Transfer-Encoding headers value.
""" """
if CONTENT_TRANSFER_ENCODING in self.headers: if CONTENT_TRANSFER_ENCODING in self.headers:
@ -478,17 +482,18 @@ class BodyPartReader:
@reify @reify
def name(self) -> Optional[str]: def name(self) -> Optional[str]:
"""Returns name specified in Content-Disposition header or None """Returns name specified in Content-Disposition header.
if missed or header is malformed.
"""
If the header is missing or malformed, returns None.
"""
_, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))
return content_disposition_filename(params, "name") return content_disposition_filename(params, "name")
@reify @reify
def filename(self) -> Optional[str]: def filename(self) -> Optional[str]:
"""Returns filename specified in Content-Disposition header or None """Returns filename specified in Content-Disposition header.
if missed or header is malformed.
Returns None if the header is missing or malformed.
""" """
_, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))
return content_disposition_filename(params, "filename") return content_disposition_filename(params, "filename")
@ -499,7 +504,7 @@ class BodyPartReaderPayload(Payload):
def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None:
super().__init__(value, *args, **kwargs) super().__init__(value, *args, **kwargs)
params = {} # type: Dict[str, str] params: Dict[str, str] = {}
if value.name is not None: if value.name is not None:
params["name"] = value.name params["name"] = value.name
if value.filename is not None: if value.filename is not None:
@ -510,10 +515,10 @@ class BodyPartReaderPayload(Payload):
async def write(self, writer: Any) -> None: async def write(self, writer: Any) -> None:
field = self._value field = self._value
chunk = await field.read_chunk(size=2 ** 16) chunk = await field.read_chunk(size=2**16)
while chunk: while chunk:
await writer.write(field.decode(chunk)) await writer.write(field.decode(chunk))
chunk = await field.read_chunk(size=2 ** 16) chunk = await field.read_chunk(size=2**16)
class MultipartReader: class MultipartReader:
@ -531,17 +536,15 @@ class MultipartReader:
self.headers = headers self.headers = headers
self._boundary = ("--" + self._get_boundary()).encode() self._boundary = ("--" + self._get_boundary()).encode()
self._content = content self._content = content
self._last_part = ( self._last_part: Optional[Union["MultipartReader", BodyPartReader]] = None
None
) # type: Optional[Union['MultipartReader', BodyPartReader]]
self._at_eof = False self._at_eof = False
self._at_bof = True self._at_bof = True
self._unread = [] # type: List[bytes] self._unread: List[bytes] = []
def __aiter__( def __aiter__(
self, self,
) -> AsyncIterator["BodyPartReader"]: ) -> AsyncIterator["BodyPartReader"]:
return self # type: ignore return self # type: ignore[return-value]
async def __anext__( async def __anext__(
self, self,
@ -566,9 +569,7 @@ class MultipartReader:
return obj return obj
def at_eof(self) -> bool: def at_eof(self) -> bool:
"""Returns True if the final boundary was reached or """Returns True if the final boundary was reached, false otherwise."""
False otherwise.
"""
return self._at_eof return self._at_eof
async def next( async def next(
@ -608,8 +609,9 @@ class MultipartReader:
self, self,
headers: "CIMultiDictProxy[str]", headers: "CIMultiDictProxy[str]",
) -> Union["MultipartReader", BodyPartReader]: ) -> Union["MultipartReader", BodyPartReader]:
"""Dispatches the response by the `Content-Type` header, returning """Dispatches the response by the `Content-Type` header.
suitable reader instance.
Returns a suitable reader instance.
:param dict headers: Response headers :param dict headers: Response headers
""" """
@ -723,7 +725,7 @@ class MultipartWriter(Payload):
super().__init__(None, content_type=ctype) super().__init__(None, content_type=ctype)
self._parts = [] # type: List[_Part] self._parts: List[_Part] = []
def __enter__(self) -> "MultipartWriter": def __enter__(self) -> "MultipartWriter":
return self return self
@ -745,8 +747,8 @@ class MultipartWriter(Payload):
def __bool__(self) -> bool: def __bool__(self) -> bool:
return True return True
_valid_tchar_regex = re.compile(br"\A[!#$%&'*+\-.^_`|~\w]+\Z") _valid_tchar_regex = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z")
_invalid_qdtext_char_regex = re.compile(br"[\x00-\x08\x0A-\x1F\x7F]") _invalid_qdtext_char_regex = re.compile(rb"[\x00-\x08\x0A-\x1F\x7F]")
@property @property
def _boundary_value(self) -> str: def _boundary_value(self) -> str:
@ -802,20 +804,20 @@ class MultipartWriter(Payload):
def append_payload(self, payload: Payload) -> Payload: def append_payload(self, payload: Payload) -> Payload:
"""Adds a new body part to multipart writer.""" """Adds a new body part to multipart writer."""
# compression # compression
encoding = payload.headers.get( encoding: Optional[str] = payload.headers.get(
CONTENT_ENCODING, CONTENT_ENCODING,
"", "",
).lower() # type: Optional[str] ).lower()
if encoding and encoding not in ("deflate", "gzip", "identity"): if encoding and encoding not in ("deflate", "gzip", "identity"):
raise RuntimeError(f"unknown content encoding: {encoding}") raise RuntimeError(f"unknown content encoding: {encoding}")
if encoding == "identity": if encoding == "identity":
encoding = None encoding = None
# te encoding # te encoding
te_encoding = payload.headers.get( te_encoding: Optional[str] = payload.headers.get(
CONTENT_TRANSFER_ENCODING, CONTENT_TRANSFER_ENCODING,
"", "",
).lower() # type: Optional[str] ).lower()
if te_encoding not in ("", "base64", "quoted-printable", "binary"): if te_encoding not in ("", "base64", "quoted-printable", "binary"):
raise RuntimeError( raise RuntimeError(
"unknown content transfer encoding: {}" "".format(te_encoding) "unknown content transfer encoding: {}" "".format(te_encoding)
@ -828,7 +830,7 @@ class MultipartWriter(Payload):
if size is not None and not (encoding or te_encoding): if size is not None and not (encoding or te_encoding):
payload.headers[CONTENT_LENGTH] = str(size) payload.headers[CONTENT_LENGTH] = str(size)
self._parts.append((payload, encoding, te_encoding)) # type: ignore self._parts.append((payload, encoding, te_encoding)) # type: ignore[arg-type]
return payload return payload
def append_json( def append_json(
@ -893,7 +895,7 @@ class MultipartWriter(Payload):
w.enable_compression(encoding) w.enable_compression(encoding)
if te_encoding: if te_encoding:
w.enable_encoding(te_encoding) w.enable_encoding(te_encoding)
await part.write(w) # type: ignore await part.write(w) # type: ignore[arg-type]
await w.write_eof() await w.write_eof()
else: else:
await part.write(writer) await part.write(writer)
@ -907,9 +909,9 @@ class MultipartWriter(Payload):
class MultipartPayloadWriter: class MultipartPayloadWriter:
def __init__(self, writer: Any) -> None: def __init__(self, writer: Any) -> None:
self._writer = writer self._writer = writer
self._encoding = None # type: Optional[str] self._encoding: Optional[str] = None
self._compress = None # type: Any self._compress: Any = None
self._encoding_buffer = None # type: Optional[bytearray] self._encoding_buffer: Optional[bytearray] = None
def enable_encoding(self, encoding: str) -> None: def enable_encoding(self, encoding: str) -> None:
if encoding == "base64": if encoding == "base64":
@ -918,9 +920,11 @@ class MultipartPayloadWriter:
elif encoding == "quoted-printable": elif encoding == "quoted-printable":
self._encoding = "quoted-printable" self._encoding = "quoted-printable"
def enable_compression(self, encoding: str = "deflate") -> None: def enable_compression(
self, encoding: str = "deflate", strategy: int = zlib.Z_DEFAULT_STRATEGY
) -> None:
zlib_mode = 16 + zlib.MAX_WBITS if encoding == "gzip" else -zlib.MAX_WBITS zlib_mode = 16 + zlib.MAX_WBITS if encoding == "gzip" else -zlib.MAX_WBITS
self._compress = zlib.compressobj(wbits=zlib_mode) self._compress = zlib.compressobj(wbits=zlib_mode, strategy=strategy)
async def write_eof(self) -> None: async def write_eof(self) -> None:
if self._compress is not None: if self._compress is not None:

67
third_party/python/aiohttp/aiohttp/payload.py поставляемый
Просмотреть файл

@ -15,7 +15,6 @@ from typing import (
Dict, Dict,
Iterable, Iterable,
Optional, Optional,
Text,
TextIO, TextIO,
Tuple, Tuple,
Type, Type,
@ -34,7 +33,7 @@ from .helpers import (
sentinel, sentinel,
) )
from .streams import StreamReader from .streams import StreamReader
from .typedefs import JSONEncoder, _CIMultiDict from .typedefs import Final, JSONEncoder, _CIMultiDict
__all__ = ( __all__ = (
"PAYLOAD_REGISTRY", "PAYLOAD_REGISTRY",
@ -52,8 +51,7 @@ __all__ = (
"AsyncIterablePayload", "AsyncIterablePayload",
) )
TOO_LARGE_BYTES_BODY = 2 ** 20 # 1 MB TOO_LARGE_BYTES_BODY: Final[int] = 2**20 # 1 MB
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from typing import List from typing import List
@ -89,6 +87,10 @@ class payload_type:
return factory return factory
PayloadType = Type["Payload"]
_PayloadRegistryItem = Tuple[PayloadType, Any]
class PayloadRegistry: class PayloadRegistry:
"""Payload registry. """Payload registry.
@ -96,12 +98,16 @@ class PayloadRegistry:
""" """
def __init__(self) -> None: def __init__(self) -> None:
self._first = [] # type: List[Tuple[Type[Payload], Any]] self._first: List[_PayloadRegistryItem] = []
self._normal = [] # type: List[Tuple[Type[Payload], Any]] self._normal: List[_PayloadRegistryItem] = []
self._last = [] # type: List[Tuple[Type[Payload], Any]] self._last: List[_PayloadRegistryItem] = []
def get( def get(
self, data: Any, *args: Any, _CHAIN: Any = chain, **kwargs: Any self,
data: Any,
*args: Any,
_CHAIN: "Type[chain[_PayloadRegistryItem]]" = chain,
**kwargs: Any,
) -> "Payload": ) -> "Payload":
if isinstance(data, Payload): if isinstance(data, Payload):
return data return data
@ -112,7 +118,7 @@ class PayloadRegistry:
raise LookupError() raise LookupError()
def register( def register(
self, factory: Type["Payload"], type: Any, *, order: Order = Order.normal self, factory: PayloadType, type: Any, *, order: Order = Order.normal
) -> None: ) -> None:
if order is Order.try_first: if order is Order.try_first:
self._first.append((factory, type)) self._first.append((factory, type))
@ -126,8 +132,8 @@ class PayloadRegistry:
class Payload(ABC): class Payload(ABC):
_default_content_type = "application/octet-stream" # type: str _default_content_type: str = "application/octet-stream"
_size = None # type: Optional[int] _size: Optional[int] = None
def __init__( def __init__(
self, self,
@ -142,7 +148,7 @@ class Payload(ABC):
) -> None: ) -> None:
self._encoding = encoding self._encoding = encoding
self._filename = filename self._filename = filename
self._headers = CIMultiDict() # type: _CIMultiDict self._headers: _CIMultiDict = CIMultiDict()
self._value = value self._value = value
if content_type is not sentinel and content_type is not None: if content_type is not sentinel and content_type is not None:
self._headers[hdrs.CONTENT_TYPE] = content_type self._headers[hdrs.CONTENT_TYPE] = content_type
@ -190,11 +196,15 @@ class Payload(ABC):
return self._headers[hdrs.CONTENT_TYPE] return self._headers[hdrs.CONTENT_TYPE]
def set_content_disposition( def set_content_disposition(
self, disptype: str, quote_fields: bool = True, **params: Any self,
disptype: str,
quote_fields: bool = True,
_charset: str = "utf-8",
**params: Any,
) -> None: ) -> None:
"""Sets ``Content-Disposition`` header.""" """Sets ``Content-Disposition`` header."""
self._headers[hdrs.CONTENT_DISPOSITION] = content_disposition_header( self._headers[hdrs.CONTENT_DISPOSITION] = content_disposition_header(
disptype, quote_fields=quote_fields, **params disptype, quote_fields=quote_fields, _charset=_charset, **params
) )
@abstractmethod @abstractmethod
@ -208,9 +218,7 @@ class Payload(ABC):
class BytesPayload(Payload): class BytesPayload(Payload):
def __init__(self, value: ByteString, *args: Any, **kwargs: Any) -> None: def __init__(self, value: ByteString, *args: Any, **kwargs: Any) -> None:
if not isinstance(value, (bytes, bytearray, memoryview)): if not isinstance(value, (bytes, bytearray, memoryview)):
raise TypeError( raise TypeError(f"value argument must be byte-ish, not {type(value)!r}")
"value argument must be byte-ish, not {!r}".format(type(value))
)
if "content_type" not in kwargs: if "content_type" not in kwargs:
kwargs["content_type"] = "application/octet-stream" kwargs["content_type"] = "application/octet-stream"
@ -242,7 +250,7 @@ class BytesPayload(Payload):
class StringPayload(BytesPayload): class StringPayload(BytesPayload):
def __init__( def __init__(
self, self,
value: Text, value: str,
*args: Any, *args: Any,
encoding: Optional[str] = None, encoding: Optional[str] = None,
content_type: Optional[str] = None, content_type: Optional[str] = None,
@ -276,6 +284,8 @@ class StringIOPayload(StringPayload):
class IOBasePayload(Payload): class IOBasePayload(Payload):
_value: IO[Any]
def __init__( def __init__(
self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any
) -> None: ) -> None:
@ -291,15 +301,17 @@ class IOBasePayload(Payload):
async def write(self, writer: AbstractStreamWriter) -> None: async def write(self, writer: AbstractStreamWriter) -> None:
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
try: try:
chunk = await loop.run_in_executor(None, self._value.read, 2 ** 16) chunk = await loop.run_in_executor(None, self._value.read, 2**16)
while chunk: while chunk:
await writer.write(chunk) await writer.write(chunk)
chunk = await loop.run_in_executor(None, self._value.read, 2 ** 16) chunk = await loop.run_in_executor(None, self._value.read, 2**16)
finally: finally:
await loop.run_in_executor(None, self._value.close) await loop.run_in_executor(None, self._value.close)
class TextIOPayload(IOBasePayload): class TextIOPayload(IOBasePayload):
_value: TextIO
def __init__( def __init__(
self, self,
value: TextIO, value: TextIO,
@ -338,10 +350,15 @@ class TextIOPayload(IOBasePayload):
async def write(self, writer: AbstractStreamWriter) -> None: async def write(self, writer: AbstractStreamWriter) -> None:
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
try: try:
chunk = await loop.run_in_executor(None, self._value.read, 2 ** 16) chunk = await loop.run_in_executor(None, self._value.read, 2**16)
while chunk: while chunk:
await writer.write(chunk.encode(self._encoding)) data = (
chunk = await loop.run_in_executor(None, self._value.read, 2 ** 16) chunk.encode(encoding=self._encoding)
if self._encoding
else chunk.encode()
)
await writer.write(data)
chunk = await loop.run_in_executor(None, self._value.read, 2**16)
finally: finally:
await loop.run_in_executor(None, self._value.close) await loop.run_in_executor(None, self._value.close)
@ -400,13 +417,13 @@ else:
class AsyncIterablePayload(Payload): class AsyncIterablePayload(Payload):
_iter = None # type: Optional[_AsyncIterator] _iter: Optional[_AsyncIterator] = None
def __init__(self, value: _AsyncIterable, *args: Any, **kwargs: Any) -> None: def __init__(self, value: _AsyncIterable, *args: Any, **kwargs: Any) -> None:
if not isinstance(value, AsyncIterable): if not isinstance(value, AsyncIterable):
raise TypeError( raise TypeError(
"value argument must support " "value argument must support "
"collections.abc.AsyncIterablebe interface, " "collections.abc.AsyncIterable interface, "
"got {!r}".format(type(value)) "got {!r}".format(type(value))
) )

Просмотреть файл

@ -1,4 +1,5 @@
""" Payload implemenation for coroutines as data provider. """
Payload implemenation for coroutines as data provider.
As a simple case, you can upload data from file:: As a simple case, you can upload data from file::
@ -43,7 +44,7 @@ class _stream_wrapper:
self.kwargs = kwargs self.kwargs = kwargs
async def __call__(self, writer: AbstractStreamWriter) -> None: async def __call__(self, writer: AbstractStreamWriter) -> None:
await self.coro(writer, *self.args, **self.kwargs) # type: ignore await self.coro(writer, *self.args, **self.kwargs) # type: ignore[operator]
class streamer: class streamer:

Просмотреть файл

@ -2,6 +2,7 @@ import asyncio
import contextlib import contextlib
import warnings import warnings
from collections.abc import Callable from collections.abc import Callable
from typing import Any, Awaitable, Callable, Dict, Generator, Optional, Union
import pytest import pytest
@ -29,8 +30,10 @@ try:
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
tokio = None tokio = None
AiohttpClient = Callable[[Union[Application, BaseTestServer]], Awaitable[TestClient]]
def pytest_addoption(parser): # type: ignore
def pytest_addoption(parser): # type: ignore[no-untyped-def]
parser.addoption( parser.addoption(
"--aiohttp-fast", "--aiohttp-fast",
action="store_true", action="store_true",
@ -51,8 +54,9 @@ def pytest_addoption(parser): # type: ignore
) )
def pytest_fixture_setup(fixturedef): # type: ignore def pytest_fixture_setup(fixturedef): # type: ignore[no-untyped-def]
""" """Set up pytest fixture.
Allow fixtures to be coroutines. Run coroutine fixtures in an event loop. Allow fixtures to be coroutines. Run coroutine fixtures in an event loop.
""" """
func = fixturedef.func func = fixturedef.func
@ -72,7 +76,7 @@ def pytest_fixture_setup(fixturedef): # type: ignore
fixturedef.argnames += ("request",) fixturedef.argnames += ("request",)
strip_request = True strip_request = True
def wrapper(*args, **kwargs): # type: ignore def wrapper(*args, **kwargs): # type: ignore[no-untyped-def]
request = kwargs["request"] request = kwargs["request"]
if strip_request: if strip_request:
del kwargs["request"] del kwargs["request"]
@ -93,7 +97,7 @@ def pytest_fixture_setup(fixturedef): # type: ignore
# then advance it again in a finalizer # then advance it again in a finalizer
gen = func(*args, **kwargs) gen = func(*args, **kwargs)
def finalizer(): # type: ignore def finalizer(): # type: ignore[no-untyped-def]
try: try:
return _loop.run_until_complete(gen.__anext__()) return _loop.run_until_complete(gen.__anext__())
except StopAsyncIteration: except StopAsyncIteration:
@ -108,21 +112,22 @@ def pytest_fixture_setup(fixturedef): # type: ignore
@pytest.fixture @pytest.fixture
def fast(request): # type: ignore def fast(request): # type: ignore[no-untyped-def]
"""--fast config option""" """--fast config option"""
return request.config.getoption("--aiohttp-fast") return request.config.getoption("--aiohttp-fast")
@pytest.fixture @pytest.fixture
def loop_debug(request): # type: ignore def loop_debug(request): # type: ignore[no-untyped-def]
"""--enable-loop-debug config option""" """--enable-loop-debug config option"""
return request.config.getoption("--aiohttp-enable-loop-debug") return request.config.getoption("--aiohttp-enable-loop-debug")
@contextlib.contextmanager @contextlib.contextmanager
def _runtime_warning_context(): # type: ignore def _runtime_warning_context(): # type: ignore[no-untyped-def]
""" """Context manager which checks for RuntimeWarnings.
Context manager which checks for RuntimeWarnings, specifically to
This exists specifically to
avoid "coroutine 'X' was never awaited" warnings being missed. avoid "coroutine 'X' was never awaited" warnings being missed.
If RuntimeWarnings occur in the context a RuntimeError is raised. If RuntimeWarnings occur in the context a RuntimeError is raised.
@ -143,9 +148,10 @@ def _runtime_warning_context(): # type: ignore
@contextlib.contextmanager @contextlib.contextmanager
def _passthrough_loop_context(loop, fast=False): # type: ignore def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def]
""" """Passthrough loop context.
setups and tears down a loop unless one is passed in via the loop
Sets up and tears down a loop unless one is passed in via the loop
argument when it's passed straight through. argument when it's passed straight through.
""" """
if loop: if loop:
@ -158,18 +164,14 @@ def _passthrough_loop_context(loop, fast=False): # type: ignore
teardown_test_loop(loop, fast=fast) teardown_test_loop(loop, fast=fast)
def pytest_pycollect_makeitem(collector, name, obj): # type: ignore def pytest_pycollect_makeitem(collector, name, obj): # type: ignore[no-untyped-def]
""" """Fix pytest collecting for coroutines."""
Fix pytest collecting for coroutines.
"""
if collector.funcnamefilter(name) and asyncio.iscoroutinefunction(obj): if collector.funcnamefilter(name) and asyncio.iscoroutinefunction(obj):
return list(collector._genfunctions(name, obj)) return list(collector._genfunctions(name, obj))
def pytest_pyfunc_call(pyfuncitem): # type: ignore def pytest_pyfunc_call(pyfuncitem): # type: ignore[no-untyped-def]
""" """Run coroutines in an event loop instead of a normal function call."""
Run coroutines in an event loop instead of a normal function call.
"""
fast = pyfuncitem.config.getoption("--aiohttp-fast") fast = pyfuncitem.config.getoption("--aiohttp-fast")
if asyncio.iscoroutinefunction(pyfuncitem.function): if asyncio.iscoroutinefunction(pyfuncitem.function):
existing_loop = pyfuncitem.funcargs.get( existing_loop = pyfuncitem.funcargs.get(
@ -186,7 +188,7 @@ def pytest_pyfunc_call(pyfuncitem): # type: ignore
return True return True
def pytest_generate_tests(metafunc): # type: ignore def pytest_generate_tests(metafunc): # type: ignore[no-untyped-def]
if "loop_factory" not in metafunc.fixturenames: if "loop_factory" not in metafunc.fixturenames:
return return
@ -202,7 +204,7 @@ def pytest_generate_tests(metafunc): # type: ignore
if loops == "all": if loops == "all":
loops = "pyloop,uvloop?,tokio?" loops = "pyloop,uvloop?,tokio?"
factories = {} # type: ignore factories = {} # type: ignore[var-annotated]
for name in loops.split(","): for name in loops.split(","):
required = not name.endswith("?") required = not name.endswith("?")
name = name.strip(" ?") name = name.strip(" ?")
@ -221,7 +223,7 @@ def pytest_generate_tests(metafunc): # type: ignore
@pytest.fixture @pytest.fixture
def loop(loop_factory, fast, loop_debug): # type: ignore def loop(loop_factory, fast, loop_debug): # type: ignore[no-untyped-def]
"""Return an instance of the event loop.""" """Return an instance of the event loop."""
policy = loop_factory() policy = loop_factory()
asyncio.set_event_loop_policy(policy) asyncio.set_event_loop_policy(policy)
@ -233,12 +235,12 @@ def loop(loop_factory, fast, loop_debug): # type: ignore
@pytest.fixture @pytest.fixture
def proactor_loop(): # type: ignore def proactor_loop(): # type: ignore[no-untyped-def]
if not PY_37: if not PY_37:
policy = asyncio.get_event_loop_policy() policy = asyncio.get_event_loop_policy()
policy._loop_factory = asyncio.ProactorEventLoop # type: ignore policy._loop_factory = asyncio.ProactorEventLoop # type: ignore[attr-defined]
else: else:
policy = asyncio.WindowsProactorEventLoopPolicy() # type: ignore policy = asyncio.WindowsProactorEventLoopPolicy() # type: ignore[attr-defined]
asyncio.set_event_loop_policy(policy) asyncio.set_event_loop_policy(policy)
with loop_context(policy.new_event_loop) as _loop: with loop_context(policy.new_event_loop) as _loop:
@ -247,7 +249,7 @@ def proactor_loop(): # type: ignore
@pytest.fixture @pytest.fixture
def unused_port(aiohttp_unused_port): # type: ignore # pragma: no cover def unused_port(aiohttp_unused_port): # type: ignore[no-untyped-def] # pragma: no cover
warnings.warn( warnings.warn(
"Deprecated, use aiohttp_unused_port fixture instead", "Deprecated, use aiohttp_unused_port fixture instead",
DeprecationWarning, DeprecationWarning,
@ -257,20 +259,20 @@ def unused_port(aiohttp_unused_port): # type: ignore # pragma: no cover
@pytest.fixture @pytest.fixture
def aiohttp_unused_port(): # type: ignore def aiohttp_unused_port(): # type: ignore[no-untyped-def]
"""Return a port that is unused on the current host.""" """Return a port that is unused on the current host."""
return _unused_port return _unused_port
@pytest.fixture @pytest.fixture
def aiohttp_server(loop): # type: ignore def aiohttp_server(loop): # type: ignore[no-untyped-def]
"""Factory to create a TestServer instance, given an app. """Factory to create a TestServer instance, given an app.
aiohttp_server(app, **kwargs) aiohttp_server(app, **kwargs)
""" """
servers = [] servers = []
async def go(app, *, port=None, **kwargs): # type: ignore async def go(app, *, port=None, **kwargs): # type: ignore[no-untyped-def]
server = TestServer(app, port=port) server = TestServer(app, port=port)
await server.start_server(loop=loop, **kwargs) await server.start_server(loop=loop, **kwargs)
servers.append(server) servers.append(server)
@ -278,7 +280,7 @@ def aiohttp_server(loop): # type: ignore
yield go yield go
async def finalize(): # type: ignore async def finalize() -> None:
while servers: while servers:
await servers.pop().close() await servers.pop().close()
@ -286,7 +288,7 @@ def aiohttp_server(loop): # type: ignore
@pytest.fixture @pytest.fixture
def test_server(aiohttp_server): # type: ignore # pragma: no cover def test_server(aiohttp_server): # type: ignore[no-untyped-def] # pragma: no cover
warnings.warn( warnings.warn(
"Deprecated, use aiohttp_server fixture instead", "Deprecated, use aiohttp_server fixture instead",
DeprecationWarning, DeprecationWarning,
@ -296,14 +298,14 @@ def test_server(aiohttp_server): # type: ignore # pragma: no cover
@pytest.fixture @pytest.fixture
def aiohttp_raw_server(loop): # type: ignore def aiohttp_raw_server(loop): # type: ignore[no-untyped-def]
"""Factory to create a RawTestServer instance, given a web handler. """Factory to create a RawTestServer instance, given a web handler.
aiohttp_raw_server(handler, **kwargs) aiohttp_raw_server(handler, **kwargs)
""" """
servers = [] servers = []
async def go(handler, *, port=None, **kwargs): # type: ignore async def go(handler, *, port=None, **kwargs): # type: ignore[no-untyped-def]
server = RawTestServer(handler, port=port) server = RawTestServer(handler, port=port)
await server.start_server(loop=loop, **kwargs) await server.start_server(loop=loop, **kwargs)
servers.append(server) servers.append(server)
@ -311,7 +313,7 @@ def aiohttp_raw_server(loop): # type: ignore
yield go yield go
async def finalize(): # type: ignore async def finalize() -> None:
while servers: while servers:
await servers.pop().close() await servers.pop().close()
@ -319,7 +321,9 @@ def aiohttp_raw_server(loop): # type: ignore
@pytest.fixture @pytest.fixture
def raw_test_server(aiohttp_raw_server): # type: ignore # pragma: no cover def raw_test_server( # type: ignore[no-untyped-def] # pragma: no cover
aiohttp_raw_server,
):
warnings.warn( warnings.warn(
"Deprecated, use aiohttp_raw_server fixture instead", "Deprecated, use aiohttp_raw_server fixture instead",
DeprecationWarning, DeprecationWarning,
@ -329,7 +333,9 @@ def raw_test_server(aiohttp_raw_server): # type: ignore # pragma: no cover
@pytest.fixture @pytest.fixture
def aiohttp_client(loop): # type: ignore def aiohttp_client(
loop: asyncio.AbstractEventLoop,
) -> Generator[AiohttpClient, None, None]:
"""Factory to create a TestClient instance. """Factory to create a TestClient instance.
aiohttp_client(app, **kwargs) aiohttp_client(app, **kwargs)
@ -338,9 +344,14 @@ def aiohttp_client(loop): # type: ignore
""" """
clients = [] clients = []
async def go(__param, *args, server_kwargs=None, **kwargs): # type: ignore async def go(
__param: Union[Application, BaseTestServer],
*args: Any,
server_kwargs: Optional[Dict[str, Any]] = None,
**kwargs: Any
) -> TestClient:
if isinstance(__param, Callable) and not isinstance( # type: ignore if isinstance(__param, Callable) and not isinstance( # type: ignore[arg-type]
__param, (Application, BaseTestServer) __param, (Application, BaseTestServer)
): ):
__param = __param(loop, *args, **kwargs) __param = __param(loop, *args, **kwargs)
@ -363,7 +374,7 @@ def aiohttp_client(loop): # type: ignore
yield go yield go
async def finalize(): # type: ignore async def finalize() -> None:
while clients: while clients:
await clients.pop().close() await clients.pop().close()
@ -371,7 +382,7 @@ def aiohttp_client(loop): # type: ignore
@pytest.fixture @pytest.fixture
def test_client(aiohttp_client): # type: ignore # pragma: no cover def test_client(aiohttp_client): # type: ignore[no-untyped-def] # pragma: no cover
warnings.warn( warnings.warn(
"Deprecated, use aiohttp_client fixture instead", "Deprecated, use aiohttp_client fixture instead",
DeprecationWarning, DeprecationWarning,

Просмотреть файл

@ -1,6 +1,6 @@
import asyncio import asyncio
import socket import socket
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional, Type, Union
from .abc import AbstractResolver from .abc import AbstractResolver
from .helpers import get_running_loop from .helpers import get_running_loop
@ -18,8 +18,10 @@ aiodns_default = False
class ThreadedResolver(AbstractResolver): class ThreadedResolver(AbstractResolver):
"""Use Executor for synchronous getaddrinfo() calls, which defaults to """Threaded resolver.
concurrent.futures.ThreadPoolExecutor.
Uses an Executor for synchronous getaddrinfo() calls.
concurrent.futures.ThreadPoolExecutor is used by default.
""" """
def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
@ -38,16 +40,24 @@ class ThreadedResolver(AbstractResolver):
hosts = [] hosts = []
for family, _, proto, _, address in infos: for family, _, proto, _, address in infos:
if family == socket.AF_INET6 and address[3]: # type: ignore if family == socket.AF_INET6:
# This is essential for link-local IPv6 addresses. if len(address) < 3:
# LL IPv6 is a VERY rare case. Strictly speaking, we should use # IPv6 is not supported by Python build,
# getnameinfo() unconditionally, but performance makes sense. # or IPv6 is not enabled in the host
host, _port = socket.getnameinfo( continue
address, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV if address[3]: # type: ignore[misc]
) # This is essential for link-local IPv6 addresses.
port = int(_port) # LL IPv6 is a VERY rare case. Strictly speaking, we should use
else: # getnameinfo() unconditionally, but performance makes sense.
host, port = address[:2] host, _port = socket.getnameinfo(
address, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV
)
port = int(_port)
else:
host, port = address[:2]
else: # IPv4
assert family == socket.AF_INET
host, port = address # type: ignore[misc]
hosts.append( hosts.append(
{ {
"hostname": hostname, "hostname": hostname,
@ -143,7 +153,8 @@ class AsyncResolver(AbstractResolver):
return hosts return hosts
async def close(self) -> None: async def close(self) -> None:
return self._resolver.cancel() self._resolver.cancel()
DefaultResolver = AsyncResolver if aiodns_default else ThreadedResolver _DefaultType = Type[Union[AsyncResolver, ThreadedResolver]]
DefaultResolver: _DefaultType = AsyncResolver if aiodns_default else ThreadedResolver

97
third_party/python/aiohttp/aiohttp/streams.py поставляемый
Просмотреть файл

@ -1,16 +1,12 @@
import asyncio import asyncio
import collections import collections
import warnings import warnings
from typing import Awaitable, Callable, Generic, List, Optional, Tuple, TypeVar from typing import Awaitable, Callable, Deque, Generic, List, Optional, Tuple, TypeVar
from .base_protocol import BaseProtocol from .base_protocol import BaseProtocol
from .helpers import BaseTimerContext, set_exception, set_result from .helpers import BaseTimerContext, set_exception, set_result
from .log import internal_logger from .log import internal_logger
from .typedefs import Final
try: # pragma: no cover
from typing import Deque
except ImportError:
from typing_extensions import Deque
__all__ = ( __all__ = (
"EMPTY_PAYLOAD", "EMPTY_PAYLOAD",
@ -60,31 +56,33 @@ class ChunkTupleAsyncStreamIterator:
class AsyncStreamReaderMixin: class AsyncStreamReaderMixin:
def __aiter__(self) -> AsyncStreamIterator[bytes]: def __aiter__(self) -> AsyncStreamIterator[bytes]:
return AsyncStreamIterator(self.readline) # type: ignore return AsyncStreamIterator(self.readline) # type: ignore[attr-defined]
def iter_chunked(self, n: int) -> AsyncStreamIterator[bytes]: def iter_chunked(self, n: int) -> AsyncStreamIterator[bytes]:
"""Returns an asynchronous iterator that yields chunks of size n. """Returns an asynchronous iterator that yields chunks of size n.
Python-3.5 available for Python 3.5+ only Python-3.5 available for Python 3.5+ only
""" """
return AsyncStreamIterator(lambda: self.read(n)) # type: ignore return AsyncStreamIterator(
lambda: self.read(n) # type: ignore[attr-defined,no-any-return]
)
def iter_any(self) -> AsyncStreamIterator[bytes]: def iter_any(self) -> AsyncStreamIterator[bytes]:
"""Returns an asynchronous iterator that yields all the available """Yield all available data as soon as it is received.
data as soon as it is received
Python-3.5 available for Python 3.5+ only Python-3.5 available for Python 3.5+ only
""" """
return AsyncStreamIterator(self.readany) # type: ignore return AsyncStreamIterator(self.readany) # type: ignore[attr-defined]
def iter_chunks(self) -> ChunkTupleAsyncStreamIterator: def iter_chunks(self) -> ChunkTupleAsyncStreamIterator:
"""Returns an asynchronous iterator that yields chunks of data """Yield chunks of data as they are received by the server.
as they are received by the server. The yielded objects are tuples
The yielded objects are tuples
of (bytes, bool) as returned by the StreamReader.readchunk method. of (bytes, bool) as returned by the StreamReader.readchunk method.
Python-3.5 available for Python 3.5+ only Python-3.5 available for Python 3.5+ only
""" """
return ChunkTupleAsyncStreamIterator(self) # type: ignore return ChunkTupleAsyncStreamIterator(self) # type: ignore[arg-type]
class StreamReader(AsyncStreamReaderMixin): class StreamReader(AsyncStreamReaderMixin):
@ -109,7 +107,7 @@ class StreamReader(AsyncStreamReaderMixin):
limit: int, limit: int,
*, *,
timer: Optional[BaseTimerContext] = None, timer: Optional[BaseTimerContext] = None,
loop: Optional[asyncio.AbstractEventLoop] = None loop: Optional[asyncio.AbstractEventLoop] = None,
) -> None: ) -> None:
self._protocol = protocol self._protocol = protocol
self._low_water = limit self._low_water = limit
@ -119,15 +117,15 @@ class StreamReader(AsyncStreamReaderMixin):
self._loop = loop self._loop = loop
self._size = 0 self._size = 0
self._cursor = 0 self._cursor = 0
self._http_chunk_splits = None # type: Optional[List[int]] self._http_chunk_splits: Optional[List[int]] = None
self._buffer = collections.deque() # type: Deque[bytes] self._buffer: Deque[bytes] = collections.deque()
self._buffer_offset = 0 self._buffer_offset = 0
self._eof = False self._eof = False
self._waiter = None # type: Optional[asyncio.Future[None]] self._waiter: Optional[asyncio.Future[None]] = None
self._eof_waiter = None # type: Optional[asyncio.Future[None]] self._eof_waiter: Optional[asyncio.Future[None]] = None
self._exception = None # type: Optional[BaseException] self._exception: Optional[BaseException] = None
self._timer = timer self._timer = timer
self._eof_callbacks = [] # type: List[Callable[[], None]] self._eof_callbacks: List[Callable[[], None]] = []
def __repr__(self) -> str: def __repr__(self) -> str:
info = [self.__class__.__name__] info = [self.__class__.__name__]
@ -135,7 +133,7 @@ class StreamReader(AsyncStreamReaderMixin):
info.append("%d bytes" % self._size) info.append("%d bytes" % self._size)
if self._eof: if self._eof:
info.append("eof") info.append("eof")
if self._low_water != 2 ** 16: # default limit if self._low_water != 2**16: # default limit
info.append("low=%d high=%d" % (self._low_water, self._high_water)) info.append("low=%d high=%d" % (self._low_water, self._high_water))
if self._waiter: if self._waiter:
info.append("w=%r" % self._waiter) info.append("w=%r" % self._waiter)
@ -310,34 +308,41 @@ class StreamReader(AsyncStreamReaderMixin):
self._waiter = None self._waiter = None
async def readline(self) -> bytes: async def readline(self) -> bytes:
return await self.readuntil()
async def readuntil(self, separator: bytes = b"\n") -> bytes:
seplen = len(separator)
if seplen == 0:
raise ValueError("Separator should be at least one-byte string")
if self._exception is not None: if self._exception is not None:
raise self._exception raise self._exception
line = [] chunk = b""
line_size = 0 chunk_size = 0
not_enough = True not_enough = True
while not_enough: while not_enough:
while self._buffer and not_enough: while self._buffer and not_enough:
offset = self._buffer_offset offset = self._buffer_offset
ichar = self._buffer[0].find(b"\n", offset) + 1 ichar = self._buffer[0].find(separator, offset) + 1
# Read from current offset to found b'\n' or to the end. # Read from current offset to found separator or to the end.
data = self._read_nowait_chunk(ichar - offset if ichar else -1) data = self._read_nowait_chunk(ichar - offset if ichar else -1)
line.append(data) chunk += data
line_size += len(data) chunk_size += len(data)
if ichar: if ichar:
not_enough = False not_enough = False
if line_size > self._high_water: if chunk_size > self._high_water:
raise ValueError("Line is too long") raise ValueError("Chunk too big")
if self._eof: if self._eof:
break break
if not_enough: if not_enough:
await self._wait("readline") await self._wait("readuntil")
return b"".join(line) return chunk
async def read(self, n: int = -1) -> bytes: async def read(self, n: int = -1) -> bytes:
if self._exception is not None: if self._exception is not None:
@ -394,7 +399,9 @@ class StreamReader(AsyncStreamReaderMixin):
return self._read_nowait(-1) return self._read_nowait(-1)
async def readchunk(self) -> Tuple[bytes, bool]: async def readchunk(self) -> Tuple[bytes, bool]:
"""Returns a tuple of (data, end_of_http_chunk). When chunked transfer """Returns a tuple of (data, end_of_http_chunk).
When chunked transfer
encoding is used, end_of_http_chunk is a boolean indicating if the end encoding is used, end_of_http_chunk is a boolean indicating if the end
of the data corresponds to the end of a HTTP chunk , otherwise it is of the data corresponds to the end of a HTTP chunk , otherwise it is
always False. always False.
@ -429,7 +436,7 @@ class StreamReader(AsyncStreamReaderMixin):
if self._exception is not None: if self._exception is not None:
raise self._exception raise self._exception
blocks = [] # type: List[bytes] blocks: List[bytes] = []
while n > 0: while n > 0:
block = await self.read(n) block = await self.read(n)
if not block: if not block:
@ -483,7 +490,7 @@ class StreamReader(AsyncStreamReaderMixin):
return data return data
def _read_nowait(self, n: int) -> bytes: def _read_nowait(self, n: int) -> bytes:
""" Read not more than n bytes, or whole buffer if n == -1 """ """Read not more than n bytes, or whole buffer if n == -1"""
chunks = [] chunks = []
while self._buffer: while self._buffer:
@ -497,7 +504,10 @@ class StreamReader(AsyncStreamReaderMixin):
return b"".join(chunks) if chunks else b"" return b"".join(chunks) if chunks else b""
class EmptyStreamReader(AsyncStreamReaderMixin): class EmptyStreamReader(StreamReader): # lgtm [py/missing-call-to-init]
def __init__(self) -> None:
pass
def exception(self) -> Optional[BaseException]: def exception(self) -> Optional[BaseException]:
return None return None
@ -531,6 +541,8 @@ class EmptyStreamReader(AsyncStreamReaderMixin):
async def read(self, n: int = -1) -> bytes: async def read(self, n: int = -1) -> bytes:
return b"" return b""
# TODO add async def readuntil
async def readany(self) -> bytes: async def readany(self) -> bytes:
return b"" return b""
@ -540,11 +552,11 @@ class EmptyStreamReader(AsyncStreamReaderMixin):
async def readexactly(self, n: int) -> bytes: async def readexactly(self, n: int) -> bytes:
raise asyncio.IncompleteReadError(b"", n) raise asyncio.IncompleteReadError(b"", n)
def read_nowait(self) -> bytes: def read_nowait(self, n: int = -1) -> bytes:
return b"" return b""
EMPTY_PAYLOAD = EmptyStreamReader() EMPTY_PAYLOAD: Final[StreamReader] = EmptyStreamReader()
class DataQueue(Generic[_T]): class DataQueue(Generic[_T]):
@ -553,10 +565,10 @@ class DataQueue(Generic[_T]):
def __init__(self, loop: asyncio.AbstractEventLoop) -> None: def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
self._loop = loop self._loop = loop
self._eof = False self._eof = False
self._waiter = None # type: Optional[asyncio.Future[None]] self._waiter: Optional[asyncio.Future[None]] = None
self._exception = None # type: Optional[BaseException] self._exception: Optional[BaseException] = None
self._size = 0 self._size = 0
self._buffer = collections.deque() # type: Deque[Tuple[_T, int]] self._buffer: Deque[Tuple[_T, int]] = collections.deque()
def __len__(self) -> int: def __len__(self) -> int:
return len(self._buffer) return len(self._buffer)
@ -623,7 +635,8 @@ class DataQueue(Generic[_T]):
class FlowControlDataQueue(DataQueue[_T]): class FlowControlDataQueue(DataQueue[_T]):
"""FlowControlDataQueue resumes and pauses an underlying stream. """FlowControlDataQueue resumes and pauses an underlying stream.
It is a destination for parsed data.""" It is a destination for parsed data.
"""
def __init__( def __init__(
self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop

Просмотреть файл

@ -15,7 +15,6 @@ if hasattr(socket, "SO_KEEPALIVE"):
if sock is not None: if sock is not None:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
else: else:
def tcp_keepalive(transport: asyncio.Transport) -> None: # pragma: no cover def tcp_keepalive(transport: asyncio.Transport) -> None: # pragma: no cover

Просмотреть файл

@ -2,35 +2,41 @@
import asyncio import asyncio
import contextlib import contextlib
import functools
import gc import gc
import inspect import inspect
import ipaddress
import os import os
import socket import socket
import sys import sys
import unittest import warnings
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from types import TracebackType from types import TracebackType
from typing import TYPE_CHECKING, Any, Callable, Iterator, List, Optional, Type, Union from typing import (
TYPE_CHECKING,
Any,
Callable,
Iterator,
List,
Optional,
Type,
Union,
cast,
)
from unittest import mock from unittest import mock
from aiosignal import Signal
from multidict import CIMultiDict, CIMultiDictProxy from multidict import CIMultiDict, CIMultiDictProxy
from yarl import URL from yarl import URL
import aiohttp import aiohttp
from aiohttp.client import ( from aiohttp.client import _RequestContextManager, _WSRequestContextManager
ClientResponse,
_RequestContextManager,
_WSRequestContextManager,
)
from . import ClientSession, hdrs from . import ClientSession, hdrs
from .abc import AbstractCookieJar from .abc import AbstractCookieJar
from .client_reqrep import ClientResponse from .client_reqrep import ClientResponse
from .client_ws import ClientWebSocketResponse from .client_ws import ClientWebSocketResponse
from .helpers import sentinel from .helpers import PY_38, sentinel
from .http import HttpVersion, RawRequestMessage from .http import HttpVersion, RawRequestMessage
from .signals import Signal
from .web import ( from .web import (
Application, Application,
AppRunner, AppRunner,
@ -48,16 +54,24 @@ if TYPE_CHECKING: # pragma: no cover
else: else:
SSLContext = None SSLContext = None
if PY_38:
from unittest import IsolatedAsyncioTestCase as TestCase
else:
from asynctest import TestCase # type: ignore[no-redef]
REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin" REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin"
def get_unused_port_socket(host: str) -> socket.socket: def get_unused_port_socket(
return get_port_socket(host, 0) host: str, family: socket.AddressFamily = socket.AF_INET
) -> socket.socket:
return get_port_socket(host, 0, family)
def get_port_socket(host: str, port: int) -> socket.socket: def get_port_socket(
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host: str, port: int, family: socket.AddressFamily
) -> socket.socket:
s = socket.socket(family, socket.SOCK_STREAM)
if REUSE_ADDRESS: if REUSE_ADDRESS:
# Windows has different semantics for SO_REUSEADDR, # Windows has different semantics for SO_REUSEADDR,
# so don't set it. Ref: # so don't set it. Ref:
@ -71,7 +85,7 @@ def unused_port() -> int:
"""Return a port that is unused on the current host.""" """Return a port that is unused on the current host."""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("127.0.0.1", 0)) s.bind(("127.0.0.1", 0))
return s.getsockname()[1] return cast(int, s.getsockname()[1])
class BaseTestServer(ABC): class BaseTestServer(ABC):
@ -85,16 +99,20 @@ class BaseTestServer(ABC):
host: str = "127.0.0.1", host: str = "127.0.0.1",
port: Optional[int] = None, port: Optional[int] = None,
skip_url_asserts: bool = False, skip_url_asserts: bool = False,
socket_factory: Callable[
[str, int, socket.AddressFamily], socket.socket
] = get_port_socket,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
self._loop = loop self._loop = loop
self.runner = None # type: Optional[BaseRunner] self.runner: Optional[BaseRunner] = None
self._root = None # type: Optional[URL] self._root: Optional[URL] = None
self.host = host self.host = host
self.port = port self.port = port
self._closed = False self._closed = False
self.scheme = scheme self.scheme = scheme
self.skip_url_asserts = skip_url_asserts self.skip_url_asserts = skip_url_asserts
self.socket_factory = socket_factory
async def start_server( async def start_server(
self, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs: Any self, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs: Any
@ -107,7 +125,12 @@ class BaseTestServer(ABC):
await self.runner.setup() await self.runner.setup()
if not self.port: if not self.port:
self.port = 0 self.port = 0
_sock = get_port_socket(self.host, self.port) try:
version = ipaddress.ip_address(self.host).version
except ValueError:
version = 4
family = socket.AF_INET6 if version == 6 else socket.AF_INET
_sock = self.socket_factory(self.host, self.port, family)
self.host, self.port = _sock.getsockname()[:2] self.host, self.port = _sock.getsockname()[:2]
site = SockSite(self.runner, sock=_sock, ssl_context=self._ssl) site = SockSite(self.runner, sock=_sock, ssl_context=self._ssl)
await site.start() await site.start()
@ -261,8 +284,8 @@ class TestClient:
cookie_jar = aiohttp.CookieJar(unsafe=True, loop=loop) cookie_jar = aiohttp.CookieJar(unsafe=True, loop=loop)
self._session = ClientSession(loop=loop, cookie_jar=cookie_jar, **kwargs) self._session = ClientSession(loop=loop, cookie_jar=cookie_jar, **kwargs)
self._closed = False self._closed = False
self._responses = [] # type: List[ClientResponse] self._responses: List[ClientResponse] = []
self._websockets = [] # type: List[ClientWebSocketResponse] self._websockets: List[ClientWebSocketResponse] = []
async def start_server(self) -> None: async def start_server(self) -> None:
await self._server.start_server(loop=self._loop) await self._server.start_server(loop=self._loop)
@ -280,8 +303,8 @@ class TestClient:
return self._server return self._server
@property @property
def app(self) -> Application: def app(self) -> Optional[Application]:
return getattr(self._server, "app", None) return cast(Optional[Application], getattr(self._server, "app", None))
@property @property
def session(self) -> ClientSession: def session(self) -> ClientSession:
@ -400,9 +423,8 @@ class TestClient:
await self.close() await self.close()
class AioHTTPTestCase(unittest.TestCase): class AioHTTPTestCase(TestCase):
"""A base class to allow for unittest web applications using """A base class to allow for unittest web applications using aiohttp.
aiohttp.
Provides the following: Provides the following:
@ -417,43 +439,49 @@ class AioHTTPTestCase(unittest.TestCase):
""" """
async def get_application(self) -> Application: async def get_application(self) -> Application:
""" """Get application.
This method should be overridden This method should be overridden
to return the aiohttp.web.Application to return the aiohttp.web.Application
object to test. object to test.
""" """
return self.get_app() return self.get_app()
def get_app(self) -> Application: def get_app(self) -> Application:
"""Obsolete method used to constructing web application. """Obsolete method used to constructing web application.
Use .get_application() coroutine instead Use .get_application() coroutine instead.
""" """
raise RuntimeError("Did you forget to define get_application()?") raise RuntimeError("Did you forget to define get_application()?")
def setUp(self) -> None: def setUp(self) -> None:
self.loop = setup_test_loop() if not PY_38:
asyncio.get_event_loop().run_until_complete(self.asyncSetUp())
self.app = self.loop.run_until_complete(self.get_application()) async def asyncSetUp(self) -> None:
self.server = self.loop.run_until_complete(self.get_server(self.app)) try:
self.client = self.loop.run_until_complete(self.get_client(self.server)) self.loop = asyncio.get_running_loop()
except (AttributeError, RuntimeError): # AttributeError->py36
self.loop = asyncio.get_event_loop_policy().get_event_loop()
self.loop.run_until_complete(self.client.start_server()) return await self.setUpAsync()
self.loop.run_until_complete(self.setUpAsync())
async def setUpAsync(self) -> None: async def setUpAsync(self) -> None:
pass self.app = await self.get_application()
self.server = await self.get_server(self.app)
self.client = await self.get_client(self.server)
await self.client.start_server()
def tearDown(self) -> None: def tearDown(self) -> None:
self.loop.run_until_complete(self.tearDownAsync()) if not PY_38:
self.loop.run_until_complete(self.client.close()) self.loop.run_until_complete(self.asyncTearDown())
teardown_test_loop(self.loop)
async def asyncTearDown(self) -> None:
return await self.tearDownAsync()
async def tearDownAsync(self) -> None: async def tearDownAsync(self) -> None:
pass await self.client.close()
async def get_server(self, app: Application) -> TestServer: async def get_server(self, app: Application) -> TestServer:
"""Return a TestServer instance.""" """Return a TestServer instance."""
@ -465,18 +493,17 @@ class AioHTTPTestCase(unittest.TestCase):
def unittest_run_loop(func: Any, *args: Any, **kwargs: Any) -> Any: def unittest_run_loop(func: Any, *args: Any, **kwargs: Any) -> Any:
"""A decorator dedicated to use with asynchronous methods of an
AioHTTPTestCase.
Handles executing an asynchronous function, using
the self.loop of the AioHTTPTestCase.
""" """
A decorator dedicated to use with asynchronous AioHTTPTestCase test methods.
@functools.wraps(func, *args, **kwargs) In 3.8+, this does nothing.
def new_func(self: Any, *inner_args: Any, **inner_kwargs: Any) -> Any: """
return self.loop.run_until_complete(func(self, *inner_args, **inner_kwargs)) warnings.warn(
"Decorator `@unittest_run_loop` is no longer needed in aiohttp 3.8+",
return new_func DeprecationWarning,
stacklevel=2,
)
return func
_LOOP_FACTORY = Callable[[], asyncio.AbstractEventLoop] _LOOP_FACTORY = Callable[[], asyncio.AbstractEventLoop]
@ -498,8 +525,7 @@ def loop_context(
def setup_test_loop( def setup_test_loop(
loop_factory: _LOOP_FACTORY = asyncio.new_event_loop, loop_factory: _LOOP_FACTORY = asyncio.new_event_loop,
) -> asyncio.AbstractEventLoop: ) -> asyncio.AbstractEventLoop:
"""Create and return an asyncio.BaseEventLoop """Create and return an asyncio.BaseEventLoop instance.
instance.
The caller should also call teardown_test_loop, The caller should also call teardown_test_loop,
once they are done with the loop. once they are done with the loop.
@ -514,7 +540,16 @@ def setup_test_loop(
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
if sys.platform != "win32" and not skip_watcher: if sys.platform != "win32" and not skip_watcher:
policy = asyncio.get_event_loop_policy() policy = asyncio.get_event_loop_policy()
watcher = asyncio.SafeChildWatcher() watcher: asyncio.AbstractChildWatcher
try: # Python >= 3.8
# Refs:
# * https://github.com/pytest-dev/pytest-xdist/issues/620
# * https://stackoverflow.com/a/58614689/595220
# * https://bugs.python.org/issue35621
# * https://github.com/python/cpython/pull/14344
watcher = asyncio.ThreadedChildWatcher()
except AttributeError: # Python < 3.8
watcher = asyncio.SafeChildWatcher()
watcher.attach_loop(loop) watcher.attach_loop(loop)
with contextlib.suppress(NotImplementedError): with contextlib.suppress(NotImplementedError):
policy.set_child_watcher(watcher) policy.set_child_watcher(watcher)
@ -522,10 +557,7 @@ def setup_test_loop(
def teardown_test_loop(loop: asyncio.AbstractEventLoop, fast: bool = False) -> None: def teardown_test_loop(loop: asyncio.AbstractEventLoop, fast: bool = False) -> None:
"""Teardown and cleanup an event_loop created """Teardown and cleanup an event_loop created by setup_test_loop."""
by setup_test_loop.
"""
closed = loop.is_closed() closed = loop.is_closed()
if not closed: if not closed:
loop.call_soon(loop.stop) loop.call_soon(loop.stop)
@ -545,7 +577,7 @@ def _create_app_mock() -> mock.MagicMock:
def set_dict(app: Any, key: str, value: Any) -> None: def set_dict(app: Any, key: str, value: Any) -> None:
app.__app_dict[key] = value app.__app_dict[key] = value
app = mock.MagicMock() app = mock.MagicMock(spec=Application)
app.__app_dict = {} app.__app_dict = {}
app.__getitem__ = get_dict app.__getitem__ = get_dict
app.__setitem__ = set_dict app.__setitem__ = set_dict
@ -583,16 +615,14 @@ def make_mocked_request(
transport: Any = sentinel, transport: Any = sentinel,
payload: Any = sentinel, payload: Any = sentinel,
sslcontext: Optional[SSLContext] = None, sslcontext: Optional[SSLContext] = None,
client_max_size: int = 1024 ** 2, client_max_size: int = 1024**2,
loop: Any = ..., loop: Any = ...,
) -> Request: ) -> Request:
"""Creates mocked web.Request testing purposes. """Creates mocked web.Request testing purposes.
Useful in unit tests, when spinning full web server is overkill or Useful in unit tests, when spinning full web server is overkill or
specific conditions and errors are hard to trigger. specific conditions and errors are hard to trigger.
""" """
task = mock.Mock() task = mock.Mock()
if loop is ...: if loop is ...:
loop = mock.Mock() loop = mock.Mock()
@ -619,7 +649,7 @@ def make_mocked_request(
headers, headers,
raw_hdrs, raw_hdrs,
closing, closing,
False, None,
False, False,
chunked, chunked,
URL(path), URL(path),

164
third_party/python/aiohttp/aiohttp/tracing.py поставляемый
Просмотреть файл

@ -2,16 +2,15 @@ from types import SimpleNamespace
from typing import TYPE_CHECKING, Awaitable, Optional, Type, TypeVar from typing import TYPE_CHECKING, Awaitable, Optional, Type, TypeVar
import attr import attr
from aiosignal import Signal
from multidict import CIMultiDict from multidict import CIMultiDict
from yarl import URL from yarl import URL
from .client_reqrep import ClientResponse from .client_reqrep import ClientResponse
from .signals import Signal
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from typing_extensions import Protocol
from .client import ClientSession from .client import ClientSession
from .typedefs import Protocol
_ParamT_contra = TypeVar("_ParamT_contra", contravariant=True) _ParamT_contra = TypeVar("_ParamT_contra", contravariant=True)
@ -42,68 +41,71 @@ __all__ = (
"TraceRequestRedirectParams", "TraceRequestRedirectParams",
"TraceRequestChunkSentParams", "TraceRequestChunkSentParams",
"TraceResponseChunkReceivedParams", "TraceResponseChunkReceivedParams",
"TraceRequestHeadersSentParams",
) )
class TraceConfig: class TraceConfig:
"""First-class used to trace requests launched via ClientSession """First-class used to trace requests launched via ClientSession objects."""
objects."""
def __init__( def __init__(
self, trace_config_ctx_factory: Type[SimpleNamespace] = SimpleNamespace self, trace_config_ctx_factory: Type[SimpleNamespace] = SimpleNamespace
) -> None: ) -> None:
self._on_request_start = Signal( self._on_request_start: Signal[
_SignalCallback[TraceRequestStartParams]
] = Signal(self)
self._on_request_chunk_sent: Signal[
_SignalCallback[TraceRequestChunkSentParams]
] = Signal(self)
self._on_response_chunk_received: Signal[
_SignalCallback[TraceResponseChunkReceivedParams]
] = Signal(self)
self._on_request_end: Signal[_SignalCallback[TraceRequestEndParams]] = Signal(
self self
) # type: Signal[_SignalCallback[TraceRequestStartParams]] )
self._on_request_chunk_sent = Signal( self._on_request_exception: Signal[
self _SignalCallback[TraceRequestExceptionParams]
) # type: Signal[_SignalCallback[TraceRequestChunkSentParams]] ] = Signal(self)
self._on_response_chunk_received = Signal( self._on_request_redirect: Signal[
self _SignalCallback[TraceRequestRedirectParams]
) # type: Signal[_SignalCallback[TraceResponseChunkReceivedParams]] ] = Signal(self)
self._on_request_end = Signal( self._on_connection_queued_start: Signal[
self _SignalCallback[TraceConnectionQueuedStartParams]
) # type: Signal[_SignalCallback[TraceRequestEndParams]] ] = Signal(self)
self._on_request_exception = Signal( self._on_connection_queued_end: Signal[
self _SignalCallback[TraceConnectionQueuedEndParams]
) # type: Signal[_SignalCallback[TraceRequestExceptionParams]] ] = Signal(self)
self._on_request_redirect = Signal( self._on_connection_create_start: Signal[
self _SignalCallback[TraceConnectionCreateStartParams]
) # type: Signal[_SignalCallback[TraceRequestRedirectParams]] ] = Signal(self)
self._on_connection_queued_start = Signal( self._on_connection_create_end: Signal[
self _SignalCallback[TraceConnectionCreateEndParams]
) # type: Signal[_SignalCallback[TraceConnectionQueuedStartParams]] ] = Signal(self)
self._on_connection_queued_end = Signal( self._on_connection_reuseconn: Signal[
self _SignalCallback[TraceConnectionReuseconnParams]
) # type: Signal[_SignalCallback[TraceConnectionQueuedEndParams]] ] = Signal(self)
self._on_connection_create_start = Signal( self._on_dns_resolvehost_start: Signal[
self _SignalCallback[TraceDnsResolveHostStartParams]
) # type: Signal[_SignalCallback[TraceConnectionCreateStartParams]] ] = Signal(self)
self._on_connection_create_end = Signal( self._on_dns_resolvehost_end: Signal[
self _SignalCallback[TraceDnsResolveHostEndParams]
) # type: Signal[_SignalCallback[TraceConnectionCreateEndParams]] ] = Signal(self)
self._on_connection_reuseconn = Signal( self._on_dns_cache_hit: Signal[
self _SignalCallback[TraceDnsCacheHitParams]
) # type: Signal[_SignalCallback[TraceConnectionReuseconnParams]] ] = Signal(self)
self._on_dns_resolvehost_start = Signal( self._on_dns_cache_miss: Signal[
self _SignalCallback[TraceDnsCacheMissParams]
) # type: Signal[_SignalCallback[TraceDnsResolveHostStartParams]] ] = Signal(self)
self._on_dns_resolvehost_end = Signal( self._on_request_headers_sent: Signal[
self _SignalCallback[TraceRequestHeadersSentParams]
) # type: Signal[_SignalCallback[TraceDnsResolveHostEndParams]] ] = Signal(self)
self._on_dns_cache_hit = Signal(
self
) # type: Signal[_SignalCallback[TraceDnsCacheHitParams]]
self._on_dns_cache_miss = Signal(
self
) # type: Signal[_SignalCallback[TraceDnsCacheMissParams]]
self._trace_config_ctx_factory = trace_config_ctx_factory self._trace_config_ctx_factory = trace_config_ctx_factory
def trace_config_ctx( def trace_config_ctx(
self, trace_request_ctx: Optional[SimpleNamespace] = None self, trace_request_ctx: Optional[SimpleNamespace] = None
) -> SimpleNamespace: ) -> SimpleNamespace:
""" Return a new trace_config_ctx instance """ """Return a new trace_config_ctx instance"""
return self._trace_config_ctx_factory(trace_request_ctx=trace_request_ctx) return self._trace_config_ctx_factory(trace_request_ctx=trace_request_ctx)
def freeze(self) -> None: def freeze(self) -> None:
@ -122,6 +124,7 @@ class TraceConfig:
self._on_dns_resolvehost_end.freeze() self._on_dns_resolvehost_end.freeze()
self._on_dns_cache_hit.freeze() self._on_dns_cache_hit.freeze()
self._on_dns_cache_miss.freeze() self._on_dns_cache_miss.freeze()
self._on_request_headers_sent.freeze()
@property @property
def on_request_start(self) -> "Signal[_SignalCallback[TraceRequestStartParams]]": def on_request_start(self) -> "Signal[_SignalCallback[TraceRequestStartParams]]":
@ -205,10 +208,16 @@ class TraceConfig:
def on_dns_cache_miss(self) -> "Signal[_SignalCallback[TraceDnsCacheMissParams]]": def on_dns_cache_miss(self) -> "Signal[_SignalCallback[TraceDnsCacheMissParams]]":
return self._on_dns_cache_miss return self._on_dns_cache_miss
@property
def on_request_headers_sent(
self,
) -> "Signal[_SignalCallback[TraceRequestHeadersSentParams]]":
return self._on_request_headers_sent
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceRequestStartParams: class TraceRequestStartParams:
""" Parameters sent by the `on_request_start` signal""" """Parameters sent by the `on_request_start` signal"""
method: str method: str
url: URL url: URL
@ -217,7 +226,7 @@ class TraceRequestStartParams:
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceRequestChunkSentParams: class TraceRequestChunkSentParams:
""" Parameters sent by the `on_request_chunk_sent` signal""" """Parameters sent by the `on_request_chunk_sent` signal"""
method: str method: str
url: URL url: URL
@ -226,7 +235,7 @@ class TraceRequestChunkSentParams:
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceResponseChunkReceivedParams: class TraceResponseChunkReceivedParams:
""" Parameters sent by the `on_response_chunk_received` signal""" """Parameters sent by the `on_response_chunk_received` signal"""
method: str method: str
url: URL url: URL
@ -235,7 +244,7 @@ class TraceResponseChunkReceivedParams:
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceRequestEndParams: class TraceRequestEndParams:
""" Parameters sent by the `on_request_end` signal""" """Parameters sent by the `on_request_end` signal"""
method: str method: str
url: URL url: URL
@ -245,7 +254,7 @@ class TraceRequestEndParams:
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceRequestExceptionParams: class TraceRequestExceptionParams:
""" Parameters sent by the `on_request_exception` signal""" """Parameters sent by the `on_request_exception` signal"""
method: str method: str
url: URL url: URL
@ -255,7 +264,7 @@ class TraceRequestExceptionParams:
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceRequestRedirectParams: class TraceRequestRedirectParams:
""" Parameters sent by the `on_request_redirect` signal""" """Parameters sent by the `on_request_redirect` signal"""
method: str method: str
url: URL url: URL
@ -265,60 +274,72 @@ class TraceRequestRedirectParams:
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceConnectionQueuedStartParams: class TraceConnectionQueuedStartParams:
""" Parameters sent by the `on_connection_queued_start` signal""" """Parameters sent by the `on_connection_queued_start` signal"""
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceConnectionQueuedEndParams: class TraceConnectionQueuedEndParams:
""" Parameters sent by the `on_connection_queued_end` signal""" """Parameters sent by the `on_connection_queued_end` signal"""
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceConnectionCreateStartParams: class TraceConnectionCreateStartParams:
""" Parameters sent by the `on_connection_create_start` signal""" """Parameters sent by the `on_connection_create_start` signal"""
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceConnectionCreateEndParams: class TraceConnectionCreateEndParams:
""" Parameters sent by the `on_connection_create_end` signal""" """Parameters sent by the `on_connection_create_end` signal"""
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceConnectionReuseconnParams: class TraceConnectionReuseconnParams:
""" Parameters sent by the `on_connection_reuseconn` signal""" """Parameters sent by the `on_connection_reuseconn` signal"""
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceDnsResolveHostStartParams: class TraceDnsResolveHostStartParams:
""" Parameters sent by the `on_dns_resolvehost_start` signal""" """Parameters sent by the `on_dns_resolvehost_start` signal"""
host: str host: str
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceDnsResolveHostEndParams: class TraceDnsResolveHostEndParams:
""" Parameters sent by the `on_dns_resolvehost_end` signal""" """Parameters sent by the `on_dns_resolvehost_end` signal"""
host: str host: str
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceDnsCacheHitParams: class TraceDnsCacheHitParams:
""" Parameters sent by the `on_dns_cache_hit` signal""" """Parameters sent by the `on_dns_cache_hit` signal"""
host: str host: str
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceDnsCacheMissParams: class TraceDnsCacheMissParams:
""" Parameters sent by the `on_dns_cache_miss` signal""" """Parameters sent by the `on_dns_cache_miss` signal"""
host: str host: str
@attr.s(auto_attribs=True, frozen=True, slots=True)
class TraceRequestHeadersSentParams:
"""Parameters sent by the `on_request_headers_sent` signal"""
method: str
url: URL
headers: "CIMultiDict[str]"
class Trace: class Trace:
"""Internal class used to keep together the main dependencies used """Internal dependency holder class.
at the moment of send a signal."""
Used to keep together the main dependencies used
at the moment of send a signal.
"""
def __init__( def __init__(
self, self,
@ -440,3 +461,12 @@ class Trace:
return await self._trace_config.on_dns_cache_miss.send( return await self._trace_config.on_dns_cache_miss.send(
self._session, self._trace_config_ctx, TraceDnsCacheMissParams(host) self._session, self._trace_config_ctx, TraceDnsCacheMissParams(host)
) )
async def send_request_headers(
self, method: str, url: URL, headers: "CIMultiDict[str]"
) -> None:
return await self._trace_config._on_request_headers_sent.send(
self._session,
self._trace_config_ctx,
TraceRequestHeadersSentParams(method, url, headers),
)

Просмотреть файл

@ -1,12 +1,30 @@
import json import json
import os import os
import pathlib
import sys import sys
from typing import TYPE_CHECKING, Any, Callable, Iterable, Mapping, Tuple, Union from typing import (
TYPE_CHECKING,
Any,
Awaitable,
Callable,
Iterable,
Mapping,
Tuple,
Union,
)
from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr
from yarl import URL from yarl import URL
# These are for other modules to use (to avoid repeating the conditional import).
if sys.version_info >= (3, 8):
from typing import Final as Final, Protocol as Protocol, TypedDict as TypedDict
else:
from typing_extensions import ( # noqa: F401
Final,
Protocol as Protocol,
TypedDict as TypedDict,
)
DEFAULT_JSON_ENCODER = json.dumps DEFAULT_JSON_ENCODER = json.dumps
DEFAULT_JSON_DECODER = json.loads DEFAULT_JSON_DECODER = json.loads
@ -16,6 +34,8 @@ if TYPE_CHECKING: # pragma: no cover
_MultiDict = MultiDict[str] _MultiDict = MultiDict[str]
_MultiDictProxy = MultiDictProxy[str] _MultiDictProxy = MultiDictProxy[str]
from http.cookies import BaseCookie, Morsel from http.cookies import BaseCookie, Morsel
from .web import Request, StreamResponse
else: else:
_CIMultiDict = CIMultiDict _CIMultiDict = CIMultiDict
_CIMultiDictProxy = CIMultiDictProxy _CIMultiDictProxy = CIMultiDictProxy
@ -39,8 +59,6 @@ LooseCookies = Union[
"BaseCookie[str]", "BaseCookie[str]",
] ]
Handler = Callable[["Request"], Awaitable["StreamResponse"]]
if sys.version_info >= (3, 6): PathLike = Union[str, "os.PathLike[str]"]
PathLike = Union[str, "os.PathLike[str]"]
else:
PathLike = Union[str, pathlib.PurePath]

85
third_party/python/aiohttp/aiohttp/web.py поставляемый
Просмотреть файл

@ -6,16 +6,16 @@ from argparse import ArgumentParser
from collections.abc import Iterable from collections.abc import Iterable
from importlib import import_module from importlib import import_module
from typing import ( from typing import (
Any as Any, Any,
Awaitable as Awaitable, Awaitable,
Callable as Callable, Callable,
Iterable as TypingIterable, Iterable as TypingIterable,
List as List, List,
Optional as Optional, Optional,
Set as Set, Set,
Type as Type, Type,
Union as Union, Union,
cast as cast, cast,
) )
from .abc import AbstractAccessLogger from .abc import AbstractAccessLogger
@ -136,6 +136,7 @@ from .web_urldispatcher import (
AbstractRoute as AbstractRoute, AbstractRoute as AbstractRoute,
DynamicResource as DynamicResource, DynamicResource as DynamicResource,
PlainResource as PlainResource, PlainResource as PlainResource,
PrefixedSubAppResource as PrefixedSubAppResource,
Resource as Resource, Resource as Resource,
ResourceRoute as ResourceRoute, ResourceRoute as ResourceRoute,
StaticResource as StaticResource, StaticResource as StaticResource,
@ -261,6 +262,7 @@ __all__ = (
"AbstractRoute", "AbstractRoute",
"DynamicResource", "DynamicResource",
"PlainResource", "PlainResource",
"PrefixedSubAppResource",
"Resource", "Resource",
"ResourceRoute", "ResourceRoute",
"StaticResource", "StaticResource",
@ -279,7 +281,7 @@ __all__ = (
try: try:
from ssl import SSLContext from ssl import SSLContext
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
SSLContext = Any # type: ignore SSLContext = Any # type: ignore[misc,assignment]
HostSequence = TypingIterable[str] HostSequence = TypingIterable[str]
@ -290,8 +292,9 @@ async def _run_app(
host: Optional[Union[str, HostSequence]] = None, host: Optional[Union[str, HostSequence]] = None,
port: Optional[int] = None, port: Optional[int] = None,
path: Optional[str] = None, path: Optional[str] = None,
sock: Optional[socket.socket] = None, sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None,
shutdown_timeout: float = 60.0, shutdown_timeout: float = 60.0,
keepalive_timeout: float = 75.0,
ssl_context: Optional[SSLContext] = None, ssl_context: Optional[SSLContext] = None,
print: Callable[..., None] = print, print: Callable[..., None] = print,
backlog: int = 128, backlog: int = 128,
@ -304,7 +307,7 @@ async def _run_app(
) -> None: ) -> None:
# A internal functio to actually do all dirty job for application running # A internal functio to actually do all dirty job for application running
if asyncio.iscoroutine(app): if asyncio.iscoroutine(app):
app = await app # type: ignore app = await app # type: ignore[misc]
app = cast(Application, app) app = cast(Application, app)
@ -314,11 +317,12 @@ async def _run_app(
access_log_class=access_log_class, access_log_class=access_log_class,
access_log_format=access_log_format, access_log_format=access_log_format,
access_log=access_log, access_log=access_log,
keepalive_timeout=keepalive_timeout,
) )
await runner.setup() await runner.setup()
sites = [] # type: List[BaseSite] sites: List[BaseSite] = []
try: try:
if host is not None: if host is not None:
@ -440,9 +444,7 @@ def _cancel_tasks(
for task in to_cancel: for task in to_cancel:
task.cancel() task.cancel()
loop.run_until_complete( loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True))
asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)
)
for task in to_cancel: for task in to_cancel:
if task.cancelled(): if task.cancelled():
@ -463,8 +465,9 @@ def run_app(
host: Optional[Union[str, HostSequence]] = None, host: Optional[Union[str, HostSequence]] = None,
port: Optional[int] = None, port: Optional[int] = None,
path: Optional[str] = None, path: Optional[str] = None,
sock: Optional[socket.socket] = None, sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None,
shutdown_timeout: float = 60.0, shutdown_timeout: float = 60.0,
keepalive_timeout: float = 75.0,
ssl_context: Optional[SSLContext] = None, ssl_context: Optional[SSLContext] = None,
print: Callable[..., None] = print, print: Callable[..., None] = print,
backlog: int = 128, backlog: int = 128,
@ -474,9 +477,11 @@ def run_app(
handle_signals: bool = True, handle_signals: bool = True,
reuse_address: Optional[bool] = None, reuse_address: Optional[bool] = None,
reuse_port: Optional[bool] = None, reuse_port: Optional[bool] = None,
loop: Optional[asyncio.AbstractEventLoop] = None,
) -> None: ) -> None:
"""Run an app locally""" """Run an app locally"""
loop = asyncio.get_event_loop() if loop is None:
loop = asyncio.new_event_loop()
# Configure if and only if in debugging mode and using the default logger # Configure if and only if in debugging mode and using the default logger
if loop.get_debug() and access_log and access_log.name == "aiohttp.access": if loop.get_debug() and access_log and access_log.name == "aiohttp.access":
@ -485,34 +490,36 @@ def run_app(
if not access_log.hasHandlers(): if not access_log.hasHandlers():
access_log.addHandler(logging.StreamHandler()) access_log.addHandler(logging.StreamHandler())
try: main_task = loop.create_task(
main_task = loop.create_task( _run_app(
_run_app( app,
app, host=host,
host=host, port=port,
port=port, path=path,
path=path, sock=sock,
sock=sock, shutdown_timeout=shutdown_timeout,
shutdown_timeout=shutdown_timeout, keepalive_timeout=keepalive_timeout,
ssl_context=ssl_context, ssl_context=ssl_context,
print=print, print=print,
backlog=backlog, backlog=backlog,
access_log_class=access_log_class, access_log_class=access_log_class,
access_log_format=access_log_format, access_log_format=access_log_format,
access_log=access_log, access_log=access_log,
handle_signals=handle_signals, handle_signals=handle_signals,
reuse_address=reuse_address, reuse_address=reuse_address,
reuse_port=reuse_port, reuse_port=reuse_port,
)
) )
)
try:
asyncio.set_event_loop(loop)
loop.run_until_complete(main_task) loop.run_until_complete(main_task)
except (GracefulExit, KeyboardInterrupt): # pragma: no cover except (GracefulExit, KeyboardInterrupt): # pragma: no cover
pass pass
finally: finally:
_cancel_tasks({main_task}, loop) _cancel_tasks({main_task}, loop)
_cancel_tasks(all_tasks(loop), loop) _cancel_tasks(all_tasks(loop), loop)
if sys.version_info >= (3, 6): # don't use PY_36 to pass mypy loop.run_until_complete(loop.shutdown_asyncgens())
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close() loop.close()

59
third_party/python/aiohttp/aiohttp/web_app.py поставляемый
Просмотреть файл

@ -22,6 +22,9 @@ from typing import (
cast, cast,
) )
from aiosignal import Signal
from frozenlist import FrozenList
from . import hdrs from . import hdrs
from .abc import ( from .abc import (
AbstractAccessLogger, AbstractAccessLogger,
@ -29,11 +32,9 @@ from .abc import (
AbstractRouter, AbstractRouter,
AbstractStreamWriter, AbstractStreamWriter,
) )
from .frozenlist import FrozenList
from .helpers import DEBUG from .helpers import DEBUG
from .http_parser import RawRequestMessage from .http_parser import RawRequestMessage
from .log import web_logger from .log import web_logger
from .signals import Signal
from .streams import StreamReader from .streams import StreamReader
from .web_log import AccessLogger from .web_log import AccessLogger
from .web_middlewares import _fix_request_current_app from .web_middlewares import _fix_request_current_app
@ -56,12 +57,13 @@ __all__ = ("Application", "CleanupError")
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from .typedefs import Handler
_AppSignal = Signal[Callable[["Application"], Awaitable[None]]] _AppSignal = Signal[Callable[["Application"], Awaitable[None]]]
_RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]] _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]]
_Handler = Callable[[Request], Awaitable[StreamResponse]]
_Middleware = Union[ _Middleware = Union[
Callable[[Request, _Handler], Awaitable[StreamResponse]], Callable[[Request, Handler], Awaitable[StreamResponse]],
Callable[["Application", _Handler], Awaitable[_Handler]], # old-style Callable[["Application", Handler], Awaitable[Handler]], # old-style
] ]
_Middlewares = FrozenList[_Middleware] _Middlewares = FrozenList[_Middleware]
_MiddlewaresHandlers = Optional[Sequence[Tuple[_Middleware, bool]]] _MiddlewaresHandlers = Optional[Sequence[Tuple[_Middleware, bool]]]
@ -70,7 +72,6 @@ else:
# No type checker mode, skip types # No type checker mode, skip types
_AppSignal = Signal _AppSignal = Signal
_RespPrepareSignal = Signal _RespPrepareSignal = Signal
_Handler = Callable
_Middleware = Callable _Middleware = Callable
_Middlewares = FrozenList _Middlewares = FrozenList
_MiddlewaresHandlers = Optional[Sequence] _MiddlewaresHandlers = Optional[Sequence]
@ -108,7 +109,7 @@ class Application(MutableMapping[str, Any]):
router: Optional[UrlDispatcher] = None, router: Optional[UrlDispatcher] = None,
middlewares: Iterable[_Middleware] = (), middlewares: Iterable[_Middleware] = (),
handler_args: Optional[Mapping[str, Any]] = None, handler_args: Optional[Mapping[str, Any]] = None,
client_max_size: int = 1024 ** 2, client_max_size: int = 1024**2,
loop: Optional[asyncio.AbstractEventLoop] = None, loop: Optional[asyncio.AbstractEventLoop] = None,
debug: Any = ..., # mypy doesn't support ellipsis debug: Any = ..., # mypy doesn't support ellipsis
) -> None: ) -> None:
@ -130,27 +131,27 @@ class Application(MutableMapping[str, Any]):
"debug argument is deprecated", DeprecationWarning, stacklevel=2 "debug argument is deprecated", DeprecationWarning, stacklevel=2
) )
self._debug = debug self._debug = debug
self._router = router # type: UrlDispatcher self._router: UrlDispatcher = router
self._loop = loop self._loop = loop
self._handler_args = handler_args self._handler_args = handler_args
self.logger = logger self.logger = logger
self._middlewares = FrozenList(middlewares) # type: _Middlewares self._middlewares: _Middlewares = FrozenList(middlewares)
# initialized on freezing # initialized on freezing
self._middlewares_handlers = None # type: _MiddlewaresHandlers self._middlewares_handlers: _MiddlewaresHandlers = None
# initialized on freezing # initialized on freezing
self._run_middlewares = None # type: Optional[bool] self._run_middlewares: Optional[bool] = None
self._state = {} # type: Dict[str, Any] self._state: Dict[str, Any] = {}
self._frozen = False self._frozen = False
self._pre_frozen = False self._pre_frozen = False
self._subapps = [] # type: _Subapps self._subapps: _Subapps = []
self._on_response_prepare = Signal(self) # type: _RespPrepareSignal self._on_response_prepare: _RespPrepareSignal = Signal(self)
self._on_startup = Signal(self) # type: _AppSignal self._on_startup: _AppSignal = Signal(self)
self._on_shutdown = Signal(self) # type: _AppSignal self._on_shutdown: _AppSignal = Signal(self)
self._on_cleanup = Signal(self) # type: _AppSignal self._on_cleanup: _AppSignal = Signal(self)
self._cleanup_ctx = CleanupContext() self._cleanup_ctx = CleanupContext()
self._on_startup.append(self._cleanup_ctx._on_startup) self._on_startup.append(self._cleanup_ctx._on_startup)
self._on_cleanup.append(self._cleanup_ctx._on_cleanup) self._on_cleanup.append(self._cleanup_ctx._on_cleanup)
@ -278,7 +279,7 @@ class Application(MutableMapping[str, Any]):
@property @property
def debug(self) -> bool: def debug(self) -> bool:
warnings.warn("debug property is deprecated", DeprecationWarning, stacklevel=2) warnings.warn("debug property is deprecated", DeprecationWarning, stacklevel=2)
return self._debug return self._debug # type: ignore[no-any-return]
def _reg_subapp_signals(self, subapp: "Application") -> None: def _reg_subapp_signals(self, subapp: "Application") -> None:
def reg_handler(signame: str) -> None: def reg_handler(signame: str) -> None:
@ -323,7 +324,7 @@ class Application(MutableMapping[str, Any]):
if not isinstance(domain, str): if not isinstance(domain, str):
raise TypeError("Domain must be str") raise TypeError("Domain must be str")
elif "*" in domain: elif "*" in domain:
rule = MaskDomain(domain) # type: Domain rule: Domain = MaskDomain(domain)
else: else:
rule = Domain(domain) rule = Domain(domain)
factory = partial(MatchedSubAppResource, rule, subapp) factory = partial(MatchedSubAppResource, rule, subapp)
@ -384,7 +385,7 @@ class Application(MutableMapping[str, Any]):
kwargs[k] = v kwargs[k] = v
return Server( return Server(
self._handle, # type: ignore self._handle, # type: ignore[arg-type]
request_factory=self._make_request, request_factory=self._make_request,
loop=self._loop, loop=self._loop,
**kwargs, **kwargs,
@ -427,7 +428,11 @@ class Application(MutableMapping[str, Any]):
Should be called after shutdown() Should be called after shutdown()
""" """
await self.on_cleanup.send(self) if self.on_cleanup.frozen:
await self.on_cleanup.send(self)
else:
# If an exception occurs in startup, ensure cleanup contexts are completed.
await self._cleanup_ctx._on_cleanup(self)
def _make_request( def _make_request(
self, self,
@ -477,7 +482,7 @@ class Application(MutableMapping[str, Any]):
match_info.freeze() match_info.freeze()
resp = None resp = None
request._match_info = match_info # type: ignore request._match_info = match_info
expect = request.headers.get(hdrs.EXPECT) expect = request.headers.get(hdrs.EXPECT)
if expect: if expect:
resp = await match_info.expect_handler(request) resp = await match_info.expect_handler(request)
@ -488,13 +493,13 @@ class Application(MutableMapping[str, Any]):
if self._run_middlewares: if self._run_middlewares:
for app in match_info.apps[::-1]: for app in match_info.apps[::-1]:
for m, new_style in app._middlewares_handlers: # type: ignore for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] # noqa
if new_style: if new_style:
handler = update_wrapper( handler = update_wrapper(
partial(m, handler=handler), handler partial(m, handler=handler), handler
) )
else: else:
handler = await m(app, handler) # type: ignore handler = await m(app, handler) # type: ignore[arg-type]
resp = await handler(request) resp = await handler(request)
@ -505,7 +510,7 @@ class Application(MutableMapping[str, Any]):
return self return self
def __repr__(self) -> str: def __repr__(self) -> str:
return "<Application 0x{:x}>".format(id(self)) return f"<Application 0x{id(self):x}>"
def __bool__(self) -> bool: def __bool__(self) -> bool:
return True return True
@ -514,7 +519,7 @@ class Application(MutableMapping[str, Any]):
class CleanupError(RuntimeError): class CleanupError(RuntimeError):
@property @property
def exceptions(self) -> List[BaseException]: def exceptions(self) -> List[BaseException]:
return self.args[1] return cast(List[BaseException], self.args[1])
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
@ -526,7 +531,7 @@ else:
class CleanupContext(_CleanupContextBase): class CleanupContext(_CleanupContextBase):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self._exits = [] # type: List[AsyncIterator[None]] self._exits: List[AsyncIterator[None]] = []
async def _on_startup(self, app: Application) -> None: async def _on_startup(self, app: Application) -> None:
for cb in self: for cb in self:

Просмотреть файл

@ -273,7 +273,7 @@ class HTTPMethodNotAllowed(HTTPClientError):
content_type=content_type, content_type=content_type,
) )
self.headers["Allow"] = allow self.headers["Allow"] = allow
self.allowed_methods = set(allowed_methods) # type: Set[str] self.allowed_methods: Set[str] = set(allowed_methods)
self.method = method.upper() self.method = method.upper()

Просмотреть файл

@ -9,15 +9,18 @@ from typing import ( # noqa
Any, Any,
Awaitable, Awaitable,
Callable, Callable,
Iterator,
List, List,
Optional, Optional,
Tuple,
Union, Union,
cast, cast,
) )
from . import hdrs from . import hdrs
from .abc import AbstractStreamWriter from .abc import AbstractStreamWriter
from .typedefs import LooseHeaders from .helpers import ETAG_ANY, ETag
from .typedefs import Final, LooseHeaders
from .web_exceptions import ( from .web_exceptions import (
HTTPNotModified, HTTPNotModified,
HTTPPartialContent, HTTPPartialContent,
@ -35,7 +38,7 @@ if TYPE_CHECKING: # pragma: no cover
_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] _T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]]
NOSENDFILE = bool(os.environ.get("AIOHTTP_NOSENDFILE")) NOSENDFILE: Final[bool] = bool(os.environ.get("AIOHTTP_NOSENDFILE"))
class FileResponse(StreamResponse): class FileResponse(StreamResponse):
@ -100,6 +103,30 @@ class FileResponse(StreamResponse):
await super().write_eof() await super().write_eof()
return writer return writer
@staticmethod
def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:
if len(etags) == 1 and etags[0].value == ETAG_ANY:
return True
return any(etag.value == etag_value for etag in etags if not etag.is_weak)
async def _not_modified(
self, request: "BaseRequest", etag_value: str, last_modified: float
) -> Optional[AbstractStreamWriter]:
self.set_status(HTTPNotModified.status_code)
self._length_check = False
self.etag = etag_value # type: ignore[assignment]
self.last_modified = last_modified # type: ignore[assignment]
# Delete any Content-Length headers provided by user. HTTP 304
# should always have empty response body
return await super().prepare(request)
async def _precondition_failed(
self, request: "BaseRequest"
) -> Optional[AbstractStreamWriter]:
self.set_status(HTTPPreconditionFailed.status_code)
self.content_length = 0
return await super().prepare(request)
async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]: async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]:
filepath = self._path filepath = self._path
@ -112,20 +139,35 @@ class FileResponse(StreamResponse):
gzip = True gzip = True
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
st = await loop.run_in_executor(None, filepath.stat) st: os.stat_result = await loop.run_in_executor(None, filepath.stat)
modsince = request.if_modified_since etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}"
if modsince is not None and st.st_mtime <= modsince.timestamp(): last_modified = st.st_mtime
self.set_status(HTTPNotModified.status_code)
self._length_check = False # https://tools.ietf.org/html/rfc7232#section-6
# Delete any Content-Length headers provided by user. HTTP 304 ifmatch = request.if_match
# should always have empty response body if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):
return await super().prepare(request) return await self._precondition_failed(request)
unmodsince = request.if_unmodified_since unmodsince = request.if_unmodified_since
if unmodsince is not None and st.st_mtime > unmodsince.timestamp(): if (
self.set_status(HTTPPreconditionFailed.status_code) unmodsince is not None
return await super().prepare(request) and ifmatch is None
and st.st_mtime > unmodsince.timestamp()
):
return await self._precondition_failed(request)
ifnonematch = request.if_none_match
if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):
return await self._not_modified(request, etag_value, last_modified)
modsince = request.if_modified_since
if (
modsince is not None
and ifnonematch is None
and st.st_mtime <= modsince.timestamp()
):
return await self._not_modified(request, etag_value, last_modified)
if hdrs.CONTENT_TYPE not in self.headers: if hdrs.CONTENT_TYPE not in self.headers:
ct, encoding = mimetypes.guess_type(str(filepath)) ct, encoding = mimetypes.guess_type(str(filepath))
@ -211,12 +253,14 @@ class FileResponse(StreamResponse):
self.set_status(status) self.set_status(status)
if should_set_ct: if should_set_ct:
self.content_type = ct # type: ignore self.content_type = ct # type: ignore[assignment]
if encoding: if encoding:
self.headers[hdrs.CONTENT_ENCODING] = encoding self.headers[hdrs.CONTENT_ENCODING] = encoding
if gzip: if gzip:
self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING
self.last_modified = st.st_mtime # type: ignore
self.etag = etag_value # type: ignore[assignment]
self.last_modified = st.st_mtime # type: ignore[assignment]
self.content_length = count self.content_length = count
self.headers[hdrs.ACCEPT_RANGES] = "bytes" self.headers[hdrs.ACCEPT_RANGES] = "bytes"
@ -228,7 +272,8 @@ class FileResponse(StreamResponse):
real_start, real_start + count - 1, file_size real_start, real_start + count - 1, file_size
) )
if request.method == hdrs.METH_HEAD or self.status in [204, 304]: # If we are sending 0 bytes calling sendfile() will throw a ValueError
if count == 0 or request.method == hdrs.METH_HEAD or self.status in [204, 304]:
return await super().prepare(request) return await super().prepare(request)
fobj = await loop.run_in_executor(None, filepath.open, "rb") fobj = await loop.run_in_executor(None, filepath.open, "rb")

10
third_party/python/aiohttp/aiohttp/web_log.py поставляемый
Просмотреть файл

@ -57,7 +57,7 @@ class AccessLogger(AbstractAccessLogger):
LOG_FORMAT = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"' LOG_FORMAT = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
FORMAT_RE = re.compile(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbOD]|Tf?)") FORMAT_RE = re.compile(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbOD]|Tf?)")
CLEANUP_RE = re.compile(r"(%[^s])") CLEANUP_RE = re.compile(r"(%[^s])")
_FORMAT_CACHE = {} # type: Dict[str, Tuple[str, List[KeyMethod]]] _FORMAT_CACHE: Dict[str, Tuple[str, List[KeyMethod]]] = {}
def __init__(self, logger: logging.Logger, log_format: str = LOG_FORMAT) -> None: def __init__(self, logger: logging.Logger, log_format: str = LOG_FORMAT) -> None:
"""Initialise the logger. """Initialise the logger.
@ -198,10 +198,10 @@ class AccessLogger(AbstractAccessLogger):
if key.__class__ is str: if key.__class__ is str:
extra[key] = value extra[key] = value
else: else:
k1, k2 = key # type: ignore k1, k2 = key # type: ignore[misc]
dct = extra.get(k1, {}) # type: ignore dct = extra.get(k1, {}) # type: ignore[var-annotated,has-type]
dct[k2] = value # type: ignore dct[k2] = value # type: ignore[index,has-type]
extra[k1] = dct # type: ignore extra[k1] = dct # type: ignore[has-type,assignment]
self.logger.info(self._log_format % tuple(values), extra=extra) self.logger.info(self._log_format % tuple(values), extra=extra)
except Exception: except Exception:

Просмотреть файл

@ -1,6 +1,7 @@
import re import re
from typing import TYPE_CHECKING, Awaitable, Callable, Tuple, Type, TypeVar from typing import TYPE_CHECKING, Awaitable, Callable, Tuple, Type, TypeVar
from .typedefs import Handler
from .web_exceptions import HTTPPermanentRedirect, _HTTPMove from .web_exceptions import HTTPPermanentRedirect, _HTTPMove
from .web_request import Request from .web_request import Request
from .web_response import StreamResponse from .web_response import StreamResponse
@ -21,7 +22,7 @@ async def _check_request_resolves(request: Request, path: str) -> Tuple[bool, Re
alt_request = request.clone(rel_url=path) alt_request = request.clone(rel_url=path)
match_info = await request.app.router.resolve(alt_request) match_info = await request.app.router.resolve(alt_request)
alt_request._match_info = match_info # type: ignore alt_request._match_info = match_info
if match_info.http_exception is None: if match_info.http_exception is None:
return True, alt_request return True, alt_request
@ -30,12 +31,11 @@ async def _check_request_resolves(request: Request, path: str) -> Tuple[bool, Re
def middleware(f: _Func) -> _Func: def middleware(f: _Func) -> _Func:
f.__middleware_version__ = 1 # type: ignore f.__middleware_version__ = 1 # type: ignore[attr-defined]
return f return f
_Handler = Callable[[Request], Awaitable[StreamResponse]] _Middleware = Callable[[Request, Handler], Awaitable[StreamResponse]]
_Middleware = Callable[[Request, _Handler], Awaitable[StreamResponse]]
def normalize_path_middleware( def normalize_path_middleware(
@ -43,12 +43,11 @@ def normalize_path_middleware(
append_slash: bool = True, append_slash: bool = True,
remove_slash: bool = False, remove_slash: bool = False,
merge_slashes: bool = True, merge_slashes: bool = True,
redirect_class: Type[_HTTPMove] = HTTPPermanentRedirect redirect_class: Type[_HTTPMove] = HTTPPermanentRedirect,
) -> _Middleware: ) -> _Middleware:
""" """Factory for producing a middleware that normalizes the path of a request.
Middleware factory which produces a middleware that normalizes
the path of a request. By normalizing it means:
Normalizing means:
- Add or remove a trailing slash to the path. - Add or remove a trailing slash to the path.
- Double slashes are replaced by one. - Double slashes are replaced by one.
@ -74,12 +73,11 @@ def normalize_path_middleware(
If merge_slashes is True, merge multiple consecutive slashes in the If merge_slashes is True, merge multiple consecutive slashes in the
path into one. path into one.
""" """
correct_configuration = not (append_slash and remove_slash) correct_configuration = not (append_slash and remove_slash)
assert correct_configuration, "Cannot both remove and append slash" assert correct_configuration, "Cannot both remove and append slash"
@middleware @middleware
async def impl(request: Request, handler: _Handler) -> StreamResponse: async def impl(request: Request, handler: Handler) -> StreamResponse:
if isinstance(request.match_info.route, SystemRoute): if isinstance(request.match_info.route, SystemRoute):
paths_to_check = [] paths_to_check = []
if "?" in request.raw_path: if "?" in request.raw_path:
@ -114,7 +112,7 @@ def normalize_path_middleware(
def _fix_request_current_app(app: "Application") -> _Middleware: def _fix_request_current_app(app: "Application") -> _Middleware:
@middleware @middleware
async def impl(request: Request, handler: _Handler) -> StreamResponse: async def impl(request: Request, handler: Handler) -> StreamResponse:
with request.match_info.set_current_app(app): with request.match_info.set_current_app(app):
return await handler(request) return await handler(request)

Просмотреть файл

@ -7,13 +7,26 @@ from contextlib import suppress
from html import escape as html_escape from html import escape as html_escape
from http import HTTPStatus from http import HTTPStatus
from logging import Logger from logging import Logger
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Optional, Tuple, Type, cast from typing import (
TYPE_CHECKING,
Any,
Awaitable,
Callable,
Deque,
Optional,
Sequence,
Tuple,
Type,
Union,
cast,
)
import attr
import yarl import yarl
from .abc import AbstractAccessLogger, AbstractStreamWriter from .abc import AbstractAccessLogger, AbstractStreamWriter
from .base_protocol import BaseProtocol from .base_protocol import BaseProtocol
from .helpers import CeilTimeout, current_task from .helpers import ceil_timeout
from .http import ( from .http import (
HttpProcessingError, HttpProcessingError,
HttpRequestParser, HttpRequestParser,
@ -48,9 +61,17 @@ _RequestFactory = Callable[
_RequestHandler = Callable[[BaseRequest], Awaitable[StreamResponse]] _RequestHandler = Callable[[BaseRequest], Awaitable[StreamResponse]]
ERROR = RawRequestMessage( ERROR = RawRequestMessage(
"UNKNOWN", "/", HttpVersion10, {}, {}, True, False, False, False, yarl.URL("/") "UNKNOWN",
"/",
HttpVersion10,
{}, # type: ignore[arg-type]
{}, # type: ignore[arg-type]
True,
None,
False,
False,
yarl.URL("/"),
) )
@ -62,6 +83,16 @@ class PayloadAccessError(Exception):
"""Payload was accessed after response was sent.""" """Payload was accessed after response was sent."""
@attr.s(auto_attribs=True, frozen=True, slots=True)
class _ErrInfo:
status: int
exc: BaseException
message: str
_MsgType = Tuple[Union[RawRequestMessage, _ErrInfo], StreamReader]
class RequestHandler(BaseProtocol): class RequestHandler(BaseProtocol):
"""HTTP protocol implementation. """HTTP protocol implementation.
@ -73,32 +104,28 @@ class RequestHandler(BaseProtocol):
status line, bad headers or incomplete payload. If any error occurs, status line, bad headers or incomplete payload. If any error occurs,
connection gets closed. connection gets closed.
:param keepalive_timeout: number of seconds before closing keepalive_timeout -- number of seconds before closing
keep-alive connection keep-alive connection
:type keepalive_timeout: int or None
:param bool tcp_keepalive: TCP keep-alive is on, default is on tcp_keepalive -- TCP keep-alive is on, default is on
:param bool debug: enable debug mode debug -- enable debug mode
:param logger: custom logger object logger -- custom logger object
:type logger: aiohttp.log.server_logger
:param access_log_class: custom class for access_logger access_log_class -- custom class for access_logger
:type access_log_class: aiohttp.abc.AbstractAccessLogger
:param access_log: custom logging object access_log -- custom logging object
:type access_log: aiohttp.log.server_logger
:param str access_log_format: access log format string access_log_format -- access log format string
:param loop: Optional event loop loop -- Optional event loop
:param int max_line_size: Optional maximum header line size max_line_size -- Optional maximum header line size
:param int max_field_size: Optional maximum header field size max_field_size -- Optional maximum header field size
:param int max_headers: Optional maximum header size max_headers -- Optional maximum header size
""" """
@ -118,7 +145,6 @@ class RequestHandler(BaseProtocol):
"_messages", "_messages",
"_message_tail", "_message_tail",
"_waiter", "_waiter",
"_error_handler",
"_task_handler", "_task_handler",
"_upgrade", "_upgrade",
"_payload_parser", "_payload_parser",
@ -149,39 +175,34 @@ class RequestHandler(BaseProtocol):
max_headers: int = 32768, max_headers: int = 32768,
max_field_size: int = 8190, max_field_size: int = 8190,
lingering_time: float = 10.0, lingering_time: float = 10.0,
read_bufsize: int = 2 ** 16, read_bufsize: int = 2**16,
auto_decompress: bool = True,
): ):
super().__init__(loop) super().__init__(loop)
self._request_count = 0 self._request_count = 0
self._keepalive = False self._keepalive = False
self._current_request = None # type: Optional[BaseRequest] self._current_request: Optional[BaseRequest] = None
self._manager = manager # type: Optional[Server] self._manager: Optional[Server] = manager
self._request_handler = ( self._request_handler: Optional[_RequestHandler] = manager.request_handler
manager.request_handler self._request_factory: Optional[_RequestFactory] = manager.request_factory
) # type: Optional[_RequestHandler]
self._request_factory = (
manager.request_factory
) # type: Optional[_RequestFactory]
self._tcp_keepalive = tcp_keepalive self._tcp_keepalive = tcp_keepalive
# placeholder to be replaced on keepalive timeout setup # placeholder to be replaced on keepalive timeout setup
self._keepalive_time = 0.0 self._keepalive_time = 0.0
self._keepalive_handle = None # type: Optional[asyncio.Handle] self._keepalive_handle: Optional[asyncio.Handle] = None
self._keepalive_timeout = keepalive_timeout self._keepalive_timeout = keepalive_timeout
self._lingering_time = float(lingering_time) self._lingering_time = float(lingering_time)
self._messages = deque() # type: Any # Python 3.5 has no typing.Deque self._messages: Deque[_MsgType] = deque()
self._message_tail = b"" self._message_tail = b""
self._waiter = None # type: Optional[asyncio.Future[None]] self._waiter: Optional[asyncio.Future[None]] = None
self._error_handler = None # type: Optional[asyncio.Task[None]] self._task_handler: Optional[asyncio.Task[None]] = None
self._task_handler = None # type: Optional[asyncio.Task[None]]
self._upgrade = False self._upgrade = False
self._payload_parser = None # type: Any self._payload_parser: Any = None
self._request_parser = HttpRequestParser( self._request_parser: Optional[HttpRequestParser] = HttpRequestParser(
self, self,
loop, loop,
read_bufsize, read_bufsize,
@ -189,15 +210,16 @@ class RequestHandler(BaseProtocol):
max_field_size=max_field_size, max_field_size=max_field_size,
max_headers=max_headers, max_headers=max_headers,
payload_exception=RequestPayloadError, payload_exception=RequestPayloadError,
) # type: Optional[HttpRequestParser] auto_decompress=auto_decompress,
)
self.logger = logger self.logger = logger
self.debug = debug self.debug = debug
self.access_log = access_log self.access_log = access_log
if access_log: if access_log:
self.access_logger = access_log_class( self.access_logger: Optional[AbstractAccessLogger] = access_log_class(
access_log, access_log_format access_log, access_log_format
) # type: Optional[AbstractAccessLogger] )
else: else:
self.access_logger = None self.access_logger = None
@ -215,9 +237,11 @@ class RequestHandler(BaseProtocol):
return self._keepalive_timeout return self._keepalive_timeout
async def shutdown(self, timeout: Optional[float] = 15.0) -> None: async def shutdown(self, timeout: Optional[float] = 15.0) -> None:
"""Worker process is about to exit, we need cleanup everything and """Do worker process exit preparations.
stop accepting requests. It is especially important for keep-alive
connections.""" We need to clean up everything and stop accepting requests.
It is especially important for keep-alive connections.
"""
self._force_close = True self._force_close = True
if self._keepalive_handle is not None: if self._keepalive_handle is not None:
@ -228,10 +252,7 @@ class RequestHandler(BaseProtocol):
# wait for handlers # wait for handlers
with suppress(asyncio.CancelledError, asyncio.TimeoutError): with suppress(asyncio.CancelledError, asyncio.TimeoutError):
with CeilTimeout(timeout, loop=self._loop): async with ceil_timeout(timeout):
if self._error_handler is not None and not self._error_handler.done():
await self._error_handler
if self._current_request is not None: if self._current_request is not None:
self._current_request._cancel(asyncio.CancelledError()) self._current_request._cancel(asyncio.CancelledError())
@ -278,10 +299,6 @@ class RequestHandler(BaseProtocol):
exc = ConnectionResetError("Connection lost") exc = ConnectionResetError("Connection lost")
self._current_request._cancel(exc) self._current_request._cancel(exc)
if self._error_handler is not None:
self._error_handler.cancel()
if self._task_handler is not None:
self._task_handler.cancel()
if self._waiter is not None: if self._waiter is not None:
self._waiter.cancel() self._waiter.cancel()
@ -308,40 +325,30 @@ class RequestHandler(BaseProtocol):
if self._force_close or self._close: if self._force_close or self._close:
return return
# parse http messages # parse http messages
messages: Sequence[_MsgType]
if self._payload_parser is None and not self._upgrade: if self._payload_parser is None and not self._upgrade:
assert self._request_parser is not None assert self._request_parser is not None
try: try:
messages, upgraded, tail = self._request_parser.feed_data(data) messages, upgraded, tail = self._request_parser.feed_data(data)
except HttpProcessingError as exc: except HttpProcessingError as exc:
# something happened during parsing messages = [
self._error_handler = self._loop.create_task( (_ErrInfo(status=400, exc=exc, message=exc.message), EMPTY_PAYLOAD)
self.handle_parse_error( ]
StreamWriter(self, self._loop), 400, exc, exc.message upgraded = False
) tail = b""
)
self.close()
except Exception as exc:
# 500: internal error
self._error_handler = self._loop.create_task(
self.handle_parse_error(StreamWriter(self, self._loop), 500, exc)
)
self.close()
else:
if messages:
# sometimes the parser returns no messages
for (msg, payload) in messages:
self._request_count += 1
self._messages.append((msg, payload))
waiter = self._waiter for msg, payload in messages or ():
if waiter is not None: self._request_count += 1
if not waiter.done(): self._messages.append((msg, payload))
# don't set result twice
waiter.set_result(None)
self._upgrade = upgraded waiter = self._waiter
if upgraded and tail: if messages and waiter is not None and not waiter.done():
self._message_tail = tail # don't set result twice
waiter.set_result(None)
self._upgrade = upgraded
if upgraded and tail:
self._message_tail = tail
# no parser, just store # no parser, just store
elif self._payload_parser is None and self._upgrade and data: elif self._payload_parser is None and self._upgrade and data:
@ -364,14 +371,17 @@ class RequestHandler(BaseProtocol):
self._keepalive_handle = None self._keepalive_handle = None
def close(self) -> None: def close(self) -> None:
"""Stop accepting new pipelinig messages and close """Close connection.
connection when handlers done processing messages"""
Stop accepting new pipelining messages and close
connection when handlers done processing messages.
"""
self._close = True self._close = True
if self._waiter: if self._waiter:
self._waiter.cancel() self._waiter.cancel()
def force_close(self) -> None: def force_close(self) -> None:
"""Force close connection""" """Forcefully close connection."""
self._force_close = True self._force_close = True
if self._waiter: if self._waiter:
self._waiter.cancel() self._waiter.cancel()
@ -414,18 +424,17 @@ class RequestHandler(BaseProtocol):
self, self,
request: BaseRequest, request: BaseRequest,
start_time: float, start_time: float,
request_handler: Callable[[BaseRequest], Awaitable[StreamResponse]],
) -> Tuple[StreamResponse, bool]: ) -> Tuple[StreamResponse, bool]:
assert self._request_handler is not None assert self._request_handler is not None
try: try:
try: try:
self._current_request = request self._current_request = request
resp = await self._request_handler(request) resp = await request_handler(request)
finally: finally:
self._current_request = None self._current_request = None
except HTTPException as exc: except HTTPException as exc:
resp = Response( resp = exc
status=exc.status, reason=exc.reason, text=exc.text, headers=exc.headers
)
reset = await self.finish_response(request, resp, start_time) reset = await self.finish_response(request, resp, start_time)
except asyncio.CancelledError: except asyncio.CancelledError:
raise raise
@ -437,6 +446,15 @@ class RequestHandler(BaseProtocol):
resp = self.handle_error(request, 500, exc) resp = self.handle_error(request, 500, exc)
reset = await self.finish_response(request, resp, start_time) reset = await self.finish_response(request, resp, start_time)
else: else:
# Deprecation warning (See #2415)
if getattr(resp, "__http_exception__", False):
warnings.warn(
"returning HTTPException object is deprecated "
"(#2415) and will be removed, "
"please raise the exception instead",
DeprecationWarning,
)
reset = await self.finish_response(request, resp, start_time) reset = await self.finish_response(request, resp, start_time)
return resp, reset return resp, reset
@ -477,23 +495,24 @@ class RequestHandler(BaseProtocol):
manager.requests_count += 1 manager.requests_count += 1
writer = StreamWriter(self, loop) writer = StreamWriter(self, loop)
if isinstance(message, _ErrInfo):
# make request_factory work
request_handler = self._make_error_handler(message)
message = ERROR
else:
request_handler = self._request_handler
request = self._request_factory(message, payload, self, writer, handler) request = self._request_factory(message, payload, self, writer, handler)
try: try:
# a new task is used for copy context vars (#3406) # a new task is used for copy context vars (#3406)
task = self._loop.create_task(self._handle_request(request, start)) task = self._loop.create_task(
self._handle_request(request, start, request_handler)
)
try: try:
resp, reset = await task resp, reset = await task
except (asyncio.CancelledError, ConnectionError): except (asyncio.CancelledError, ConnectionError):
self.log_debug("Ignored premature client disconnection") self.log_debug("Ignored premature client disconnection")
break break
# Deprecation warning (See #2415)
if getattr(resp, "__http_exception__", False):
warnings.warn(
"returning HTTPException object is deprecated "
"(#2415) and will be removed, "
"please raise the exception instead",
DeprecationWarning,
)
# Drop the processed task from asyncio.Task.all_tasks() early # Drop the processed task from asyncio.Task.all_tasks() early
del task del task
@ -517,7 +536,7 @@ class RequestHandler(BaseProtocol):
with suppress(asyncio.TimeoutError, asyncio.CancelledError): with suppress(asyncio.TimeoutError, asyncio.CancelledError):
while not payload.is_eof() and now < end_t: while not payload.is_eof() and now < end_t:
with CeilTimeout(end_t - now, loop=loop): async with ceil_timeout(end_t - now):
# read and ignore # read and ignore
await payload.readany() await payload.readany()
now = loop.time() now = loop.time()
@ -558,14 +577,15 @@ class RequestHandler(BaseProtocol):
# remove handler, close transport if no handlers left # remove handler, close transport if no handlers left
if not self._force_close: if not self._force_close:
self._task_handler = None self._task_handler = None
if self.transport is not None and self._error_handler is None: if self.transport is not None:
self.transport.close() self.transport.close()
async def finish_response( async def finish_response(
self, request: BaseRequest, resp: StreamResponse, start_time: float self, request: BaseRequest, resp: StreamResponse, start_time: float
) -> bool: ) -> bool:
""" """Prepare the response and write_eof, then log access.
Prepare the response and write_eof, then log access. This has to
This has to
be called within the context of any exception so the access logger be called within the context of any exception so the access logger
can get exception information. Returns True if the client disconnects can get exception information. Returns True if the client disconnects
prematurely. prematurely.
@ -607,9 +627,17 @@ class RequestHandler(BaseProtocol):
"""Handle errors. """Handle errors.
Returns HTTP response with specific status code. Logs additional Returns HTTP response with specific status code. Logs additional
information. It always closes current connection.""" information. It always closes current connection.
"""
self.log_exception("Error handling request", exc_info=exc) self.log_exception("Error handling request", exc_info=exc)
# some data already got sent, connection is broken
if request.writer.output_size > 0:
raise ConnectionError(
"Response is sent already, cannot send another response "
"with the error message"
)
ct = "text/plain" ct = "text/plain"
if status == HTTPStatus.INTERNAL_SERVER_ERROR: if status == HTTPStatus.INTERNAL_SERVER_ERROR:
title = "{0.value} {0.phrase}".format(HTTPStatus.INTERNAL_SERVER_ERROR) title = "{0.value} {0.phrase}".format(HTTPStatus.INTERNAL_SERVER_ERROR)
@ -638,30 +666,14 @@ class RequestHandler(BaseProtocol):
resp = Response(status=status, text=message, content_type=ct) resp = Response(status=status, text=message, content_type=ct)
resp.force_close() resp.force_close()
# some data already got sent, connection is broken
if request.writer.output_size > 0 or self.transport is None:
self.force_close()
return resp return resp
async def handle_parse_error( def _make_error_handler(
self, self, err_info: _ErrInfo
writer: AbstractStreamWriter, ) -> Callable[[BaseRequest], Awaitable[StreamResponse]]:
status: int, async def handler(request: BaseRequest) -> StreamResponse:
exc: Optional[BaseException] = None, return self.handle_error(
message: Optional[str] = None, request, err_info.status, err_info.exc, err_info.message
) -> None: )
task = current_task()
assert task is not None
request = BaseRequest(
ERROR, EMPTY_PAYLOAD, self, writer, task, self._loop # type: ignore
)
resp = self.handle_error(request, status, exc, message) return handler
await resp.prepare(request)
await resp.write_eof()
if self.transport is not None:
self.transport.close()
self._error_handler = None

Просмотреть файл

@ -7,7 +7,6 @@ import string
import tempfile import tempfile
import types import types
import warnings import warnings
from email.utils import parsedate
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
from types import MappingProxyType from types import MappingProxyType
from typing import ( from typing import (
@ -18,6 +17,7 @@ from typing import (
Mapping, Mapping,
MutableMapping, MutableMapping,
Optional, Optional,
Pattern,
Tuple, Tuple,
Union, Union,
cast, cast,
@ -30,13 +30,24 @@ from yarl import URL
from . import hdrs from . import hdrs
from .abc import AbstractStreamWriter from .abc import AbstractStreamWriter
from .helpers import DEBUG, ChainMapProxy, HeadersMixin, reify, sentinel from .helpers import (
DEBUG,
ETAG_ANY,
LIST_QUOTED_ETAG_RE,
ChainMapProxy,
ETag,
HeadersMixin,
parse_http_date,
reify,
sentinel,
)
from .http_parser import RawRequestMessage from .http_parser import RawRequestMessage
from .http_writer import HttpVersion from .http_writer import HttpVersion
from .multipart import BodyPartReader, MultipartReader from .multipart import BodyPartReader, MultipartReader
from .streams import EmptyStreamReader, StreamReader from .streams import EmptyStreamReader, StreamReader
from .typedefs import ( from .typedefs import (
DEFAULT_JSON_DECODER, DEFAULT_JSON_DECODER,
Final,
JSONDecoder, JSONDecoder,
LooseHeaders, LooseHeaders,
RawHeaders, RawHeaders,
@ -63,31 +74,33 @@ class FileField:
headers: "CIMultiDictProxy[str]" headers: "CIMultiDictProxy[str]"
_TCHAR = string.digits + string.ascii_letters + r"!#$%&'*+.^_`|~-" _TCHAR: Final[str] = string.digits + string.ascii_letters + r"!#$%&'*+.^_`|~-"
# '-' at the end to prevent interpretation as range in a char class # '-' at the end to prevent interpretation as range in a char class
_TOKEN = fr"[{_TCHAR}]+" _TOKEN: Final[str] = rf"[{_TCHAR}]+"
_QDTEXT = r"[{}]".format( _QDTEXT: Final[str] = r"[{}]".format(
r"".join(chr(c) for c in (0x09, 0x20, 0x21) + tuple(range(0x23, 0x7F))) r"".join(chr(c) for c in (0x09, 0x20, 0x21) + tuple(range(0x23, 0x7F)))
) )
# qdtext includes 0x5C to escape 0x5D ('\]') # qdtext includes 0x5C to escape 0x5D ('\]')
# qdtext excludes obs-text (because obsoleted, and encoding not specified) # qdtext excludes obs-text (because obsoleted, and encoding not specified)
_QUOTED_PAIR = r"\\[\t !-~]" _QUOTED_PAIR: Final[str] = r"\\[\t !-~]"
_QUOTED_STRING = r'"(?:{quoted_pair}|{qdtext})*"'.format( _QUOTED_STRING: Final[str] = r'"(?:{quoted_pair}|{qdtext})*"'.format(
qdtext=_QDTEXT, quoted_pair=_QUOTED_PAIR qdtext=_QDTEXT, quoted_pair=_QUOTED_PAIR
) )
_FORWARDED_PAIR = r"({token})=({token}|{quoted_string})(:\d{{1,4}})?".format( _FORWARDED_PAIR: Final[
str
] = r"({token})=({token}|{quoted_string})(:\d{{1,4}})?".format(
token=_TOKEN, quoted_string=_QUOTED_STRING token=_TOKEN, quoted_string=_QUOTED_STRING
) )
_QUOTED_PAIR_REPLACE_RE = re.compile(r"\\([\t !-~])") _QUOTED_PAIR_REPLACE_RE: Final[Pattern[str]] = re.compile(r"\\([\t !-~])")
# same pattern as _QUOTED_PAIR but contains a capture group # same pattern as _QUOTED_PAIR but contains a capture group
_FORWARDED_PAIR_RE = re.compile(_FORWARDED_PAIR) _FORWARDED_PAIR_RE: Final[Pattern[str]] = re.compile(_FORWARDED_PAIR)
############################################################ ############################################################
# HTTP Request # HTTP Request
@ -135,7 +148,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
task: "asyncio.Task[None]", task: "asyncio.Task[None]",
loop: asyncio.AbstractEventLoop, loop: asyncio.AbstractEventLoop,
*, *,
client_max_size: int = 1024 ** 2, client_max_size: int = 1024**2,
state: Optional[Dict[str, Any]] = None, state: Optional[Dict[str, Any]] = None,
scheme: Optional[str] = None, scheme: Optional[str] = None,
host: Optional[str] = None, host: Optional[str] = None,
@ -151,14 +164,22 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
self._headers = message.headers self._headers = message.headers
self._method = message.method self._method = message.method
self._version = message.version self._version = message.version
self._rel_url = message.url self._cache: Dict[str, Any] = {}
self._post = ( url = message.url
None if url.is_absolute():
) # type: Optional[MultiDictProxy[Union[str, bytes, FileField]]] # absolute URL is given,
self._read_bytes = None # type: Optional[bytes] # override auto-calculating url, host, and scheme
# all other properties should be good
self._cache["url"] = url
self._cache["host"] = url.host
self._cache["scheme"] = url.scheme
self._rel_url = url.relative()
else:
self._rel_url = message.url
self._post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None
self._read_bytes: Optional[bytes] = None
self._state = state self._state = state
self._cache = {} # type: Dict[str, Any]
self._task = task self._task = task
self._client_max_size = client_max_size self._client_max_size = client_max_size
self._loop = loop self._loop = loop
@ -190,13 +211,11 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
Creates and returns a new instance of Request object. If no parameters Creates and returns a new instance of Request object. If no parameters
are given, an exact copy is returned. If a parameter is not passed, it are given, an exact copy is returned. If a parameter is not passed, it
will reuse the one from the current request object. will reuse the one from the current request object.
""" """
if self._read_bytes: if self._read_bytes:
raise RuntimeError("Cannot clone request " "after reading its content") raise RuntimeError("Cannot clone request " "after reading its content")
dct = {} # type: Dict[str, Any] dct: Dict[str, Any] = {}
if method is not sentinel: if method is not sentinel:
dct["method"] = method dct["method"] = method
if rel_url is not sentinel: if rel_url is not sentinel:
@ -315,7 +334,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
length = len(field_value) length = len(field_value)
pos = 0 pos = 0
need_separator = False need_separator = False
elem = {} # type: Dict[str, str] elem: Dict[str, str] = {}
elems.append(types.MappingProxyType(elem)) elems.append(types.MappingProxyType(elem))
while 0 <= pos < length: while 0 <= pos < length:
match = _FORWARDED_PAIR_RE.match(field_value, pos) match = _FORWARDED_PAIR_RE.match(field_value, pos)
@ -396,8 +415,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
host = self._message.headers.get(hdrs.HOST) host = self._message.headers.get(hdrs.HOST)
if host is not None: if host is not None:
return host return host
else: return socket.getfqdn()
return socket.getfqdn()
@reify @reify
def remote(self) -> Optional[str]: def remote(self) -> Optional[str]:
@ -408,10 +426,11 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
- overridden value by .clone(remote=new_remote) call. - overridden value by .clone(remote=new_remote) call.
- peername of opened socket - peername of opened socket
""" """
if self._transport_peername is None:
return None
if isinstance(self._transport_peername, (list, tuple)): if isinstance(self._transport_peername, (list, tuple)):
return self._transport_peername[0] return str(self._transport_peername[0])
else: return str(self._transport_peername)
return self._transport_peername
@reify @reify
def url(self) -> URL: def url(self) -> URL:
@ -437,6 +456,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
@reify @reify
def raw_path(self) -> str: def raw_path(self) -> str:
"""The URL including raw *PATH INFO* without the host or scheme. """The URL including raw *PATH INFO* without the host or scheme.
Warning, the path is unquoted and may contains non valid URL characters Warning, the path is unquoted and may contains non valid URL characters
E.g., ``/my%2Fpath%7Cwith%21some%25strange%24characters`` E.g., ``/my%2Fpath%7Cwith%21some%25strange%24characters``
@ -446,7 +466,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
@reify @reify
def query(self) -> "MultiDictProxy[str]": def query(self) -> "MultiDictProxy[str]":
"""A multidict with all the variables in the query string.""" """A multidict with all the variables in the query string."""
return self._rel_url.query return MultiDictProxy(self._rel_url.query)
@reify @reify
def query_string(self) -> str: def query_string(self) -> str:
@ -466,22 +486,13 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
"""A sequence of pairs for all headers.""" """A sequence of pairs for all headers."""
return self._message.raw_headers return self._message.raw_headers
@staticmethod
def _http_date(_date_str: Optional[str]) -> Optional[datetime.datetime]:
"""Process a date string, return a datetime object"""
if _date_str is not None:
timetuple = parsedate(_date_str)
if timetuple is not None:
return datetime.datetime(*timetuple[:6], tzinfo=datetime.timezone.utc)
return None
@reify @reify
def if_modified_since(self) -> Optional[datetime.datetime]: def if_modified_since(self) -> Optional[datetime.datetime]:
"""The value of If-Modified-Since HTTP header, or None. """The value of If-Modified-Since HTTP header, or None.
This header is represented as a `datetime` object. This header is represented as a `datetime` object.
""" """
return self._http_date(self.headers.get(hdrs.IF_MODIFIED_SINCE)) return parse_http_date(self.headers.get(hdrs.IF_MODIFIED_SINCE))
@reify @reify
def if_unmodified_since(self) -> Optional[datetime.datetime]: def if_unmodified_since(self) -> Optional[datetime.datetime]:
@ -489,7 +500,53 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
This header is represented as a `datetime` object. This header is represented as a `datetime` object.
""" """
return self._http_date(self.headers.get(hdrs.IF_UNMODIFIED_SINCE)) return parse_http_date(self.headers.get(hdrs.IF_UNMODIFIED_SINCE))
@staticmethod
def _etag_values(etag_header: str) -> Iterator[ETag]:
"""Extract `ETag` objects from raw header."""
if etag_header == ETAG_ANY:
yield ETag(
is_weak=False,
value=ETAG_ANY,
)
else:
for match in LIST_QUOTED_ETAG_RE.finditer(etag_header):
is_weak, value, garbage = match.group(2, 3, 4)
# Any symbol captured by 4th group means
# that the following sequence is invalid.
if garbage:
break
yield ETag(
is_weak=bool(is_weak),
value=value,
)
@classmethod
def _if_match_or_none_impl(
cls, header_value: Optional[str]
) -> Optional[Tuple[ETag, ...]]:
if not header_value:
return None
return tuple(cls._etag_values(header_value))
@reify
def if_match(self) -> Optional[Tuple[ETag, ...]]:
"""The value of If-Match HTTP header, or None.
This header is represented as a `tuple` of `ETag` objects.
"""
return self._if_match_or_none_impl(self.headers.get(hdrs.IF_MATCH))
@reify
def if_none_match(self) -> Optional[Tuple[ETag, ...]]:
"""The value of If-None-Match HTTP header, or None.
This header is represented as a `tuple` of `ETag` objects.
"""
return self._if_match_or_none_impl(self.headers.get(hdrs.IF_NONE_MATCH))
@reify @reify
def if_range(self) -> Optional[datetime.datetime]: def if_range(self) -> Optional[datetime.datetime]:
@ -497,7 +554,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
This header is represented as a `datetime` object. This header is represented as a `datetime` object.
""" """
return self._http_date(self.headers.get(hdrs.IF_RANGE)) return parse_http_date(self.headers.get(hdrs.IF_RANGE))
@reify @reify
def keep_alive(self) -> bool: def keep_alive(self) -> bool:
@ -511,7 +568,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
A read-only dictionary-like object. A read-only dictionary-like object.
""" """
raw = self.headers.get(hdrs.COOKIE, "") raw = self.headers.get(hdrs.COOKIE, "")
parsed = SimpleCookie(raw) # type: SimpleCookie[str] parsed: SimpleCookie[str] = SimpleCookie(raw)
return MappingProxyType({key: val.value for key, val in parsed.items()}) return MappingProxyType({key: val.value for key, val in parsed.items()})
@reify @reify
@ -634,7 +691,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
self._post = MultiDictProxy(MultiDict()) self._post = MultiDictProxy(MultiDict())
return self._post return self._post
out = MultiDict() # type: MultiDict[Union[str, bytes, FileField]] out: MultiDict[Union[str, bytes, FileField]] = MultiDict()
if content_type == "multipart/form-data": if content_type == "multipart/form-data":
multipart = await self.multipart() multipart = await self.multipart()
@ -655,16 +712,17 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
if field.filename: if field.filename:
# store file in temp file # store file in temp file
tmp = tempfile.TemporaryFile() tmp = tempfile.TemporaryFile()
chunk = await field.read_chunk(size=2 ** 16) chunk = await field.read_chunk(size=2**16)
while chunk: while chunk:
chunk = field.decode(chunk) chunk = field.decode(chunk)
tmp.write(chunk) tmp.write(chunk)
size += len(chunk) size += len(chunk)
if 0 < max_size < size: if 0 < max_size < size:
tmp.close()
raise HTTPRequestEntityTooLarge( raise HTTPRequestEntityTooLarge(
max_size=max_size, actual_size=size max_size=max_size, actual_size=size
) )
chunk = await field.read_chunk(size=2 ** 16) chunk = await field.read_chunk(size=2**16)
tmp.seek(0) tmp.seek(0)
if field_ct is None: if field_ct is None:
@ -756,7 +814,7 @@ class Request(BaseRequest):
# or information about traversal lookup # or information about traversal lookup
# initialized after route resolving # initialized after route resolving
self._match_info = None # type: Optional[UrlMappingMatchInfo] self._match_info: Optional[UrlMappingMatchInfo] = None
if DEBUG: if DEBUG:

Просмотреть файл

@ -8,7 +8,6 @@ import time
import warnings import warnings
import zlib import zlib
from concurrent.futures import Executor from concurrent.futures import Executor
from email.utils import parsedate
from http.cookies import Morsel, SimpleCookie from http.cookies import Morsel, SimpleCookie
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
@ -27,7 +26,17 @@ from multidict import CIMultiDict, istr
from . import hdrs, payload from . import hdrs, payload
from .abc import AbstractStreamWriter from .abc import AbstractStreamWriter
from .helpers import PY_38, HeadersMixin, rfc822_formatted_time, sentinel from .helpers import (
ETAG_ANY,
PY_38,
QUOTED_ETAG_RE,
ETag,
HeadersMixin,
parse_http_date,
rfc822_formatted_time,
sentinel,
validate_etag_value,
)
from .http import RESPONSES, SERVER_SOFTWARE, HttpVersion10, HttpVersion11 from .http import RESPONSES, SERVER_SOFTWARE, HttpVersion10, HttpVersion11
from .payload import Payload from .payload import Payload
from .typedefs import JSONEncoder, LooseHeaders from .typedefs import JSONEncoder, LooseHeaders
@ -46,7 +55,7 @@ else:
if not PY_38: if not PY_38:
# allow samesite to be used in python < 3.8 # allow samesite to be used in python < 3.8
# already permitted in python 3.8, see https://bugs.python.org/issue29613 # already permitted in python 3.8, see https://bugs.python.org/issue29613
Morsel._reserved["samesite"] = "SameSite" # type: ignore Morsel._reserved["samesite"] = "SameSite" # type: ignore[attr-defined]
class ContentCoding(enum.Enum): class ContentCoding(enum.Enum):
@ -76,20 +85,20 @@ class StreamResponse(BaseClass, HeadersMixin):
headers: Optional[LooseHeaders] = None, headers: Optional[LooseHeaders] = None,
) -> None: ) -> None:
self._body = None self._body = None
self._keep_alive = None # type: Optional[bool] self._keep_alive: Optional[bool] = None
self._chunked = False self._chunked = False
self._compression = False self._compression = False
self._compression_force = None # type: Optional[ContentCoding] self._compression_force: Optional[ContentCoding] = None
self._cookies = SimpleCookie() # type: SimpleCookie[str] self._cookies: SimpleCookie[str] = SimpleCookie()
self._req = None # type: Optional[BaseRequest] self._req: Optional[BaseRequest] = None
self._payload_writer = None # type: Optional[AbstractStreamWriter] self._payload_writer: Optional[AbstractStreamWriter] = None
self._eof_sent = False self._eof_sent = False
self._body_length = 0 self._body_length = 0
self._state = {} # type: Dict[str, Any] self._state: Dict[str, Any] = {}
if headers is not None: if headers is not None:
self._headers = CIMultiDict(headers) # type: CIMultiDict[str] self._headers: CIMultiDict[str] = CIMultiDict(headers)
else: else:
self._headers = CIMultiDict() self._headers = CIMultiDict()
@ -100,8 +109,11 @@ class StreamResponse(BaseClass, HeadersMixin):
return self._payload_writer is not None return self._payload_writer is not None
@property @property
def task(self) -> "asyncio.Task[None]": def task(self) -> "Optional[asyncio.Task[None]]":
return getattr(self._req, "task", None) if self._req:
return self._req.task
else:
return None
@property @property
def status(self) -> int: def status(self) -> int:
@ -209,7 +221,6 @@ class StreamResponse(BaseClass, HeadersMixin):
Sets new cookie or updates existent with new value. Sets new cookie or updates existent with new value.
Also updates only those params which are not None. Also updates only those params which are not None.
""" """
old = self._cookies.get(name) old = self._cookies.get(name)
if old is not None and old.coded_value == "": if old is not None and old.coded_value == "":
# deleted cookie # deleted cookie
@ -314,12 +325,7 @@ class StreamResponse(BaseClass, HeadersMixin):
This header is represented as a `datetime` object. This header is represented as a `datetime` object.
""" """
httpdate = self._headers.get(hdrs.LAST_MODIFIED) return parse_http_date(self._headers.get(hdrs.LAST_MODIFIED))
if httpdate is not None:
timetuple = parsedate(httpdate)
if timetuple is not None:
return datetime.datetime(*timetuple[:6], tzinfo=datetime.timezone.utc)
return None
@last_modified.setter @last_modified.setter
def last_modified( def last_modified(
@ -338,6 +344,43 @@ class StreamResponse(BaseClass, HeadersMixin):
elif isinstance(value, str): elif isinstance(value, str):
self._headers[hdrs.LAST_MODIFIED] = value self._headers[hdrs.LAST_MODIFIED] = value
@property
def etag(self) -> Optional[ETag]:
quoted_value = self._headers.get(hdrs.ETAG)
if not quoted_value:
return None
elif quoted_value == ETAG_ANY:
return ETag(value=ETAG_ANY)
match = QUOTED_ETAG_RE.fullmatch(quoted_value)
if not match:
return None
is_weak, value = match.group(1, 2)
return ETag(
is_weak=bool(is_weak),
value=value,
)
@etag.setter
def etag(self, value: Optional[Union[ETag, str]]) -> None:
if value is None:
self._headers.pop(hdrs.ETAG, None)
elif (isinstance(value, str) and value == ETAG_ANY) or (
isinstance(value, ETag) and value.value == ETAG_ANY
):
self._headers[hdrs.ETAG] = ETAG_ANY
elif isinstance(value, str):
validate_etag_value(value)
self._headers[hdrs.ETAG] = f'"{value}"'
elif isinstance(value, ETag) and isinstance(value.value, str):
validate_etag_value(value.value)
hdr_value = f'W/"{value.value}"' if value.is_weak else f'"{value.value}"'
self._headers[hdrs.ETAG] = hdr_value
else:
raise ValueError(
f"Unsupported etag type: {type(value)}. "
f"etag must be str, ETag or None"
)
def _generate_content_type_header( def _generate_content_type_header(
self, CONTENT_TYPE: istr = hdrs.CONTENT_TYPE self, CONTENT_TYPE: istr = hdrs.CONTENT_TYPE
) -> None: ) -> None:
@ -420,7 +463,7 @@ class StreamResponse(BaseClass, HeadersMixin):
elif self._length_check: elif self._length_check:
writer.length = self.content_length writer.length = self.content_length
if writer.length is None: if writer.length is None:
if version >= HttpVersion11: if version >= HttpVersion11 and self.status != 204:
writer.enable_chunking() writer.enable_chunking()
headers[hdrs.TRANSFER_ENCODING] = "chunked" headers[hdrs.TRANSFER_ENCODING] = "chunked"
if hdrs.CONTENT_LENGTH in headers: if hdrs.CONTENT_LENGTH in headers:
@ -432,7 +475,8 @@ class StreamResponse(BaseClass, HeadersMixin):
elif version >= HttpVersion11 and self.status in (100, 101, 102, 103, 204): elif version >= HttpVersion11 and self.status in (100, 101, 102, 103, 204):
del headers[hdrs.CONTENT_LENGTH] del headers[hdrs.CONTENT_LENGTH]
headers.setdefault(hdrs.CONTENT_TYPE, "application/octet-stream") if self.status not in (204, 304):
headers.setdefault(hdrs.CONTENT_TYPE, "application/octet-stream")
headers.setdefault(hdrs.DATE, rfc822_formatted_time()) headers.setdefault(hdrs.DATE, rfc822_formatted_time())
headers.setdefault(hdrs.SERVER, SERVER_SOFTWARE) headers.setdefault(hdrs.SERVER, SERVER_SOFTWARE)
@ -545,7 +589,7 @@ class Response(StreamResponse):
raise ValueError("body and text are not allowed together") raise ValueError("body and text are not allowed together")
if headers is None: if headers is None:
real_headers = CIMultiDict() # type: CIMultiDict[str] real_headers: CIMultiDict[str] = CIMultiDict()
elif not isinstance(headers, CIMultiDict): elif not isinstance(headers, CIMultiDict):
real_headers = CIMultiDict(headers) real_headers = CIMultiDict(headers)
else: else:
@ -594,7 +638,7 @@ class Response(StreamResponse):
else: else:
self.body = body self.body = body
self._compressed_body = None # type: Optional[bytes] self._compressed_body: Optional[bytes] = None
self._zlib_executor_size = zlib_executor_size self._zlib_executor_size = zlib_executor_size
self._zlib_executor = zlib_executor self._zlib_executor = zlib_executor
@ -610,8 +654,8 @@ class Response(StreamResponse):
CONTENT_LENGTH: istr = hdrs.CONTENT_LENGTH, CONTENT_LENGTH: istr = hdrs.CONTENT_LENGTH,
) -> None: ) -> None:
if body is None: if body is None:
self._body = None # type: Optional[bytes] self._body: Optional[bytes] = None
self._body_payload = False # type: bool self._body_payload: bool = False
elif isinstance(body, (bytes, bytearray)): elif isinstance(body, (bytes, bytearray)):
self._body = body self._body = body
self._body_payload = False self._body_payload = False
@ -691,7 +735,7 @@ class Response(StreamResponse):
if self._eof_sent: if self._eof_sent:
return return
if self._compressed_body is None: if self._compressed_body is None:
body = self._body # type: Optional[Union[bytes, Payload]] body: Optional[Union[bytes, Payload]] = self._body
else: else:
body = self._compressed_body body = self._compressed_body
assert not data, f"data arg is not supported, got {data!r}" assert not data, f"data arg is not supported, got {data!r}"

Просмотреть файл

@ -3,7 +3,6 @@ import os # noqa
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
Awaitable,
Callable, Callable,
Dict, Dict,
Iterator, Iterator,
@ -19,7 +18,7 @@ import attr
from . import hdrs from . import hdrs
from .abc import AbstractView from .abc import AbstractView
from .typedefs import PathLike from .typedefs import Handler, PathLike
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from .web_request import Request from .web_request import Request
@ -53,8 +52,7 @@ class AbstractRouteDef(abc.ABC):
pass # pragma: no cover pass # pragma: no cover
_SimpleHandler = Callable[[Request], Awaitable[StreamResponse]] _HandlerType = Union[Type[AbstractView], Handler]
_HandlerType = Union[Type[AbstractView], _SimpleHandler]
@attr.s(auto_attribs=True, frozen=True, repr=False, slots=True) @attr.s(auto_attribs=True, frozen=True, repr=False, slots=True)
@ -158,10 +156,10 @@ class RouteTableDef(Sequence[AbstractRouteDef]):
"""Route definition table""" """Route definition table"""
def __init__(self) -> None: def __init__(self) -> None:
self._items = [] # type: List[AbstractRouteDef] self._items: List[AbstractRouteDef] = []
def __repr__(self) -> str: def __repr__(self) -> str:
return "<RouteTableDef count={}>".format(len(self._items)) return f"<RouteTableDef count={len(self._items)}>"
@overload @overload
def __getitem__(self, index: int) -> AbstractRouteDef: def __getitem__(self, index: int) -> AbstractRouteDef:
@ -171,7 +169,7 @@ class RouteTableDef(Sequence[AbstractRouteDef]):
def __getitem__(self, index: slice) -> List[AbstractRouteDef]: def __getitem__(self, index: slice) -> List[AbstractRouteDef]:
... ...
def __getitem__(self, index): # type: ignore def __getitem__(self, index): # type: ignore[no-untyped-def]
return self._items[index] return self._items[index]
def __iter__(self) -> Iterator[AbstractRouteDef]: def __iter__(self) -> Iterator[AbstractRouteDef]:
@ -208,6 +206,9 @@ class RouteTableDef(Sequence[AbstractRouteDef]):
def delete(self, path: str, **kwargs: Any) -> _Deco: def delete(self, path: str, **kwargs: Any) -> _Deco:
return self.route(hdrs.METH_DELETE, path, **kwargs) return self.route(hdrs.METH_DELETE, path, **kwargs)
def options(self, path: str, **kwargs: Any) -> _Deco:
return self.route(hdrs.METH_OPTIONS, path, **kwargs)
def view(self, path: str, **kwargs: Any) -> _Deco: def view(self, path: str, **kwargs: Any) -> _Deco:
return self.route(hdrs.METH_ANY, path, **kwargs) return self.route(hdrs.METH_ANY, path, **kwargs)

Просмотреть файл

@ -12,7 +12,7 @@ from .web_server import Server
try: try:
from ssl import SSLContext from ssl import SSLContext
except ImportError: except ImportError:
SSLContext = object # type: ignore SSLContext = object # type: ignore[misc,assignment]
__all__ = ( __all__ = (
@ -53,7 +53,7 @@ class BaseSite(ABC):
self._shutdown_timeout = shutdown_timeout self._shutdown_timeout = shutdown_timeout
self._ssl_context = ssl_context self._ssl_context = ssl_context
self._backlog = backlog self._backlog = backlog
self._server = None # type: Optional[asyncio.AbstractServer] self._server: Optional[asyncio.AbstractServer] = None
@property @property
@abstractmethod @abstractmethod
@ -171,7 +171,9 @@ class NamedPipeSite(BaseSite):
self, runner: "BaseRunner", path: str, *, shutdown_timeout: float = 60.0 self, runner: "BaseRunner", path: str, *, shutdown_timeout: float = 60.0
) -> None: ) -> None:
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
if not isinstance(loop, asyncio.ProactorEventLoop): # type: ignore if not isinstance(
loop, asyncio.ProactorEventLoop # type: ignore[attr-defined]
):
raise RuntimeError( raise RuntimeError(
"Named Pipes only available in proactor" "loop under windows" "Named Pipes only available in proactor" "loop under windows"
) )
@ -187,7 +189,9 @@ class NamedPipeSite(BaseSite):
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
server = self._runner.server server = self._runner.server
assert server is not None assert server is not None
_server = await loop.start_serving_pipe(server, self._path) # type: ignore _server = await loop.start_serving_pipe( # type: ignore[attr-defined]
server, self._path
)
self._server = _server[0] self._server = _server[0]
@ -238,8 +242,8 @@ class BaseRunner(ABC):
def __init__(self, *, handle_signals: bool = False, **kwargs: Any) -> None: def __init__(self, *, handle_signals: bool = False, **kwargs: Any) -> None:
self._handle_signals = handle_signals self._handle_signals = handle_signals
self._kwargs = kwargs self._kwargs = kwargs
self._server = None # type: Optional[Server] self._server: Optional[Server] = None
self._sites = [] # type: List[BaseSite] self._sites: List[BaseSite] = []
@property @property
def server(self) -> Optional[Server]: def server(self) -> Optional[Server]:
@ -247,7 +251,7 @@ class BaseRunner(ABC):
@property @property
def addresses(self) -> List[Any]: def addresses(self) -> List[Any]:
ret = [] # type: List[Any] ret: List[Any] = []
for site in self._sites: for site in self._sites:
server = site._server server = site._server
if server is not None: if server is not None:
@ -281,10 +285,6 @@ class BaseRunner(ABC):
async def cleanup(self) -> None: async def cleanup(self) -> None:
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
if self._server is None:
# no started yet, do nothing
return
# The loop over sites is intentional, an exception on gather() # The loop over sites is intentional, an exception on gather()
# leaves self._sites in unpredictable state. # leaves self._sites in unpredictable state.
# The loop guaranties that a site is either deleted on success or # The loop guaranties that a site is either deleted on success or

Просмотреть файл

@ -22,7 +22,7 @@ class Server:
**kwargs: Any **kwargs: Any
) -> None: ) -> None:
self._loop = get_running_loop(loop) self._loop = get_running_loop(loop)
self._connections = {} # type: Dict[RequestHandler, asyncio.Transport] self._connections: Dict[RequestHandler, asyncio.Transport] = {}
self._kwargs = kwargs self._kwargs = kwargs
self.requests_count = 0 self.requests_count = 0
self.request_handler = handler self.request_handler = handler

Просмотреть файл

@ -33,14 +33,13 @@ from typing import (
cast, cast,
) )
from typing_extensions import TypedDict from yarl import URL, __version__ as yarl_version # type: ignore[attr-defined]
from yarl import URL, __version__ as yarl_version # type: ignore
from . import hdrs from . import hdrs
from .abc import AbstractMatchInfo, AbstractRouter, AbstractView from .abc import AbstractMatchInfo, AbstractRouter, AbstractView
from .helpers import DEBUG from .helpers import DEBUG
from .http import HttpVersion11 from .http import HttpVersion11
from .typedefs import PathLike from .typedefs import Final, Handler, PathLike, TypedDict
from .web_exceptions import ( from .web_exceptions import (
HTTPException, HTTPException,
HTTPExpectationFailed, HTTPExpectationFailed,
@ -74,16 +73,19 @@ if TYPE_CHECKING: # pragma: no cover
else: else:
BaseDict = dict BaseDict = dict
YARL_VERSION = tuple(map(int, yarl_version.split(".")[:2])) YARL_VERSION: Final[Tuple[int, ...]] = tuple(map(int, yarl_version.split(".")[:2]))
HTTP_METHOD_RE = re.compile(r"^[0-9A-Za-z!#\$%&'\*\+\-\.\^_`\|~]+$") HTTP_METHOD_RE: Final[Pattern[str]] = re.compile(
ROUTE_RE = re.compile(r"(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})") r"^[0-9A-Za-z!#\$%&'\*\+\-\.\^_`\|~]+$"
PATH_SEP = re.escape("/") )
ROUTE_RE: Final[Pattern[str]] = re.compile(
r"(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})"
)
PATH_SEP: Final[str] = re.escape("/")
_WebHandler = Callable[[Request], Awaitable[StreamResponse]]
_ExpectHandler = Callable[[Request], Awaitable[None]] _ExpectHandler = Callable[[Request], Awaitable[None]]
_Resolve = Tuple[Optional[AbstractMatchInfo], Set[str]] _Resolve = Tuple[Optional["UrlMappingMatchInfo"], Set[str]]
class _InfoDict(TypedDict, total=False): class _InfoDict(TypedDict, total=False):
@ -128,16 +130,16 @@ class AbstractResource(Sized, Iterable["AbstractRoute"]):
@abc.abstractmethod # pragma: no branch @abc.abstractmethod # pragma: no branch
async def resolve(self, request: Request) -> _Resolve: async def resolve(self, request: Request) -> _Resolve:
"""Resolve resource """Resolve resource.
Return (UrlMappingMatchInfo, allowed_methods) pair.""" Return (UrlMappingMatchInfo, allowed_methods) pair.
"""
@abc.abstractmethod @abc.abstractmethod
def add_prefix(self, prefix: str) -> None: def add_prefix(self, prefix: str) -> None:
"""Add a prefix to processed URLs. """Add a prefix to processed URLs.
Required for subapplications support. Required for subapplications support.
""" """
@abc.abstractmethod @abc.abstractmethod
@ -156,7 +158,7 @@ class AbstractRoute(abc.ABC):
def __init__( def __init__(
self, self,
method: str, method: str,
handler: Union[_WebHandler, Type[AbstractView]], handler: Union[Handler, Type[AbstractView]],
*, *,
expect_handler: Optional[_ExpectHandler] = None, expect_handler: Optional[_ExpectHandler] = None,
resource: Optional[AbstractResource] = None, resource: Optional[AbstractResource] = None,
@ -193,7 +195,7 @@ class AbstractRoute(abc.ABC):
result = old_handler(request) result = old_handler(request)
if asyncio.iscoroutine(result): if asyncio.iscoroutine(result):
return await result return await result
return result # type: ignore return result # type: ignore[return-value]
old_handler = handler old_handler = handler
handler = handler_wrapper handler = handler_wrapper
@ -208,7 +210,7 @@ class AbstractRoute(abc.ABC):
return self._method return self._method
@property @property
def handler(self) -> _WebHandler: def handler(self) -> Handler:
return self._handler return self._handler
@property @property
@ -236,12 +238,12 @@ class UrlMappingMatchInfo(BaseDict, AbstractMatchInfo):
def __init__(self, match_dict: Dict[str, str], route: AbstractRoute): def __init__(self, match_dict: Dict[str, str], route: AbstractRoute):
super().__init__(match_dict) super().__init__(match_dict)
self._route = route self._route = route
self._apps = [] # type: List[Application] self._apps: List[Application] = []
self._current_app = None # type: Optional[Application] self._current_app: Optional[Application] = None
self._frozen = False self._frozen = False
@property @property
def handler(self) -> _WebHandler: def handler(self) -> Handler:
return self._route.handler return self._route.handler
@property @property
@ -256,7 +258,7 @@ class UrlMappingMatchInfo(BaseDict, AbstractMatchInfo):
def http_exception(self) -> Optional[HTTPException]: def http_exception(self) -> Optional[HTTPException]:
return None return None
def get_info(self) -> _InfoDict: # type: ignore def get_info(self) -> _InfoDict: # type: ignore[override]
return self._route.get_info() return self._route.get_info()
@property @property
@ -331,12 +333,12 @@ async def _default_expect_handler(request: Request) -> None:
class Resource(AbstractResource): class Resource(AbstractResource):
def __init__(self, *, name: Optional[str] = None) -> None: def __init__(self, *, name: Optional[str] = None) -> None:
super().__init__(name=name) super().__init__(name=name)
self._routes = [] # type: List[ResourceRoute] self._routes: List[ResourceRoute] = []
def add_route( def add_route(
self, self,
method: str, method: str,
handler: Union[Type[AbstractView], _WebHandler], handler: Union[Type[AbstractView], Handler],
*, *,
expect_handler: Optional[_ExpectHandler] = None, expect_handler: Optional[_ExpectHandler] = None,
) -> "ResourceRoute": ) -> "ResourceRoute":
@ -360,7 +362,7 @@ class Resource(AbstractResource):
self._routes.append(route) self._routes.append(route)
async def resolve(self, request: Request) -> _Resolve: async def resolve(self, request: Request) -> _Resolve:
allowed_methods = set() # type: Set[str] allowed_methods: Set[str] = set()
match_dict = self._match(request.rel_url.raw_path) match_dict = self._match(request.rel_url.raw_path)
if match_dict is None: if match_dict is None:
@ -421,7 +423,7 @@ class PlainResource(Resource):
def get_info(self) -> _InfoDict: def get_info(self) -> _InfoDict:
return {"path": self._path} return {"path": self._path}
def url_for(self) -> URL: # type: ignore def url_for(self) -> URL: # type: ignore[override]
return URL.build(path=self._path, encoded=True) return URL.build(path=self._path, encoded=True)
def __repr__(self) -> str: def __repr__(self) -> str:
@ -511,6 +513,7 @@ class PrefixResource(AbstractResource):
assert prefix in ("", "/") or not prefix.endswith("/"), prefix assert prefix in ("", "/") or not prefix.endswith("/"), prefix
super().__init__(name=name) super().__init__(name=name)
self._prefix = _requote_path(prefix) self._prefix = _requote_path(prefix)
self._prefix2 = self._prefix + "/"
@property @property
def canonical(self) -> str: def canonical(self) -> str:
@ -521,6 +524,7 @@ class PrefixResource(AbstractResource):
assert not prefix.endswith("/") assert not prefix.endswith("/")
assert len(prefix) > 1 assert len(prefix) > 1
self._prefix = prefix + self._prefix self._prefix = prefix + self._prefix
self._prefix2 = self._prefix + "/"
def raw_match(self, prefix: str) -> bool: def raw_match(self, prefix: str) -> bool:
return False return False
@ -569,7 +573,7 @@ class StaticResource(PrefixResource):
), ),
} }
def url_for( # type: ignore def url_for( # type: ignore[override]
self, self,
*, *,
filename: Union[str, Path], filename: Union[str, Path],
@ -621,7 +625,7 @@ class StaticResource(PrefixResource):
"routes": self._routes, "routes": self._routes,
} }
def set_options_route(self, handler: _WebHandler) -> None: def set_options_route(self, handler: Handler) -> None:
if "OPTIONS" in self._routes: if "OPTIONS" in self._routes:
raise RuntimeError("OPTIONS route was set already") raise RuntimeError("OPTIONS route was set already")
self._routes["OPTIONS"] = ResourceRoute( self._routes["OPTIONS"] = ResourceRoute(
@ -632,7 +636,7 @@ class StaticResource(PrefixResource):
path = request.rel_url.raw_path path = request.rel_url.raw_path
method = request.method method = request.method
allowed_methods = set(self._routes) allowed_methods = set(self._routes)
if not path.startswith(self._prefix): if not path.startswith(self._prefix2) and path != self._prefix:
return None, set() return None, set()
if method not in allowed_methods: if method not in allowed_methods:
@ -748,7 +752,7 @@ class PrefixedSubAppResource(PrefixResource):
async def resolve(self, request: Request) -> _Resolve: async def resolve(self, request: Request) -> _Resolve:
if ( if (
not request.url.raw_path.startswith(self._prefix + "/") not request.url.raw_path.startswith(self._prefix2)
and request.url.raw_path != self._prefix and request.url.raw_path != self._prefix
): ):
return None, set() return None, set()
@ -878,7 +882,7 @@ class ResourceRoute(AbstractRoute):
def __init__( def __init__(
self, self,
method: str, method: str,
handler: Union[_WebHandler, Type[AbstractView]], handler: Union[Handler, Type[AbstractView]],
resource: AbstractResource, resource: AbstractResource,
*, *,
expect_handler: Optional[_ExpectHandler] = None, expect_handler: Optional[_ExpectHandler] = None,
@ -942,7 +946,9 @@ class View(AbstractView):
async def _iter(self) -> StreamResponse: async def _iter(self) -> StreamResponse:
if self.request.method not in hdrs.METH_ALL: if self.request.method not in hdrs.METH_ALL:
self._raise_allowed_methods() self._raise_allowed_methods()
method = getattr(self, self.request.method.lower(), None) method: Callable[[], Awaitable[StreamResponse]] = getattr(
self, self.request.method.lower(), None
)
if method is None: if method is None:
self._raise_allowed_methods() self._raise_allowed_methods()
resp = await method() resp = await method()
@ -972,7 +978,7 @@ class ResourcesView(Sized, Iterable[AbstractResource], Container[AbstractResourc
class RoutesView(Sized, Iterable[AbstractRoute], Container[AbstractRoute]): class RoutesView(Sized, Iterable[AbstractRoute], Container[AbstractRoute]):
def __init__(self, resources: List[AbstractResource]): def __init__(self, resources: List[AbstractResource]):
self._routes = [] # type: List[AbstractRoute] self._routes: List[AbstractRoute] = []
for resource in resources: for resource in resources:
for route in resource: for route in resource:
self._routes.append(route) self._routes.append(route)
@ -993,12 +999,12 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self._resources = [] # type: List[AbstractResource] self._resources: List[AbstractResource] = []
self._named_resources = {} # type: Dict[str, AbstractResource] self._named_resources: Dict[str, AbstractResource] = {}
async def resolve(self, request: Request) -> AbstractMatchInfo: async def resolve(self, request: Request) -> UrlMappingMatchInfo:
method = request.method method = request.method
allowed_methods = set() # type: Set[str] allowed_methods: Set[str] = set()
for resource in self._resources: for resource in self._resources:
match_dict, allowed = await resource.resolve(request) match_dict, allowed = await resource.resolve(request)
@ -1006,11 +1012,11 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
return match_dict return match_dict
else: else:
allowed_methods |= allowed allowed_methods |= allowed
if allowed_methods:
return MatchInfoError(HTTPMethodNotAllowed(method, allowed_methods))
else: else:
if allowed_methods: return MatchInfoError(HTTPNotFound())
return MatchInfoError(HTTPMethodNotAllowed(method, allowed_methods))
else:
return MatchInfoError(HTTPNotFound())
def __iter__(self) -> Iterator[str]: def __iter__(self) -> Iterator[str]:
return iter(self._named_resources) return iter(self._named_resources)
@ -1086,7 +1092,7 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
self, self,
method: str, method: str,
path: str, path: str,
handler: Union[_WebHandler, Type[AbstractView]], handler: Union[Handler, Type[AbstractView]],
*, *,
name: Optional[str] = None, name: Optional[str] = None,
expect_handler: Optional[_ExpectHandler] = None, expect_handler: Optional[_ExpectHandler] = None,
@ -1128,72 +1134,53 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
self.register_resource(resource) self.register_resource(resource)
return resource return resource
def add_head(self, path: str, handler: _WebHandler, **kwargs: Any) -> AbstractRoute: def add_head(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute:
""" """Shortcut for add_route with method HEAD."""
Shortcut for add_route with method HEAD
"""
return self.add_route(hdrs.METH_HEAD, path, handler, **kwargs) return self.add_route(hdrs.METH_HEAD, path, handler, **kwargs)
def add_options( def add_options(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute:
self, path: str, handler: _WebHandler, **kwargs: Any """Shortcut for add_route with method OPTIONS."""
) -> AbstractRoute:
"""
Shortcut for add_route with method OPTIONS
"""
return self.add_route(hdrs.METH_OPTIONS, path, handler, **kwargs) return self.add_route(hdrs.METH_OPTIONS, path, handler, **kwargs)
def add_get( def add_get(
self, self,
path: str, path: str,
handler: _WebHandler, handler: Handler,
*, *,
name: Optional[str] = None, name: Optional[str] = None,
allow_head: bool = True, allow_head: bool = True,
**kwargs: Any, **kwargs: Any,
) -> AbstractRoute: ) -> AbstractRoute:
""" """Shortcut for add_route with method GET.
Shortcut for add_route with method GET, if allow_head is true another
route is added allowing head requests to the same endpoint If allow_head is true, another
route is added allowing head requests to the same endpoint.
""" """
resource = self.add_resource(path, name=name) resource = self.add_resource(path, name=name)
if allow_head: if allow_head:
resource.add_route(hdrs.METH_HEAD, handler, **kwargs) resource.add_route(hdrs.METH_HEAD, handler, **kwargs)
return resource.add_route(hdrs.METH_GET, handler, **kwargs) return resource.add_route(hdrs.METH_GET, handler, **kwargs)
def add_post(self, path: str, handler: _WebHandler, **kwargs: Any) -> AbstractRoute: def add_post(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute:
""" """Shortcut for add_route with method POST."""
Shortcut for add_route with method POST
"""
return self.add_route(hdrs.METH_POST, path, handler, **kwargs) return self.add_route(hdrs.METH_POST, path, handler, **kwargs)
def add_put(self, path: str, handler: _WebHandler, **kwargs: Any) -> AbstractRoute: def add_put(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute:
""" """Shortcut for add_route with method PUT."""
Shortcut for add_route with method PUT
"""
return self.add_route(hdrs.METH_PUT, path, handler, **kwargs) return self.add_route(hdrs.METH_PUT, path, handler, **kwargs)
def add_patch( def add_patch(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute:
self, path: str, handler: _WebHandler, **kwargs: Any """Shortcut for add_route with method PATCH."""
) -> AbstractRoute:
"""
Shortcut for add_route with method PATCH
"""
return self.add_route(hdrs.METH_PATCH, path, handler, **kwargs) return self.add_route(hdrs.METH_PATCH, path, handler, **kwargs)
def add_delete( def add_delete(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute:
self, path: str, handler: _WebHandler, **kwargs: Any """Shortcut for add_route with method DELETE."""
) -> AbstractRoute:
"""
Shortcut for add_route with method DELETE
"""
return self.add_route(hdrs.METH_DELETE, path, handler, **kwargs) return self.add_route(hdrs.METH_DELETE, path, handler, **kwargs)
def add_view( def add_view(
self, path: str, handler: Type[AbstractView], **kwargs: Any self, path: str, handler: Type[AbstractView], **kwargs: Any
) -> AbstractRoute: ) -> AbstractRoute:
""" """Shortcut for add_route with ANY methods for a class-based view."""
Shortcut for add_route with ANY methods for a class-based view
"""
return self.add_route(hdrs.METH_ANY, path, handler, **kwargs) return self.add_route(hdrs.METH_ANY, path, handler, **kwargs)
def freeze(self) -> None: def freeze(self) -> None:

74
third_party/python/aiohttp/aiohttp/web_ws.py поставляемый
Просмотреть файл

@ -3,7 +3,7 @@ import base64
import binascii import binascii
import hashlib import hashlib
import json import json
from typing import Any, Iterable, Optional, Tuple from typing import Any, Iterable, Optional, Tuple, cast
import async_timeout import async_timeout
import attr import attr
@ -19,6 +19,7 @@ from .http import (
WebSocketError, WebSocketError,
WebSocketReader, WebSocketReader,
WebSocketWriter, WebSocketWriter,
WSCloseCode,
WSMessage, WSMessage,
WSMsgType as WSMsgType, WSMsgType as WSMsgType,
ws_ext_gen, ws_ext_gen,
@ -26,7 +27,7 @@ from .http import (
) )
from .log import ws_logger from .log import ws_logger
from .streams import EofStream, FlowControlDataQueue from .streams import EofStream, FlowControlDataQueue
from .typedefs import JSONDecoder, JSONEncoder from .typedefs import Final, JSONDecoder, JSONEncoder
from .web_exceptions import HTTPBadRequest, HTTPException from .web_exceptions import HTTPBadRequest, HTTPException
from .web_request import BaseRequest from .web_request import BaseRequest
from .web_response import StreamResponse from .web_response import StreamResponse
@ -37,7 +38,7 @@ __all__ = (
"WSMsgType", "WSMsgType",
) )
THRESHOLD_CONNLOST_ACCESS = 5 THRESHOLD_CONNLOST_ACCESS: Final[int] = 5
@attr.s(auto_attribs=True, frozen=True, slots=True) @attr.s(auto_attribs=True, frozen=True, slots=True)
@ -67,25 +68,25 @@ class WebSocketResponse(StreamResponse):
) -> None: ) -> None:
super().__init__(status=101) super().__init__(status=101)
self._protocols = protocols self._protocols = protocols
self._ws_protocol = None # type: Optional[str] self._ws_protocol: Optional[str] = None
self._writer = None # type: Optional[WebSocketWriter] self._writer: Optional[WebSocketWriter] = None
self._reader = None # type: Optional[FlowControlDataQueue[WSMessage]] self._reader: Optional[FlowControlDataQueue[WSMessage]] = None
self._closed = False self._closed = False
self._closing = False self._closing = False
self._conn_lost = 0 self._conn_lost = 0
self._close_code = None # type: Optional[int] self._close_code: Optional[int] = None
self._loop = None # type: Optional[asyncio.AbstractEventLoop] self._loop: Optional[asyncio.AbstractEventLoop] = None
self._waiting = None # type: Optional[asyncio.Future[bool]] self._waiting: Optional[asyncio.Future[bool]] = None
self._exception = None # type: Optional[BaseException] self._exception: Optional[BaseException] = None
self._timeout = timeout self._timeout = timeout
self._receive_timeout = receive_timeout self._receive_timeout = receive_timeout
self._autoclose = autoclose self._autoclose = autoclose
self._autoping = autoping self._autoping = autoping
self._heartbeat = heartbeat self._heartbeat = heartbeat
self._heartbeat_cb = None self._heartbeat_cb: Optional[asyncio.TimerHandle] = None
if heartbeat is not None: if heartbeat is not None:
self._pong_heartbeat = heartbeat / 2.0 self._pong_heartbeat = heartbeat / 2.0
self._pong_response_cb = None self._pong_response_cb: Optional[asyncio.TimerHandle] = None
self._compress = compress self._compress = compress
self._max_msg_size = max_msg_size self._max_msg_size = max_msg_size
@ -102,16 +103,18 @@ class WebSocketResponse(StreamResponse):
self._cancel_heartbeat() self._cancel_heartbeat()
if self._heartbeat is not None: if self._heartbeat is not None:
assert self._loop is not None
self._heartbeat_cb = call_later( self._heartbeat_cb = call_later(
self._send_heartbeat, self._heartbeat, self._loop self._send_heartbeat, self._heartbeat, self._loop
) )
def _send_heartbeat(self) -> None: def _send_heartbeat(self) -> None:
if self._heartbeat is not None and not self._closed: if self._heartbeat is not None and not self._closed:
assert self._loop is not None
# fire-and-forget a task is not perfect but maybe ok for # fire-and-forget a task is not perfect but maybe ok for
# sending ping. Otherwise we need a long-living heartbeat # sending ping. Otherwise we need a long-living heartbeat
# task in the class. # task in the class.
self._loop.create_task(self._writer.ping()) # type: ignore self._loop.create_task(self._writer.ping()) # type: ignore[union-attr]
if self._pong_response_cb is not None: if self._pong_response_cb is not None:
self._pong_response_cb.cancel() self._pong_response_cb.cancel()
@ -122,7 +125,7 @@ class WebSocketResponse(StreamResponse):
def _pong_not_received(self) -> None: def _pong_not_received(self) -> None:
if self._req is not None and self._req.transport is not None: if self._req is not None and self._req.transport is not None:
self._closed = True self._closed = True
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = asyncio.TimeoutError() self._exception = asyncio.TimeoutError()
self._req.transport.close() self._req.transport.close()
@ -193,9 +196,9 @@ class WebSocketResponse(StreamResponse):
accept_val = base64.b64encode( accept_val = base64.b64encode(
hashlib.sha1(key.encode() + WS_KEY).digest() hashlib.sha1(key.encode() + WS_KEY).digest()
).decode() ).decode()
response_headers = CIMultiDict( # type: ignore response_headers = CIMultiDict(
{ {
hdrs.UPGRADE: "websocket", # type: ignore hdrs.UPGRADE: "websocket",
hdrs.CONNECTION: "upgrade", hdrs.CONNECTION: "upgrade",
hdrs.SEC_WEBSOCKET_ACCEPT: accept_val, hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,
} }
@ -216,7 +219,12 @@ class WebSocketResponse(StreamResponse):
if protocol: if protocol:
response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol
return (response_headers, protocol, compress, notakeover) # type: ignore return (
response_headers,
protocol,
compress,
notakeover,
) # type: ignore[return-value]
def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]: def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:
self._loop = request._loop self._loop = request._loop
@ -245,7 +253,7 @@ class WebSocketResponse(StreamResponse):
loop = self._loop loop = self._loop
assert loop is not None assert loop is not None
self._reader = FlowControlDataQueue(request._protocol, 2 ** 16, loop=loop) self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)
request.protocol.set_parser( request.protocol.set_parser(
WebSocketReader(self._reader, self._max_msg_size, compress=self._compress) WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)
) )
@ -315,7 +323,7 @@ class WebSocketResponse(StreamResponse):
) -> None: ) -> None:
await self.send_str(dumps(data), compress=compress) await self.send_str(dumps(data), compress=compress)
async def write_eof(self) -> None: # type: ignore async def write_eof(self) -> None: # type: ignore[override]
if self._eof_sent: if self._eof_sent:
return return
if self._payload_writer is None: if self._payload_writer is None:
@ -324,7 +332,7 @@ class WebSocketResponse(StreamResponse):
await self.close() await self.close()
self._eof_sent = True self._eof_sent = True
async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool:
if self._writer is None: if self._writer is None:
raise RuntimeError("Call .prepare() first") raise RuntimeError("Call .prepare() first")
@ -346,10 +354,10 @@ class WebSocketResponse(StreamResponse):
assert writer is not None assert writer is not None
await writer.drain() await writer.drain()
except (asyncio.CancelledError, asyncio.TimeoutError): except (asyncio.CancelledError, asyncio.TimeoutError):
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
raise raise
except Exception as exc: except Exception as exc:
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = exc self._exception = exc
return True return True
@ -359,13 +367,13 @@ class WebSocketResponse(StreamResponse):
reader = self._reader reader = self._reader
assert reader is not None assert reader is not None
try: try:
with async_timeout.timeout(self._timeout, loop=self._loop): async with async_timeout.timeout(self._timeout):
msg = await reader.read() msg = await reader.read()
except asyncio.CancelledError: except asyncio.CancelledError:
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
raise raise
except Exception as exc: except Exception as exc:
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = exc self._exception = exc
return True return True
@ -373,7 +381,7 @@ class WebSocketResponse(StreamResponse):
self._close_code = msg.data self._close_code = msg.data
return True return True
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = asyncio.TimeoutError() self._exception = asyncio.TimeoutError()
return True return True
else: else:
@ -400,9 +408,7 @@ class WebSocketResponse(StreamResponse):
try: try:
self._waiting = loop.create_future() self._waiting = loop.create_future()
try: try:
with async_timeout.timeout( async with async_timeout.timeout(timeout or self._receive_timeout):
timeout or self._receive_timeout, loop=self._loop
):
msg = await self._reader.read() msg = await self._reader.read()
self._reset_heartbeat() self._reset_heartbeat()
finally: finally:
@ -410,10 +416,10 @@ class WebSocketResponse(StreamResponse):
set_result(waiter, True) set_result(waiter, True)
self._waiting = None self._waiting = None
except (asyncio.CancelledError, asyncio.TimeoutError): except (asyncio.CancelledError, asyncio.TimeoutError):
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
raise raise
except EofStream: except EofStream:
self._close_code = 1000 self._close_code = WSCloseCode.OK
await self.close() await self.close()
return WSMessage(WSMsgType.CLOSED, None, None) return WSMessage(WSMsgType.CLOSED, None, None)
except WebSocketError as exc: except WebSocketError as exc:
@ -423,7 +429,7 @@ class WebSocketResponse(StreamResponse):
except Exception as exc: except Exception as exc:
self._exception = exc self._exception = exc
self._closing = True self._closing = True
self._close_code = 1006 self._close_code = WSCloseCode.ABNORMAL_CLOSURE
await self.close() await self.close()
return WSMessage(WSMsgType.ERROR, exc, None) return WSMessage(WSMsgType.ERROR, exc, None)
@ -450,13 +456,13 @@ class WebSocketResponse(StreamResponse):
msg.type, msg.data msg.type, msg.data
) )
) )
return msg.data return cast(str, msg.data)
async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:
msg = await self.receive(timeout) msg = await self.receive(timeout)
if msg.type != WSMsgType.BINARY: if msg.type != WSMsgType.BINARY:
raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes") raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes")
return msg.data return cast(bytes, msg.data)
async def receive_json( async def receive_json(
self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None

51
third_party/python/aiohttp/aiohttp/worker.py поставляемый
Просмотреть файл

@ -22,14 +22,14 @@ try:
SSLContext = ssl.SSLContext SSLContext = ssl.SSLContext
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
ssl = None # type: ignore ssl = None # type: ignore[assignment]
SSLContext = object # type: ignore SSLContext = object # type: ignore[misc,assignment]
__all__ = ("GunicornWebWorker", "GunicornUVLoopWebWorker", "GunicornTokioWebWorker") __all__ = ("GunicornWebWorker", "GunicornUVLoopWebWorker", "GunicornTokioWebWorker")
class GunicornWebWorker(base.Worker): class GunicornWebWorker(base.Worker): # type: ignore[misc,no-any-unimported]
DEFAULT_AIOHTTP_LOG_FORMAT = AccessLogger.LOG_FORMAT DEFAULT_AIOHTTP_LOG_FORMAT = AccessLogger.LOG_FORMAT
DEFAULT_GUNICORN_LOG_FORMAT = GunicornAccessLogFormat.default DEFAULT_GUNICORN_LOG_FORMAT = GunicornAccessLogFormat.default
@ -37,9 +37,9 @@ class GunicornWebWorker(base.Worker):
def __init__(self, *args: Any, **kw: Any) -> None: # pragma: no cover def __init__(self, *args: Any, **kw: Any) -> None: # pragma: no cover
super().__init__(*args, **kw) super().__init__(*args, **kw)
self._task = None # type: Optional[asyncio.Task[None]] self._task: Optional[asyncio.Task[None]] = None
self.exit_code = 0 self.exit_code = 0
self._notify_waiter = None # type: Optional[asyncio.Future[bool]] self._notify_waiter: Optional[asyncio.Future[bool]] = None
def init_process(self) -> None: def init_process(self) -> None:
# create new event_loop after fork # create new event_loop after fork
@ -57,30 +57,39 @@ class GunicornWebWorker(base.Worker):
self.loop.run_until_complete(self._task) self.loop.run_until_complete(self._task)
except Exception: except Exception:
self.log.exception("Exception in gunicorn worker") self.log.exception("Exception in gunicorn worker")
if sys.version_info >= (3, 6): self.loop.run_until_complete(self.loop.shutdown_asyncgens())
self.loop.run_until_complete(self.loop.shutdown_asyncgens())
self.loop.close() self.loop.close()
sys.exit(self.exit_code) sys.exit(self.exit_code)
async def _run(self) -> None: async def _run(self) -> None:
runner = None
if isinstance(self.wsgi, Application): if isinstance(self.wsgi, Application):
app = self.wsgi app = self.wsgi
elif asyncio.iscoroutinefunction(self.wsgi): elif asyncio.iscoroutinefunction(self.wsgi):
app = await self.wsgi() wsgi = await self.wsgi()
if isinstance(wsgi, web.AppRunner):
runner = wsgi
app = runner.app
else:
app = wsgi
else: else:
raise RuntimeError( raise RuntimeError(
"wsgi app should be either Application or " "wsgi app should be either Application or "
"async function returning Application, got {}".format(self.wsgi) "async function returning Application, got {}".format(self.wsgi)
) )
access_log = self.log.access_log if self.cfg.accesslog else None
runner = web.AppRunner( if runner is None:
app, access_log = self.log.access_log if self.cfg.accesslog else None
logger=self.log, runner = web.AppRunner(
keepalive_timeout=self.cfg.keepalive, app,
access_log=access_log, logger=self.log,
access_log_format=self._get_valid_log_format(self.cfg.access_log_format), keepalive_timeout=self.cfg.keepalive,
) access_log=access_log,
access_log_format=self._get_valid_log_format(
self.cfg.access_log_format
),
)
await runner.setup() await runner.setup()
ctx = self._create_ssl_context(self.cfg) if self.cfg.is_ssl else None ctx = self._create_ssl_context(self.cfg) if self.cfg.is_ssl else None
@ -101,7 +110,7 @@ class GunicornWebWorker(base.Worker):
# If our parent changed then we shut down. # If our parent changed then we shut down.
pid = os.getpid() pid = os.getpid()
try: try:
while self.alive: # type: ignore while self.alive: # type: ignore[has-type]
self.notify() self.notify()
cnt = server.requests_count cnt = server.requests_count
@ -171,6 +180,14 @@ class GunicornWebWorker(base.Worker):
# by interrupting system calls # by interrupting system calls
signal.siginterrupt(signal.SIGTERM, False) signal.siginterrupt(signal.SIGTERM, False)
signal.siginterrupt(signal.SIGUSR1, False) signal.siginterrupt(signal.SIGUSR1, False)
# Reset signals so Gunicorn doesn't swallow subprocess return codes
# See: https://github.com/aio-libs/aiohttp/issues/6130
if sys.version_info < (3, 8):
# Starting from Python 3.8,
# the default child watcher is ThreadedChildWatcher.
# The watcher doesn't depend on SIGCHLD signal,
# there is no need to reset it.
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
def handle_quit(self, sig: int, frame: FrameType) -> None: def handle_quit(self, sig: int, frame: FrameType) -> None:
self.alive = False self.alive = False

0
third_party/python/aiohttp/examples/__init__.py поставляемый Normal file
Просмотреть файл

Просмотреть файл

@ -43,8 +43,8 @@ async def listen_to_redis(app):
print("Redis connection closed.") print("Redis connection closed.")
async def start_background_tasks(app): async def start_background_tasks(app: web.Application) -> None:
app["redis_listener"] = app.loop.create_task(listen_to_redis(app)) app["redis_listener"] = asyncio.create_task(listen_to_redis(app))
async def cleanup_background_tasks(app): async def cleanup_background_tasks(app):

Просмотреть файл

@ -4,7 +4,7 @@ import asyncio
import aiohttp import aiohttp
async def fetch(session): async def fetch(session: aiohttp.ClientSession) -> None:
print("Query http://httpbin.org/get") print("Query http://httpbin.org/get")
async with session.get("http://httpbin.org/get") as resp: async with session.get("http://httpbin.org/get") as resp:
print(resp.status) print(resp.status)

Просмотреть файл

@ -44,8 +44,9 @@ async def start_client(loop, url):
break break
# send request # send request
async with aiohttp.ws_connect(url, autoclose=False, autoping=False) as ws: async with aiohttp.ClientSession() as session:
await dispatch() async with session.ws_connect(url, autoclose=False, autoping=False) as ws:
await dispatch()
ARGS = argparse.ArgumentParser( ARGS = argparse.ArgumentParser(

Просмотреть файл

@ -6,11 +6,12 @@ import ssl
import aiohttp import aiohttp
from aiohttp import web from aiohttp import web
from aiohttp.abc import AbstractResolver
from aiohttp.resolver import DefaultResolver from aiohttp.resolver import DefaultResolver
from aiohttp.test_utils import unused_port from aiohttp.test_utils import unused_port
class FakeResolver: class FakeResolver(AbstractResolver):
_LOCAL_HOST = {0: "127.0.0.1", socket.AF_INET: "127.0.0.1", socket.AF_INET6: "::1"} _LOCAL_HOST = {0: "127.0.0.1", socket.AF_INET: "127.0.0.1", socket.AF_INET6: "::1"}
def __init__(self, fakes, *, loop): def __init__(self, fakes, *, loop):
@ -34,6 +35,9 @@ class FakeResolver:
else: else:
return await self._resolver.resolve(host, port, family) return await self._resolver.resolve(host, port, family)
async def close(self) -> None:
self._resolver.close()
class FakeFacebook: class FakeFacebook:
def __init__(self, *, loop): def __init__(self, *, loop):
@ -45,7 +49,7 @@ class FakeFacebook:
web.get("/v2.7/me/friends", self.on_my_friends), web.get("/v2.7/me/friends", self.on_my_friends),
] ]
) )
self.runner = None self.runner = web.AppRunner(self.app)
here = pathlib.Path(__file__) here = pathlib.Path(__file__)
ssl_cert = here.parent / "server.crt" ssl_cert = here.parent / "server.crt"
ssl_key = here.parent / "server.key" ssl_key = here.parent / "server.key"

Просмотреть файл

@ -1,108 +0,0 @@
#!/usr/bin/env python3
import asyncio
import logging
import re
import signal
import sys
import urllib.parse
import aiohttp
class Crawler:
def __init__(self, rooturl, loop, maxtasks=100):
self.rooturl = rooturl
self.loop = loop
self.todo = set()
self.busy = set()
self.done = {}
self.tasks = set()
self.sem = asyncio.Semaphore(maxtasks, loop=loop)
# connector stores cookies between requests and uses connection pool
self.session = aiohttp.ClientSession(loop=loop)
async def run(self):
t = asyncio.ensure_future(self.addurls([(self.rooturl, "")]), loop=self.loop)
await asyncio.sleep(1, loop=self.loop)
while self.busy:
await asyncio.sleep(1, loop=self.loop)
await t
await self.session.close()
self.loop.stop()
async def addurls(self, urls):
for url, parenturl in urls:
url = urllib.parse.urljoin(parenturl, url)
url, frag = urllib.parse.urldefrag(url)
if (
url.startswith(self.rooturl)
and url not in self.busy
and url not in self.done
and url not in self.todo
):
self.todo.add(url)
await self.sem.acquire()
task = asyncio.ensure_future(self.process(url), loop=self.loop)
task.add_done_callback(lambda t: self.sem.release())
task.add_done_callback(self.tasks.remove)
self.tasks.add(task)
async def process(self, url):
print("processing:", url)
self.todo.remove(url)
self.busy.add(url)
try:
resp = await self.session.get(url)
except Exception as exc:
print("...", url, "has error", repr(str(exc)))
self.done[url] = False
else:
if resp.status == 200 and ("text/html" in resp.headers.get("content-type")):
data = (await resp.read()).decode("utf-8", "replace")
urls = re.findall(r'(?i)href=["\']?([^\s"\'<>]+)', data)
asyncio.Task(self.addurls([(u, url) for u in urls]))
resp.close()
self.done[url] = True
self.busy.remove(url)
print(
len(self.done),
"completed tasks,",
len(self.tasks),
"still pending, todo",
len(self.todo),
)
def main():
loop = asyncio.get_event_loop()
c = Crawler(sys.argv[1], loop)
asyncio.ensure_future(c.run(), loop=loop)
try:
loop.add_signal_handler(signal.SIGINT, loop.stop)
except RuntimeError:
pass
loop.run_forever()
print("todo:", len(c.todo))
print("busy:", len(c.busy))
print("done:", len(c.done), "; ok:", sum(c.done.values()))
print("tasks:", len(c.tasks))
if __name__ == "__main__":
if "--iocp" in sys.argv:
from asyncio import events, windows_events
sys.argv.remove("--iocp")
logging.info("using iocp")
el = windows_events.ProactorEventLoop()
events.set_event_loop(el)
main()

Просмотреть файл

@ -1,178 +0,0 @@
#!/usr/bin/env python3
"""Simple server written using an event loop."""
import argparse
import asyncio
import logging
import os
import sys
import aiohttp
import aiohttp.server
try:
import ssl
except ImportError: # pragma: no cover
ssl = None
class HttpRequestHandler(aiohttp.server.ServerHttpProtocol):
async def handle_request(self, message, payload):
print(
"method = {!r}; path = {!r}; version = {!r}".format(
message.method, message.path, message.version
)
)
path = message.path
if not (path.isprintable() and path.startswith("/")) or "/." in path:
print("bad path", repr(path))
path = None
else:
path = "." + path
if not os.path.exists(path):
print("no file", repr(path))
path = None
else:
isdir = os.path.isdir(path)
if not path:
raise aiohttp.HttpProcessingError(code=404)
for hdr, val in message.headers.items():
print(hdr, val)
if isdir and not path.endswith("/"):
path = path + "/"
raise aiohttp.HttpProcessingError(
code=302, headers=(("URI", path), ("Location", path))
)
response = aiohttp.Response(self.writer, 200, http_version=message.version)
response.add_header("Transfer-Encoding", "chunked")
# content encoding
accept_encoding = message.headers.get("accept-encoding", "").lower()
if "deflate" in accept_encoding:
response.add_header("Content-Encoding", "deflate")
response.add_compression_filter("deflate")
elif "gzip" in accept_encoding:
response.add_header("Content-Encoding", "gzip")
response.add_compression_filter("gzip")
response.add_chunking_filter(1025)
if isdir:
response.add_header("Content-type", "text/html")
response.send_headers()
response.write(b"<ul>\r\n")
for name in sorted(os.listdir(path)):
if name.isprintable() and not name.startswith("."):
try:
bname = name.encode("ascii")
except UnicodeError:
pass
else:
if os.path.isdir(os.path.join(path, name)):
response.write(
b'<li><a href="'
+ bname
+ b'/">'
+ bname
+ b"/</a></li>\r\n"
)
else:
response.write(
b'<li><a href="'
+ bname
+ b'">'
+ bname
+ b"</a></li>\r\n"
)
response.write(b"</ul>")
else:
response.add_header("Content-type", "text/plain")
response.send_headers()
try:
with open(path, "rb") as fp:
chunk = fp.read(8192)
while chunk:
response.write(chunk)
chunk = fp.read(8192)
except OSError:
response.write(b"Cannot open")
await response.write_eof()
if response.keep_alive():
self.keep_alive(True)
ARGS = argparse.ArgumentParser(description="Run simple HTTP server.")
ARGS.add_argument(
"--host", action="store", dest="host", default="127.0.0.1", help="Host name"
)
ARGS.add_argument(
"--port", action="store", dest="port", default=8080, type=int, help="Port number"
)
# make iocp and ssl mutually exclusive because ProactorEventLoop is
# incompatible with SSL
group = ARGS.add_mutually_exclusive_group()
group.add_argument(
"--iocp", action="store_true", dest="iocp", help="Windows IOCP event loop"
)
group.add_argument("--ssl", action="store_true", dest="ssl", help="Run ssl mode.")
ARGS.add_argument("--sslcert", action="store", dest="certfile", help="SSL cert file.")
ARGS.add_argument("--sslkey", action="store", dest="keyfile", help="SSL key file.")
def main():
args = ARGS.parse_args()
if ":" in args.host:
args.host, port = args.host.split(":", 1)
args.port = int(port)
if args.iocp:
from asyncio import windows_events
sys.argv.remove("--iocp")
logging.info("using iocp")
el = windows_events.ProactorEventLoop()
asyncio.set_event_loop(el)
if args.ssl:
here = os.path.join(os.path.dirname(__file__), "tests")
if args.certfile:
certfile = args.certfile or os.path.join(here, "sample.crt")
keyfile = args.keyfile or os.path.join(here, "sample.key")
else:
certfile = os.path.join(here, "sample.crt")
keyfile = os.path.join(here, "sample.key")
sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
sslcontext.load_cert_chain(certfile, keyfile)
else:
sslcontext = None
loop = asyncio.get_event_loop()
f = loop.create_server(
lambda: HttpRequestHandler(debug=True, keep_alive=75),
args.host,
args.port,
ssl=sslcontext,
)
svr = loop.run_until_complete(f)
socks = svr.sockets
print("serving on", socks[0].getsockname())
try:
loop.run_forever()
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()

Просмотреть файл

@ -1,172 +0,0 @@
#!/usr/bin/env python3
"""Protocol parser example."""
import argparse
import asyncio
import collections
import aiohttp
try:
import signal
except ImportError:
signal = None
MSG_TEXT = b"text:"
MSG_PING = b"ping:"
MSG_PONG = b"pong:"
MSG_STOP = b"stop:"
Message = collections.namedtuple("Message", ("tp", "data"))
def my_protocol_parser(out, buf):
"""Parser is used with StreamParser for incremental protocol parsing.
Parser is a generator function, but it is not a coroutine. Usually
parsers are implemented as a state machine.
more details in asyncio/parsers.py
existing parsers:
* HTTP protocol parsers asyncio/http/protocol.py
* websocket parser asyncio/http/websocket.py
"""
while True:
tp = yield from buf.read(5)
if tp in (MSG_PING, MSG_PONG):
# skip line
yield from buf.skipuntil(b"\r\n")
out.feed_data(Message(tp, None))
elif tp == MSG_STOP:
out.feed_data(Message(tp, None))
elif tp == MSG_TEXT:
# read text
text = yield from buf.readuntil(b"\r\n")
out.feed_data(Message(tp, text.strip().decode("utf-8")))
else:
raise ValueError("Unknown protocol prefix.")
class MyProtocolWriter:
def __init__(self, transport):
self.transport = transport
def ping(self):
self.transport.write(b"ping:\r\n")
def pong(self):
self.transport.write(b"pong:\r\n")
def stop(self):
self.transport.write(b"stop:\r\n")
def send_text(self, text):
self.transport.write(f"text:{text.strip()}\r\n".encode("utf-8"))
class EchoServer(asyncio.Protocol):
def connection_made(self, transport):
print("Connection made")
self.transport = transport
self.stream = aiohttp.StreamParser()
asyncio.Task(self.dispatch())
def data_received(self, data):
self.stream.feed_data(data)
def eof_received(self):
self.stream.feed_eof()
def connection_lost(self, exc):
print("Connection lost")
async def dispatch(self):
reader = self.stream.set_parser(my_protocol_parser)
writer = MyProtocolWriter(self.transport)
while True:
try:
msg = await reader.read()
except aiohttp.ConnectionError:
# client has been disconnected
break
print(f"Message received: {msg}")
if msg.type == MSG_PING:
writer.pong()
elif msg.type == MSG_TEXT:
writer.send_text("Re: " + msg.data)
elif msg.type == MSG_STOP:
self.transport.close()
break
async def start_client(loop, host, port):
transport, stream = await loop.create_connection(aiohttp.StreamProtocol, host, port)
reader = stream.reader.set_parser(my_protocol_parser)
writer = MyProtocolWriter(transport)
writer.ping()
message = "This is the message. It will be echoed."
while True:
try:
msg = await reader.read()
except aiohttp.ConnectionError:
print("Server has been disconnected.")
break
print(f"Message received: {msg}")
if msg.type == MSG_PONG:
writer.send_text(message)
print("data sent:", message)
elif msg.type == MSG_TEXT:
writer.stop()
print("stop sent")
break
transport.close()
def start_server(loop, host, port):
f = loop.create_server(EchoServer, host, port)
srv = loop.run_until_complete(f)
x = srv.sockets[0]
print("serving on", x.getsockname())
loop.run_forever()
ARGS = argparse.ArgumentParser(description="Protocol parser example.")
ARGS.add_argument(
"--server", action="store_true", dest="server", default=False, help="Run tcp server"
)
ARGS.add_argument(
"--client", action="store_true", dest="client", default=False, help="Run tcp client"
)
ARGS.add_argument(
"--host", action="store", dest="host", default="127.0.0.1", help="Host name"
)
ARGS.add_argument(
"--port", action="store", dest="port", default=9999, type=int, help="Port number"
)
if __name__ == "__main__":
args = ARGS.parse_args()
if ":" in args.host:
args.host, port = args.host.split(":", 1)
args.port = int(port)
if (not (args.server or args.client)) or (args.server and args.client):
print("Please specify --server or --client\n")
ARGS.print_help()
else:
loop = asyncio.get_event_loop()
if signal is not None:
loop.add_signal_handler(signal.SIGINT, loop.stop)
if args.server:
start_server(loop, args.host, args.port)
else:
loop.run_until_complete(start_client(loop, args.host, args.port))

Просмотреть файл

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Example for aiohttp.web class based views """Example for aiohttp.web class based views."""
"""
import functools import functools
@ -14,7 +13,7 @@ class MyView(web.View):
return web.json_response( return web.json_response(
{ {
"method": "get", "method": "get",
"args": dict(self.request.GET), "args": dict(self.request.query),
"headers": dict(self.request.headers), "headers": dict(self.request.headers),
}, },
dumps=functools.partial(json.dumps, indent=4), dumps=functools.partial(json.dumps, indent=4),
@ -25,7 +24,7 @@ class MyView(web.View):
return web.json_response( return web.json_response(
{ {
"method": "post", "method": "post",
"args": dict(self.request.GET), "args": dict(self.request.query),
"data": dict(data), "data": dict(data),
"headers": dict(self.request.headers), "headers": dict(self.request.headers),
}, },

Просмотреть файл

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Example for aiohttp.web basic server with cookies. """Example for aiohttp.web basic server with cookies."""
"""
from pprint import pformat from pprint import pformat
from typing import NoReturn
from aiohttp import web from aiohttp import web
@ -22,20 +22,20 @@ async def root(request):
return resp return resp
async def login(request): async def login(request: web.Request) -> NoReturn:
resp = web.HTTPFound(location="/") exc = web.HTTPFound(location="/")
resp.set_cookie("AUTH", "secret") exc.set_cookie("AUTH", "secret")
return resp raise exc
async def logout(request): async def logout(request: web.Request) -> NoReturn:
resp = web.HTTPFound(location="/") exc = web.HTTPFound(location="/")
resp.del_cookie("AUTH") exc.del_cookie("AUTH")
return resp raise exc
def init(loop): def init():
app = web.Application(loop=loop) app = web.Application()
app.router.add_get("/", root) app.router.add_get("/", root)
app.router.add_get("/login", login) app.router.add_get("/login", login)
app.router.add_get("/logout", logout) app.router.add_get("/logout", logout)

Просмотреть файл

@ -1,9 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """Example for rewriting response headers by middleware."""
Example for rewriting response headers by middleware.
"""
from aiohttp import web from aiohttp import web
from aiohttp.typedefs import Handler
async def handler(request): async def handler(request):
@ -11,7 +10,7 @@ async def handler(request):
@web.middleware @web.middleware
async def middleware(request, handler): async def middleware(request: web.Request, handler: Handler) -> web.StreamResponse:
try: try:
response = await handler(request) response = await handler(request)
except web.HTTPException as exc: except web.HTTPException as exc:

Просмотреть файл

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Example for aiohttp.web basic server """Example for aiohttp.web basic server."""
"""
import textwrap import textwrap

Просмотреть файл

@ -1,7 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Example for aiohttp.web basic server """Example for aiohttp.web basic server with decorator definition for routes."""
with decorator definition for routes
"""
import textwrap import textwrap

Просмотреть файл

@ -1,7 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Example for aiohttp.web basic server """Example for aiohttp.web basic server with table definition for routes."""
with table definition for routes
"""
import textwrap import textwrap

Просмотреть файл

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Example for aiohttp.web websocket server """Example for aiohttp.web websocket server."""
"""
import os import os

12
third_party/python/aiohttp/pyproject.toml поставляемый
Просмотреть файл

@ -1,3 +1,9 @@
[build-system]
requires = [
"setuptools >= 46.4.0",
]
build-backend = "setuptools.build_meta"
[tool.towncrier] [tool.towncrier]
package = "aiohttp" package = "aiohttp"
filename = "CHANGES.rst" filename = "CHANGES.rst"
@ -5,3 +11,9 @@ directory = "CHANGES/"
title_format = "{version} ({project_date})" title_format = "{version} ({project_date})"
template = "CHANGES/.TEMPLATE.rst" template = "CHANGES/.TEMPLATE.rst"
issue_format = "`#{issue} <https://github.com/aio-libs/aiohttp/issues/{issue}>`_" issue_format = "`#{issue} <https://github.com/aio-libs/aiohttp/issues/{issue}>`_"
[tool.cibuildwheel]
test-command = ""
# don't build PyPy wheels, install from source instead
skip = "pp*"

144
third_party/python/aiohttp/setup.cfg поставляемый
Просмотреть файл

@ -1,8 +1,81 @@
[aliases]
test = pytest
[metadata] [metadata]
license_file = LICENSE.txt name = aiohttp
version = attr: aiohttp.__version__
url = https://github.com/aio-libs/aiohttp
project_urls =
Chat: Matrix = https://matrix.to/#/#aio-libs:matrix.org
Chat: Matrix Space = https://matrix.to/#/#aio-libs-space:matrix.org
CI: GitHub Actions = https://github.com/aio-libs/aiohttp/actions?query=workflow%%3ACI
Coverage: codecov = https://codecov.io/github/aio-libs/aiohttp
Docs: Changelog = https://docs.aiohttp.org/en/stable/changes.html
Docs: RTD = https://docs.aiohttp.org
GitHub: issues = https://github.com/aio-libs/aiohttp/issues
GitHub: repo = https://github.com/aio-libs/aiohttp
description = Async http client/server framework (asyncio)
long_description = file: README.rst
long_description_content_type = text/x-rst
maintainer = aiohttp team <team@aiohttp.org>
maintainer_email = team@aiohttp.org
license = Apache 2
license_files = LICENSE.txt
classifiers =
Development Status :: 5 - Production/Stable
Framework :: AsyncIO
Intended Audience :: Developers
License :: OSI Approved :: Apache Software License
Operating System :: POSIX
Operating System :: MacOS :: MacOS X
Operating System :: Microsoft :: Windows
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Topic :: Internet :: WWW/HTTP
[options]
python_requires = >=3.6
packages = find:
zip_safe = False
include_package_data = True
install_requires =
attrs >= 17.3.0
charset-normalizer >=2.0, < 4.0
multidict >=4.5, < 7.0
async_timeout >= 4.0.0a3, < 5.0
asynctest == 0.13.0; python_version<"3.8"
yarl >= 1.0, < 2.0
idna-ssl >= 1.0; python_version<"3.7"
typing_extensions >= 3.7.4; python_version<"3.8"
frozenlist >= 1.1.1
aiosignal >= 1.1.2
[options.exclude_package_data]
* =
*.c
*.h
[options.extras_require]
speedups =
aiodns
Brotli
cchardet; python_version < "3.10"
[options.packages.find]
exclude =
examples
[options.package_data]
* =
*.so
[pep8] [pep8]
max-line-length = 79 max-line-length = 79
@ -11,7 +84,7 @@ max-line-length = 79
zip_ok = false zip_ok = false
[flake8] [flake8]
ignore = N801,N802,N803,E203,E226,E305,W504,E252,E301,E302,E704,W503,W504,F811 ignore = N801,N802,N803,E203,E226,E305,W504,E252,E301,E302,E704,W503,W504,F811,D1,D4
max-line-length = 88 max-line-length = 88
[isort] [isort]
@ -34,10 +107,27 @@ source = aiohttp, tests
omit = site-packages omit = site-packages
[tool:pytest] [tool:pytest]
addopts = --cov=aiohttp -v -rxXs --durations 10 addopts =
--durations=10
-v
-ra
--showlocals
--cov=aiohttp
--cov=tests/
filterwarnings = filterwarnings =
error error
ignore:module 'ssl' has no attribute 'OP_NO_COMPRESSION'. The Python interpreter is compiled against OpenSSL < 1.0.0. Ref. https.//docs.python.org/3/library/ssl.html#ssl.OP_NO_COMPRESSION:UserWarning ignore:module 'ssl' has no attribute 'OP_NO_COMPRESSION'. The Python interpreter is compiled against OpenSSL < 1.0.0. Ref. https.//docs.python.org/3/library/ssl.html#ssl.OP_NO_COMPRESSION:UserWarning
ignore:unclosed transport <asyncio.sslproto._SSLProtocolTransport object.*:ResourceWarning
ignore:unclosed transport <_ProactorSocketTransport closing fd=-1>:ResourceWarning
ignore:Unclosed client session <aiohttp.client.ClientSession object at 0x:ResourceWarning
ignore:The loop argument is deprecated:DeprecationWarning:asyncio
ignore:Creating a LegacyVersion has been deprecated and will be removed in the next major release:DeprecationWarning::
ignore:module 'sre_constants' is deprecated:DeprecationWarning:pkg_resources._vendor.pyparsing
ignore:path is deprecated. Use files.. instead. Refer to https.//importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.:DeprecationWarning:certifi.core
junit_suite_name = aiohttp_test_suite junit_suite_name = aiohttp_test_suite
norecursedirs = dist docs build .tox .eggs norecursedirs = dist docs build .tox .eggs
minversion = 3.8.2 minversion = 3.8.2
@ -45,48 +135,6 @@ testpaths = tests/
junit_family = xunit2 junit_family = xunit2
xfail_strict = true xfail_strict = true
[mypy]
follow_imports = silent
strict_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
check_untyped_defs = True
disallow_any_generics = True
disallow_untyped_defs = True
[mypy-pytest]
ignore_missing_imports = true
[mypy-uvloop]
ignore_missing_imports = true
[mypy-tokio]
ignore_missing_imports = true
[mypy-async_generator]
ignore_missing_imports = true
[mypy-aiodns]
ignore_missing_imports = true
[mypy-gunicorn.config]
ignore_missing_imports = true
[mypy-gunicorn.workers]
ignore_missing_imports = true
[mypy-brotli]
ignore_missing_imports = true
[mypy-chardet]
ignore_missing_imports = true
[mypy-cchardet]
ignore_missing_imports = true
[mypy-idna_ssl]
ignore_missing_imports = true
[egg_info] [egg_info]
tag_build = tag_build =
tag_date = 0 tag_date = 0

151
third_party/python/aiohttp/setup.py поставляемый
Просмотреть файл

@ -1,18 +1,23 @@
import os
import pathlib import pathlib
import re
import sys import sys
from distutils.command.build_ext import build_ext
from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError
from setuptools import Extension, setup from setuptools import Extension, setup
if sys.version_info < (3, 6): if sys.version_info < (3, 6):
raise RuntimeError("aiohttp 3.7+ requires Python 3.6+") raise RuntimeError("aiohttp 3.x requires Python 3.6+")
here = pathlib.Path(__file__).parent
if (here / ".git").exists() and not (here / "vendor/http-parser/README.md").exists(): NO_EXTENSIONS: bool = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS"))
HERE = pathlib.Path(__file__).parent
IS_GIT_REPO = (HERE / ".git").exists()
if sys.implementation.name != "cpython":
NO_EXTENSIONS = True
if IS_GIT_REPO and not (HERE / "vendor/llhttp/README.md").exists():
print("Install submodules when building from git clone", file=sys.stderr) print("Install submodules when building from git clone", file=sys.stderr)
print("Hint:", file=sys.stderr) print("Hint:", file=sys.stderr)
print(" git submodule update --init", file=sys.stderr) print(" git submodule update --init", file=sys.stderr)
@ -27,133 +32,23 @@ extensions = [
"aiohttp._http_parser", "aiohttp._http_parser",
[ [
"aiohttp/_http_parser.c", "aiohttp/_http_parser.c",
"vendor/http-parser/http_parser.c",
"aiohttp/_find_header.c", "aiohttp/_find_header.c",
"vendor/llhttp/build/c/llhttp.c",
"vendor/llhttp/src/native/api.c",
"vendor/llhttp/src/native/http.c",
], ],
define_macros=[("HTTP_PARSER_STRICT", 0)], define_macros=[("LLHTTP_STRICT_MODE", 0)],
include_dirs=["vendor/llhttp/build"],
), ),
Extension("aiohttp._frozenlist", ["aiohttp/_frozenlist.c"]),
Extension("aiohttp._helpers", ["aiohttp/_helpers.c"]), Extension("aiohttp._helpers", ["aiohttp/_helpers.c"]),
Extension("aiohttp._http_writer", ["aiohttp/_http_writer.c"]), Extension("aiohttp._http_writer", ["aiohttp/_http_writer.c"]),
] ]
class BuildFailed(Exception): build_type = "Pure" if NO_EXTENSIONS else "Accelerated"
pass setup_kwargs = {} if NO_EXTENSIONS else {"ext_modules": extensions}
print("*********************", file=sys.stderr)
class ve_build_ext(build_ext): print("* {build_type} build *".format_map(locals()), file=sys.stderr)
# This class allows C extension building to fail. print("*********************", file=sys.stderr)
setup(**setup_kwargs)
def run(self):
try:
build_ext.run(self)
except (DistutilsPlatformError, FileNotFoundError):
raise BuildFailed()
def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except (CCompilerError, DistutilsExecError, DistutilsPlatformError, ValueError):
raise BuildFailed()
txt = (here / "aiohttp" / "__init__.py").read_text("utf-8")
try:
version = re.findall(r'^__version__ = "([^"]+)"\r?$', txt, re.M)[0]
except IndexError:
raise RuntimeError("Unable to determine version.")
install_requires = [
"attrs>=17.3.0",
"chardet>=2.0,<5.0",
"multidict>=4.5,<7.0",
"async_timeout>=3.0,<4.0",
"yarl>=1.0,<2.0",
'idna-ssl>=1.0; python_version<"3.7"',
"typing_extensions>=3.6.5",
]
def read(f):
return (here / f).read_text("utf-8").strip()
NEEDS_PYTEST = {"pytest", "test"}.intersection(sys.argv)
pytest_runner = ["pytest-runner"] if NEEDS_PYTEST else []
tests_require = [
"pytest",
"gunicorn",
"pytest-timeout",
"async-generator",
"pytest-xdist",
]
args = dict(
name="aiohttp",
version=version,
description="Async http client/server framework (asyncio)",
long_description="\n\n".join((read("README.rst"), read("CHANGES.rst"))),
classifiers=[
"License :: OSI Approved :: Apache Software License",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Development Status :: 5 - Production/Stable",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
"Topic :: Internet :: WWW/HTTP",
"Framework :: AsyncIO",
],
author="Nikolay Kim",
author_email="fafhrd91@gmail.com",
maintainer=", ".join(
(
"Nikolay Kim <fafhrd91@gmail.com>",
"Andrew Svetlov <andrew.svetlov@gmail.com>",
)
),
maintainer_email="aio-libs@googlegroups.com",
url="https://github.com/aio-libs/aiohttp",
project_urls={
"Chat: Gitter": "https://gitter.im/aio-libs/Lobby",
"CI: Azure Pipelines": "https://dev.azure.com/aio-libs/aiohttp/_build",
"Coverage: codecov": "https://codecov.io/github/aio-libs/aiohttp",
"Docs: RTD": "https://docs.aiohttp.org",
"GitHub: issues": "https://github.com/aio-libs/aiohttp/issues",
"GitHub: repo": "https://github.com/aio-libs/aiohttp",
},
license="Apache 2",
packages=["aiohttp"],
python_requires=">=3.6",
install_requires=install_requires,
extras_require={
"speedups": [
"aiodns",
"brotlipy",
"cchardet",
],
},
tests_require=tests_require,
setup_requires=pytest_runner,
include_package_data=True,
ext_modules=extensions,
cmdclass=dict(build_ext=ve_build_ext),
)
try:
setup(**args)
except BuildFailed:
print("************************************************************")
print("Cannot compile C accelerator module, use pure python version")
print("************************************************************")
del args["ext_modules"]
del args["cmdclass"]
setup(**args)

23
third_party/python/aiohttp/vendor/README.rst поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
LLHTTP
------
When building aiohttp from source, there is a pure Python parser used by default.
For better performance, you may want to build the higher performance C parser.
To build this ``llhttp`` parser, first get/update the submodules (to update to a
newer release, add ``--remote`` and check the branch in ``.gitmodules``)::
git submodule update --init --recursive
Then build ``llhttp``::
cd vendor/llhttp/
npm install
make
Then build our parser::
cd -
make cythonize
Then you can build or install it with ``python -m build`` or ``pip install -e .``

Просмотреть файл

@ -1,30 +0,0 @@
/out/
core
tags
*.o
test
test_g
test_fast
bench
url_parser
parsertrace
parsertrace_g
*.mk
*.Makefile
*.so.*
*.exe.*
*.exe
*.a
# Visual Studio uglies
*.suo
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
*.opensdf
*.ncrunchsolution*
*.sdf
*.vsp
*.psess

Просмотреть файл

@ -1,8 +0,0 @@
# update AUTHORS with:
# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS
Ryan Dahl <ry@tinyclouds.org>
Salman Haq <salman.haq@asti-usa.com>
Simon Zimmermann <simonz05@gmail.com>
Thomas LE ROUX <thomas@november-eleven.fr> LE ROUX Thomas <thomas@procheo.fr>
Thomas LE ROUX <thomas@november-eleven.fr> Thomas LE ROUX <thomas@procheo.fr>
Fedor Indutny <fedor@indutny.com>

Просмотреть файл

@ -1,13 +0,0 @@
language: c
compiler:
- clang
- gcc
script:
- "make"
notifications:
email: false
irc:
- "irc.freenode.net#node-ci"

Просмотреть файл

@ -1,68 +0,0 @@
# Authors ordered by first contribution.
Ryan Dahl <ry@tinyclouds.org>
Jeremy Hinegardner <jeremy@hinegardner.org>
Sergey Shepelev <temotor@gmail.com>
Joe Damato <ice799@gmail.com>
tomika <tomika_nospam@freemail.hu>
Phoenix Sol <phoenix@burninglabs.com>
Cliff Frey <cliff@meraki.com>
Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
Santiago Gala <sgala@apache.org>
Tim Becker <tim.becker@syngenio.de>
Jeff Terrace <jterrace@gmail.com>
Ben Noordhuis <info@bnoordhuis.nl>
Nathan Rajlich <nathan@tootallnate.net>
Mark Nottingham <mnot@mnot.net>
Aman Gupta <aman@tmm1.net>
Tim Becker <tim.becker@kuriositaet.de>
Sean Cunningham <sean.cunningham@mandiant.com>
Peter Griess <pg@std.in>
Salman Haq <salman.haq@asti-usa.com>
Cliff Frey <clifffrey@gmail.com>
Jon Kolb <jon@b0g.us>
Fouad Mardini <f.mardini@gmail.com>
Paul Querna <pquerna@apache.org>
Felix Geisendörfer <felix@debuggable.com>
koichik <koichik@improvement.jp>
Andre Caron <andre.l.caron@gmail.com>
Ivo Raisr <ivosh@ivosh.net>
James McLaughlin <jamie@lacewing-project.org>
David Gwynne <loki@animata.net>
Thomas LE ROUX <thomas@november-eleven.fr>
Randy Rizun <rrizun@ortivawireless.com>
Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
Simon Zimmermann <simonz05@gmail.com>
Erik Dubbelboer <erik@dubbelboer.com>
Martell Malone <martellmalone@gmail.com>
Bertrand Paquet <bpaquet@octo.com>
BogDan Vatra <bogdan@kde.org>
Peter Faiman <peter@thepicard.org>
Corey Richardson <corey@octayn.net>
Tóth Tamás <tomika_nospam@freemail.hu>
Cam Swords <cam.swords@gmail.com>
Chris Dickinson <christopher.s.dickinson@gmail.com>
Uli Köhler <ukoehler@btronik.de>
Charlie Somerville <charlie@charliesomerville.com>
Patrik Stutz <patrik.stutz@gmail.com>
Fedor Indutny <fedor.indutny@gmail.com>
runner <runner.mei@gmail.com>
Alexis Campailla <alexis@janeasystems.com>
David Wragg <david@wragg.org>
Vinnie Falco <vinnie.falco@gmail.com>
Alex Butum <alexbutum@linux.com>
Rex Feng <rexfeng@gmail.com>
Alex Kocharin <alex@kocharin.ru>
Mark Koopman <markmontymark@yahoo.com>
Helge Heß <me@helgehess.eu>
Alexis La Goutte <alexis.lagoutte@gmail.com>
George Miroshnykov <george.miroshnykov@gmail.com>
Maciej Małecki <me@mmalecki.com>
Marc O'Morain <github.com@marcomorain.com>
Jeff Pinner <jpinner@twitter.com>
Timothy J Fontaine <tjfontaine@gmail.com>
Akagi201 <akagi201@gmail.com>
Romain Giraud <giraud.romain@gmail.com>
Jay Satiro <raysatiro@yahoo.com>
Arne Steen <Arne.Steen@gmx.de>
Kjell Schubert <kjell.schubert@gmail.com>
Olivier Mengué <dolmen@cpan.org>

Просмотреть файл

@ -1,160 +0,0 @@
# Copyright Joyent, Inc. and other Node contributors. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"')
HELPER ?=
BINEXT ?=
SOLIBNAME = libhttp_parser
SOMAJOR = 2
SOMINOR = 9
SOREV = 4
ifeq (darwin,$(PLATFORM))
SOEXT ?= dylib
SONAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOEXT)
LIBNAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOREV).$(SOEXT)
else ifeq (wine,$(PLATFORM))
CC = winegcc
BINEXT = .exe.so
HELPER = wine
else
SOEXT ?= so
SONAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR)
LIBNAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR).$(SOREV)
endif
CC?=gcc
AR?=ar
CPPFLAGS ?=
LDFLAGS ?=
CPPFLAGS += -I.
CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1
CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA)
CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0
CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA)
CPPFLAGS_BENCH = $(CPPFLAGS_FAST)
CFLAGS += -Wall -Wextra -Werror
CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA)
CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA)
CFLAGS_BENCH = $(CFLAGS_FAST) -Wno-unused-parameter
CFLAGS_LIB = $(CFLAGS_FAST) -fPIC
LDFLAGS_LIB = $(LDFLAGS) -shared
INSTALL ?= install
PREFIX ?= /usr/local
LIBDIR = $(PREFIX)/lib
INCLUDEDIR = $(PREFIX)/include
ifeq (darwin,$(PLATFORM))
LDFLAGS_LIB += -Wl,-install_name,$(LIBDIR)/$(SONAME)
else
# TODO(bnoordhuis) The native SunOS linker expects -h rather than -soname...
LDFLAGS_LIB += -Wl,-soname=$(SONAME)
endif
test: test_g test_fast
$(HELPER) ./test_g$(BINEXT)
$(HELPER) ./test_fast$(BINEXT)
test_g: http_parser_g.o test_g.o
$(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o test_g.o -o $@
test_g.o: test.c http_parser.h Makefile
$(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c test.c -o $@
http_parser_g.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c http_parser.c -o $@
test_fast: http_parser.o test.o http_parser.h
$(CC) $(CFLAGS_FAST) $(LDFLAGS) http_parser.o test.o -o $@
test.o: test.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@
bench: http_parser.o bench.o
$(CC) $(CFLAGS_BENCH) $(LDFLAGS) http_parser.o bench.o -o $@
bench.o: bench.c http_parser.h Makefile
$(CC) $(CPPFLAGS_BENCH) $(CFLAGS_BENCH) -c bench.c -o $@
http_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c
test-run-timed: test_fast
while(true) do time $(HELPER) ./test_fast$(BINEXT) > /dev/null; done
test-valgrind: test_g
valgrind ./test_g
libhttp_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_LIB) -c http_parser.c -o libhttp_parser.o
library: libhttp_parser.o
$(CC) $(LDFLAGS_LIB) -o $(LIBNAME) $<
package: http_parser.o
$(AR) rcs libhttp_parser.a http_parser.o
url_parser: http_parser.o contrib/url_parser.c
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o $@
url_parser_g: http_parser_g.o contrib/url_parser.c
$(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o $@
parsertrace: http_parser.o contrib/parsertrace.c
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o parsertrace$(BINEXT)
parsertrace_g: http_parser_g.o contrib/parsertrace.c
$(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o parsertrace_g$(BINEXT)
tags: http_parser.c http_parser.h test.c
ctags $^
install: library
$(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h
$(INSTALL) -D $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME)
ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
install-strip: library
$(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h
$(INSTALL) -D -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME)
ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
uninstall:
rm $(DESTDIR)$(INCLUDEDIR)/http_parser.h
rm $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
rm $(DESTDIR)$(LIBDIR)/$(SONAME)
rm $(DESTDIR)$(LIBDIR)/$(LIBNAME)
clean:
rm -f *.o *.a tags test test_fast test_g \
http_parser.tar libhttp_parser.so.* \
url_parser url_parser_g parsertrace parsertrace_g \
*.exe *.exe.so
contrib/url_parser.c: http_parser.h
contrib/parsertrace.c: http_parser.h
.PHONY: clean package test-run test-run-timed test-valgrind install install-strip uninstall

Просмотреть файл

@ -1,246 +0,0 @@
HTTP Parser
===========
[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
This is a parser for HTTP messages written in C. It parses both requests and
responses. The parser is designed to be used in performance HTTP
applications. It does not make any syscalls nor allocations, it does not
buffer data, it can be interrupted at anytime. Depending on your
architecture, it only requires about 40 bytes of data per message
stream (in a web server that is per connection).
Features:
* No dependencies
* Handles persistent streams (keep-alive).
* Decodes chunked encoding.
* Upgrade support
* Defends against buffer overflow attacks.
The parser extracts the following information from HTTP messages:
* Header fields and values
* Content-Length
* Request method
* Response status code
* Transfer-Encoding
* HTTP version
* Request URL
* Message body
Usage
-----
One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something
like this for a request parser:
```c
http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
```
When data is received on the socket execute the parser and check for errors.
```c
size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;
recved = recv(fd, buf, len, 0);
if (recved < 0) {
/* Handle error. */
}
/* Start up / continue the parser.
* Note we pass recved==0 to signal that EOF has been received.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
if (parser->upgrade) {
/* handle new protocol */
} else if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
}
```
`http_parser` needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell `http_parser` about EOF, give
`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared
to receive them.
Scalar valued message information such as `status_code`, `method`, and the
HTTP version are stored in the parser structure. This data is only
temporally stored in `http_parser` and gets reset on each new message. If
this information is needed later, copy it out of the structure during the
`headers_complete` callback.
The parser decodes the transfer-encoding for both requests and responses
transparently. That is, a chunked encoding is decoded before being sent to
the on_body callback.
The Special Problem of Upgrade
------------------------------
`http_parser` supports upgrading the connection to a different protocol. An
increasingly common example of this is the WebSocket protocol which sends
a request like
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample
followed by non-HTTP data.
(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
WebSocket protocol.)
To support this, the parser will treat this as a normal HTTP message without a
body, issuing both on_headers_complete and on_message_complete callbacks. However
http_parser_execute() will stop parsing at the end of the headers and return.
The user is expected to check if `parser->upgrade` has been set to 1 after
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
offset by the return value of `http_parser_execute()`.
Callbacks
---------
During the `http_parser_execute()` call, the callbacks set in
`http_parser_settings` will be executed. The parser maintains state and
never looks behind, so buffering the data is not necessary. If you need to
save certain data for later usage, you can do that from the callbacks.
There are two types of callbacks:
* notification `typedef int (*http_cb) (http_parser*);`
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
Callbacks: (requests only) on_url,
(common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates
error to the parser, making it exit immediately.
For cases where it is necessary to pass local information to/from a callback,
the `http_parser` object's `data` field can be used.
An example of such a case is when using threads to handle a socket connection,
parse a request, and then give a response over that socket. By instantiation
of a thread-local struct containing relevant data (e.g. accepted socket,
allocated memory for callbacks to write into, etc), a parser's callbacks are
able to communicate data between the scope of the thread and the scope of the
callback in a threadsafe manner. This allows `http_parser` to be used in
multi-threaded contexts.
Example:
```c
typedef struct {
socket_t sock;
void* buffer;
int buf_len;
} custom_data_t;
int my_url_callback(http_parser* parser, const char *at, size_t length) {
/* access to thread local custom_data_t struct.
Use this access save parsed data for later use into thread local
buffer, or communicate over socket
*/
parser->data;
...
return 0;
}
...
void http_parser_thread(socket_t sock) {
int nparsed = 0;
/* allocate memory for user data */
custom_data_t *my_data = malloc(sizeof(custom_data_t));
/* some information for use by callbacks.
* achieves thread -> callback information flow */
my_data->sock = sock;
/* instantiate a thread-local parser */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
/* this custom data reference is accessible through the reference to the
parser supplied to callback functions */
parser->data = my_data;
http_parser_settings settings; /* set up callbacks */
settings.on_url = my_url_callback;
/* execute parser */
nparsed = http_parser_execute(parser, &settings, buf, recved);
...
/* parsed information copied from callback.
can now perform action on data copied into thread-local memory from callbacks.
achieves callback -> thread information flow */
my_data->buffer;
...
}
```
In case you parse HTTP message in chunks (i.e. `read()` request line
from socket, parse, read half headers, parse, etc) your data callbacks
may be called more than once. `http_parser` guarantees that data pointer is only
valid for the lifetime of callback. You can also `read()` into a heap allocated
buffer to avoid copying memory around if this fits your application.
Reading headers may be a tricky task if you read/parse headers partially.
Basically, you need to remember whether last header callback was field or value
and apply the following logic:
(on_header_field and on_header_value shortened to on_h_*)
------------------------ ------------ --------------------------------------------
| State (prev. callback) | Callback | Description/action |
------------------------ ------------ --------------------------------------------
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
| | | into it |
------------------------ ------------ --------------------------------------------
| value | on_h_field | New header started. |
| | | Copy current name,value buffers to headers |
| | | list and allocate new buffer for new name |
------------------------ ------------ --------------------------------------------
| field | on_h_field | Previous name continues. Reallocate name |
| | | buffer and append callback data to it |
------------------------ ------------ --------------------------------------------
| field | on_h_value | Value for current header started. Allocate |
| | | new buffer and copy callback data to it |
------------------------ ------------ --------------------------------------------
| value | on_h_value | Value continues. Reallocate value buffer |
| | | and append callback data to it |
------------------------ ------------ --------------------------------------------
Parsing URLs
------------
A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
Users of this library may wish to use it to parse URLs constructed from
consecutive `on_url` callbacks.
See examples of reading in headers:
* [partial example](http://gist.github.com/155877) in C
* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript

Просмотреть файл

@ -1,128 +0,0 @@
/* Copyright Fedor Indutny. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "http_parser.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
/* 8 gb */
static const int64_t kBytes = 8LL << 30;
static const char data[] =
"POST /joyent/http-parser HTTP/1.1\r\n"
"Host: github.com\r\n"
"DNT: 1\r\n"
"Accept-Encoding: gzip, deflate, sdch\r\n"
"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n"
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/39.0.2171.65 Safari/537.36\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/webp,*/*;q=0.8\r\n"
"Referer: https://github.com/joyent/http-parser\r\n"
"Connection: keep-alive\r\n"
"Transfer-Encoding: chunked\r\n"
"Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n";
static const size_t data_len = sizeof(data) - 1;
static int on_info(http_parser* p) {
return 0;
}
static int on_data(http_parser* p, const char *at, size_t length) {
return 0;
}
static http_parser_settings settings = {
.on_message_begin = on_info,
.on_headers_complete = on_info,
.on_message_complete = on_info,
.on_header_field = on_data,
.on_header_value = on_data,
.on_url = on_data,
.on_status = on_data,
.on_body = on_data
};
int bench(int iter_count, int silent) {
struct http_parser parser;
int i;
int err;
struct timeval start;
struct timeval end;
if (!silent) {
err = gettimeofday(&start, NULL);
assert(err == 0);
}
fprintf(stderr, "req_len=%d\n", (int) data_len);
for (i = 0; i < iter_count; i++) {
size_t parsed;
http_parser_init(&parser, HTTP_REQUEST);
parsed = http_parser_execute(&parser, &settings, data, data_len);
assert(parsed == data_len);
}
if (!silent) {
double elapsed;
double bw;
double total;
err = gettimeofday(&end, NULL);
assert(err == 0);
fprintf(stdout, "Benchmark result:\n");
elapsed = (double) (end.tv_sec - start.tv_sec) +
(end.tv_usec - start.tv_usec) * 1e-6f;
total = (double) iter_count * data_len;
bw = (double) total / elapsed;
fprintf(stdout, "%.2f mb | %.2f mb/s | %.2f req/sec | %.2f s\n",
(double) total / (1024 * 1024),
bw / (1024 * 1024),
(double) iter_count / elapsed,
elapsed);
fflush(stdout);
}
return 0;
}
int main(int argc, char** argv) {
int64_t iterations;
iterations = kBytes / (int64_t) data_len;
if (argc == 2 && strcmp(argv[1], "infinite") == 0) {
for (;;)
bench(iterations, 1);
return 0;
} else {
return bench(iterations, 0);
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше