| | |
| | """ |
| | h2/config |
| | ~~~~~~~~~ |
| | |
| | Objects for controlling the configuration of the HTTP/2 stack. |
| | """ |
| |
|
| | import sys |
| |
|
| |
|
| | 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): |
| | self.name = name |
| | self.attr_name = '_%s' % self.name |
| |
|
| | def __get__(self, instance, owner): |
| | return getattr(instance, self.attr_name) |
| |
|
| | def __set__(self, instance, value): |
| | if not isinstance(value, bool): |
| | raise ValueError("%s must be a bool" % self.name) |
| | 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): |
| | pass |
| |
|
| | def debug(self, *vargs, **kwargs): |
| | """ |
| | No-op logging. Only level needed for now. |
| | """ |
| | pass |
| |
|
| | def trace(self, *vargs, **kwargs): |
| | """ |
| | No-op logging. Only level needed for now. |
| | """ |
| | pass |
| |
|
| |
|
| | 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): |
| | super().__init__() |
| | self.file = file or sys.stderr |
| | self.trace_level = trace_level |
| |
|
| | def debug(self, fmtstr, *args): |
| | print(f"h2 (debug): {fmtstr % args}", file=self.file) |
| |
|
| | def trace(self, fmtstr, *args): |
| | 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 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' |
| | ) |
| | validate_inbound_headers = _BooleanConfigOption( |
| | 'validate_inbound_headers' |
| | ) |
| | normalize_inbound_headers = _BooleanConfigOption( |
| | 'normalize_inbound_headers' |
| | ) |
| |
|
| | def __init__(self, |
| | client_side=True, |
| | header_encoding=None, |
| | validate_outbound_headers=True, |
| | normalize_outbound_headers=True, |
| | validate_inbound_headers=True, |
| | normalize_inbound_headers=True, |
| | logger=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.validate_inbound_headers = validate_inbound_headers |
| | self.normalize_inbound_headers = normalize_inbound_headers |
| | self.logger = logger or DummyLogger(__name__) |
| |
|
| | @property |
| | def header_encoding(self): |
| | """ |
| | 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): |
| | """ |
| | Enforces constraints on the value of header encoding. |
| | """ |
| | if not isinstance(value, (bool, str, type(None))): |
| | raise ValueError("header_encoding must be bool, string, or None") |
| | if value is True: |
| | raise ValueError("header_encoding cannot be True") |
| | self._header_encoding = value |
| |
|