Wrap a stream so that it doesn't read more than a given limit. This is used to limit ``wsgi.input`` to the ``Content-Length`` header value or :attr:`.Request.max_content_length`. When attempting to read after the limit has been reached, :meth:`on_exhausted` is called. When the limit
| 437 | |
| 438 | |
| 439 | class LimitedStream(io.RawIOBase): |
| 440 | """Wrap a stream so that it doesn't read more than a given limit. This is used to |
| 441 | limit ``wsgi.input`` to the ``Content-Length`` header value or |
| 442 | :attr:`.Request.max_content_length`. |
| 443 | |
| 444 | When attempting to read after the limit has been reached, :meth:`on_exhausted` is |
| 445 | called. When the limit is a maximum, this raises :exc:`.RequestEntityTooLarge`. |
| 446 | |
| 447 | If reading from the stream returns zero bytes or raises an error, |
| 448 | :meth:`on_disconnect` is called, which raises :exc:`.ClientDisconnected`. When the |
| 449 | limit is a maximum and zero bytes were read, no error is raised, since it may be the |
| 450 | end of the stream. |
| 451 | |
| 452 | If the limit is reached before the underlying stream is exhausted (such as a file |
| 453 | that is too large, or an infinite stream), the remaining contents of the stream |
| 454 | cannot be read safely. Depending on how the server handles this, clients may show a |
| 455 | "connection reset" failure instead of seeing the 413 response. |
| 456 | |
| 457 | :param stream: The stream to read from. Must be a readable binary IO object. |
| 458 | :param limit: The limit in bytes to not read past. Should be either the |
| 459 | ``Content-Length`` header value or ``request.max_content_length``. |
| 460 | :param is_max: Whether the given ``limit`` is ``request.max_content_length`` instead |
| 461 | of the ``Content-Length`` header value. This changes how exhausted and |
| 462 | disconnect events are handled. |
| 463 | |
| 464 | .. versionchanged:: 2.3 |
| 465 | Handle ``max_content_length`` differently than ``Content-Length``. |
| 466 | |
| 467 | .. versionchanged:: 2.3 |
| 468 | Implements ``io.RawIOBase`` rather than ``io.IOBase``. |
| 469 | """ |
| 470 | |
| 471 | def __init__(self, stream: t.IO[bytes], limit: int, is_max: bool = False) -> None: |
| 472 | self._stream = stream |
| 473 | self._pos = 0 |
| 474 | self.limit = limit |
| 475 | self._limit_is_max = is_max |
| 476 | |
| 477 | @property |
| 478 | def is_exhausted(self) -> bool: |
| 479 | """Whether the current stream position has reached the limit.""" |
| 480 | return self._pos >= self.limit |
| 481 | |
| 482 | def on_exhausted(self) -> None: |
| 483 | """Called when attempting to read after the limit has been reached. |
| 484 | |
| 485 | The default behavior is to do nothing, unless the limit is a maximum, in which |
| 486 | case it raises :exc:`.RequestEntityTooLarge`. |
| 487 | |
| 488 | .. versionchanged:: 2.3 |
| 489 | Raises ``RequestEntityTooLarge`` if the limit is a maximum. |
| 490 | |
| 491 | .. versionchanged:: 2.3 |
| 492 | Any return value is ignored. |
| 493 | """ |
| 494 | if self._limit_is_max: |
| 495 | raise RequestEntityTooLarge() |
| 496 |