Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| import json | |
| import typing as t | |
| from http import HTTPStatus | |
| from urllib.parse import urljoin | |
| from .._internal import _get_environ | |
| from ..datastructures import Headers | |
| from ..http import generate_etag | |
| from ..http import http_date | |
| from ..http import is_resource_modified | |
| from ..http import parse_etags | |
| from ..http import parse_range_header | |
| from ..http import remove_entity_headers | |
| from ..sansio.response import Response as _SansIOResponse | |
| from ..urls import iri_to_uri | |
| from ..utils import cached_property | |
| from ..wsgi import _RangeWrapper | |
| from ..wsgi import ClosingIterator | |
| from ..wsgi import get_current_url | |
| if t.TYPE_CHECKING: | |
| from _typeshed.wsgi import StartResponse | |
| from _typeshed.wsgi import WSGIApplication | |
| from _typeshed.wsgi import WSGIEnvironment | |
| from .request import Request | |
| def _iter_encoded(iterable: t.Iterable[str | bytes]) -> t.Iterator[bytes]: | |
| for item in iterable: | |
| if isinstance(item, str): | |
| yield item.encode() | |
| else: | |
| yield item | |
| class Response(_SansIOResponse): | |
| """Represents an outgoing WSGI HTTP response with body, status, and | |
| headers. Has properties and methods for using the functionality | |
| defined by various HTTP specs. | |
| The response body is flexible to support different use cases. The | |
| simple form is passing bytes, or a string which will be encoded as | |
| UTF-8. Passing an iterable of bytes or strings makes this a | |
| streaming response. A generator is particularly useful for building | |
| a CSV file in memory or using SSE (Server Sent Events). A file-like | |
| object is also iterable, although the | |
| :func:`~werkzeug.utils.send_file` helper should be used in that | |
| case. | |
| The response object is itself a WSGI application callable. When | |
| called (:meth:`__call__`) with ``environ`` and ``start_response``, | |
| it will pass its status and headers to ``start_response`` then | |
| return its body as an iterable. | |
| .. code-block:: python | |
| from werkzeug.wrappers.response import Response | |
| def index(): | |
| return Response("Hello, World!") | |
| def application(environ, start_response): | |
| path = environ.get("PATH_INFO") or "/" | |
| if path == "/": | |
| response = index() | |
| else: | |
| response = Response("Not Found", status=404) | |
| return response(environ, start_response) | |
| :param response: The data for the body of the response. A string or | |
| bytes, or tuple or list of strings or bytes, for a fixed-length | |
| response, or any other iterable of strings or bytes for a | |
| streaming response. Defaults to an empty body. | |
| :param status: The status code for the response. Either an int, in | |
| which case the default status message is added, or a string in | |
| the form ``{code} {message}``, like ``404 Not Found``. Defaults | |
| to 200. | |
| :param headers: A :class:`~werkzeug.datastructures.Headers` object, | |
| or a list of ``(key, value)`` tuples that will be converted to a | |
| ``Headers`` object. | |
| :param mimetype: The mime type (content type without charset or | |
| other parameters) of the response. If the value starts with | |
| ``text/`` (or matches some other special cases), the charset | |
| will be added to create the ``content_type``. | |
| :param content_type: The full content type of the response. | |
| Overrides building the value from ``mimetype``. | |
| :param direct_passthrough: Pass the response body directly through | |
| as the WSGI iterable. This can be used when the body is a binary | |
| file or other iterator of bytes, to skip some unnecessary | |
| checks. Use :func:`~werkzeug.utils.send_file` instead of setting | |
| this manually. | |
| .. versionchanged:: 2.1 | |
| Old ``BaseResponse`` and mixin classes were removed. | |
| .. versionchanged:: 2.0 | |
| Combine ``BaseResponse`` and mixins into a single ``Response`` | |
| class. | |
| .. versionchanged:: 0.5 | |
| The ``direct_passthrough`` parameter was added. | |
| """ | |
| #: if set to `False` accessing properties on the response object will | |
| #: not try to consume the response iterator and convert it into a list. | |
| #: | |
| #: .. versionadded:: 0.6.2 | |
| #: | |
| #: That attribute was previously called `implicit_seqence_conversion`. | |
| #: (Notice the typo). If you did use this feature, you have to adapt | |
| #: your code to the name change. | |
| implicit_sequence_conversion = True | |
| #: If a redirect ``Location`` header is a relative URL, make it an | |
| #: absolute URL, including scheme and domain. | |
| #: | |
| #: .. versionchanged:: 2.1 | |
| #: This is disabled by default, so responses will send relative | |
| #: redirects. | |
| #: | |
| #: .. versionadded:: 0.8 | |
| autocorrect_location_header = False | |
| #: Should this response object automatically set the content-length | |
| #: header if possible? This is true by default. | |
| #: | |
| #: .. versionadded:: 0.8 | |
| automatically_set_content_length = True | |
| #: The response body to send as the WSGI iterable. A list of strings | |
| #: or bytes represents a fixed-length response, any other iterable | |
| #: is a streaming response. Strings are encoded to bytes as UTF-8. | |
| #: | |
| #: Do not set to a plain string or bytes, that will cause sending | |
| #: the response to be very inefficient as it will iterate one byte | |
| #: at a time. | |
| response: t.Iterable[str] | t.Iterable[bytes] | |
| def __init__( | |
| self, | |
| response: t.Iterable[bytes] | bytes | t.Iterable[str] | str | None = None, | |
| status: int | str | HTTPStatus | None = None, | |
| headers: t.Mapping[str, str | t.Iterable[str]] | |
| | t.Iterable[tuple[str, str]] | |
| | None = None, | |
| mimetype: str | None = None, | |
| content_type: str | None = None, | |
| direct_passthrough: bool = False, | |
| ) -> None: | |
| super().__init__( | |
| status=status, | |
| headers=headers, | |
| mimetype=mimetype, | |
| content_type=content_type, | |
| ) | |
| #: Pass the response body directly through as the WSGI iterable. | |
| #: This can be used when the body is a binary file or other | |
| #: iterator of bytes, to skip some unnecessary checks. Use | |
| #: :func:`~werkzeug.utils.send_file` instead of setting this | |
| #: manually. | |
| self.direct_passthrough = direct_passthrough | |
| self._on_close: list[t.Callable[[], t.Any]] = [] | |
| # we set the response after the headers so that if a class changes | |
| # the charset attribute, the data is set in the correct charset. | |
| if response is None: | |
| self.response = [] | |
| elif isinstance(response, (str, bytes, bytearray)): | |
| self.set_data(response) | |
| else: | |
| self.response = response | |
| def call_on_close(self, func: t.Callable[[], t.Any]) -> t.Callable[[], t.Any]: | |
| """Adds a function to the internal list of functions that should | |
| be called as part of closing down the response. Since 0.7 this | |
| function also returns the function that was passed so that this | |
| can be used as a decorator. | |
| .. versionadded:: 0.6 | |
| """ | |
| self._on_close.append(func) | |
| return func | |
| def __repr__(self) -> str: | |
| if self.is_sequence: | |
| body_info = f"{sum(map(len, self.iter_encoded()))} bytes" | |
| else: | |
| body_info = "streamed" if self.is_streamed else "likely-streamed" | |
| return f"<{type(self).__name__} {body_info} [{self.status}]>" | |
| def force_type( | |
| cls, response: Response, environ: WSGIEnvironment | None = None | |
| ) -> Response: | |
| """Enforce that the WSGI response is a response object of the current | |
| type. Werkzeug will use the :class:`Response` internally in many | |
| situations like the exceptions. If you call :meth:`get_response` on an | |
| exception you will get back a regular :class:`Response` object, even | |
| if you are using a custom subclass. | |
| This method can enforce a given response type, and it will also | |
| convert arbitrary WSGI callables into response objects if an environ | |
| is provided:: | |
| # convert a Werkzeug response object into an instance of the | |
| # MyResponseClass subclass. | |
| response = MyResponseClass.force_type(response) | |
| # convert any WSGI application into a response object | |
| response = MyResponseClass.force_type(response, environ) | |
| This is especially useful if you want to post-process responses in | |
| the main dispatcher and use functionality provided by your subclass. | |
| Keep in mind that this will modify response objects in place if | |
| possible! | |
| :param response: a response object or wsgi application. | |
| :param environ: a WSGI environment object. | |
| :return: a response object. | |
| """ | |
| if not isinstance(response, Response): | |
| if environ is None: | |
| raise TypeError( | |
| "cannot convert WSGI application into response" | |
| " objects without an environ" | |
| ) | |
| from ..test import run_wsgi_app | |
| response = Response(*run_wsgi_app(response, environ)) | |
| response.__class__ = cls | |
| return response | |
| def from_app( | |
| cls, app: WSGIApplication, environ: WSGIEnvironment, buffered: bool = False | |
| ) -> Response: | |
| """Create a new response object from an application output. This | |
| works best if you pass it an application that returns a generator all | |
| the time. Sometimes applications may use the `write()` callable | |
| returned by the `start_response` function. This tries to resolve such | |
| edge cases automatically. But if you don't get the expected output | |
| you should set `buffered` to `True` which enforces buffering. | |
| :param app: the WSGI application to execute. | |
| :param environ: the WSGI environment to execute against. | |
| :param buffered: set to `True` to enforce buffering. | |
| :return: a response object. | |
| """ | |
| from ..test import run_wsgi_app | |
| return cls(*run_wsgi_app(app, environ, buffered)) | |
| def get_data(self, as_text: t.Literal[False] = False) -> bytes: ... | |
| def get_data(self, as_text: t.Literal[True]) -> str: ... | |
| def get_data(self, as_text: bool = False) -> bytes | str: | |
| """The string representation of the response body. Whenever you call | |
| this property the response iterable is encoded and flattened. This | |
| can lead to unwanted behavior if you stream big data. | |
| This behavior can be disabled by setting | |
| :attr:`implicit_sequence_conversion` to `False`. | |
| If `as_text` is set to `True` the return value will be a decoded | |
| string. | |
| .. versionadded:: 0.9 | |
| """ | |
| self._ensure_sequence() | |
| rv = b"".join(self.iter_encoded()) | |
| if as_text: | |
| return rv.decode() | |
| return rv | |
| def set_data(self, value: bytes | str) -> None: | |
| """Sets a new string as response. The value must be a string or | |
| bytes. If a string is set it's encoded to the charset of the | |
| response (utf-8 by default). | |
| .. versionadded:: 0.9 | |
| """ | |
| if isinstance(value, str): | |
| value = value.encode() | |
| self.response = [value] | |
| if self.automatically_set_content_length: | |
| self.headers["Content-Length"] = str(len(value)) | |
| data = property( | |
| get_data, | |
| set_data, | |
| doc="A descriptor that calls :meth:`get_data` and :meth:`set_data`.", | |
| ) | |
| def calculate_content_length(self) -> int | None: | |
| """Returns the content length if available or `None` otherwise.""" | |
| try: | |
| self._ensure_sequence() | |
| except RuntimeError: | |
| return None | |
| return sum(len(x) for x in self.iter_encoded()) | |
| def _ensure_sequence(self, mutable: bool = False) -> None: | |
| """This method can be called by methods that need a sequence. If | |
| `mutable` is true, it will also ensure that the response sequence | |
| is a standard Python list. | |
| .. versionadded:: 0.6 | |
| """ | |
| if self.is_sequence: | |
| # if we need a mutable object, we ensure it's a list. | |
| if mutable and not isinstance(self.response, list): | |
| self.response = list(self.response) # type: ignore | |
| return | |
| if self.direct_passthrough: | |
| raise RuntimeError( | |
| "Attempted implicit sequence conversion but the" | |
| " response object is in direct passthrough mode." | |
| ) | |
| if not self.implicit_sequence_conversion: | |
| raise RuntimeError( | |
| "The response object required the iterable to be a" | |
| " sequence, but the implicit conversion was disabled." | |
| " Call make_sequence() yourself." | |
| ) | |
| self.make_sequence() | |
| def make_sequence(self) -> None: | |
| """Converts the response iterator in a list. By default this happens | |
| automatically if required. If `implicit_sequence_conversion` is | |
| disabled, this method is not automatically called and some properties | |
| might raise exceptions. This also encodes all the items. | |
| .. versionadded:: 0.6 | |
| """ | |
| if not self.is_sequence: | |
| # if we consume an iterable we have to ensure that the close | |
| # method of the iterable is called if available when we tear | |
| # down the response | |
| close = getattr(self.response, "close", None) | |
| self.response = list(self.iter_encoded()) | |
| if close is not None: | |
| self.call_on_close(close) | |
| def iter_encoded(self) -> t.Iterator[bytes]: | |
| """Iter the response encoded with the encoding of the response. | |
| If the response object is invoked as WSGI application the return | |
| value of this method is used as application iterator unless | |
| :attr:`direct_passthrough` was activated. | |
| """ | |
| # Encode in a separate function so that self.response is fetched | |
| # early. This allows us to wrap the response with the return | |
| # value from get_app_iter or iter_encoded. | |
| return _iter_encoded(self.response) | |
| def is_streamed(self) -> bool: | |
| """If the response is streamed (the response is not an iterable with | |
| a length information) this property is `True`. In this case streamed | |
| means that there is no information about the number of iterations. | |
| This is usually `True` if a generator is passed to the response object. | |
| This is useful for checking before applying some sort of post | |
| filtering that should not take place for streamed responses. | |
| """ | |
| try: | |
| len(self.response) # type: ignore | |
| except (TypeError, AttributeError): | |
| return True | |
| return False | |
| def is_sequence(self) -> bool: | |
| """If the iterator is buffered, this property will be `True`. A | |
| response object will consider an iterator to be buffered if the | |
| response attribute is a list or tuple. | |
| .. versionadded:: 0.6 | |
| """ | |
| return isinstance(self.response, (tuple, list)) | |
| def close(self) -> None: | |
| """Close the wrapped response if possible. You can also use the object | |
| in a with statement which will automatically close it. | |
| .. versionadded:: 0.9 | |
| Can now be used in a with statement. | |
| """ | |
| if hasattr(self.response, "close"): | |
| self.response.close() | |
| for func in self._on_close: | |
| func() | |
| def __enter__(self) -> Response: | |
| return self | |
| def __exit__(self, exc_type, exc_value, tb): # type: ignore | |
| self.close() | |
| def freeze(self) -> None: | |
| """Make the response object ready to be pickled. Does the | |
| following: | |
| * Buffer the response into a list, ignoring | |
| :attr:`implicity_sequence_conversion` and | |
| :attr:`direct_passthrough`. | |
| * Set the ``Content-Length`` header. | |
| * Generate an ``ETag`` header if one is not already set. | |
| .. versionchanged:: 2.1 | |
| Removed the ``no_etag`` parameter. | |
| .. versionchanged:: 2.0 | |
| An ``ETag`` header is always added. | |
| .. versionchanged:: 0.6 | |
| The ``Content-Length`` header is set. | |
| """ | |
| # Always freeze the encoded response body, ignore | |
| # implicit_sequence_conversion and direct_passthrough. | |
| self.response = list(self.iter_encoded()) | |
| self.headers["Content-Length"] = str(sum(map(len, self.response))) | |
| self.add_etag() | |
| def get_wsgi_headers(self, environ: WSGIEnvironment) -> Headers: | |
| """This is automatically called right before the response is started | |
| and returns headers modified for the given environment. It returns a | |
| copy of the headers from the response with some modifications applied | |
| if necessary. | |
| For example the location header (if present) is joined with the root | |
| URL of the environment. Also the content length is automatically set | |
| to zero here for certain status codes. | |
| .. versionchanged:: 0.6 | |
| Previously that function was called `fix_headers` and modified | |
| the response object in place. Also since 0.6, IRIs in location | |
| and content-location headers are handled properly. | |
| Also starting with 0.6, Werkzeug will attempt to set the content | |
| length if it is able to figure it out on its own. This is the | |
| case if all the strings in the response iterable are already | |
| encoded and the iterable is buffered. | |
| :param environ: the WSGI environment of the request. | |
| :return: returns a new :class:`~werkzeug.datastructures.Headers` | |
| object. | |
| """ | |
| headers = Headers(self.headers) | |
| location: str | None = None | |
| content_location: str | None = None | |
| content_length: str | int | None = None | |
| status = self.status_code | |
| # iterate over the headers to find all values in one go. Because | |
| # get_wsgi_headers is used each response that gives us a tiny | |
| # speedup. | |
| for key, value in headers: | |
| ikey = key.lower() | |
| if ikey == "location": | |
| location = value | |
| elif ikey == "content-location": | |
| content_location = value | |
| elif ikey == "content-length": | |
| content_length = value | |
| if location is not None: | |
| location = iri_to_uri(location) | |
| if self.autocorrect_location_header: | |
| # Make the location header an absolute URL. | |
| current_url = get_current_url(environ, strip_querystring=True) | |
| current_url = iri_to_uri(current_url) | |
| location = urljoin(current_url, location) | |
| headers["Location"] = location | |
| # make sure the content location is a URL | |
| if content_location is not None: | |
| headers["Content-Location"] = iri_to_uri(content_location) | |
| if 100 <= status < 200 or status == 204: | |
| # Per section 3.3.2 of RFC 7230, "a server MUST NOT send a | |
| # Content-Length header field in any response with a status | |
| # code of 1xx (Informational) or 204 (No Content)." | |
| headers.remove("Content-Length") | |
| elif status == 304: | |
| remove_entity_headers(headers) | |
| # if we can determine the content length automatically, we | |
| # should try to do that. But only if this does not involve | |
| # flattening the iterator or encoding of strings in the | |
| # response. We however should not do that if we have a 304 | |
| # response. | |
| if ( | |
| self.automatically_set_content_length | |
| and self.is_sequence | |
| and content_length is None | |
| and status not in (204, 304) | |
| and not (100 <= status < 200) | |
| ): | |
| content_length = sum(len(x) for x in self.iter_encoded()) | |
| headers["Content-Length"] = str(content_length) | |
| return headers | |
| def get_app_iter(self, environ: WSGIEnvironment) -> t.Iterable[bytes]: | |
| """Returns the application iterator for the given environ. Depending | |
| on the request method and the current status code the return value | |
| might be an empty response rather than the one from the response. | |
| If the request method is `HEAD` or the status code is in a range | |
| where the HTTP specification requires an empty response, an empty | |
| iterable is returned. | |
| .. versionadded:: 0.6 | |
| :param environ: the WSGI environment of the request. | |
| :return: a response iterable. | |
| """ | |
| status = self.status_code | |
| if ( | |
| environ["REQUEST_METHOD"] == "HEAD" | |
| or 100 <= status < 200 | |
| or status in (204, 304) | |
| ): | |
| iterable: t.Iterable[bytes] = () | |
| elif self.direct_passthrough: | |
| return self.response # type: ignore | |
| else: | |
| iterable = self.iter_encoded() | |
| return ClosingIterator(iterable, self.close) | |
| def get_wsgi_response( | |
| self, environ: WSGIEnvironment | |
| ) -> tuple[t.Iterable[bytes], str, list[tuple[str, str]]]: | |
| """Returns the final WSGI response as tuple. The first item in | |
| the tuple is the application iterator, the second the status and | |
| the third the list of headers. The response returned is created | |
| specially for the given environment. For example if the request | |
| method in the WSGI environment is ``'HEAD'`` the response will | |
| be empty and only the headers and status code will be present. | |
| .. versionadded:: 0.6 | |
| :param environ: the WSGI environment of the request. | |
| :return: an ``(app_iter, status, headers)`` tuple. | |
| """ | |
| headers = self.get_wsgi_headers(environ) | |
| app_iter = self.get_app_iter(environ) | |
| return app_iter, self.status, headers.to_wsgi_list() | |
| def __call__( | |
| self, environ: WSGIEnvironment, start_response: StartResponse | |
| ) -> t.Iterable[bytes]: | |
| """Process this response as WSGI application. | |
| :param environ: the WSGI environment. | |
| :param start_response: the response callable provided by the WSGI | |
| server. | |
| :return: an application iterator | |
| """ | |
| app_iter, status, headers = self.get_wsgi_response(environ) | |
| start_response(status, headers) | |
| return app_iter | |
| # 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. | |
| """ | |
| return self.get_json() | |
| def get_json(self, force: bool = ..., silent: t.Literal[False] = ...) -> t.Any: ... | |
| def get_json(self, force: bool = ..., silent: bool = ...) -> t.Any | None: ... | |
| def get_json(self, force: bool = False, silent: bool = False) -> t.Any | None: | |
| """Parse :attr:`data` as JSON. Useful during testing. | |
| If the mimetype does not indicate JSON | |
| (:mimetype:`application/json`, see :attr:`is_json`), this | |
| returns ``None``. | |
| Unlike :meth:`Request.get_json`, the result is not cached. | |
| :param force: Ignore the mimetype and always try to parse JSON. | |
| :param silent: Silence parsing errors and return ``None`` | |
| instead. | |
| """ | |
| if not (force or self.is_json): | |
| return None | |
| data = self.get_data() | |
| try: | |
| return self.json_module.loads(data) | |
| except ValueError: | |
| if not silent: | |
| raise | |
| return None | |
| # Stream | |
| def stream(self) -> ResponseStream: | |
| """The response iterable as write-only stream.""" | |
| return ResponseStream(self) | |
| def _wrap_range_response(self, start: int, length: int) -> None: | |
| """Wrap existing Response in case of Range Request context.""" | |
| if self.status_code == 206: | |
| self.response = _RangeWrapper(self.response, start, length) # type: ignore | |
| def _is_range_request_processable(self, environ: WSGIEnvironment) -> bool: | |
| """Return ``True`` if `Range` header is present and if underlying | |
| resource is considered unchanged when compared with `If-Range` header. | |
| """ | |
| return ( | |
| "HTTP_IF_RANGE" not in environ | |
| or not is_resource_modified( | |
| environ, | |
| self.headers.get("etag"), | |
| None, | |
| self.headers.get("last-modified"), | |
| ignore_if_range=False, | |
| ) | |
| ) and "HTTP_RANGE" in environ | |
| def _process_range_request( | |
| self, | |
| environ: WSGIEnvironment, | |
| complete_length: int | None, | |
| accept_ranges: bool | str, | |
| ) -> bool: | |
| """Handle Range Request related headers (RFC7233). If `Accept-Ranges` | |
| header is valid, and Range Request is processable, we set the headers | |
| as described by the RFC, and wrap the underlying response in a | |
| RangeWrapper. | |
| Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise. | |
| :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` | |
| if `Range` header could not be parsed or satisfied. | |
| .. versionchanged:: 2.0 | |
| Returns ``False`` if the length is 0. | |
| """ | |
| from ..exceptions import RequestedRangeNotSatisfiable | |
| if ( | |
| not accept_ranges | |
| or complete_length is None | |
| or complete_length == 0 | |
| or not self._is_range_request_processable(environ) | |
| ): | |
| return False | |
| if accept_ranges is True: | |
| accept_ranges = "bytes" | |
| parsed_range = parse_range_header(environ.get("HTTP_RANGE")) | |
| if parsed_range is None: | |
| raise RequestedRangeNotSatisfiable(complete_length) | |
| range_tuple = parsed_range.range_for_length(complete_length) | |
| content_range_header = parsed_range.to_content_range_header(complete_length) | |
| if range_tuple is None or content_range_header is None: | |
| raise RequestedRangeNotSatisfiable(complete_length) | |
| content_length = range_tuple[1] - range_tuple[0] | |
| self.headers["Content-Length"] = str(content_length) | |
| self.headers["Accept-Ranges"] = accept_ranges | |
| self.content_range = content_range_header # type: ignore | |
| self.status_code = 206 | |
| self._wrap_range_response(range_tuple[0], content_length) | |
| return True | |
| def make_conditional( | |
| self, | |
| request_or_environ: WSGIEnvironment | Request, | |
| accept_ranges: bool | str = False, | |
| complete_length: int | None = None, | |
| ) -> Response: | |
| """Make the response conditional to the request. This method works | |
| best if an etag was defined for the response already. The `add_etag` | |
| method can be used to do that. If called without etag just the date | |
| header is set. | |
| This does nothing if the request method in the request or environ is | |
| anything but GET or HEAD. | |
| For optimal performance when handling range requests, it's recommended | |
| that your response data object implements `seekable`, `seek` and `tell` | |
| methods as described by :py:class:`io.IOBase`. Objects returned by | |
| :meth:`~werkzeug.wsgi.wrap_file` automatically implement those methods. | |
| It does not remove the body of the response because that's something | |
| the :meth:`__call__` function does for us automatically. | |
| Returns self so that you can do ``return resp.make_conditional(req)`` | |
| but modifies the object in-place. | |
| :param request_or_environ: a request object or WSGI environment to be | |
| used to make the response conditional | |
| against. | |
| :param accept_ranges: This parameter dictates the value of | |
| `Accept-Ranges` header. If ``False`` (default), | |
| the header is not set. If ``True``, it will be set | |
| to ``"bytes"``. If it's a string, it will use this | |
| value. | |
| :param complete_length: Will be used only in valid Range Requests. | |
| It will set `Content-Range` complete length | |
| value and compute `Content-Length` real value. | |
| This parameter is mandatory for successful | |
| Range Requests completion. | |
| :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` | |
| if `Range` header could not be parsed or satisfied. | |
| .. versionchanged:: 2.0 | |
| Range processing is skipped if length is 0 instead of | |
| raising a 416 Range Not Satisfiable error. | |
| """ | |
| environ = _get_environ(request_or_environ) | |
| if environ["REQUEST_METHOD"] in ("GET", "HEAD"): | |
| # if the date is not in the headers, add it now. We however | |
| # will not override an already existing header. Unfortunately | |
| # this header will be overridden by many WSGI servers including | |
| # wsgiref. | |
| if "date" not in self.headers: | |
| self.headers["Date"] = http_date() | |
| is206 = self._process_range_request(environ, complete_length, accept_ranges) | |
| if not is206 and not is_resource_modified( | |
| environ, | |
| self.headers.get("etag"), | |
| None, | |
| self.headers.get("last-modified"), | |
| ): | |
| if parse_etags(environ.get("HTTP_IF_MATCH")): | |
| self.status_code = 412 | |
| else: | |
| self.status_code = 304 | |
| if ( | |
| self.automatically_set_content_length | |
| and "content-length" not in self.headers | |
| ): | |
| length = self.calculate_content_length() | |
| if length is not None: | |
| self.headers["Content-Length"] = str(length) | |
| return self | |
| def add_etag(self, overwrite: bool = False, weak: bool = False) -> None: | |
| """Add an etag for the current response if there is none yet. | |
| .. versionchanged:: 2.0 | |
| SHA-1 is used to generate the value. MD5 may not be | |
| available in some environments. | |
| """ | |
| if overwrite or "etag" not in self.headers: | |
| self.set_etag(generate_etag(self.get_data()), weak) | |
| class ResponseStream: | |
| """A file descriptor like object used by :meth:`Response.stream` to | |
| represent the body of the stream. It directly pushes into the | |
| response iterable of the response object. | |
| """ | |
| mode = "wb+" | |
| def __init__(self, response: Response): | |
| self.response = response | |
| self.closed = False | |
| def write(self, value: bytes) -> int: | |
| if self.closed: | |
| raise ValueError("I/O operation on closed file") | |
| self.response._ensure_sequence(mutable=True) | |
| self.response.response.append(value) # type: ignore | |
| self.response.headers.pop("Content-Length", None) | |
| return len(value) | |
| def writelines(self, seq: t.Iterable[bytes]) -> None: | |
| for item in seq: | |
| self.write(item) | |
| def close(self) -> None: | |
| self.closed = True | |
| def flush(self) -> None: | |
| if self.closed: | |
| raise ValueError("I/O operation on closed file") | |
| def isatty(self) -> bool: | |
| if self.closed: | |
| raise ValueError("I/O operation on closed file") | |
| return False | |
| def tell(self) -> int: | |
| self.response._ensure_sequence() | |
| return sum(map(len, self.response.response)) | |
| def encoding(self) -> str: | |
| return "utf-8" | |