1 from __future__ import annotations 2 3 import typing as t 4 5 from werkzeug.exceptions import BadRequest 6 from werkzeug.exceptions import HTTPException 7 from werkzeug.wrappers import Request as RequestBase 8 from werkzeug.wrappers import Response as ResponseBase 9 10 from . import json 11 from .globals import current_app 12 from .helpers import _split_blueprint_path 13 14 if t.TYPE_CHECKING: # pragma: no cover 15 from werkzeug.routing import Rule 16 17 18 class Request(RequestBase): 19 """The request object used by default in Flask. Remembers the 20 matched endpoint and view arguments. 21 22 It is what ends up as :class:`~flask.request`. If you want to replace 23 the request object used you can subclass this and set 24 :attr:`~flask.Flask.request_class` to your subclass. 25 26 The request object is a :class:`~werkzeug.wrappers.Request` subclass and 27 provides all of the attributes Werkzeug defines plus a few Flask 28 specific ones. 29 """ 30 31 json_module: t.Any = json 32 33 #: The internal URL rule that matched the request. This can be 34 #: useful to inspect which methods are allowed for the URL from 35 #: a before/after handler (``request.url_rule.methods``) etc. 36 #: Though if the request's method was invalid for the URL rule, 37 #: the valid list is available in ``routing_exception.valid_methods`` 38 #: instead (an attribute of the Werkzeug exception 39 #: :exc:`~werkzeug.exceptions.MethodNotAllowed`) 40 #: because the request was never internally bound. 41 #: 42 #: .. versionadded:: 0.6 43 url_rule: Rule | None = None 44 45 #: A dict of view arguments that matched the request. If an exception 46 #: happened when matching, this will be ``None``. 47 view_args: dict[str, t.Any] | None = None 48 49 #: If matching the URL failed, this is the exception that will be 50 #: raised / was raised as part of the request handling. This is 51 #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or 52 #: something similar. 53 routing_exception: HTTPException | None = None 54 55 _max_content_length: int | None = None 56 _max_form_memory_size: int | None = None 57 _max_form_parts: int | None = None 58 59 @property 60 def max_content_length(self) -> int | None: 61 """The maximum number of bytes that will be read during this request. If 62 this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge` 63 error is raised. If it is set to ``None``, no limit is enforced at the 64 Flask application level. However, if it is ``None`` and the request has 65 no ``Content-Length`` header and the WSGI server does not indicate that 66 it terminates the stream, then no data is read to avoid an infinite 67 stream. 68 69 Each request defaults to the :data:`MAX_CONTENT_LENGTH` config, which 70 defaults to ``None``. It can be set on a specific ``request`` to apply 71 the limit to that specific view. This should be set appropriately based 72 on an application's or view's specific needs. 73 74 .. versionchanged:: 3.1 75 This can be set per-request. 76 77 .. versionchanged:: 0.6 78 This is configurable through Flask config. 79 """ 80 if self._max_content_length is not None: 81 return self._max_content_length 82 83 if not current_app: 84 return super().max_content_length 85 86 return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return] 87 88 @max_content_length.setter 89 def max_content_length(self, value: int | None) -> None: 90 self._max_content_length = value 91 92 @property 93 def max_form_memory_size(self) -> int | None: 94 """The maximum size in bytes any non-file form field may be in a 95 ``multipart/form-data`` body. If this limit is exceeded, a 413 96 :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it 97 is set to ``None``, no limit is enforced at the Flask application level. 98 99 Each request defaults to the :data:`MAX_FORM_MEMORY_SIZE` config, which 100 defaults to ``500_000``. It can be set on a specific ``request`` to 101 apply the limit to that specific view. This should be set appropriately 102 based on an application's or view's specific needs. 103 104 .. versionchanged:: 3.1 105 This is configurable through Flask config. 106 """ 107 if self._max_form_memory_size is not None: 108 return self._max_form_memory_size 109 110 if not current_app: 111 return super().max_form_memory_size 112 113 return current_app.config["MAX_FORM_MEMORY_SIZE"] # type: ignore[no-any-return] 114 115 @max_form_memory_size.setter 116 def max_form_memory_size(self, value: int | None) -> None: 117 self._max_form_memory_size = value 118 119 @property # type: ignore[override] 120 def max_form_parts(self) -> int | None: 121 """The maximum number of fields that may be present in a 122 ``multipart/form-data`` body. If this limit is exceeded, a 413 123 :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it 124 is set to ``None``, no limit is enforced at the Flask application level. 125 126 Each request defaults to the :data:`MAX_FORM_PARTS` config, which 127 defaults to ``1_000``. It can be set on a specific ``request`` to apply 128 the limit to that specific view. This should be set appropriately based 129 on an application's or view's specific needs. 130 131 .. versionchanged:: 3.1 132 This is configurable through Flask config. 133 """ 134 if self._max_form_parts is not None: 135 return self._max_form_parts 136 137 if not current_app: 138 return super().max_form_parts 139 140 return current_app.config["MAX_FORM_PARTS"] # type: ignore[no-any-return] 141 142 @max_form_parts.setter 143 def max_form_parts(self, value: int | None) -> None: 144 self._max_form_parts = value 145 146 @property 147 def endpoint(self) -> str | None: 148 """The endpoint that matched the request URL. 149 150 This will be ``None`` if matching failed or has not been 151 performed yet. 152 153 This in combination with :attr:`view_args` can be used to 154 reconstruct the same URL or a modified URL. 155 """ 156 if self.url_rule is not None: 157 return self.url_rule.endpoint # type: ignore[no-any-return] 158 159 return None 160 161 @property 162 def blueprint(self) -> str | None: 163 """The registered name of the current blueprint. 164 165 This will be ``None`` if the endpoint is not part of a 166 blueprint, or if URL matching failed or has not been performed 167 yet. 168 169 This does not necessarily match the name the blueprint was 170 created with. It may have been nested, or registered with a 171 different name. 172 """ 173 endpoint = self.endpoint 174 175 if endpoint is not None and "." in endpoint: 176 return endpoint.rpartition(".")[0] 177 178 return None 179 180 @property 181 def blueprints(self) -> list[str]: 182 """The registered names of the current blueprint upwards through 183 parent blueprints. 184 185 This will be an empty list if there is no current blueprint, or 186 if URL matching failed. 187 188 .. versionadded:: 2.0.1 189 """ 190 name = self.blueprint 191 192 if name is None: 193 return [] 194 195 return _split_blueprint_path(name) 196 197 def _load_form_data(self) -> None: 198 super()._load_form_data() 199 200 # In debug mode we're replacing the files multidict with an ad-hoc 201 # subclass that raises a different error for key errors. 202 if ( 203 current_app 204 and current_app.debug 205 and self.mimetype != "multipart/form-data" 206 and not self.files 207 ): 208 from .debughelpers import attach_enctype_error_multidict 209 210 attach_enctype_error_multidict(self) 211 212 def on_json_loading_failed(self, e: ValueError | None) -> t.Any: 213 try: 214 return super().on_json_loading_failed(e) 215 except BadRequest as ebr: 216 if current_app and current_app.debug: 217 raise 218 219 raise BadRequest() from ebr 220 221 222 class Response(ResponseBase): 223 """The response object that is used by default in Flask. Works like the 224 response object from Werkzeug but is set to have an HTML mimetype by 225 default. Quite often you don't have to create this object yourself because 226 :meth:`~flask.Flask.make_response` will take care of that for you. 227 228 If you want to replace the response object used you can subclass this and 229 set :attr:`~flask.Flask.response_class` to your subclass. 230 231 .. versionchanged:: 1.0 232 JSON support is added to the response, like the request. This is useful 233 when testing to get the test client response data as JSON. 234 235 .. versionchanged:: 1.0 236 237 Added :attr:`max_cookie_size`. 238 """ 239 240 default_mimetype: str | None = "text/html" 241 242 json_module = json 243 244 autocorrect_location_header = False 245 246 @property 247 def max_cookie_size(self) -> int: # type: ignore 248 """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. 249 250 See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in 251 Werkzeug's docs. 252 """ 253 if current_app: 254 return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return] 255 256 # return Werkzeug's default when not in an app context 257 return super().max_cookie_size 258