codekingpro/portable-devtools
114k
1"""2h2/config3~~~~~~~~~4 5Objects for controlling the configuration of the HTTP/2 stack.6"""7from __future__ import annotations8 9import sys10from typing import Any11 12 13class _BooleanConfigOption:14 """15 Descriptor for handling a boolean config option. This will block16 attempts to set boolean config options to non-bools.17 """18 19 def __init__(self, name: str) -> None:20 self.name = name21 self.attr_name = f"_{self.name}"22 23 def __get__(self, instance: Any, owner: Any) -> bool:24 return getattr(instance, self.attr_name) # type: ignore25 26 def __set__(self, instance: Any, value: bool) -> None:27 if not isinstance(value, bool):28 msg = f"{self.name} must be a bool"29 raise ValueError(msg) # noqa: TRY00430 setattr(instance, self.attr_name, value)31 32 33class DummyLogger:34 """35 A Logger object that does not actual logging, hence a DummyLogger.36 37 For the class the log operation is merely a no-op. The intent is to avoid38 conditionals being sprinkled throughout the h2 code for calls to39 logging functions when no logger is passed into the corresponding object.40 """41 42 def __init__(self, *vargs) -> None: # type: ignore43 pass44 45 def debug(self, *vargs, **kwargs) -> None: # type: ignore46 """47 No-op logging. Only level needed for now.48 """49 50 def trace(self, *vargs, **kwargs) -> None: # type: ignore51 """52 No-op logging. Only level needed for now.53 """54 55 56class OutputLogger:57 """58 A Logger object that prints to stderr or any other file-like object.59 60 This class is provided for convenience and not part of the stable API.61 62 :param file: A file-like object passed to the print function.63 Defaults to ``sys.stderr``.64 :param trace: Enables trace-level output. Defaults to ``False``.65 """66 67 def __init__(self, file=None, trace_level=False) -> None: # type: ignore68 super().__init__()69 self.file = file or sys.stderr70 self.trace_level = trace_level71 72 def debug(self, fmtstr, *args) -> None: # type: ignore73 print(f"h2 (debug): {fmtstr % args}", file=self.file)74 75 def trace(self, fmtstr, *args) -> None: # type: ignore76 if self.trace_level:77 print(f"h2 (trace): {fmtstr % args}", file=self.file)78 79 80class H2Configuration:81 """82 An object that controls the way a single HTTP/2 connection behaves.83 84 This object allows the users to customize behaviour. In particular, it85 allows users to enable or disable optional features, or to otherwise handle86 various unusual behaviours.87 88 This object has very little behaviour of its own: it mostly just ensures89 that configuration is self-consistent.90 91 :param client_side: Whether this object is to be used on the client side of92 a connection, or on the server side. Affects the logic used by the93 state machine, the default settings values, the allowable stream IDs,94 and several other properties. Defaults to ``True``.95 :type client_side: ``bool``96 97 :param header_encoding: Controls whether the headers emitted by this object98 in events are transparently decoded to ``unicode`` strings, and what99 encoding is used to do that decoding. This defaults to ``None``,100 meaning that headers will be returned as bytes. To automatically101 decode headers (that is, to return them as unicode strings), this can102 be set to the string name of any encoding, e.g. ``'utf-8'``.103 104 .. versionchanged:: 3.0.0105 Changed default value from ``'utf-8'`` to ``None``106 107 :type header_encoding: ``str``, ``False``, or ``None``108 109 :param validate_outbound_headers: Controls whether the headers emitted110 by this object are validated against the rules in RFC 7540.111 Disabling this setting will cause outbound header validation to112 be skipped, and allow the object to emit headers that may be illegal113 according to RFC 7540. Defaults to ``True``.114 :type validate_outbound_headers: ``bool``115 116 :param normalize_outbound_headers: Controls whether the headers emitted117 by this object are normalized before sending. Disabling this setting118 will cause outbound header normalization to be skipped, and allow119 the object to emit headers that may be illegal according to120 RFC 7540. Defaults to ``True``.121 :type normalize_outbound_headers: ``bool``122 123 :param split_outbound_cookies: Controls whether the outbound cookie124 headers are split before sending or not. According to RFC 7540125 - 8.1.2.5 the outbound header cookie headers may be split to improve126 headers compression. Default is ``False``.127 :type split_outbound_cookies: ``bool``128 129 :param validate_inbound_headers: Controls whether the headers received130 by this object are validated against the rules in RFC 7540.131 Disabling this setting will cause inbound header validation to132 be skipped, and allow the object to receive headers that may be illegal133 according to RFC 7540. Defaults to ``True``.134 :type validate_inbound_headers: ``bool``135 136 :param normalize_inbound_headers: Controls whether the headers received by137 this object are normalized according to the rules of RFC 7540.138 Disabling this setting may lead to h2 emitting header blocks that139 some RFCs forbid, e.g. with multiple cookie fields.140 141 .. versionadded:: 3.0.0142 143 :type normalize_inbound_headers: ``bool``144 145 :param logger: A logger that conforms to the requirements for this module,146 those being no I/O and no context switches, which is needed in order147 to run in asynchronous operation.148 149 .. versionadded:: 2.6.0150 151 :type logger: ``logging.Logger``152 """153 154 client_side = _BooleanConfigOption("client_side")155 validate_outbound_headers = _BooleanConfigOption(156 "validate_outbound_headers",157 )158 normalize_outbound_headers = _BooleanConfigOption(159 "normalize_outbound_headers",160 )161 split_outbound_cookies = _BooleanConfigOption(162 "split_outbound_cookies",163 )164 validate_inbound_headers = _BooleanConfigOption(165 "validate_inbound_headers",166 )167 normalize_inbound_headers = _BooleanConfigOption(168 "normalize_inbound_headers",169 )170 171 def __init__(self,172 client_side: bool = True,173 header_encoding: bool | str | None = None,174 validate_outbound_headers: bool = True,175 normalize_outbound_headers: bool = True,176 split_outbound_cookies: bool = False,177 validate_inbound_headers: bool = True,178 normalize_inbound_headers: bool = True,179 logger: DummyLogger | OutputLogger | None = None) -> None:180 self.client_side = client_side181 self.header_encoding = header_encoding182 self.validate_outbound_headers = validate_outbound_headers183 self.normalize_outbound_headers = normalize_outbound_headers184 self.split_outbound_cookies = split_outbound_cookies185 self.validate_inbound_headers = validate_inbound_headers186 self.normalize_inbound_headers = normalize_inbound_headers187 self.logger = logger or DummyLogger(__name__)188 189 @property190 def header_encoding(self) -> bool | str | None:191 """192 Controls whether the headers emitted by this object in events are193 transparently decoded to ``unicode`` strings, and what encoding is used194 to do that decoding. This defaults to ``None``, meaning that headers195 will be returned as bytes. To automatically decode headers (that is, to196 return them as unicode strings), this can be set to the string name of197 any encoding, e.g. ``'utf-8'``.198 """199 return self._header_encoding200 201 @header_encoding.setter202 def header_encoding(self, value: bool | str | None) -> None:203 """204 Enforces constraints on the value of header encoding.205 """206 if not isinstance(value, (bool, str, type(None))):207 msg = "header_encoding must be bool, string, or None"208 raise ValueError(msg) # noqa: TRY004209 if value is True:210 msg = "header_encoding cannot be True"211 raise ValueError(msg)212 self._header_encoding = value213 