1# -*- coding: utf-8 -*-
2"""
3h2/config
4~~~~~~~~~
5
6Objects for controlling the configuration of the HTTP/2 stack.
7"""
8
9
10class _BooleanConfigOption(object):
11    """
12    Descriptor for handling a boolean config option.  This will block
13    attempts to set boolean config options to non-bools.
14    """
15    def __init__(self, name):
16        self.name = name
17        self.attr_name = '_%s' % self.name
18
19    def __get__(self, instance, owner):
20        return getattr(instance, self.attr_name)
21
22    def __set__(self, instance, value):
23        if not isinstance(value, bool):
24            raise ValueError("%s must be a bool" % self.name)
25        setattr(instance, self.attr_name, value)
26
27
28class DummyLogger(object):
29    """
30    An Logger object that does not actual logging, hence a DummyLogger.
31
32    For the class the log operation is merely a no-op.  The intent is to avoid
33    conditionals being sprinkled throughout the hyper-h2 code for calls to
34    logging functions when no logger is passed into the corresponding object.
35    """
36    def __init__(self, *vargs):
37        pass
38
39    def debug(self, *vargs, **kwargs):
40        """
41        No-op logging. Only level needed for now.
42        """
43        pass
44
45
46class H2Configuration(object):
47    """
48    An object that controls the way a single HTTP/2 connection behaves.
49
50    This object allows the users to customize behaviour. In particular, it
51    allows users to enable or disable optional features, or to otherwise handle
52    various unusual behaviours.
53
54    This object has very little behaviour of its own: it mostly just ensures
55    that configuration is self-consistent.
56
57    :param client_side: Whether this object is to be used on the client side of
58        a connection, or on the server side. Affects the logic used by the
59        state machine, the default settings values, the allowable stream IDs,
60        and several other properties. Defaults to ``True``.
61    :type client_side: ``bool``
62
63    :param header_encoding: Controls whether the headers emitted by this object
64        in events are transparently decoded to ``unicode`` strings, and what
65        encoding is used to do that decoding. For historical reasons, this
66        defaults to ``'utf-8'``. To prevent the decoding of headers (that is,
67        to force them to be returned as bytestrings), this can be set to
68        ``False`` or the empty string.
69    :type header_encoding: ``str``, ``False``, or ``None``
70
71    :param validate_outbound_headers: Controls whether the headers emitted
72        by this object are validated against the rules in RFC 7540.
73        Disabling this setting will cause outbound header validation to
74        be skipped, and allow the object to emit headers that may be illegal
75        according to RFC 7540. Defaults to ``True``.
76    :type validate_outbound_headers: ``bool``
77
78    :param normalize_outbound_headers: Controls whether the headers emitted
79        by this object are normalized before sending.  Disabling this setting
80        will cause outbound header normalization to be skipped, and allow
81        the object to emit headers that may be illegal according to
82        RFC 7540. Defaults to ``True``.
83    :type normalize_outbound_headers: ``bool``
84
85    :param validate_inbound_headers: Controls whether the headers received
86        by this object are validated against the rules in RFC 7540.
87        Disabling this setting will cause inbound header validation to
88        be skipped, and allow the object to receive headers that may be illegal
89        according to RFC 7540. Defaults to ``True``.
90    :type validate_inbound_headers: ``bool``
91
92    :param logger: A logger that conforms to the requirements for this module,
93        those being no I/O and no context switches, which is needed in order
94        to run in asynchronous operation.
95
96        .. versionadded:: 2.6.0
97
98    :type logger: ``logging.Logger``
99    """
100    client_side = _BooleanConfigOption('client_side')
101    validate_outbound_headers = _BooleanConfigOption(
102        'validate_outbound_headers'
103    )
104    normalize_outbound_headers = _BooleanConfigOption(
105        'normalize_outbound_headers'
106    )
107    validate_inbound_headers = _BooleanConfigOption(
108        'validate_inbound_headers'
109    )
110
111    def __init__(self,
112                 client_side=True,
113                 header_encoding='utf-8',
114                 validate_outbound_headers=True,
115                 normalize_outbound_headers=True,
116                 validate_inbound_headers=True,
117                 logger=None):
118        self.client_side = client_side
119        self.header_encoding = header_encoding
120        self.validate_outbound_headers = validate_outbound_headers
121        self.normalize_outbound_headers = normalize_outbound_headers
122        self.validate_inbound_headers = validate_inbound_headers
123        self.logger = logger or DummyLogger(__name__)
124
125    @property
126    def header_encoding(self):
127        """
128        Controls whether the headers emitted by this object in events are
129        transparently decoded to ``unicode`` strings, and what encoding is used
130        to do that decoding. For historical reasons, this defaults to
131        ``'utf-8'``. To prevent the decoding of headers (that is, to force them
132        to be returned as bytestrings), this can be set to ``False`` or the
133        empty string.
134        """
135        return self._header_encoding
136
137    @header_encoding.setter
138    def header_encoding(self, value):
139        """
140        Enforces constraints on the value of header encoding.
141        """
142        if not isinstance(value, (bool, str, type(None))):
143            raise ValueError("header_encoding must be bool, string, or None")
144        if value is True:
145            raise ValueError("header_encoding cannot be True")
146        self._header_encoding = value
147