Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/werkzeug/wrappers/request.py: 41%

174 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1import functools 

2import json 

3import typing 

4import typing as t 

5from io import BytesIO 

6 

7from .._internal import _wsgi_decoding_dance 

8from ..datastructures import CombinedMultiDict 

9from ..datastructures import EnvironHeaders 

10from ..datastructures import FileStorage 

11from ..datastructures import ImmutableMultiDict 

12from ..datastructures import iter_multi_items 

13from ..datastructures import MultiDict 

14from ..formparser import default_stream_factory 

15from ..formparser import FormDataParser 

16from ..sansio.request import Request as _SansIORequest 

17from ..utils import cached_property 

18from ..utils import environ_property 

19from ..wsgi import _get_server 

20from ..wsgi import get_input_stream 

21from werkzeug.exceptions import BadRequest 

22 

23if t.TYPE_CHECKING: 

24 import typing_extensions as te 

25 from _typeshed.wsgi import WSGIApplication 

26 from _typeshed.wsgi import WSGIEnvironment 

27 

28 

29class Request(_SansIORequest): 

30 """Represents an incoming WSGI HTTP request, with headers and body 

31 taken from the WSGI environment. Has properties and methods for 

32 using the functionality defined by various HTTP specs. The data in 

33 requests object is read-only. 

34 

35 Text data is assumed to use UTF-8 encoding, which should be true for 

36 the vast majority of modern clients. Using an encoding set by the 

37 client is unsafe in Python due to extra encodings it provides, such 

38 as ``zip``. To change the assumed encoding, subclass and replace 

39 :attr:`charset`. 

40 

41 :param environ: The WSGI environ is generated by the WSGI server and 

42 contains information about the server configuration and client 

43 request. 

44 :param populate_request: Add this request object to the WSGI environ 

45 as ``environ['werkzeug.request']``. Can be useful when 

46 debugging. 

47 :param shallow: Makes reading from :attr:`stream` (and any method 

48 that would read from it) raise a :exc:`RuntimeError`. Useful to 

49 prevent consuming the form data in middleware, which would make 

50 it unavailable to the final application. 

51 

52 .. versionchanged:: 2.1 

53 Remove the ``disable_data_descriptor`` attribute. 

54 

55 .. versionchanged:: 2.0 

56 Combine ``BaseRequest`` and mixins into a single ``Request`` 

57 class. Using the old classes is deprecated and will be removed 

58 in Werkzeug 2.1. 

59 

60 .. versionchanged:: 0.5 

61 Read-only mode is enforced with immutable classes for all data. 

62 """ 

63 

64 #: the maximum content length. This is forwarded to the form data 

65 #: parsing function (:func:`parse_form_data`). When set and the 

66 #: :attr:`form` or :attr:`files` attribute is accessed and the 

67 #: parsing fails because more than the specified value is transmitted 

68 #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. 

69 #: 

70 #: Have a look at :doc:`/request_data` for more details. 

71 #: 

72 #: .. versionadded:: 0.5 

73 max_content_length: t.Optional[int] = None 

74 

75 #: the maximum form field size. This is forwarded to the form data 

76 #: parsing function (:func:`parse_form_data`). When set and the 

77 #: :attr:`form` or :attr:`files` attribute is accessed and the 

78 #: data in memory for post data is longer than the specified value a 

79 #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. 

80 #: 

81 #: Have a look at :doc:`/request_data` for more details. 

82 #: 

83 #: .. versionadded:: 0.5 

84 max_form_memory_size: t.Optional[int] = None 

85 

86 #: The form data parser that should be used. Can be replaced to customize 

87 #: the form date parsing. 

88 form_data_parser_class: t.Type[FormDataParser] = FormDataParser 

89 

90 #: The WSGI environment containing HTTP headers and information from 

91 #: the WSGI server. 

92 environ: "WSGIEnvironment" 

93 

94 #: Set when creating the request object. If ``True``, reading from 

95 #: the request body will cause a ``RuntimeException``. Useful to 

96 #: prevent modifying the stream from middleware. 

97 shallow: bool 

98 

99 def __init__( 

100 self, 

101 environ: "WSGIEnvironment", 

102 populate_request: bool = True, 

103 shallow: bool = False, 

104 ) -> None: 

105 super().__init__( 

106 method=environ.get("REQUEST_METHOD", "GET"), 

107 scheme=environ.get("wsgi.url_scheme", "http"), 

108 server=_get_server(environ), 

109 root_path=_wsgi_decoding_dance( 

110 environ.get("SCRIPT_NAME") or "", self.charset, self.encoding_errors 

111 ), 

112 path=_wsgi_decoding_dance( 

113 environ.get("PATH_INFO") or "", self.charset, self.encoding_errors 

114 ), 

115 query_string=environ.get("QUERY_STRING", "").encode("latin1"), 

116 headers=EnvironHeaders(environ), 

117 remote_addr=environ.get("REMOTE_ADDR"), 

118 ) 

119 self.environ = environ 

120 self.shallow = shallow 

121 

122 if populate_request and not shallow: 

123 self.environ["werkzeug.request"] = self 

124 

125 @classmethod 

126 def from_values(cls, *args: t.Any, **kwargs: t.Any) -> "Request": 

127 """Create a new request object based on the values provided. If 

128 environ is given missing values are filled from there. This method is 

129 useful for small scripts when you need to simulate a request from an URL. 

130 Do not use this method for unittesting, there is a full featured client 

131 object (:class:`Client`) that allows to create multipart requests, 

132 support for cookies etc. 

133 

134 This accepts the same options as the 

135 :class:`~werkzeug.test.EnvironBuilder`. 

136 

137 .. versionchanged:: 0.5 

138 This method now accepts the same arguments as 

139 :class:`~werkzeug.test.EnvironBuilder`. Because of this the 

140 `environ` parameter is now called `environ_overrides`. 

141 

142 :return: request object 

143 """ 

144 from ..test import EnvironBuilder 

145 

146 charset = kwargs.pop("charset", cls.charset) 

147 kwargs["charset"] = charset 

148 builder = EnvironBuilder(*args, **kwargs) 

149 try: 

150 return builder.get_request(cls) 

151 finally: 

152 builder.close() 

153 

154 @classmethod 

155 def application( 

156 cls, f: t.Callable[["Request"], "WSGIApplication"] 

157 ) -> "WSGIApplication": 

158 """Decorate a function as responder that accepts the request as 

159 the last argument. This works like the :func:`responder` 

160 decorator but the function is passed the request object as the 

161 last argument and the request object will be closed 

162 automatically:: 

163 

164 @Request.application 

165 def my_wsgi_app(request): 

166 return Response('Hello World!') 

167 

168 As of Werkzeug 0.14 HTTP exceptions are automatically caught and 

169 converted to responses instead of failing. 

170 

171 :param f: the WSGI callable to decorate 

172 :return: a new WSGI callable 

173 """ 

174 #: return a callable that wraps the -2nd argument with the request 

175 #: and calls the function with all the arguments up to that one and 

176 #: the request. The return value is then called with the latest 

177 #: two arguments. This makes it possible to use this decorator for 

178 #: both standalone WSGI functions as well as bound methods and 

179 #: partially applied functions. 

180 from ..exceptions import HTTPException 

181 

182 @functools.wraps(f) 

183 def application(*args): # type: ignore 

184 request = cls(args[-2]) 

185 with request: 

186 try: 

187 resp = f(*args[:-2] + (request,)) 

188 except HTTPException as e: 

189 resp = e.get_response(args[-2]) 

190 return resp(*args[-2:]) 

191 

192 return t.cast("WSGIApplication", application) 

193 

194 def _get_file_stream( 

195 self, 

196 total_content_length: t.Optional[int], 

197 content_type: t.Optional[str], 

198 filename: t.Optional[str] = None, 

199 content_length: t.Optional[int] = None, 

200 ) -> t.IO[bytes]: 

201 """Called to get a stream for the file upload. 

202 

203 This must provide a file-like class with `read()`, `readline()` 

204 and `seek()` methods that is both writeable and readable. 

205 

206 The default implementation returns a temporary file if the total 

207 content length is higher than 500KB. Because many browsers do not 

208 provide a content length for the files only the total content 

209 length matters. 

210 

211 :param total_content_length: the total content length of all the 

212 data in the request combined. This value 

213 is guaranteed to be there. 

214 :param content_type: the mimetype of the uploaded file. 

215 :param filename: the filename of the uploaded file. May be `None`. 

216 :param content_length: the length of this file. This value is usually 

217 not provided because webbrowsers do not provide 

218 this value. 

219 """ 

220 return default_stream_factory( 

221 total_content_length=total_content_length, 

222 filename=filename, 

223 content_type=content_type, 

224 content_length=content_length, 

225 ) 

226 

227 @property 

228 def want_form_data_parsed(self) -> bool: 

229 """``True`` if the request method carries content. By default 

230 this is true if a ``Content-Type`` is sent. 

231 

232 .. versionadded:: 0.8 

233 """ 

234 return bool(self.environ.get("CONTENT_TYPE")) 

235 

236 def make_form_data_parser(self) -> FormDataParser: 

237 """Creates the form data parser. Instantiates the 

238 :attr:`form_data_parser_class` with some parameters. 

239 

240 .. versionadded:: 0.8 

241 """ 

242 return self.form_data_parser_class( 

243 self._get_file_stream, 

244 self.charset, 

245 self.encoding_errors, 

246 self.max_form_memory_size, 

247 self.max_content_length, 

248 self.parameter_storage_class, 

249 ) 

250 

251 def _load_form_data(self) -> None: 

252 """Method used internally to retrieve submitted data. After calling 

253 this sets `form` and `files` on the request object to multi dicts 

254 filled with the incoming form data. As a matter of fact the input 

255 stream will be empty afterwards. You can also call this method to 

256 force the parsing of the form data. 

257 

258 .. versionadded:: 0.8 

259 """ 

260 # abort early if we have already consumed the stream 

261 if "form" in self.__dict__: 

262 return 

263 

264 if self.want_form_data_parsed: 

265 parser = self.make_form_data_parser() 

266 data = parser.parse( 

267 self._get_stream_for_parsing(), 

268 self.mimetype, 

269 self.content_length, 

270 self.mimetype_params, 

271 ) 

272 else: 

273 data = ( 

274 self.stream, 

275 self.parameter_storage_class(), 

276 self.parameter_storage_class(), 

277 ) 

278 

279 # inject the values into the instance dict so that we bypass 

280 # our cached_property non-data descriptor. 

281 d = self.__dict__ 

282 d["stream"], d["form"], d["files"] = data 

283 

284 def _get_stream_for_parsing(self) -> t.IO[bytes]: 

285 """This is the same as accessing :attr:`stream` with the difference 

286 that if it finds cached data from calling :meth:`get_data` first it 

287 will create a new stream out of the cached data. 

288 

289 .. versionadded:: 0.9.3 

290 """ 

291 cached_data = getattr(self, "_cached_data", None) 

292 if cached_data is not None: 

293 return BytesIO(cached_data) 

294 return self.stream 

295 

296 def close(self) -> None: 

297 """Closes associated resources of this request object. This 

298 closes all file handles explicitly. You can also use the request 

299 object in a with statement which will automatically close it. 

300 

301 .. versionadded:: 0.9 

302 """ 

303 files = self.__dict__.get("files") 

304 for _key, value in iter_multi_items(files or ()): 

305 value.close() 

306 

307 def __enter__(self) -> "Request": 

308 return self 

309 

310 def __exit__(self, exc_type, exc_value, tb) -> None: # type: ignore 

311 self.close() 

312 

313 @cached_property 

314 def stream(self) -> t.IO[bytes]: 

315 """ 

316 If the incoming form data was not encoded with a known mimetype 

317 the data is stored unmodified in this stream for consumption. Most 

318 of the time it is a better idea to use :attr:`data` which will give 

319 you that data as a string. The stream only returns the data once. 

320 

321 Unlike :attr:`input_stream` this stream is properly guarded that you 

322 can't accidentally read past the length of the input. Werkzeug will 

323 internally always refer to this stream to read data which makes it 

324 possible to wrap this object with a stream that does filtering. 

325 

326 .. versionchanged:: 0.9 

327 This stream is now always available but might be consumed by the 

328 form parser later on. Previously the stream was only set if no 

329 parsing happened. 

330 """ 

331 if self.shallow: 

332 raise RuntimeError( 

333 "This request was created with 'shallow=True', reading" 

334 " from the input stream is disabled." 

335 ) 

336 

337 return get_input_stream(self.environ) 

338 

339 input_stream = environ_property[t.IO[bytes]]( 

340 "wsgi.input", 

341 doc="""The WSGI input stream. 

342 

343 In general it's a bad idea to use this one because you can 

344 easily read past the boundary. Use the :attr:`stream` 

345 instead.""", 

346 ) 

347 

348 @cached_property 

349 def data(self) -> bytes: 

350 """ 

351 Contains the incoming request data as string in case it came with 

352 a mimetype Werkzeug does not handle. 

353 """ 

354 return self.get_data(parse_form_data=True) 

355 

356 @typing.overload 

357 def get_data( # type: ignore 

358 self, 

359 cache: bool = True, 

360 as_text: "te.Literal[False]" = False, 

361 parse_form_data: bool = False, 

362 ) -> bytes: 

363 ... 

364 

365 @typing.overload 

366 def get_data( 

367 self, 

368 cache: bool = True, 

369 as_text: "te.Literal[True]" = ..., 

370 parse_form_data: bool = False, 

371 ) -> str: 

372 ... 

373 

374 def get_data( 

375 self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False 

376 ) -> t.Union[bytes, str]: 

377 """This reads the buffered incoming data from the client into one 

378 bytes object. By default this is cached but that behavior can be 

379 changed by setting `cache` to `False`. 

380 

381 Usually it's a bad idea to call this method without checking the 

382 content length first as a client could send dozens of megabytes or more 

383 to cause memory problems on the server. 

384 

385 Note that if the form data was already parsed this method will not 

386 return anything as form data parsing does not cache the data like 

387 this method does. To implicitly invoke form data parsing function 

388 set `parse_form_data` to `True`. When this is done the return value 

389 of this method will be an empty string if the form parser handles 

390 the data. This generally is not necessary as if the whole data is 

391 cached (which is the default) the form parser will used the cached 

392 data to parse the form data. Please be generally aware of checking 

393 the content length first in any case before calling this method 

394 to avoid exhausting server memory. 

395 

396 If `as_text` is set to `True` the return value will be a decoded 

397 string. 

398 

399 .. versionadded:: 0.9 

400 """ 

401 rv = getattr(self, "_cached_data", None) 

402 if rv is None: 

403 if parse_form_data: 

404 self._load_form_data() 

405 rv = self.stream.read() 

406 if cache: 

407 self._cached_data = rv 

408 if as_text: 

409 rv = rv.decode(self.charset, self.encoding_errors) 

410 return rv 

411 

412 @cached_property 

413 def form(self) -> "ImmutableMultiDict[str, str]": 

414 """The form parameters. By default an 

415 :class:`~werkzeug.datastructures.ImmutableMultiDict` 

416 is returned from this function. This can be changed by setting 

417 :attr:`parameter_storage_class` to a different type. This might 

418 be necessary if the order of the form data is important. 

419 

420 Please keep in mind that file uploads will not end up here, but instead 

421 in the :attr:`files` attribute. 

422 

423 .. versionchanged:: 0.9 

424 

425 Previous to Werkzeug 0.9 this would only contain form data for POST 

426 and PUT requests. 

427 """ 

428 self._load_form_data() 

429 return self.form 

430 

431 @cached_property 

432 def values(self) -> "CombinedMultiDict[str, str]": 

433 """A :class:`werkzeug.datastructures.CombinedMultiDict` that 

434 combines :attr:`args` and :attr:`form`. 

435 

436 For GET requests, only ``args`` are present, not ``form``. 

437 

438 .. versionchanged:: 2.0 

439 For GET requests, only ``args`` are present, not ``form``. 

440 """ 

441 sources = [self.args] 

442 

443 if self.method != "GET": 

444 # GET requests can have a body, and some caching proxies 

445 # might not treat that differently than a normal GET 

446 # request, allowing form data to "invisibly" affect the 

447 # cache without indication in the query string / URL. 

448 sources.append(self.form) 

449 

450 args = [] 

451 

452 for d in sources: 

453 if not isinstance(d, MultiDict): 

454 d = MultiDict(d) 

455 

456 args.append(d) 

457 

458 return CombinedMultiDict(args) 

459 

460 @cached_property 

461 def files(self) -> "ImmutableMultiDict[str, FileStorage]": 

462 """:class:`~werkzeug.datastructures.MultiDict` object containing 

463 all uploaded files. Each key in :attr:`files` is the name from the 

464 ``<input type="file" name="">``. Each value in :attr:`files` is a 

465 Werkzeug :class:`~werkzeug.datastructures.FileStorage` object. 

466 

467 It basically behaves like a standard file object you know from Python, 

468 with the difference that it also has a 

469 :meth:`~werkzeug.datastructures.FileStorage.save` function that can 

470 store the file on the filesystem. 

471 

472 Note that :attr:`files` will only contain data if the request method was 

473 POST, PUT or PATCH and the ``<form>`` that posted to the request had 

474 ``enctype="multipart/form-data"``. It will be empty otherwise. 

475 

476 See the :class:`~werkzeug.datastructures.MultiDict` / 

477 :class:`~werkzeug.datastructures.FileStorage` documentation for 

478 more details about the used data structure. 

479 """ 

480 self._load_form_data() 

481 return self.files 

482 

483 @property 

484 def script_root(self) -> str: 

485 """Alias for :attr:`self.root_path`. ``environ["SCRIPT_ROOT"]`` 

486 without a trailing slash. 

487 """ 

488 return self.root_path 

489 

490 @cached_property 

491 def url_root(self) -> str: 

492 """Alias for :attr:`root_url`. The URL with scheme, host, and 

493 root path. For example, ``https://example.com/app/``. 

494 """ 

495 return self.root_url 

496 

497 remote_user = environ_property[str]( 

498 "REMOTE_USER", 

499 doc="""If the server supports user authentication, and the 

500 script is protected, this attribute contains the username the 

501 user has authenticated as.""", 

502 ) 

503 is_multithread = environ_property[bool]( 

504 "wsgi.multithread", 

505 doc="""boolean that is `True` if the application is served by a 

506 multithreaded WSGI server.""", 

507 ) 

508 is_multiprocess = environ_property[bool]( 

509 "wsgi.multiprocess", 

510 doc="""boolean that is `True` if the application is served by a 

511 WSGI server that spawns multiple processes.""", 

512 ) 

513 is_run_once = environ_property[bool]( 

514 "wsgi.run_once", 

515 doc="""boolean that is `True` if the application will be 

516 executed only once in a process lifetime. This is the case for 

517 CGI for example, but it's not guaranteed that the execution only 

518 happens one time.""", 

519 ) 

520 

521 # JSON 

522 

523 #: A module or other object that has ``dumps`` and ``loads`` 

524 #: functions that match the API of the built-in :mod:`json` module. 

525 json_module = json 

526 

527 @property 

528 def json(self) -> t.Optional[t.Any]: 

529 """The parsed JSON data if :attr:`mimetype` indicates JSON 

530 (:mimetype:`application/json`, see :attr:`is_json`). 

531 

532 Calls :meth:`get_json` with default arguments. 

533 

534 If the request content type is not ``application/json``, this 

535 will raise a 400 Bad Request error. 

536 

537 .. versionchanged:: 2.1 

538 Raise a 400 error if the content type is incorrect. 

539 """ 

540 return self.get_json() 

541 

542 # Cached values for ``(silent=False, silent=True)``. Initialized 

543 # with sentinel values. 

544 _cached_json: t.Tuple[t.Any, t.Any] = (Ellipsis, Ellipsis) 

545 

546 def get_json( 

547 self, force: bool = False, silent: bool = False, cache: bool = True 

548 ) -> t.Optional[t.Any]: 

549 """Parse :attr:`data` as JSON. 

550 

551 If the mimetype does not indicate JSON 

552 (:mimetype:`application/json`, see :attr:`is_json`), or parsing 

553 fails, :meth:`on_json_loading_failed` is called and 

554 its return value is used as the return value. By default this 

555 raises a 400 Bad Request error. 

556 

557 :param force: Ignore the mimetype and always try to parse JSON. 

558 :param silent: Silence mimetype and parsing errors, and 

559 return ``None`` instead. 

560 :param cache: Store the parsed JSON to return for subsequent 

561 calls. 

562 

563 .. versionchanged:: 2.1 

564 Raise a 400 error if the content type is incorrect. 

565 """ 

566 if cache and self._cached_json[silent] is not Ellipsis: 

567 return self._cached_json[silent] 

568 

569 if not (force or self.is_json): 

570 if not silent: 

571 return self.on_json_loading_failed(None) 

572 else: 

573 return None 

574 

575 data = self.get_data(cache=cache) 

576 

577 try: 

578 rv = self.json_module.loads(data) 

579 except ValueError as e: 

580 if silent: 

581 rv = None 

582 

583 if cache: 

584 normal_rv, _ = self._cached_json 

585 self._cached_json = (normal_rv, rv) 

586 else: 

587 rv = self.on_json_loading_failed(e) 

588 

589 if cache: 

590 _, silent_rv = self._cached_json 

591 self._cached_json = (rv, silent_rv) 

592 else: 

593 if cache: 

594 self._cached_json = (rv, rv) 

595 

596 return rv 

597 

598 def on_json_loading_failed(self, e: t.Optional[ValueError]) -> t.Any: 

599 """Called if :meth:`get_json` fails and isn't silenced. 

600 

601 If this method returns a value, it is used as the return value 

602 for :meth:`get_json`. The default implementation raises 

603 :exc:`~werkzeug.exceptions.BadRequest`. 

604 

605 :param e: If parsing failed, this is the exception. It will be 

606 ``None`` if the content type wasn't ``application/json``. 

607 """ 

608 if e is not None: 

609 raise BadRequest(f"Failed to decode JSON object: {e}") 

610 

611 raise BadRequest( 

612 "Did not attempt to load JSON data because the request" 

613 " Content-Type was not 'application/json'." 

614 )