Voice et bot modif
This commit is contained in:
parent
189d56026b
commit
7333a22bcd
10774 changed files with 634644 additions and 933308 deletions
|
|
@ -2,26 +2,14 @@ import asyncio
|
|||
import datetime
|
||||
import io
|
||||
import re
|
||||
import socket
|
||||
import string
|
||||
import tempfile
|
||||
import types
|
||||
import warnings
|
||||
from collections.abc import Iterator, Mapping, MutableMapping
|
||||
from re import Pattern
|
||||
from types import MappingProxyType
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
Final,
|
||||
Iterator,
|
||||
Mapping,
|
||||
MutableMapping,
|
||||
Optional,
|
||||
Pattern,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Any, Final, Optional, TypeVar, cast, overload
|
||||
from urllib.parse import parse_qsl
|
||||
|
||||
import attr
|
||||
|
|
@ -40,11 +28,13 @@ from .abc import AbstractStreamWriter
|
|||
from .helpers import (
|
||||
_SENTINEL,
|
||||
DEBUG,
|
||||
DEFAULT_CHUNK_SIZE,
|
||||
ETAG_ANY,
|
||||
LIST_QUOTED_ETAG_RE,
|
||||
ChainMapProxy,
|
||||
ETag,
|
||||
HeadersMixin,
|
||||
RequestKey,
|
||||
parse_http_date,
|
||||
reify,
|
||||
sentinel,
|
||||
|
|
@ -61,7 +51,7 @@ from .typedefs import (
|
|||
RawHeaders,
|
||||
StrOrURL,
|
||||
)
|
||||
from .web_exceptions import HTTPRequestEntityTooLarge
|
||||
from .web_exceptions import HTTPRequestEntityTooLarge, NotAppKeyWarning
|
||||
from .web_response import StreamResponse
|
||||
|
||||
__all__ = ("BaseRequest", "FileField", "Request")
|
||||
|
|
@ -73,6 +63,9 @@ if TYPE_CHECKING:
|
|||
from .web_urldispatcher import UrlMappingMatchInfo
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True, frozen=True, slots=True)
|
||||
class FileField:
|
||||
name: str
|
||||
|
|
@ -82,6 +75,7 @@ class FileField:
|
|||
headers: CIMultiDictProxy[str]
|
||||
|
||||
|
||||
_Post = str | bytes | bytearray | FileField
|
||||
_TCHAR: Final[str] = string.digits + string.ascii_letters + r"!#$%&'*+.^_`|~-"
|
||||
# '-' at the end to prevent interpretation as range in a char class
|
||||
|
||||
|
|
@ -95,15 +89,10 @@ _QDTEXT: Final[str] = r"[{}]".format(
|
|||
|
||||
_QUOTED_PAIR: Final[str] = r"\\[\t !-~]"
|
||||
|
||||
_QUOTED_STRING: Final[str] = r'"(?:{quoted_pair}|{qdtext})*"'.format(
|
||||
qdtext=_QDTEXT, quoted_pair=_QUOTED_PAIR
|
||||
)
|
||||
_QUOTED_STRING: Final[str] = rf'"(?:{_QUOTED_PAIR}|{_QDTEXT})*"'
|
||||
|
||||
_FORWARDED_PAIR: Final[str] = (
|
||||
r"({token})=({token}|{quoted_string})(:\d{{1,4}})?".format(
|
||||
token=_TOKEN, quoted_string=_QUOTED_STRING
|
||||
)
|
||||
)
|
||||
# This does not have a ReDOS/performance concern as long as it used with re.match().
|
||||
_FORWARDED_PAIR: Final[str] = rf"({_TOKEN})=({_TOKEN}|{_QUOTED_STRING})(:\d{{1,4}})?"
|
||||
|
||||
_QUOTED_PAIR_REPLACE_RE: Final[Pattern[str]] = re.compile(r"\\([\t !-~])")
|
||||
# same pattern as _QUOTED_PAIR but contains a capture group
|
||||
|
|
@ -115,8 +104,7 @@ _FORWARDED_PAIR_RE: Final[Pattern[str]] = re.compile(_FORWARDED_PAIR)
|
|||
############################################################
|
||||
|
||||
|
||||
class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
||||
|
||||
class BaseRequest(MutableMapping[str | RequestKey[Any], Any], HeadersMixin):
|
||||
POST_METHODS = {
|
||||
hdrs.METH_PATCH,
|
||||
hdrs.METH_POST,
|
||||
|
|
@ -146,8 +134,9 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
"_transport_peername",
|
||||
]
|
||||
)
|
||||
_post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None
|
||||
_read_bytes: Optional[bytes] = None
|
||||
_post: MultiDictProxy[_Post] | None = None
|
||||
_read_bytes: bytes | None = None
|
||||
_seen_str_keys: set[str] = set()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -159,10 +148,10 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
loop: asyncio.AbstractEventLoop,
|
||||
*,
|
||||
client_max_size: int = 1024**2,
|
||||
state: Optional[Dict[str, Any]] = None,
|
||||
scheme: Optional[str] = None,
|
||||
host: Optional[str] = None,
|
||||
remote: Optional[str] = None,
|
||||
state: dict[RequestKey[Any] | str, Any] | None = None,
|
||||
scheme: str | None = None,
|
||||
host: str | None = None,
|
||||
remote: str | None = None,
|
||||
) -> None:
|
||||
self._message = message
|
||||
self._protocol = protocol
|
||||
|
|
@ -172,7 +161,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
self._headers: CIMultiDictProxy[str] = message.headers
|
||||
self._method = message.method
|
||||
self._version = message.version
|
||||
self._cache: Dict[str, Any] = {}
|
||||
self._cache: dict[str, Any] = {}
|
||||
url = message.url
|
||||
if url.absolute:
|
||||
if scheme is not None:
|
||||
|
|
@ -200,6 +189,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
|
||||
self._transport_sslcontext = protocol.ssl_context
|
||||
self._transport_peername = protocol.peername
|
||||
self._transport_sockname = protocol.sockname
|
||||
|
||||
if remote is not None:
|
||||
self._cache["remote"] = remote
|
||||
|
|
@ -207,13 +197,13 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
def clone(
|
||||
self,
|
||||
*,
|
||||
method: Union[str, _SENTINEL] = sentinel,
|
||||
rel_url: Union[StrOrURL, _SENTINEL] = sentinel,
|
||||
headers: Union[LooseHeaders, _SENTINEL] = sentinel,
|
||||
scheme: Union[str, _SENTINEL] = sentinel,
|
||||
host: Union[str, _SENTINEL] = sentinel,
|
||||
remote: Union[str, _SENTINEL] = sentinel,
|
||||
client_max_size: Union[int, _SENTINEL] = sentinel,
|
||||
method: str | _SENTINEL = sentinel,
|
||||
rel_url: StrOrURL | _SENTINEL = sentinel,
|
||||
headers: LooseHeaders | _SENTINEL = sentinel,
|
||||
scheme: str | _SENTINEL = sentinel,
|
||||
host: str | _SENTINEL = sentinel,
|
||||
remote: str | _SENTINEL = sentinel,
|
||||
client_max_size: int | _SENTINEL = sentinel,
|
||||
) -> "BaseRequest":
|
||||
"""Clone itself with replacement some attributes.
|
||||
|
||||
|
|
@ -224,7 +214,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
if self._read_bytes:
|
||||
raise RuntimeError("Cannot clone request after reading its content")
|
||||
|
||||
dct: Dict[str, Any] = {}
|
||||
dct: dict[str, Any] = {}
|
||||
if method is not sentinel:
|
||||
dct["method"] = method
|
||||
if rel_url is not sentinel:
|
||||
|
|
@ -272,7 +262,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
return self._protocol
|
||||
|
||||
@property
|
||||
def transport(self) -> Optional[asyncio.Transport]:
|
||||
def transport(self) -> asyncio.Transport | None:
|
||||
if self._protocol is None:
|
||||
return None
|
||||
return self._protocol.transport
|
||||
|
|
@ -303,19 +293,40 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
|
||||
# MutableMapping API
|
||||
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
@overload # type: ignore[override]
|
||||
def __getitem__(self, key: RequestKey[_T]) -> _T: ...
|
||||
|
||||
@overload
|
||||
def __getitem__(self, key: str) -> Any: ...
|
||||
|
||||
def __getitem__(self, key: str | RequestKey[_T]) -> Any:
|
||||
return self._state[key]
|
||||
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
@overload # type: ignore[override]
|
||||
def __setitem__(self, key: RequestKey[_T], value: _T) -> None: ...
|
||||
|
||||
@overload
|
||||
def __setitem__(self, key: str, value: Any) -> None: ...
|
||||
|
||||
def __setitem__(self, key: str | RequestKey[_T], value: Any) -> None:
|
||||
if not isinstance(key, RequestKey) and key not in BaseRequest._seen_str_keys:
|
||||
BaseRequest._seen_str_keys.add(key)
|
||||
warnings.warn(
|
||||
"It is recommended to use web.RequestKey instances for keys.\n"
|
||||
+ "https://docs.aiohttp.org/en/stable/web_advanced.html"
|
||||
+ "#request-s-storage",
|
||||
category=NotAppKeyWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self._state[key] = value
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
def __delitem__(self, key: str | RequestKey[_T]) -> None:
|
||||
del self._state[key]
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._state)
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
def __iter__(self) -> Iterator[str | RequestKey[Any]]:
|
||||
return iter(self._state)
|
||||
|
||||
########
|
||||
|
|
@ -326,7 +337,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
return self.scheme == "https"
|
||||
|
||||
@reify
|
||||
def forwarded(self) -> Tuple[Mapping[str, str], ...]:
|
||||
def forwarded(self) -> tuple[Mapping[str, str], ...]:
|
||||
"""A tuple containing all parsed Forwarded header(s).
|
||||
|
||||
Makes an effort to parse Forwarded headers as specified by RFC 7239:
|
||||
|
|
@ -350,7 +361,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
length = len(field_value)
|
||||
pos = 0
|
||||
need_separator = False
|
||||
elem: Dict[str, str] = {}
|
||||
elem: dict[str, str] = {}
|
||||
elems.append(types.MappingProxyType(elem))
|
||||
while 0 <= pos < length:
|
||||
match = _FORWARDED_PAIR_RE.match(field_value, pos)
|
||||
|
|
@ -426,7 +437,9 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
|
||||
- overridden value by .clone(host=new_host) call.
|
||||
- HOST HTTP header
|
||||
- socket.getfqdn() value
|
||||
- local socket address the request arrived on
|
||||
(transport ``sockname``)
|
||||
- empty string if no transport information is available
|
||||
|
||||
For example, 'example.com' or 'localhost:8080'.
|
||||
|
||||
|
|
@ -435,10 +448,20 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
host = self._message.headers.get(hdrs.HOST)
|
||||
if host is not None:
|
||||
return host
|
||||
return socket.getfqdn()
|
||||
sockname = self._transport_sockname
|
||||
if sockname is None:
|
||||
return ""
|
||||
if isinstance(sockname, tuple):
|
||||
# AF_INET6 returns a 4-tuple (host, port, flowinfo, scopeid);
|
||||
# bracket the bare address so it matches the Host-header shape
|
||||
# and is a valid URL authority component.
|
||||
if len(sockname) == 4:
|
||||
return f"[{sockname[0]}]"
|
||||
return str(sockname[0])
|
||||
return str(sockname)
|
||||
|
||||
@reify
|
||||
def remote(self) -> Optional[str]:
|
||||
def remote(self) -> str | None:
|
||||
"""Remote IP of client initiated HTTP request.
|
||||
|
||||
The IP is resolved in this order:
|
||||
|
|
@ -509,7 +532,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
return self._message.raw_headers
|
||||
|
||||
@reify
|
||||
def if_modified_since(self) -> Optional[datetime.datetime]:
|
||||
def if_modified_since(self) -> datetime.datetime | None:
|
||||
"""The value of If-Modified-Since HTTP header, or None.
|
||||
|
||||
This header is represented as a `datetime` object.
|
||||
|
|
@ -517,7 +540,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
return parse_http_date(self.headers.get(hdrs.IF_MODIFIED_SINCE))
|
||||
|
||||
@reify
|
||||
def if_unmodified_since(self) -> Optional[datetime.datetime]:
|
||||
def if_unmodified_since(self) -> datetime.datetime | None:
|
||||
"""The value of If-Unmodified-Since HTTP header, or None.
|
||||
|
||||
This header is represented as a `datetime` object.
|
||||
|
|
@ -547,15 +570,15 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
|
||||
@classmethod
|
||||
def _if_match_or_none_impl(
|
||||
cls, header_value: Optional[str]
|
||||
) -> Optional[Tuple[ETag, ...]]:
|
||||
cls, header_value: str | None
|
||||
) -> tuple[ETag, ...] | None:
|
||||
if not header_value:
|
||||
return None
|
||||
|
||||
return tuple(cls._etag_values(header_value))
|
||||
|
||||
@reify
|
||||
def if_match(self) -> Optional[Tuple[ETag, ...]]:
|
||||
def if_match(self) -> tuple[ETag, ...] | None:
|
||||
"""The value of If-Match HTTP header, or None.
|
||||
|
||||
This header is represented as a `tuple` of `ETag` objects.
|
||||
|
|
@ -563,7 +586,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
return self._if_match_or_none_impl(self.headers.get(hdrs.IF_MATCH))
|
||||
|
||||
@reify
|
||||
def if_none_match(self) -> Optional[Tuple[ETag, ...]]:
|
||||
def if_none_match(self) -> tuple[ETag, ...] | None:
|
||||
"""The value of If-None-Match HTTP header, or None.
|
||||
|
||||
This header is represented as a `tuple` of `ETag` objects.
|
||||
|
|
@ -571,7 +594,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
return self._if_match_or_none_impl(self.headers.get(hdrs.IF_NONE_MATCH))
|
||||
|
||||
@reify
|
||||
def if_range(self) -> Optional[datetime.datetime]:
|
||||
def if_range(self) -> datetime.datetime | None:
|
||||
"""The value of If-Range HTTP header, or None.
|
||||
|
||||
This header is represented as a `datetime` object.
|
||||
|
|
@ -668,16 +691,18 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
Returns bytes object with full request content.
|
||||
"""
|
||||
if self._read_bytes is None:
|
||||
# Raise the buffer limits so compressed payloads decompress in
|
||||
# larger chunks instead of many small pause/resume cycles.
|
||||
if self._client_max_size:
|
||||
self._payload.set_read_chunk_size(self._client_max_size)
|
||||
body = bytearray()
|
||||
while True:
|
||||
chunk = await self._payload.readany()
|
||||
body.extend(chunk)
|
||||
if self._client_max_size:
|
||||
body_size = len(body)
|
||||
if body_size >= self._client_max_size:
|
||||
raise HTTPRequestEntityTooLarge(
|
||||
max_size=self._client_max_size, actual_size=body_size
|
||||
)
|
||||
if body_size > self._client_max_size:
|
||||
raise HTTPRequestEntityTooLarge(self._client_max_size)
|
||||
if not chunk:
|
||||
break
|
||||
self._read_bytes = bytes(body)
|
||||
|
|
@ -696,9 +721,16 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
|
||||
async def multipart(self) -> MultipartReader:
|
||||
"""Return async iterator to process BODY as multipart."""
|
||||
return MultipartReader(self._headers, self._payload)
|
||||
return MultipartReader(
|
||||
self._headers,
|
||||
self._payload,
|
||||
client_max_size=self._client_max_size,
|
||||
max_field_size=self._protocol.max_field_size,
|
||||
max_headers=self._protocol.max_headers,
|
||||
max_size_error_cls=HTTPRequestEntityTooLarge,
|
||||
)
|
||||
|
||||
async def post(self) -> "MultiDictProxy[Union[str, bytes, FileField]]":
|
||||
async def post(self) -> "MultiDictProxy[_Post]":
|
||||
"""Return POST parameters."""
|
||||
if self._post is not None:
|
||||
return self._post
|
||||
|
|
@ -715,7 +747,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
self._post = MultiDictProxy(MultiDict())
|
||||
return self._post
|
||||
|
||||
out: MultiDict[Union[str, bytes, FileField]] = MultiDict()
|
||||
out: MultiDict[_Post] = MultiDict()
|
||||
|
||||
if content_type == "multipart/form-data":
|
||||
multipart = await self.multipart()
|
||||
|
|
@ -738,17 +770,15 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
tmp = await self._loop.run_in_executor(
|
||||
None, tempfile.TemporaryFile
|
||||
)
|
||||
chunk = await field.read_chunk(size=2**16)
|
||||
while chunk:
|
||||
chunk = await field.decode(chunk)
|
||||
await self._loop.run_in_executor(None, tmp.write, chunk)
|
||||
size += len(chunk)
|
||||
if 0 < max_size < size:
|
||||
await self._loop.run_in_executor(None, tmp.close)
|
||||
raise HTTPRequestEntityTooLarge(
|
||||
max_size=max_size, actual_size=size
|
||||
while chunk := await field.read_chunk(size=DEFAULT_CHUNK_SIZE):
|
||||
async for decoded_chunk in field.decode_iter(chunk):
|
||||
await self._loop.run_in_executor(
|
||||
None, tmp.write, decoded_chunk
|
||||
)
|
||||
chunk = await field.read_chunk(size=2**16)
|
||||
size += len(decoded_chunk)
|
||||
if 0 < max_size < size:
|
||||
await self._loop.run_in_executor(None, tmp.close)
|
||||
raise HTTPRequestEntityTooLarge(max_size)
|
||||
await self._loop.run_in_executor(None, tmp.seek, 0)
|
||||
|
||||
if field_ct is None:
|
||||
|
|
@ -764,17 +794,23 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
out.add(field.name, ff)
|
||||
else:
|
||||
# deal with ordinary data
|
||||
value = await field.read(decode=True)
|
||||
raw_data = bytearray()
|
||||
while chunk := await field.read_chunk():
|
||||
size += len(chunk)
|
||||
if 0 < max_size < size:
|
||||
raise HTTPRequestEntityTooLarge(max_size)
|
||||
raw_data.extend(chunk)
|
||||
|
||||
value = bytearray()
|
||||
# form-data doesn't support compression, so don't need to check size again.
|
||||
async for d in field.decode_iter(raw_data):
|
||||
value.extend(d)
|
||||
|
||||
if field_ct is None or field_ct.startswith("text/"):
|
||||
charset = field.get_charset(default="utf-8")
|
||||
out.add(field.name, value.decode(charset))
|
||||
else:
|
||||
out.add(field.name, value)
|
||||
size += len(value)
|
||||
if 0 < max_size < size:
|
||||
raise HTTPRequestEntityTooLarge(
|
||||
max_size=max_size, actual_size=size
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"To decode nested multipart you need to use custom reader",
|
||||
|
|
@ -810,9 +846,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
|
|||
ascii_encodable_path = self.path.encode("ascii", "backslashreplace").decode(
|
||||
"ascii"
|
||||
)
|
||||
return "<{} {} {} >".format(
|
||||
self.__class__.__name__, self._method, ascii_encodable_path
|
||||
)
|
||||
return f"<{self.__class__.__name__} {self._method} {ascii_encodable_path} >"
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
return id(self) == id(other)
|
||||
|
|
@ -850,8 +884,8 @@ class Request(BaseRequest):
|
|||
def __setattr__(self, name: str, val: Any) -> None:
|
||||
if name not in self.ATTRS:
|
||||
warnings.warn(
|
||||
"Setting custom {}.{} attribute "
|
||||
"is discouraged".format(self.__class__.__name__, name),
|
||||
f"Setting custom {self.__class__.__name__}.{name} attribute "
|
||||
"is discouraged",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
|
@ -860,13 +894,13 @@ class Request(BaseRequest):
|
|||
def clone(
|
||||
self,
|
||||
*,
|
||||
method: Union[str, _SENTINEL] = sentinel,
|
||||
rel_url: Union[StrOrURL, _SENTINEL] = sentinel,
|
||||
headers: Union[LooseHeaders, _SENTINEL] = sentinel,
|
||||
scheme: Union[str, _SENTINEL] = sentinel,
|
||||
host: Union[str, _SENTINEL] = sentinel,
|
||||
remote: Union[str, _SENTINEL] = sentinel,
|
||||
client_max_size: Union[int, _SENTINEL] = sentinel,
|
||||
method: str | _SENTINEL = sentinel,
|
||||
rel_url: StrOrURL | _SENTINEL = sentinel,
|
||||
headers: LooseHeaders | _SENTINEL = sentinel,
|
||||
scheme: str | _SENTINEL = sentinel,
|
||||
host: str | _SENTINEL = sentinel,
|
||||
remote: str | _SENTINEL = sentinel,
|
||||
client_max_size: int | _SENTINEL = sentinel,
|
||||
) -> "Request":
|
||||
ret = super().clone(
|
||||
method=method,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue