123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- from datetime import datetime
- from datetime import timedelta
- from .._compat import string_types
- from ..datastructures import CallbackDict
- from ..http import dump_age
- from ..http import dump_csp_header
- from ..http import dump_header
- from ..http import dump_options_header
- from ..http import http_date
- from ..http import parse_age
- from ..http import parse_csp_header
- from ..http import parse_date
- from ..http import parse_options_header
- from ..http import parse_set_header
- from ..utils import cached_property
- from ..utils import environ_property
- from ..utils import get_content_type
- from ..utils import header_property
- from ..wsgi import get_content_length
- class CommonRequestDescriptorsMixin(object):
- """A mixin for :class:`BaseRequest` subclasses. Request objects that
- mix this class in will automatically get descriptors for a couple of
- HTTP headers with automatic type conversion.
- .. versionadded:: 0.5
- """
- content_type = environ_property(
- "CONTENT_TYPE",
- doc="""The Content-Type entity-header field indicates the media
- type of the entity-body sent to the recipient or, in the case of
- the HEAD method, the media type that would have been sent had
- the request been a GET.""",
- )
- @cached_property
- def content_length(self):
- """The Content-Length entity-header field indicates the size of the
- entity-body in bytes or, in the case of the HEAD method, the size of
- the entity-body that would have been sent had the request been a
- GET.
- """
- return get_content_length(self.environ)
- content_encoding = environ_property(
- "HTTP_CONTENT_ENCODING",
- doc="""The Content-Encoding entity-header field is used as a
- modifier to the media-type. When present, its value indicates
- what additional content codings have been applied to the
- entity-body, and thus what decoding mechanisms must be applied
- in order to obtain the media-type referenced by the Content-Type
- header field.
- .. versionadded:: 0.9""",
- )
- content_md5 = environ_property(
- "HTTP_CONTENT_MD5",
- doc="""The Content-MD5 entity-header field, as defined in
- RFC 1864, is an MD5 digest of the entity-body for the purpose of
- providing an end-to-end message integrity check (MIC) of the
- entity-body. (Note: a MIC is good for detecting accidental
- modification of the entity-body in transit, but is not proof
- against malicious attacks.)
- .. versionadded:: 0.9""",
- )
- referrer = environ_property(
- "HTTP_REFERER",
- doc="""The Referer[sic] request-header field allows the client
- to specify, for the server's benefit, the address (URI) of the
- resource from which the Request-URI was obtained (the
- "referrer", although the header field is misspelled).""",
- )
- date = environ_property(
- "HTTP_DATE",
- None,
- parse_date,
- doc="""The Date general-header field represents the date and
- time at which the message was originated, having the same
- semantics as orig-date in RFC 822.""",
- )
- max_forwards = environ_property(
- "HTTP_MAX_FORWARDS",
- None,
- int,
- doc="""The Max-Forwards request-header field provides a
- mechanism with the TRACE and OPTIONS methods to limit the number
- of proxies or gateways that can forward the request to the next
- inbound server.""",
- )
- def _parse_content_type(self):
- if not hasattr(self, "_parsed_content_type"):
- self._parsed_content_type = parse_options_header(
- self.environ.get("CONTENT_TYPE", "")
- )
- @property
- def mimetype(self):
- """Like :attr:`content_type`, but without parameters (eg, without
- charset, type etc.) and always lowercase. For example if the content
- type is ``text/HTML; charset=utf-8`` the mimetype would be
- ``'text/html'``.
- """
- self._parse_content_type()
- return self._parsed_content_type[0].lower()
- @property
- def mimetype_params(self):
- """The mimetype parameters as dict. For example if the content
- type is ``text/html; charset=utf-8`` the params would be
- ``{'charset': 'utf-8'}``.
- """
- self._parse_content_type()
- return self._parsed_content_type[1]
- @cached_property
- def pragma(self):
- """The Pragma general-header field is used to include
- implementation-specific directives that might apply to any recipient
- along the request/response chain. All pragma directives specify
- optional behavior from the viewpoint of the protocol; however, some
- systems MAY require that behavior be consistent with the directives.
- """
- return parse_set_header(self.environ.get("HTTP_PRAGMA", ""))
- class CommonResponseDescriptorsMixin(object):
- """A mixin for :class:`BaseResponse` subclasses. Response objects that
- mix this class in will automatically get descriptors for a couple of
- HTTP headers with automatic type conversion.
- """
- @property
- def mimetype(self):
- """The mimetype (content type without charset etc.)"""
- ct = self.headers.get("content-type")
- if ct:
- return ct.split(";")[0].strip()
- @mimetype.setter
- def mimetype(self, value):
- self.headers["Content-Type"] = get_content_type(value, self.charset)
- @property
- def mimetype_params(self):
- """The mimetype parameters as dict. For example if the
- content type is ``text/html; charset=utf-8`` the params would be
- ``{'charset': 'utf-8'}``.
- .. versionadded:: 0.5
- """
- def on_update(d):
- self.headers["Content-Type"] = dump_options_header(self.mimetype, d)
- d = parse_options_header(self.headers.get("content-type", ""))[1]
- return CallbackDict(d, on_update)
- location = header_property(
- "Location",
- doc="""The Location response-header field is used to redirect
- the recipient to a location other than the Request-URI for
- completion of the request or identification of a new
- resource.""",
- )
- age = header_property(
- "Age",
- None,
- parse_age,
- dump_age,
- doc="""The Age response-header field conveys the sender's
- estimate of the amount of time since the response (or its
- revalidation) was generated at the origin server.
- Age values are non-negative decimal integers, representing time
- in seconds.""",
- )
- content_type = header_property(
- "Content-Type",
- doc="""The Content-Type entity-header field indicates the media
- type of the entity-body sent to the recipient or, in the case of
- the HEAD method, the media type that would have been sent had
- the request been a GET.""",
- )
- content_length = header_property(
- "Content-Length",
- None,
- int,
- str,
- doc="""The Content-Length entity-header field indicates the size
- of the entity-body, in decimal number of OCTETs, sent to the
- recipient or, in the case of the HEAD method, the size of the
- entity-body that would have been sent had the request been a
- GET.""",
- )
- content_location = header_property(
- "Content-Location",
- doc="""The Content-Location entity-header field MAY be used to
- supply the resource location for the entity enclosed in the
- message when that entity is accessible from a location separate
- from the requested resource's URI.""",
- )
- content_encoding = header_property(
- "Content-Encoding",
- doc="""The Content-Encoding entity-header field is used as a
- modifier to the media-type. When present, its value indicates
- what additional content codings have been applied to the
- entity-body, and thus what decoding mechanisms must be applied
- in order to obtain the media-type referenced by the Content-Type
- header field.""",
- )
- content_md5 = header_property(
- "Content-MD5",
- doc="""The Content-MD5 entity-header field, as defined in
- RFC 1864, is an MD5 digest of the entity-body for the purpose of
- providing an end-to-end message integrity check (MIC) of the
- entity-body. (Note: a MIC is good for detecting accidental
- modification of the entity-body in transit, but is not proof
- against malicious attacks.)""",
- )
- content_security_policy = header_property(
- "Content-Security-Policy",
- None,
- parse_csp_header,
- dump_csp_header,
- doc="""The Content-Security-Policy header adds an additional layer of
- security to help detect and mitigate certain types of attacks.""",
- )
- content_security_policy_report_only = header_property(
- "Content-Security-Policy-Report-Only",
- None,
- parse_csp_header,
- dump_csp_header,
- doc="""The Content-Security-Policy-Report-Only header adds a csp policy
- that is not enforced but is reported thereby helping detect
- certain types of attacks.""",
- )
- date = header_property(
- "Date",
- None,
- parse_date,
- http_date,
- doc="""The Date general-header field represents the date and
- time at which the message was originated, having the same
- semantics as orig-date in RFC 822.""",
- )
- expires = header_property(
- "Expires",
- None,
- parse_date,
- http_date,
- doc="""The Expires entity-header field gives the date/time after
- which the response is considered stale. A stale cache entry may
- not normally be returned by a cache.""",
- )
- last_modified = header_property(
- "Last-Modified",
- None,
- parse_date,
- http_date,
- doc="""The Last-Modified entity-header field indicates the date
- and time at which the origin server believes the variant was
- last modified.""",
- )
- @property
- def retry_after(self):
- """The Retry-After response-header field can be used with a
- 503 (Service Unavailable) response to indicate how long the
- service is expected to be unavailable to the requesting client.
- Time in seconds until expiration or date.
- """
- value = self.headers.get("retry-after")
- if value is None:
- return
- elif value.isdigit():
- return datetime.utcnow() + timedelta(seconds=int(value))
- return parse_date(value)
- @retry_after.setter
- def retry_after(self, value):
- if value is None:
- if "retry-after" in self.headers:
- del self.headers["retry-after"]
- return
- elif isinstance(value, datetime):
- value = http_date(value)
- else:
- value = str(value)
- self.headers["Retry-After"] = value
- def _set_property(name, doc=None): # noqa: B902
- def fget(self):
- def on_update(header_set):
- if not header_set and name in self.headers:
- del self.headers[name]
- elif header_set:
- self.headers[name] = header_set.to_header()
- return parse_set_header(self.headers.get(name), on_update)
- def fset(self, value):
- if not value:
- del self.headers[name]
- elif isinstance(value, string_types):
- self.headers[name] = value
- else:
- self.headers[name] = dump_header(value)
- return property(fget, fset, doc=doc)
- vary = _set_property(
- "Vary",
- doc="""The Vary field value indicates the set of request-header
- fields that fully determines, while the response is fresh,
- whether a cache is permitted to use the response to reply to a
- subsequent request without revalidation.""",
- )
- content_language = _set_property(
- "Content-Language",
- doc="""The Content-Language entity-header field describes the
- natural language(s) of the intended audience for the enclosed
- entity. Note that this might not be equivalent to all the
- languages used within the entity-body.""",
- )
- allow = _set_property(
- "Allow",
- doc="""The Allow entity-header field lists the set of methods
- supported by the resource identified by the Request-URI. The
- purpose of this field is strictly to inform the recipient of
- valid methods associated with the resource. An Allow header
- field MUST be present in a 405 (Method Not Allowed)
- response.""",
- )
- del _set_property
|