Spaces:
Running
Running
from __future__ import annotations | |
import collections.abc as cabc | |
import functools | |
import json | |
import typing as t | |
from io import BytesIO | |
from .._internal import _wsgi_decoding_dance | |
from ..datastructures import CombinedMultiDict | |
from ..datastructures import EnvironHeaders | |
from ..datastructures import FileStorage | |
from ..datastructures import ImmutableMultiDict | |
from ..datastructures import iter_multi_items | |
from ..datastructures import MultiDict | |
from ..exceptions import BadRequest | |
from ..exceptions import UnsupportedMediaType | |
from ..formparser import default_stream_factory | |
from ..formparser import FormDataParser | |
from ..sansio.request import Request as _SansIORequest | |
from ..utils import cached_property | |
from ..utils import environ_property | |
from ..wsgi import _get_server | |
from ..wsgi import get_input_stream | |
if t.TYPE_CHECKING: | |
from _typeshed.wsgi import WSGIApplication | |
from _typeshed.wsgi import WSGIEnvironment | |
class Request(_SansIORequest): | |
"""Represents an incoming WSGI HTTP request, with headers and body | |
taken from the WSGI environment. Has properties and methods for | |
using the functionality defined by various HTTP specs. The data in | |
requests object is read-only. | |
Text data is assumed to use UTF-8 encoding, which should be true for | |
the vast majority of modern clients. Using an encoding set by the | |
client is unsafe in Python due to extra encodings it provides, such | |
as ``zip``. To change the assumed encoding, subclass and replace | |
:attr:`charset`. | |
:param environ: The WSGI environ is generated by the WSGI server and | |
contains information about the server configuration and client | |
request. | |
:param populate_request: Add this request object to the WSGI environ | |
as ``environ['werkzeug.request']``. Can be useful when | |
debugging. | |
:param shallow: Makes reading from :attr:`stream` (and any method | |
that would read from it) raise a :exc:`RuntimeError`. Useful to | |
prevent consuming the form data in middleware, which would make | |
it unavailable to the final application. | |
.. versionchanged:: 3.0 | |
The ``charset``, ``url_charset``, and ``encoding_errors`` parameters | |
were removed. | |
.. versionchanged:: 2.1 | |
Old ``BaseRequest`` and mixin classes were removed. | |
.. versionchanged:: 2.1 | |
Remove the ``disable_data_descriptor`` attribute. | |
.. versionchanged:: 2.0 | |
Combine ``BaseRequest`` and mixins into a single ``Request`` | |
class. | |
.. versionchanged:: 0.5 | |
Read-only mode is enforced with immutable classes for all data. | |
""" | |
#: the maximum content length. This is forwarded to the form data | |
#: parsing function (:func:`parse_form_data`). When set and the | |
#: :attr:`form` or :attr:`files` attribute is accessed and the | |
#: parsing fails because more than the specified value is transmitted | |
#: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. | |
#: | |
#: .. versionadded:: 0.5 | |
max_content_length: int | None = None | |
#: the maximum form field size. This is forwarded to the form data | |
#: parsing function (:func:`parse_form_data`). When set and the | |
#: :attr:`form` or :attr:`files` attribute is accessed and the | |
#: data in memory for post data is longer than the specified value a | |
#: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. | |
#: | |
#: .. versionchanged:: 3.1 | |
#: Defaults to 500kB instead of unlimited. | |
#: | |
#: .. versionadded:: 0.5 | |
max_form_memory_size: int | None = 500_000 | |
#: The maximum number of multipart parts to parse, passed to | |
#: :attr:`form_data_parser_class`. Parsing form data with more than this | |
#: many parts will raise :exc:`~.RequestEntityTooLarge`. | |
#: | |
#: .. versionadded:: 2.2.3 | |
max_form_parts = 1000 | |
#: The form data parser that should be used. Can be replaced to customize | |
#: the form date parsing. | |
form_data_parser_class: type[FormDataParser] = FormDataParser | |
#: The WSGI environment containing HTTP headers and information from | |
#: the WSGI server. | |
environ: WSGIEnvironment | |
#: Set when creating the request object. If ``True``, reading from | |
#: the request body will cause a ``RuntimeException``. Useful to | |
#: prevent modifying the stream from middleware. | |
shallow: bool | |
def __init__( | |
self, | |
environ: WSGIEnvironment, | |
populate_request: bool = True, | |
shallow: bool = False, | |
) -> None: | |
super().__init__( | |
method=environ.get("REQUEST_METHOD", "GET"), | |
scheme=environ.get("wsgi.url_scheme", "http"), | |
server=_get_server(environ), | |
root_path=_wsgi_decoding_dance(environ.get("SCRIPT_NAME") or ""), | |
path=_wsgi_decoding_dance(environ.get("PATH_INFO") or ""), | |
query_string=environ.get("QUERY_STRING", "").encode("latin1"), | |
headers=EnvironHeaders(environ), | |
remote_addr=environ.get("REMOTE_ADDR"), | |
) | |
self.environ = environ | |
self.shallow = shallow | |
if populate_request and not shallow: | |
self.environ["werkzeug.request"] = self | |
def from_values(cls, *args: t.Any, **kwargs: t.Any) -> Request: | |
"""Create a new request object based on the values provided. If | |
environ is given missing values are filled from there. This method is | |
useful for small scripts when you need to simulate a request from an URL. | |
Do not use this method for unittesting, there is a full featured client | |
object (:class:`Client`) that allows to create multipart requests, | |
support for cookies etc. | |
This accepts the same options as the | |
:class:`~werkzeug.test.EnvironBuilder`. | |
.. versionchanged:: 0.5 | |
This method now accepts the same arguments as | |
:class:`~werkzeug.test.EnvironBuilder`. Because of this the | |
`environ` parameter is now called `environ_overrides`. | |
:return: request object | |
""" | |
from ..test import EnvironBuilder | |
builder = EnvironBuilder(*args, **kwargs) | |
try: | |
return builder.get_request(cls) | |
finally: | |
builder.close() | |
def application(cls, f: t.Callable[[Request], WSGIApplication]) -> WSGIApplication: | |
"""Decorate a function as responder that accepts the request as | |
the last argument. This works like the :func:`responder` | |
decorator but the function is passed the request object as the | |
last argument and the request object will be closed | |
automatically:: | |
@Request.application | |
def my_wsgi_app(request): | |
return Response('Hello World!') | |
As of Werkzeug 0.14 HTTP exceptions are automatically caught and | |
converted to responses instead of failing. | |
:param f: the WSGI callable to decorate | |
:return: a new WSGI callable | |
""" | |
#: return a callable that wraps the -2nd argument with the request | |
#: and calls the function with all the arguments up to that one and | |
#: the request. The return value is then called with the latest | |
#: two arguments. This makes it possible to use this decorator for | |
#: both standalone WSGI functions as well as bound methods and | |
#: partially applied functions. | |
from ..exceptions import HTTPException | |
def application(*args: t.Any) -> cabc.Iterable[bytes]: | |
request = cls(args[-2]) | |
with request: | |
try: | |
resp = f(*args[:-2] + (request,)) | |
except HTTPException as e: | |
resp = t.cast("WSGIApplication", e.get_response(args[-2])) | |
return resp(*args[-2:]) | |
return t.cast("WSGIApplication", application) | |
def _get_file_stream( | |
self, | |
total_content_length: int | None, | |
content_type: str | None, | |
filename: str | None = None, | |
content_length: int | None = None, | |
) -> t.IO[bytes]: | |
"""Called to get a stream for the file upload. | |
This must provide a file-like class with `read()`, `readline()` | |
and `seek()` methods that is both writeable and readable. | |
The default implementation returns a temporary file if the total | |
content length is higher than 500KB. Because many browsers do not | |
provide a content length for the files only the total content | |
length matters. | |
:param total_content_length: the total content length of all the | |
data in the request combined. This value | |
is guaranteed to be there. | |
:param content_type: the mimetype of the uploaded file. | |
:param filename: the filename of the uploaded file. May be `None`. | |
:param content_length: the length of this file. This value is usually | |
not provided because webbrowsers do not provide | |
this value. | |
""" | |
return default_stream_factory( | |
total_content_length=total_content_length, | |
filename=filename, | |
content_type=content_type, | |
content_length=content_length, | |
) | |
def want_form_data_parsed(self) -> bool: | |
"""``True`` if the request method carries content. By default | |
this is true if a ``Content-Type`` is sent. | |
.. versionadded:: 0.8 | |
""" | |
return bool(self.environ.get("CONTENT_TYPE")) | |
def make_form_data_parser(self) -> FormDataParser: | |
"""Creates the form data parser. Instantiates the | |
:attr:`form_data_parser_class` with some parameters. | |
.. versionadded:: 0.8 | |
""" | |
return self.form_data_parser_class( | |
stream_factory=self._get_file_stream, | |
max_form_memory_size=self.max_form_memory_size, | |
max_content_length=self.max_content_length, | |
max_form_parts=self.max_form_parts, | |
cls=self.parameter_storage_class, | |
) | |
def _load_form_data(self) -> None: | |
"""Method used internally to retrieve submitted data. After calling | |
this sets `form` and `files` on the request object to multi dicts | |
filled with the incoming form data. As a matter of fact the input | |
stream will be empty afterwards. You can also call this method to | |
force the parsing of the form data. | |
.. versionadded:: 0.8 | |
""" | |
# abort early if we have already consumed the stream | |
if "form" in self.__dict__: | |
return | |
if self.want_form_data_parsed: | |
parser = self.make_form_data_parser() | |
data = parser.parse( | |
self._get_stream_for_parsing(), | |
self.mimetype, | |
self.content_length, | |
self.mimetype_params, | |
) | |
else: | |
data = ( | |
self.stream, | |
self.parameter_storage_class(), | |
self.parameter_storage_class(), | |
) | |
# inject the values into the instance dict so that we bypass | |
# our cached_property non-data descriptor. | |
d = self.__dict__ | |
d["stream"], d["form"], d["files"] = data | |
def _get_stream_for_parsing(self) -> t.IO[bytes]: | |
"""This is the same as accessing :attr:`stream` with the difference | |
that if it finds cached data from calling :meth:`get_data` first it | |
will create a new stream out of the cached data. | |
.. versionadded:: 0.9.3 | |
""" | |
cached_data = getattr(self, "_cached_data", None) | |
if cached_data is not None: | |
return BytesIO(cached_data) | |
return self.stream | |
def close(self) -> None: | |
"""Closes associated resources of this request object. This | |
closes all file handles explicitly. You can also use the request | |
object in a with statement which will automatically close it. | |
.. versionadded:: 0.9 | |
""" | |
files = self.__dict__.get("files") | |
for _key, value in iter_multi_items(files or ()): | |
value.close() | |
def __enter__(self) -> Request: | |
return self | |
def __exit__(self, exc_type, exc_value, tb) -> None: # type: ignore | |
self.close() | |
def stream(self) -> t.IO[bytes]: | |
"""The WSGI input stream, with safety checks. This stream can only be consumed | |
once. | |
Use :meth:`get_data` to get the full data as bytes or text. The :attr:`data` | |
attribute will contain the full bytes only if they do not represent form data. | |
The :attr:`form` attribute will contain the parsed form data in that case. | |
Unlike :attr:`input_stream`, this stream guards against infinite streams or | |
reading past :attr:`content_length` or :attr:`max_content_length`. | |
If ``max_content_length`` is set, it can be enforced on streams if | |
``wsgi.input_terminated`` is set. Otherwise, an empty stream is returned. | |
If the limit is reached before the underlying stream is exhausted (such as a | |
file that is too large, or an infinite stream), the remaining contents of the | |
stream cannot be read safely. Depending on how the server handles this, clients | |
may show a "connection reset" failure instead of seeing the 413 response. | |
.. versionchanged:: 2.3 | |
Check ``max_content_length`` preemptively and while reading. | |
.. versionchanged:: 0.9 | |
The stream is always set (but may be consumed) even if form parsing was | |
accessed first. | |
""" | |
if self.shallow: | |
raise RuntimeError( | |
"This request was created with 'shallow=True', reading" | |
" from the input stream is disabled." | |
) | |
return get_input_stream( | |
self.environ, max_content_length=self.max_content_length | |
) | |
input_stream = environ_property[t.IO[bytes]]( | |
"wsgi.input", | |
doc="""The raw WSGI input stream, without any safety checks. | |
This is dangerous to use. It does not guard against infinite streams or reading | |
past :attr:`content_length` or :attr:`max_content_length`. | |
Use :attr:`stream` instead. | |
""", | |
) | |
def data(self) -> bytes: | |
"""The raw data read from :attr:`stream`. Will be empty if the request | |
represents form data. | |
To get the raw data even if it represents form data, use :meth:`get_data`. | |
""" | |
return self.get_data(parse_form_data=True) | |
def get_data( | |
self, | |
cache: bool = True, | |
as_text: t.Literal[False] = False, | |
parse_form_data: bool = False, | |
) -> bytes: ... | |
def get_data( | |
self, | |
cache: bool = True, | |
as_text: t.Literal[True] = ..., | |
parse_form_data: bool = False, | |
) -> str: ... | |
def get_data( | |
self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False | |
) -> bytes | str: | |
"""This reads the buffered incoming data from the client into one | |
bytes object. By default this is cached but that behavior can be | |
changed by setting `cache` to `False`. | |
Usually it's a bad idea to call this method without checking the | |
content length first as a client could send dozens of megabytes or more | |
to cause memory problems on the server. | |
Note that if the form data was already parsed this method will not | |
return anything as form data parsing does not cache the data like | |
this method does. To implicitly invoke form data parsing function | |
set `parse_form_data` to `True`. When this is done the return value | |
of this method will be an empty string if the form parser handles | |
the data. This generally is not necessary as if the whole data is | |
cached (which is the default) the form parser will used the cached | |
data to parse the form data. Please be generally aware of checking | |
the content length first in any case before calling this method | |
to avoid exhausting server memory. | |
If `as_text` is set to `True` the return value will be a decoded | |
string. | |
.. versionadded:: 0.9 | |
""" | |
rv = getattr(self, "_cached_data", None) | |
if rv is None: | |
if parse_form_data: | |
self._load_form_data() | |
rv = self.stream.read() | |
if cache: | |
self._cached_data = rv | |
if as_text: | |
rv = rv.decode(errors="replace") | |
return rv | |
def form(self) -> ImmutableMultiDict[str, str]: | |
"""The form parameters. By default an | |
:class:`~werkzeug.datastructures.ImmutableMultiDict` | |
is returned from this function. This can be changed by setting | |
:attr:`parameter_storage_class` to a different type. This might | |
be necessary if the order of the form data is important. | |
Please keep in mind that file uploads will not end up here, but instead | |
in the :attr:`files` attribute. | |
.. versionchanged:: 0.9 | |
Previous to Werkzeug 0.9 this would only contain form data for POST | |
and PUT requests. | |
""" | |
self._load_form_data() | |
return self.form | |
def values(self) -> CombinedMultiDict[str, str]: | |
"""A :class:`werkzeug.datastructures.CombinedMultiDict` that | |
combines :attr:`args` and :attr:`form`. | |
For GET requests, only ``args`` are present, not ``form``. | |
.. versionchanged:: 2.0 | |
For GET requests, only ``args`` are present, not ``form``. | |
""" | |
sources = [self.args] | |
if self.method != "GET": | |
# GET requests can have a body, and some caching proxies | |
# might not treat that differently than a normal GET | |
# request, allowing form data to "invisibly" affect the | |
# cache without indication in the query string / URL. | |
sources.append(self.form) | |
args = [] | |
for d in sources: | |
if not isinstance(d, MultiDict): | |
d = MultiDict(d) | |
args.append(d) | |
return CombinedMultiDict(args) | |
def files(self) -> ImmutableMultiDict[str, FileStorage]: | |
""":class:`~werkzeug.datastructures.MultiDict` object containing | |
all uploaded files. Each key in :attr:`files` is the name from the | |
``<input type="file" name="">``. Each value in :attr:`files` is a | |
Werkzeug :class:`~werkzeug.datastructures.FileStorage` object. | |
It basically behaves like a standard file object you know from Python, | |
with the difference that it also has a | |
:meth:`~werkzeug.datastructures.FileStorage.save` function that can | |
store the file on the filesystem. | |
Note that :attr:`files` will only contain data if the request method was | |
POST, PUT or PATCH and the ``<form>`` that posted to the request had | |
``enctype="multipart/form-data"``. It will be empty otherwise. | |
See the :class:`~werkzeug.datastructures.MultiDict` / | |
:class:`~werkzeug.datastructures.FileStorage` documentation for | |
more details about the used data structure. | |
""" | |
self._load_form_data() | |
return self.files | |
def script_root(self) -> str: | |
"""Alias for :attr:`self.root_path`. ``environ["SCRIPT_ROOT"]`` | |
without a trailing slash. | |
""" | |
return self.root_path | |
def url_root(self) -> str: | |
"""Alias for :attr:`root_url`. The URL with scheme, host, and | |
root path. For example, ``https://example.com/app/``. | |
""" | |
return self.root_url | |
remote_user = environ_property[str]( | |
"REMOTE_USER", | |
doc="""If the server supports user authentication, and the | |
script is protected, this attribute contains the username the | |
user has authenticated as.""", | |
) | |
is_multithread = environ_property[bool]( | |
"wsgi.multithread", | |
doc="""boolean that is `True` if the application is served by a | |
multithreaded WSGI server.""", | |
) | |
is_multiprocess = environ_property[bool]( | |
"wsgi.multiprocess", | |
doc="""boolean that is `True` if the application is served by a | |
WSGI server that spawns multiple processes.""", | |
) | |
is_run_once = environ_property[bool]( | |
"wsgi.run_once", | |
doc="""boolean that is `True` if the application will be | |
executed only once in a process lifetime. This is the case for | |
CGI for example, but it's not guaranteed that the execution only | |
happens one time.""", | |
) | |
# JSON | |
#: A module or other object that has ``dumps`` and ``loads`` | |
#: functions that match the API of the built-in :mod:`json` module. | |
json_module = json | |
def json(self) -> t.Any | None: | |
"""The parsed JSON data if :attr:`mimetype` indicates JSON | |
(:mimetype:`application/json`, see :attr:`is_json`). | |
Calls :meth:`get_json` with default arguments. | |
If the request content type is not ``application/json``, this | |
will raise a 415 Unsupported Media Type error. | |
.. versionchanged:: 2.3 | |
Raise a 415 error instead of 400. | |
.. versionchanged:: 2.1 | |
Raise a 400 error if the content type is incorrect. | |
""" | |
return self.get_json() | |
# Cached values for ``(silent=False, silent=True)``. Initialized | |
# with sentinel values. | |
_cached_json: tuple[t.Any, t.Any] = (Ellipsis, Ellipsis) | |
def get_json( | |
self, force: bool = ..., silent: t.Literal[False] = ..., cache: bool = ... | |
) -> t.Any: ... | |
def get_json( | |
self, force: bool = ..., silent: bool = ..., cache: bool = ... | |
) -> t.Any | None: ... | |
def get_json( | |
self, force: bool = False, silent: bool = False, cache: bool = True | |
) -> t.Any | None: | |
"""Parse :attr:`data` as JSON. | |
If the mimetype does not indicate JSON | |
(:mimetype:`application/json`, see :attr:`is_json`), or parsing | |
fails, :meth:`on_json_loading_failed` is called and | |
its return value is used as the return value. By default this | |
raises a 415 Unsupported Media Type resp. | |
:param force: Ignore the mimetype and always try to parse JSON. | |
:param silent: Silence mimetype and parsing errors, and | |
return ``None`` instead. | |
:param cache: Store the parsed JSON to return for subsequent | |
calls. | |
.. versionchanged:: 2.3 | |
Raise a 415 error instead of 400. | |
.. versionchanged:: 2.1 | |
Raise a 400 error if the content type is incorrect. | |
""" | |
if cache and self._cached_json[silent] is not Ellipsis: | |
return self._cached_json[silent] | |
if not (force or self.is_json): | |
if not silent: | |
return self.on_json_loading_failed(None) | |
else: | |
return None | |
data = self.get_data(cache=cache) | |
try: | |
rv = self.json_module.loads(data) | |
except ValueError as e: | |
if silent: | |
rv = None | |
if cache: | |
normal_rv, _ = self._cached_json | |
self._cached_json = (normal_rv, rv) | |
else: | |
rv = self.on_json_loading_failed(e) | |
if cache: | |
_, silent_rv = self._cached_json | |
self._cached_json = (rv, silent_rv) | |
else: | |
if cache: | |
self._cached_json = (rv, rv) | |
return rv | |
def on_json_loading_failed(self, e: ValueError | None) -> t.Any: | |
"""Called if :meth:`get_json` fails and isn't silenced. | |
If this method returns a value, it is used as the return value | |
for :meth:`get_json`. The default implementation raises | |
:exc:`~werkzeug.exceptions.BadRequest`. | |
:param e: If parsing failed, this is the exception. It will be | |
``None`` if the content type wasn't ``application/json``. | |
.. versionchanged:: 2.3 | |
Raise a 415 error instead of 400. | |
""" | |
if e is not None: | |
raise BadRequest(f"Failed to decode JSON object: {e}") | |
raise UnsupportedMediaType( | |
"Did not attempt to load JSON data because the request" | |
" Content-Type was not 'application/json'." | |
) | |