Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/werkzeug/wrappers/request.py: 59%
182 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-09 06:08 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-09 06:08 +0000
1from __future__ import annotations
3import functools
4import json
5import typing as t
6from io import BytesIO
8from .._internal import _wsgi_decoding_dance
9from ..datastructures import CombinedMultiDict
10from ..datastructures import EnvironHeaders
11from ..datastructures import FileStorage
12from ..datastructures import ImmutableMultiDict
13from ..datastructures import iter_multi_items
14from ..datastructures import MultiDict
15from ..exceptions import BadRequest
16from ..exceptions import UnsupportedMediaType
17from ..formparser import default_stream_factory
18from ..formparser import FormDataParser
19from ..sansio.request import Request as _SansIORequest
20from ..utils import cached_property
21from ..utils import environ_property
22from ..wsgi import _get_server
23from ..wsgi import get_input_stream
25if t.TYPE_CHECKING:
26 from _typeshed.wsgi import WSGIApplication
27 from _typeshed.wsgi import WSGIEnvironment
30class Request(_SansIORequest):
31 """Represents an incoming WSGI HTTP request, with headers and body
32 taken from the WSGI environment. Has properties and methods for
33 using the functionality defined by various HTTP specs. The data in
34 requests object is read-only.
36 Text data is assumed to use UTF-8 encoding, which should be true for
37 the vast majority of modern clients. Using an encoding set by the
38 client is unsafe in Python due to extra encodings it provides, such
39 as ``zip``. To change the assumed encoding, subclass and replace
40 :attr:`charset`.
42 :param environ: The WSGI environ is generated by the WSGI server and
43 contains information about the server configuration and client
44 request.
45 :param populate_request: Add this request object to the WSGI environ
46 as ``environ['werkzeug.request']``. Can be useful when
47 debugging.
48 :param shallow: Makes reading from :attr:`stream` (and any method
49 that would read from it) raise a :exc:`RuntimeError`. Useful to
50 prevent consuming the form data in middleware, which would make
51 it unavailable to the final application.
53 .. versionchanged:: 2.1
54 Old ``BaseRequest`` and mixin classes were removed.
56 .. versionchanged:: 2.1
57 Remove the ``disable_data_descriptor`` attribute.
59 .. versionchanged:: 2.0
60 Combine ``BaseRequest`` and mixins into a single ``Request``
61 class.
63 .. versionchanged:: 0.5
64 Read-only mode is enforced with immutable classes for all data.
65 """
67 #: the maximum content length. This is forwarded to the form data
68 #: parsing function (:func:`parse_form_data`). When set and the
69 #: :attr:`form` or :attr:`files` attribute is accessed and the
70 #: parsing fails because more than the specified value is transmitted
71 #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised.
72 #:
73 #: .. versionadded:: 0.5
74 max_content_length: int | None = None
76 #: the maximum form field size. This is forwarded to the form data
77 #: parsing function (:func:`parse_form_data`). When set and the
78 #: :attr:`form` or :attr:`files` attribute is accessed and the
79 #: data in memory for post data is longer than the specified value a
80 #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised.
81 #:
82 #: .. versionadded:: 0.5
83 max_form_memory_size: int | None = None
85 #: The maximum number of multipart parts to parse, passed to
86 #: :attr:`form_data_parser_class`. Parsing form data with more than this
87 #: many parts will raise :exc:`~.RequestEntityTooLarge`.
88 #:
89 #: .. versionadded:: 2.2.3
90 max_form_parts = 1000
92 #: The form data parser that should be used. Can be replaced to customize
93 #: the form date parsing.
94 form_data_parser_class: type[FormDataParser] = FormDataParser
96 #: The WSGI environment containing HTTP headers and information from
97 #: the WSGI server.
98 environ: WSGIEnvironment
100 #: Set when creating the request object. If ``True``, reading from
101 #: the request body will cause a ``RuntimeException``. Useful to
102 #: prevent modifying the stream from middleware.
103 shallow: bool
105 def __init__(
106 self,
107 environ: WSGIEnvironment,
108 populate_request: bool = True,
109 shallow: bool = False,
110 ) -> None:
111 super().__init__(
112 method=environ.get("REQUEST_METHOD", "GET"),
113 scheme=environ.get("wsgi.url_scheme", "http"),
114 server=_get_server(environ),
115 root_path=_wsgi_decoding_dance(environ.get("SCRIPT_NAME") or ""),
116 path=_wsgi_decoding_dance(environ.get("PATH_INFO") or ""),
117 query_string=environ.get("QUERY_STRING", "").encode("latin1"),
118 headers=EnvironHeaders(environ),
119 remote_addr=environ.get("REMOTE_ADDR"),
120 )
121 self.environ = environ
122 self.shallow = shallow
124 if populate_request and not shallow:
125 self.environ["werkzeug.request"] = self
127 @classmethod
128 def from_values(cls, *args: t.Any, **kwargs: t.Any) -> Request:
129 """Create a new request object based on the values provided. If
130 environ is given missing values are filled from there. This method is
131 useful for small scripts when you need to simulate a request from an URL.
132 Do not use this method for unittesting, there is a full featured client
133 object (:class:`Client`) that allows to create multipart requests,
134 support for cookies etc.
136 This accepts the same options as the
137 :class:`~werkzeug.test.EnvironBuilder`.
139 .. versionchanged:: 0.5
140 This method now accepts the same arguments as
141 :class:`~werkzeug.test.EnvironBuilder`. Because of this the
142 `environ` parameter is now called `environ_overrides`.
144 :return: request object
145 """
146 from ..test import EnvironBuilder
148 kwargs.setdefault(
149 "charset", cls.charset if not isinstance(cls.charset, property) else None
150 )
151 builder = EnvironBuilder(*args, **kwargs)
152 try:
153 return builder.get_request(cls)
154 finally:
155 builder.close()
157 @classmethod
158 def application(cls, f: t.Callable[[Request], WSGIApplication]) -> WSGIApplication:
159 """Decorate a function as responder that accepts the request as
160 the last argument. This works like the :func:`responder`
161 decorator but the function is passed the request object as the
162 last argument and the request object will be closed
163 automatically::
165 @Request.application
166 def my_wsgi_app(request):
167 return Response('Hello World!')
169 As of Werkzeug 0.14 HTTP exceptions are automatically caught and
170 converted to responses instead of failing.
172 :param f: the WSGI callable to decorate
173 :return: a new WSGI callable
174 """
175 #: return a callable that wraps the -2nd argument with the request
176 #: and calls the function with all the arguments up to that one and
177 #: the request. The return value is then called with the latest
178 #: two arguments. This makes it possible to use this decorator for
179 #: both standalone WSGI functions as well as bound methods and
180 #: partially applied functions.
181 from ..exceptions import HTTPException
183 @functools.wraps(f)
184 def application(*args): # type: ignore
185 request = cls(args[-2])
186 with request:
187 try:
188 resp = f(*args[:-2] + (request,))
189 except HTTPException as e:
190 resp = e.get_response(args[-2])
191 return resp(*args[-2:])
193 return t.cast("WSGIApplication", application)
195 def _get_file_stream(
196 self,
197 total_content_length: int | None,
198 content_type: str | None,
199 filename: str | None = None,
200 content_length: int | None = None,
201 ) -> t.IO[bytes]:
202 """Called to get a stream for the file upload.
204 This must provide a file-like class with `read()`, `readline()`
205 and `seek()` methods that is both writeable and readable.
207 The default implementation returns a temporary file if the total
208 content length is higher than 500KB. Because many browsers do not
209 provide a content length for the files only the total content
210 length matters.
212 :param total_content_length: the total content length of all the
213 data in the request combined. This value
214 is guaranteed to be there.
215 :param content_type: the mimetype of the uploaded file.
216 :param filename: the filename of the uploaded file. May be `None`.
217 :param content_length: the length of this file. This value is usually
218 not provided because webbrowsers do not provide
219 this value.
220 """
221 return default_stream_factory(
222 total_content_length=total_content_length,
223 filename=filename,
224 content_type=content_type,
225 content_length=content_length,
226 )
228 @property
229 def want_form_data_parsed(self) -> bool:
230 """``True`` if the request method carries content. By default
231 this is true if a ``Content-Type`` is sent.
233 .. versionadded:: 0.8
234 """
235 return bool(self.environ.get("CONTENT_TYPE"))
237 def make_form_data_parser(self) -> FormDataParser:
238 """Creates the form data parser. Instantiates the
239 :attr:`form_data_parser_class` with some parameters.
241 .. versionadded:: 0.8
242 """
243 charset = self._charset if self._charset != "utf-8" else None
244 errors = self._encoding_errors if self._encoding_errors != "replace" else None
245 return self.form_data_parser_class(
246 stream_factory=self._get_file_stream,
247 charset=charset,
248 errors=errors,
249 max_form_memory_size=self.max_form_memory_size,
250 max_content_length=self.max_content_length,
251 max_form_parts=self.max_form_parts,
252 cls=self.parameter_storage_class,
253 )
255 def _load_form_data(self) -> None:
256 """Method used internally to retrieve submitted data. After calling
257 this sets `form` and `files` on the request object to multi dicts
258 filled with the incoming form data. As a matter of fact the input
259 stream will be empty afterwards. You can also call this method to
260 force the parsing of the form data.
262 .. versionadded:: 0.8
263 """
264 # abort early if we have already consumed the stream
265 if "form" in self.__dict__:
266 return
268 if self.want_form_data_parsed:
269 parser = self.make_form_data_parser()
270 data = parser.parse(
271 self._get_stream_for_parsing(),
272 self.mimetype,
273 self.content_length,
274 self.mimetype_params,
275 )
276 else:
277 data = (
278 self.stream,
279 self.parameter_storage_class(),
280 self.parameter_storage_class(),
281 )
283 # inject the values into the instance dict so that we bypass
284 # our cached_property non-data descriptor.
285 d = self.__dict__
286 d["stream"], d["form"], d["files"] = data
288 def _get_stream_for_parsing(self) -> t.IO[bytes]:
289 """This is the same as accessing :attr:`stream` with the difference
290 that if it finds cached data from calling :meth:`get_data` first it
291 will create a new stream out of the cached data.
293 .. versionadded:: 0.9.3
294 """
295 cached_data = getattr(self, "_cached_data", None)
296 if cached_data is not None:
297 return BytesIO(cached_data)
298 return self.stream
300 def close(self) -> None:
301 """Closes associated resources of this request object. This
302 closes all file handles explicitly. You can also use the request
303 object in a with statement which will automatically close it.
305 .. versionadded:: 0.9
306 """
307 files = self.__dict__.get("files")
308 for _key, value in iter_multi_items(files or ()):
309 value.close()
311 def __enter__(self) -> Request:
312 return self
314 def __exit__(self, exc_type, exc_value, tb) -> None: # type: ignore
315 self.close()
317 @cached_property
318 def stream(self) -> t.IO[bytes]:
319 """The WSGI input stream, with safety checks. This stream can only be consumed
320 once.
322 Use :meth:`get_data` to get the full data as bytes or text. The :attr:`data`
323 attribute will contain the full bytes only if they do not represent form data.
324 The :attr:`form` attribute will contain the parsed form data in that case.
326 Unlike :attr:`input_stream`, this stream guards against infinite streams or
327 reading past :attr:`content_length` or :attr:`max_content_length`.
329 If ``max_content_length`` is set, it can be enforced on streams if
330 ``wsgi.input_terminated`` is set. Otherwise, an empty stream is returned.
332 If the limit is reached before the underlying stream is exhausted (such as a
333 file that is too large, or an infinite stream), the remaining contents of the
334 stream cannot be read safely. Depending on how the server handles this, clients
335 may show a "connection reset" failure instead of seeing the 413 response.
337 .. versionchanged:: 2.3
338 Check ``max_content_length`` preemptively and while reading.
340 .. versionchanged:: 0.9
341 The stream is always set (but may be consumed) even if form parsing was
342 accessed first.
343 """
344 if self.shallow:
345 raise RuntimeError(
346 "This request was created with 'shallow=True', reading"
347 " from the input stream is disabled."
348 )
350 return get_input_stream(
351 self.environ, max_content_length=self.max_content_length
352 )
354 input_stream = environ_property[t.IO[bytes]](
355 "wsgi.input",
356 doc="""The raw WSGI input stream, without any safety checks.
358 This is dangerous to use. It does not guard against infinite streams or reading
359 past :attr:`content_length` or :attr:`max_content_length`.
361 Use :attr:`stream` instead.
362 """,
363 )
365 @cached_property
366 def data(self) -> bytes:
367 """The raw data read from :attr:`stream`. Will be empty if the request
368 represents form data.
370 To get the raw data even if it represents form data, use :meth:`get_data`.
371 """
372 return self.get_data(parse_form_data=True)
374 @t.overload
375 def get_data( # type: ignore
376 self,
377 cache: bool = True,
378 as_text: t.Literal[False] = False,
379 parse_form_data: bool = False,
380 ) -> bytes:
381 ...
383 @t.overload
384 def get_data(
385 self,
386 cache: bool = True,
387 as_text: t.Literal[True] = ...,
388 parse_form_data: bool = False,
389 ) -> str:
390 ...
392 def get_data(
393 self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False
394 ) -> bytes | str:
395 """This reads the buffered incoming data from the client into one
396 bytes object. By default this is cached but that behavior can be
397 changed by setting `cache` to `False`.
399 Usually it's a bad idea to call this method without checking the
400 content length first as a client could send dozens of megabytes or more
401 to cause memory problems on the server.
403 Note that if the form data was already parsed this method will not
404 return anything as form data parsing does not cache the data like
405 this method does. To implicitly invoke form data parsing function
406 set `parse_form_data` to `True`. When this is done the return value
407 of this method will be an empty string if the form parser handles
408 the data. This generally is not necessary as if the whole data is
409 cached (which is the default) the form parser will used the cached
410 data to parse the form data. Please be generally aware of checking
411 the content length first in any case before calling this method
412 to avoid exhausting server memory.
414 If `as_text` is set to `True` the return value will be a decoded
415 string.
417 .. versionadded:: 0.9
418 """
419 rv = getattr(self, "_cached_data", None)
420 if rv is None:
421 if parse_form_data:
422 self._load_form_data()
423 rv = self.stream.read()
424 if cache:
425 self._cached_data = rv
426 if as_text:
427 rv = rv.decode(self._charset, self._encoding_errors)
428 return rv
430 @cached_property
431 def form(self) -> ImmutableMultiDict[str, str]:
432 """The form parameters. By default an
433 :class:`~werkzeug.datastructures.ImmutableMultiDict`
434 is returned from this function. This can be changed by setting
435 :attr:`parameter_storage_class` to a different type. This might
436 be necessary if the order of the form data is important.
438 Please keep in mind that file uploads will not end up here, but instead
439 in the :attr:`files` attribute.
441 .. versionchanged:: 0.9
443 Previous to Werkzeug 0.9 this would only contain form data for POST
444 and PUT requests.
445 """
446 self._load_form_data()
447 return self.form
449 @cached_property
450 def values(self) -> CombinedMultiDict[str, str]:
451 """A :class:`werkzeug.datastructures.CombinedMultiDict` that
452 combines :attr:`args` and :attr:`form`.
454 For GET requests, only ``args`` are present, not ``form``.
456 .. versionchanged:: 2.0
457 For GET requests, only ``args`` are present, not ``form``.
458 """
459 sources = [self.args]
461 if self.method != "GET":
462 # GET requests can have a body, and some caching proxies
463 # might not treat that differently than a normal GET
464 # request, allowing form data to "invisibly" affect the
465 # cache without indication in the query string / URL.
466 sources.append(self.form)
468 args = []
470 for d in sources:
471 if not isinstance(d, MultiDict):
472 d = MultiDict(d)
474 args.append(d)
476 return CombinedMultiDict(args)
478 @cached_property
479 def files(self) -> ImmutableMultiDict[str, FileStorage]:
480 """:class:`~werkzeug.datastructures.MultiDict` object containing
481 all uploaded files. Each key in :attr:`files` is the name from the
482 ``<input type="file" name="">``. Each value in :attr:`files` is a
483 Werkzeug :class:`~werkzeug.datastructures.FileStorage` object.
485 It basically behaves like a standard file object you know from Python,
486 with the difference that it also has a
487 :meth:`~werkzeug.datastructures.FileStorage.save` function that can
488 store the file on the filesystem.
490 Note that :attr:`files` will only contain data if the request method was
491 POST, PUT or PATCH and the ``<form>`` that posted to the request had
492 ``enctype="multipart/form-data"``. It will be empty otherwise.
494 See the :class:`~werkzeug.datastructures.MultiDict` /
495 :class:`~werkzeug.datastructures.FileStorage` documentation for
496 more details about the used data structure.
497 """
498 self._load_form_data()
499 return self.files
501 @property
502 def script_root(self) -> str:
503 """Alias for :attr:`self.root_path`. ``environ["SCRIPT_ROOT"]``
504 without a trailing slash.
505 """
506 return self.root_path
508 @cached_property
509 def url_root(self) -> str:
510 """Alias for :attr:`root_url`. The URL with scheme, host, and
511 root path. For example, ``https://example.com/app/``.
512 """
513 return self.root_url
515 remote_user = environ_property[str](
516 "REMOTE_USER",
517 doc="""If the server supports user authentication, and the
518 script is protected, this attribute contains the username the
519 user has authenticated as.""",
520 )
521 is_multithread = environ_property[bool](
522 "wsgi.multithread",
523 doc="""boolean that is `True` if the application is served by a
524 multithreaded WSGI server.""",
525 )
526 is_multiprocess = environ_property[bool](
527 "wsgi.multiprocess",
528 doc="""boolean that is `True` if the application is served by a
529 WSGI server that spawns multiple processes.""",
530 )
531 is_run_once = environ_property[bool](
532 "wsgi.run_once",
533 doc="""boolean that is `True` if the application will be
534 executed only once in a process lifetime. This is the case for
535 CGI for example, but it's not guaranteed that the execution only
536 happens one time.""",
537 )
539 # JSON
541 #: A module or other object that has ``dumps`` and ``loads``
542 #: functions that match the API of the built-in :mod:`json` module.
543 json_module = json
545 @property
546 def json(self) -> t.Any | None:
547 """The parsed JSON data if :attr:`mimetype` indicates JSON
548 (:mimetype:`application/json`, see :attr:`is_json`).
550 Calls :meth:`get_json` with default arguments.
552 If the request content type is not ``application/json``, this
553 will raise a 415 Unsupported Media Type error.
555 .. versionchanged:: 2.3
556 Raise a 415 error instead of 400.
558 .. versionchanged:: 2.1
559 Raise a 400 error if the content type is incorrect.
560 """
561 return self.get_json()
563 # Cached values for ``(silent=False, silent=True)``. Initialized
564 # with sentinel values.
565 _cached_json: tuple[t.Any, t.Any] = (Ellipsis, Ellipsis)
567 @t.overload
568 def get_json(
569 self, force: bool = ..., silent: t.Literal[False] = ..., cache: bool = ...
570 ) -> t.Any:
571 ...
573 @t.overload
574 def get_json(
575 self, force: bool = ..., silent: bool = ..., cache: bool = ...
576 ) -> t.Any | None:
577 ...
579 def get_json(
580 self, force: bool = False, silent: bool = False, cache: bool = True
581 ) -> t.Any | None:
582 """Parse :attr:`data` as JSON.
584 If the mimetype does not indicate JSON
585 (:mimetype:`application/json`, see :attr:`is_json`), or parsing
586 fails, :meth:`on_json_loading_failed` is called and
587 its return value is used as the return value. By default this
588 raises a 415 Unsupported Media Type resp.
590 :param force: Ignore the mimetype and always try to parse JSON.
591 :param silent: Silence mimetype and parsing errors, and
592 return ``None`` instead.
593 :param cache: Store the parsed JSON to return for subsequent
594 calls.
596 .. versionchanged:: 2.3
597 Raise a 415 error instead of 400.
599 .. versionchanged:: 2.1
600 Raise a 400 error if the content type is incorrect.
601 """
602 if cache and self._cached_json[silent] is not Ellipsis:
603 return self._cached_json[silent]
605 if not (force or self.is_json):
606 if not silent:
607 return self.on_json_loading_failed(None)
608 else:
609 return None
611 data = self.get_data(cache=cache)
613 try:
614 rv = self.json_module.loads(data)
615 except ValueError as e:
616 if silent:
617 rv = None
619 if cache:
620 normal_rv, _ = self._cached_json
621 self._cached_json = (normal_rv, rv)
622 else:
623 rv = self.on_json_loading_failed(e)
625 if cache:
626 _, silent_rv = self._cached_json
627 self._cached_json = (rv, silent_rv)
628 else:
629 if cache:
630 self._cached_json = (rv, rv)
632 return rv
634 def on_json_loading_failed(self, e: ValueError | None) -> t.Any:
635 """Called if :meth:`get_json` fails and isn't silenced.
637 If this method returns a value, it is used as the return value
638 for :meth:`get_json`. The default implementation raises
639 :exc:`~werkzeug.exceptions.BadRequest`.
641 :param e: If parsing failed, this is the exception. It will be
642 ``None`` if the content type wasn't ``application/json``.
644 .. versionchanged:: 2.3
645 Raise a 415 error instead of 400.
646 """
647 if e is not None:
648 raise BadRequest(f"Failed to decode JSON object: {e}")
650 raise UnsupportedMediaType(
651 "Did not attempt to load JSON data because the request"
652 " Content-Type was not 'application/json'."
653 )