| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- """
- h2/config
- ~~~~~~~~~
- Objects for controlling the configuration of the HTTP/2 stack.
- """
- from __future__ import annotations
- import sys
- from typing import Any
- class _BooleanConfigOption:
- """
- Descriptor for handling a boolean config option. This will block
- attempts to set boolean config options to non-bools.
- """
- def __init__(self, name: str) -> None:
- self.name = name
- self.attr_name = f"_{self.name}"
- def __get__(self, instance: Any, owner: Any) -> bool:
- return getattr(instance, self.attr_name) # type: ignore
- def __set__(self, instance: Any, value: bool) -> None:
- if not isinstance(value, bool):
- msg = f"{self.name} must be a bool"
- raise ValueError(msg) # noqa: TRY004
- setattr(instance, self.attr_name, value)
- class DummyLogger:
- """
- A Logger object that does not actual logging, hence a DummyLogger.
- For the class the log operation is merely a no-op. The intent is to avoid
- conditionals being sprinkled throughout the h2 code for calls to
- logging functions when no logger is passed into the corresponding object.
- """
- def __init__(self, *vargs) -> None: # type: ignore
- pass
- def debug(self, *vargs, **kwargs) -> None: # type: ignore
- """
- No-op logging. Only level needed for now.
- """
- def trace(self, *vargs, **kwargs) -> None: # type: ignore
- """
- No-op logging. Only level needed for now.
- """
- class OutputLogger:
- """
- A Logger object that prints to stderr or any other file-like object.
- This class is provided for convenience and not part of the stable API.
- :param file: A file-like object passed to the print function.
- Defaults to ``sys.stderr``.
- :param trace: Enables trace-level output. Defaults to ``False``.
- """
- def __init__(self, file=None, trace_level=False) -> None: # type: ignore
- super().__init__()
- self.file = file or sys.stderr
- self.trace_level = trace_level
- def debug(self, fmtstr, *args) -> None: # type: ignore
- print(f"h2 (debug): {fmtstr % args}", file=self.file)
- def trace(self, fmtstr, *args) -> None: # type: ignore
- if self.trace_level:
- print(f"h2 (trace): {fmtstr % args}", file=self.file)
- class H2Configuration:
- """
- An object that controls the way a single HTTP/2 connection behaves.
- This object allows the users to customize behaviour. In particular, it
- allows users to enable or disable optional features, or to otherwise handle
- various unusual behaviours.
- This object has very little behaviour of its own: it mostly just ensures
- that configuration is self-consistent.
- :param client_side: Whether this object is to be used on the client side of
- a connection, or on the server side. Affects the logic used by the
- state machine, the default settings values, the allowable stream IDs,
- and several other properties. Defaults to ``True``.
- :type client_side: ``bool``
- :param header_encoding: Controls whether the headers emitted by this object
- in events are transparently decoded to ``unicode`` strings, and what
- encoding is used to do that decoding. This defaults to ``None``,
- meaning that headers will be returned as bytes. To automatically
- decode headers (that is, to return them as unicode strings), this can
- be set to the string name of any encoding, e.g. ``'utf-8'``.
- .. versionchanged:: 3.0.0
- Changed default value from ``'utf-8'`` to ``None``
- :type header_encoding: ``str``, ``False``, or ``None``
- :param validate_outbound_headers: Controls whether the headers emitted
- by this object are validated against the rules in RFC 7540.
- Disabling this setting will cause outbound header validation to
- be skipped, and allow the object to emit headers that may be illegal
- according to RFC 7540. Defaults to ``True``.
- :type validate_outbound_headers: ``bool``
- :param normalize_outbound_headers: Controls whether the headers emitted
- by this object are normalized before sending. Disabling this setting
- will cause outbound header normalization to be skipped, and allow
- the object to emit headers that may be illegal according to
- RFC 7540. Defaults to ``True``.
- :type normalize_outbound_headers: ``bool``
- :param split_outbound_cookies: Controls whether the outbound cookie
- headers are split before sending or not. According to RFC 7540
- - 8.1.2.5 the outbound header cookie headers may be split to improve
- headers compression. Default is ``False``.
- :type split_outbound_cookies: ``bool``
- :param validate_inbound_headers: Controls whether the headers received
- by this object are validated against the rules in RFC 7540.
- Disabling this setting will cause inbound header validation to
- be skipped, and allow the object to receive headers that may be illegal
- according to RFC 7540. Defaults to ``True``.
- :type validate_inbound_headers: ``bool``
- :param normalize_inbound_headers: Controls whether the headers received by
- this object are normalized according to the rules of RFC 7540.
- Disabling this setting may lead to h2 emitting header blocks that
- some RFCs forbid, e.g. with multiple cookie fields.
- .. versionadded:: 3.0.0
- :type normalize_inbound_headers: ``bool``
- :param logger: A logger that conforms to the requirements for this module,
- those being no I/O and no context switches, which is needed in order
- to run in asynchronous operation.
- .. versionadded:: 2.6.0
- :type logger: ``logging.Logger``
- """
- client_side = _BooleanConfigOption("client_side")
- validate_outbound_headers = _BooleanConfigOption(
- "validate_outbound_headers",
- )
- normalize_outbound_headers = _BooleanConfigOption(
- "normalize_outbound_headers",
- )
- split_outbound_cookies = _BooleanConfigOption(
- "split_outbound_cookies",
- )
- validate_inbound_headers = _BooleanConfigOption(
- "validate_inbound_headers",
- )
- normalize_inbound_headers = _BooleanConfigOption(
- "normalize_inbound_headers",
- )
- def __init__(self,
- client_side: bool = True,
- header_encoding: bool | str | None = None,
- validate_outbound_headers: bool = True,
- normalize_outbound_headers: bool = True,
- split_outbound_cookies: bool = False,
- validate_inbound_headers: bool = True,
- normalize_inbound_headers: bool = True,
- logger: DummyLogger | OutputLogger | None = None) -> None:
- self.client_side = client_side
- self.header_encoding = header_encoding
- self.validate_outbound_headers = validate_outbound_headers
- self.normalize_outbound_headers = normalize_outbound_headers
- self.split_outbound_cookies = split_outbound_cookies
- self.validate_inbound_headers = validate_inbound_headers
- self.normalize_inbound_headers = normalize_inbound_headers
- self.logger = logger or DummyLogger(__name__)
- @property
- def header_encoding(self) -> bool | str | None:
- """
- Controls whether the headers emitted by this object in events are
- transparently decoded to ``unicode`` strings, and what encoding is used
- to do that decoding. This defaults to ``None``, meaning that headers
- will be returned as bytes. To automatically decode headers (that is, to
- return them as unicode strings), this can be set to the string name of
- any encoding, e.g. ``'utf-8'``.
- """
- return self._header_encoding
- @header_encoding.setter
- def header_encoding(self, value: bool | str | None) -> None:
- """
- Enforces constraints on the value of header encoding.
- """
- if not isinstance(value, (bool, str, type(None))):
- msg = "header_encoding must be bool, string, or None"
- raise ValueError(msg) # noqa: TRY004
- if value is True:
- msg = "header_encoding cannot be True"
- raise ValueError(msg)
- self._header_encoding = value
|