xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/asyncio/sslproto.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Workerimport collections
2*cda5da8dSAndroid Build Coastguard Workerimport enum
3*cda5da8dSAndroid Build Coastguard Workerimport warnings
4*cda5da8dSAndroid Build Coastguard Workertry:
5*cda5da8dSAndroid Build Coastguard Worker    import ssl
6*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:  # pragma: no cover
7*cda5da8dSAndroid Build Coastguard Worker    ssl = None
8*cda5da8dSAndroid Build Coastguard Worker
9*cda5da8dSAndroid Build Coastguard Workerfrom . import constants
10*cda5da8dSAndroid Build Coastguard Workerfrom . import exceptions
11*cda5da8dSAndroid Build Coastguard Workerfrom . import protocols
12*cda5da8dSAndroid Build Coastguard Workerfrom . import transports
13*cda5da8dSAndroid Build Coastguard Workerfrom .log import logger
14*cda5da8dSAndroid Build Coastguard Worker
15*cda5da8dSAndroid Build Coastguard Workerif ssl is not None:
16*cda5da8dSAndroid Build Coastguard Worker    SSLAgainErrors = (ssl.SSLWantReadError, ssl.SSLSyscallError)
17*cda5da8dSAndroid Build Coastguard Worker
18*cda5da8dSAndroid Build Coastguard Worker
19*cda5da8dSAndroid Build Coastguard Workerclass SSLProtocolState(enum.Enum):
20*cda5da8dSAndroid Build Coastguard Worker    UNWRAPPED = "UNWRAPPED"
21*cda5da8dSAndroid Build Coastguard Worker    DO_HANDSHAKE = "DO_HANDSHAKE"
22*cda5da8dSAndroid Build Coastguard Worker    WRAPPED = "WRAPPED"
23*cda5da8dSAndroid Build Coastguard Worker    FLUSHING = "FLUSHING"
24*cda5da8dSAndroid Build Coastguard Worker    SHUTDOWN = "SHUTDOWN"
25*cda5da8dSAndroid Build Coastguard Worker
26*cda5da8dSAndroid Build Coastguard Worker
27*cda5da8dSAndroid Build Coastguard Workerclass AppProtocolState(enum.Enum):
28*cda5da8dSAndroid Build Coastguard Worker    # This tracks the state of app protocol (https://git.io/fj59P):
29*cda5da8dSAndroid Build Coastguard Worker    #
30*cda5da8dSAndroid Build Coastguard Worker    #     INIT -cm-> CON_MADE [-dr*->] [-er-> EOF?] -cl-> CON_LOST
31*cda5da8dSAndroid Build Coastguard Worker    #
32*cda5da8dSAndroid Build Coastguard Worker    # * cm: connection_made()
33*cda5da8dSAndroid Build Coastguard Worker    # * dr: data_received()
34*cda5da8dSAndroid Build Coastguard Worker    # * er: eof_received()
35*cda5da8dSAndroid Build Coastguard Worker    # * cl: connection_lost()
36*cda5da8dSAndroid Build Coastguard Worker
37*cda5da8dSAndroid Build Coastguard Worker    STATE_INIT = "STATE_INIT"
38*cda5da8dSAndroid Build Coastguard Worker    STATE_CON_MADE = "STATE_CON_MADE"
39*cda5da8dSAndroid Build Coastguard Worker    STATE_EOF = "STATE_EOF"
40*cda5da8dSAndroid Build Coastguard Worker    STATE_CON_LOST = "STATE_CON_LOST"
41*cda5da8dSAndroid Build Coastguard Worker
42*cda5da8dSAndroid Build Coastguard Worker
43*cda5da8dSAndroid Build Coastguard Workerdef _create_transport_context(server_side, server_hostname):
44*cda5da8dSAndroid Build Coastguard Worker    if server_side:
45*cda5da8dSAndroid Build Coastguard Worker        raise ValueError('Server side SSL needs a valid SSLContext')
46*cda5da8dSAndroid Build Coastguard Worker
47*cda5da8dSAndroid Build Coastguard Worker    # Client side may pass ssl=True to use a default
48*cda5da8dSAndroid Build Coastguard Worker    # context; in that case the sslcontext passed is None.
49*cda5da8dSAndroid Build Coastguard Worker    # The default is secure for client connections.
50*cda5da8dSAndroid Build Coastguard Worker    # Python 3.4+: use up-to-date strong settings.
51*cda5da8dSAndroid Build Coastguard Worker    sslcontext = ssl.create_default_context()
52*cda5da8dSAndroid Build Coastguard Worker    if not server_hostname:
53*cda5da8dSAndroid Build Coastguard Worker        sslcontext.check_hostname = False
54*cda5da8dSAndroid Build Coastguard Worker    return sslcontext
55*cda5da8dSAndroid Build Coastguard Worker
56*cda5da8dSAndroid Build Coastguard Worker
57*cda5da8dSAndroid Build Coastguard Workerdef add_flowcontrol_defaults(high, low, kb):
58*cda5da8dSAndroid Build Coastguard Worker    if high is None:
59*cda5da8dSAndroid Build Coastguard Worker        if low is None:
60*cda5da8dSAndroid Build Coastguard Worker            hi = kb * 1024
61*cda5da8dSAndroid Build Coastguard Worker        else:
62*cda5da8dSAndroid Build Coastguard Worker            lo = low
63*cda5da8dSAndroid Build Coastguard Worker            hi = 4 * lo
64*cda5da8dSAndroid Build Coastguard Worker    else:
65*cda5da8dSAndroid Build Coastguard Worker        hi = high
66*cda5da8dSAndroid Build Coastguard Worker    if low is None:
67*cda5da8dSAndroid Build Coastguard Worker        lo = hi // 4
68*cda5da8dSAndroid Build Coastguard Worker    else:
69*cda5da8dSAndroid Build Coastguard Worker        lo = low
70*cda5da8dSAndroid Build Coastguard Worker
71*cda5da8dSAndroid Build Coastguard Worker    if not hi >= lo >= 0:
72*cda5da8dSAndroid Build Coastguard Worker        raise ValueError('high (%r) must be >= low (%r) must be >= 0' %
73*cda5da8dSAndroid Build Coastguard Worker                         (hi, lo))
74*cda5da8dSAndroid Build Coastguard Worker
75*cda5da8dSAndroid Build Coastguard Worker    return hi, lo
76*cda5da8dSAndroid Build Coastguard Worker
77*cda5da8dSAndroid Build Coastguard Worker
78*cda5da8dSAndroid Build Coastguard Workerclass _SSLProtocolTransport(transports._FlowControlMixin,
79*cda5da8dSAndroid Build Coastguard Worker                            transports.Transport):
80*cda5da8dSAndroid Build Coastguard Worker
81*cda5da8dSAndroid Build Coastguard Worker    _start_tls_compatible = True
82*cda5da8dSAndroid Build Coastguard Worker    _sendfile_compatible = constants._SendfileMode.FALLBACK
83*cda5da8dSAndroid Build Coastguard Worker
84*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, loop, ssl_protocol):
85*cda5da8dSAndroid Build Coastguard Worker        self._loop = loop
86*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol = ssl_protocol
87*cda5da8dSAndroid Build Coastguard Worker        self._closed = False
88*cda5da8dSAndroid Build Coastguard Worker
89*cda5da8dSAndroid Build Coastguard Worker    def get_extra_info(self, name, default=None):
90*cda5da8dSAndroid Build Coastguard Worker        """Get optional transport information."""
91*cda5da8dSAndroid Build Coastguard Worker        return self._ssl_protocol._get_extra_info(name, default)
92*cda5da8dSAndroid Build Coastguard Worker
93*cda5da8dSAndroid Build Coastguard Worker    def set_protocol(self, protocol):
94*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._set_app_protocol(protocol)
95*cda5da8dSAndroid Build Coastguard Worker
96*cda5da8dSAndroid Build Coastguard Worker    def get_protocol(self):
97*cda5da8dSAndroid Build Coastguard Worker        return self._ssl_protocol._app_protocol
98*cda5da8dSAndroid Build Coastguard Worker
99*cda5da8dSAndroid Build Coastguard Worker    def is_closing(self):
100*cda5da8dSAndroid Build Coastguard Worker        return self._closed
101*cda5da8dSAndroid Build Coastguard Worker
102*cda5da8dSAndroid Build Coastguard Worker    def close(self):
103*cda5da8dSAndroid Build Coastguard Worker        """Close the transport.
104*cda5da8dSAndroid Build Coastguard Worker
105*cda5da8dSAndroid Build Coastguard Worker        Buffered data will be flushed asynchronously.  No more data
106*cda5da8dSAndroid Build Coastguard Worker        will be received.  After all buffered data is flushed, the
107*cda5da8dSAndroid Build Coastguard Worker        protocol's connection_lost() method will (eventually) called
108*cda5da8dSAndroid Build Coastguard Worker        with None as its argument.
109*cda5da8dSAndroid Build Coastguard Worker        """
110*cda5da8dSAndroid Build Coastguard Worker        if not self._closed:
111*cda5da8dSAndroid Build Coastguard Worker            self._closed = True
112*cda5da8dSAndroid Build Coastguard Worker            self._ssl_protocol._start_shutdown()
113*cda5da8dSAndroid Build Coastguard Worker        else:
114*cda5da8dSAndroid Build Coastguard Worker            self._ssl_protocol = None
115*cda5da8dSAndroid Build Coastguard Worker
116*cda5da8dSAndroid Build Coastguard Worker    def __del__(self, _warnings=warnings):
117*cda5da8dSAndroid Build Coastguard Worker        if not self._closed:
118*cda5da8dSAndroid Build Coastguard Worker            self._closed = True
119*cda5da8dSAndroid Build Coastguard Worker            _warnings.warn(
120*cda5da8dSAndroid Build Coastguard Worker                "unclosed transport <asyncio._SSLProtocolTransport "
121*cda5da8dSAndroid Build Coastguard Worker                "object>", ResourceWarning)
122*cda5da8dSAndroid Build Coastguard Worker
123*cda5da8dSAndroid Build Coastguard Worker    def is_reading(self):
124*cda5da8dSAndroid Build Coastguard Worker        return not self._ssl_protocol._app_reading_paused
125*cda5da8dSAndroid Build Coastguard Worker
126*cda5da8dSAndroid Build Coastguard Worker    def pause_reading(self):
127*cda5da8dSAndroid Build Coastguard Worker        """Pause the receiving end.
128*cda5da8dSAndroid Build Coastguard Worker
129*cda5da8dSAndroid Build Coastguard Worker        No data will be passed to the protocol's data_received()
130*cda5da8dSAndroid Build Coastguard Worker        method until resume_reading() is called.
131*cda5da8dSAndroid Build Coastguard Worker        """
132*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._pause_reading()
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker    def resume_reading(self):
135*cda5da8dSAndroid Build Coastguard Worker        """Resume the receiving end.
136*cda5da8dSAndroid Build Coastguard Worker
137*cda5da8dSAndroid Build Coastguard Worker        Data received will once again be passed to the protocol's
138*cda5da8dSAndroid Build Coastguard Worker        data_received() method.
139*cda5da8dSAndroid Build Coastguard Worker        """
140*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._resume_reading()
141*cda5da8dSAndroid Build Coastguard Worker
142*cda5da8dSAndroid Build Coastguard Worker    def set_write_buffer_limits(self, high=None, low=None):
143*cda5da8dSAndroid Build Coastguard Worker        """Set the high- and low-water limits for write flow control.
144*cda5da8dSAndroid Build Coastguard Worker
145*cda5da8dSAndroid Build Coastguard Worker        These two values control when to call the protocol's
146*cda5da8dSAndroid Build Coastguard Worker        pause_writing() and resume_writing() methods.  If specified,
147*cda5da8dSAndroid Build Coastguard Worker        the low-water limit must be less than or equal to the
148*cda5da8dSAndroid Build Coastguard Worker        high-water limit.  Neither value can be negative.
149*cda5da8dSAndroid Build Coastguard Worker
150*cda5da8dSAndroid Build Coastguard Worker        The defaults are implementation-specific.  If only the
151*cda5da8dSAndroid Build Coastguard Worker        high-water limit is given, the low-water limit defaults to an
152*cda5da8dSAndroid Build Coastguard Worker        implementation-specific value less than or equal to the
153*cda5da8dSAndroid Build Coastguard Worker        high-water limit.  Setting high to zero forces low to zero as
154*cda5da8dSAndroid Build Coastguard Worker        well, and causes pause_writing() to be called whenever the
155*cda5da8dSAndroid Build Coastguard Worker        buffer becomes non-empty.  Setting low to zero causes
156*cda5da8dSAndroid Build Coastguard Worker        resume_writing() to be called only once the buffer is empty.
157*cda5da8dSAndroid Build Coastguard Worker        Use of zero for either limit is generally sub-optimal as it
158*cda5da8dSAndroid Build Coastguard Worker        reduces opportunities for doing I/O and computation
159*cda5da8dSAndroid Build Coastguard Worker        concurrently.
160*cda5da8dSAndroid Build Coastguard Worker        """
161*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._set_write_buffer_limits(high, low)
162*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._control_app_writing()
163*cda5da8dSAndroid Build Coastguard Worker
164*cda5da8dSAndroid Build Coastguard Worker    def get_write_buffer_limits(self):
165*cda5da8dSAndroid Build Coastguard Worker        return (self._ssl_protocol._outgoing_low_water,
166*cda5da8dSAndroid Build Coastguard Worker                self._ssl_protocol._outgoing_high_water)
167*cda5da8dSAndroid Build Coastguard Worker
168*cda5da8dSAndroid Build Coastguard Worker    def get_write_buffer_size(self):
169*cda5da8dSAndroid Build Coastguard Worker        """Return the current size of the write buffers."""
170*cda5da8dSAndroid Build Coastguard Worker        return self._ssl_protocol._get_write_buffer_size()
171*cda5da8dSAndroid Build Coastguard Worker
172*cda5da8dSAndroid Build Coastguard Worker    def set_read_buffer_limits(self, high=None, low=None):
173*cda5da8dSAndroid Build Coastguard Worker        """Set the high- and low-water limits for read flow control.
174*cda5da8dSAndroid Build Coastguard Worker
175*cda5da8dSAndroid Build Coastguard Worker        These two values control when to call the upstream transport's
176*cda5da8dSAndroid Build Coastguard Worker        pause_reading() and resume_reading() methods.  If specified,
177*cda5da8dSAndroid Build Coastguard Worker        the low-water limit must be less than or equal to the
178*cda5da8dSAndroid Build Coastguard Worker        high-water limit.  Neither value can be negative.
179*cda5da8dSAndroid Build Coastguard Worker
180*cda5da8dSAndroid Build Coastguard Worker        The defaults are implementation-specific.  If only the
181*cda5da8dSAndroid Build Coastguard Worker        high-water limit is given, the low-water limit defaults to an
182*cda5da8dSAndroid Build Coastguard Worker        implementation-specific value less than or equal to the
183*cda5da8dSAndroid Build Coastguard Worker        high-water limit.  Setting high to zero forces low to zero as
184*cda5da8dSAndroid Build Coastguard Worker        well, and causes pause_reading() to be called whenever the
185*cda5da8dSAndroid Build Coastguard Worker        buffer becomes non-empty.  Setting low to zero causes
186*cda5da8dSAndroid Build Coastguard Worker        resume_reading() to be called only once the buffer is empty.
187*cda5da8dSAndroid Build Coastguard Worker        Use of zero for either limit is generally sub-optimal as it
188*cda5da8dSAndroid Build Coastguard Worker        reduces opportunities for doing I/O and computation
189*cda5da8dSAndroid Build Coastguard Worker        concurrently.
190*cda5da8dSAndroid Build Coastguard Worker        """
191*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._set_read_buffer_limits(high, low)
192*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._control_ssl_reading()
193*cda5da8dSAndroid Build Coastguard Worker
194*cda5da8dSAndroid Build Coastguard Worker    def get_read_buffer_limits(self):
195*cda5da8dSAndroid Build Coastguard Worker        return (self._ssl_protocol._incoming_low_water,
196*cda5da8dSAndroid Build Coastguard Worker                self._ssl_protocol._incoming_high_water)
197*cda5da8dSAndroid Build Coastguard Worker
198*cda5da8dSAndroid Build Coastguard Worker    def get_read_buffer_size(self):
199*cda5da8dSAndroid Build Coastguard Worker        """Return the current size of the read buffer."""
200*cda5da8dSAndroid Build Coastguard Worker        return self._ssl_protocol._get_read_buffer_size()
201*cda5da8dSAndroid Build Coastguard Worker
202*cda5da8dSAndroid Build Coastguard Worker    @property
203*cda5da8dSAndroid Build Coastguard Worker    def _protocol_paused(self):
204*cda5da8dSAndroid Build Coastguard Worker        # Required for sendfile fallback pause_writing/resume_writing logic
205*cda5da8dSAndroid Build Coastguard Worker        return self._ssl_protocol._app_writing_paused
206*cda5da8dSAndroid Build Coastguard Worker
207*cda5da8dSAndroid Build Coastguard Worker    def write(self, data):
208*cda5da8dSAndroid Build Coastguard Worker        """Write some data bytes to the transport.
209*cda5da8dSAndroid Build Coastguard Worker
210*cda5da8dSAndroid Build Coastguard Worker        This does not block; it buffers the data and arranges for it
211*cda5da8dSAndroid Build Coastguard Worker        to be sent out asynchronously.
212*cda5da8dSAndroid Build Coastguard Worker        """
213*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(data, (bytes, bytearray, memoryview)):
214*cda5da8dSAndroid Build Coastguard Worker            raise TypeError(f"data: expecting a bytes-like instance, "
215*cda5da8dSAndroid Build Coastguard Worker                            f"got {type(data).__name__}")
216*cda5da8dSAndroid Build Coastguard Worker        if not data:
217*cda5da8dSAndroid Build Coastguard Worker            return
218*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._write_appdata((data,))
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Worker    def writelines(self, list_of_data):
221*cda5da8dSAndroid Build Coastguard Worker        """Write a list (or any iterable) of data bytes to the transport.
222*cda5da8dSAndroid Build Coastguard Worker
223*cda5da8dSAndroid Build Coastguard Worker        The default implementation concatenates the arguments and
224*cda5da8dSAndroid Build Coastguard Worker        calls write() on the result.
225*cda5da8dSAndroid Build Coastguard Worker        """
226*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._write_appdata(list_of_data)
227*cda5da8dSAndroid Build Coastguard Worker
228*cda5da8dSAndroid Build Coastguard Worker    def write_eof(self):
229*cda5da8dSAndroid Build Coastguard Worker        """Close the write end after flushing buffered data.
230*cda5da8dSAndroid Build Coastguard Worker
231*cda5da8dSAndroid Build Coastguard Worker        This raises :exc:`NotImplementedError` right now.
232*cda5da8dSAndroid Build Coastguard Worker        """
233*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError
234*cda5da8dSAndroid Build Coastguard Worker
235*cda5da8dSAndroid Build Coastguard Worker    def can_write_eof(self):
236*cda5da8dSAndroid Build Coastguard Worker        """Return True if this transport supports write_eof(), False if not."""
237*cda5da8dSAndroid Build Coastguard Worker        return False
238*cda5da8dSAndroid Build Coastguard Worker
239*cda5da8dSAndroid Build Coastguard Worker    def abort(self):
240*cda5da8dSAndroid Build Coastguard Worker        """Close the transport immediately.
241*cda5da8dSAndroid Build Coastguard Worker
242*cda5da8dSAndroid Build Coastguard Worker        Buffered data will be lost.  No more data will be received.
243*cda5da8dSAndroid Build Coastguard Worker        The protocol's connection_lost() method will (eventually) be
244*cda5da8dSAndroid Build Coastguard Worker        called with None as its argument.
245*cda5da8dSAndroid Build Coastguard Worker        """
246*cda5da8dSAndroid Build Coastguard Worker        self._closed = True
247*cda5da8dSAndroid Build Coastguard Worker        if self._ssl_protocol is not None:
248*cda5da8dSAndroid Build Coastguard Worker            self._ssl_protocol._abort()
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker    def _force_close(self, exc):
251*cda5da8dSAndroid Build Coastguard Worker        self._closed = True
252*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._abort(exc)
253*cda5da8dSAndroid Build Coastguard Worker
254*cda5da8dSAndroid Build Coastguard Worker    def _test__append_write_backlog(self, data):
255*cda5da8dSAndroid Build Coastguard Worker        # for test only
256*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._write_backlog.append(data)
257*cda5da8dSAndroid Build Coastguard Worker        self._ssl_protocol._write_buffer_size += len(data)
258*cda5da8dSAndroid Build Coastguard Worker
259*cda5da8dSAndroid Build Coastguard Worker
260*cda5da8dSAndroid Build Coastguard Workerclass SSLProtocol(protocols.BufferedProtocol):
261*cda5da8dSAndroid Build Coastguard Worker    max_size = 256 * 1024   # Buffer size passed to read()
262*cda5da8dSAndroid Build Coastguard Worker
263*cda5da8dSAndroid Build Coastguard Worker    _handshake_start_time = None
264*cda5da8dSAndroid Build Coastguard Worker    _handshake_timeout_handle = None
265*cda5da8dSAndroid Build Coastguard Worker    _shutdown_timeout_handle = None
266*cda5da8dSAndroid Build Coastguard Worker
267*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, loop, app_protocol, sslcontext, waiter,
268*cda5da8dSAndroid Build Coastguard Worker                 server_side=False, server_hostname=None,
269*cda5da8dSAndroid Build Coastguard Worker                 call_connection_made=True,
270*cda5da8dSAndroid Build Coastguard Worker                 ssl_handshake_timeout=None,
271*cda5da8dSAndroid Build Coastguard Worker                 ssl_shutdown_timeout=None):
272*cda5da8dSAndroid Build Coastguard Worker        if ssl is None:
273*cda5da8dSAndroid Build Coastguard Worker            raise RuntimeError("stdlib ssl module not available")
274*cda5da8dSAndroid Build Coastguard Worker
275*cda5da8dSAndroid Build Coastguard Worker        self._ssl_buffer = bytearray(self.max_size)
276*cda5da8dSAndroid Build Coastguard Worker        self._ssl_buffer_view = memoryview(self._ssl_buffer)
277*cda5da8dSAndroid Build Coastguard Worker
278*cda5da8dSAndroid Build Coastguard Worker        if ssl_handshake_timeout is None:
279*cda5da8dSAndroid Build Coastguard Worker            ssl_handshake_timeout = constants.SSL_HANDSHAKE_TIMEOUT
280*cda5da8dSAndroid Build Coastguard Worker        elif ssl_handshake_timeout <= 0:
281*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(
282*cda5da8dSAndroid Build Coastguard Worker                f"ssl_handshake_timeout should be a positive number, "
283*cda5da8dSAndroid Build Coastguard Worker                f"got {ssl_handshake_timeout}")
284*cda5da8dSAndroid Build Coastguard Worker        if ssl_shutdown_timeout is None:
285*cda5da8dSAndroid Build Coastguard Worker            ssl_shutdown_timeout = constants.SSL_SHUTDOWN_TIMEOUT
286*cda5da8dSAndroid Build Coastguard Worker        elif ssl_shutdown_timeout <= 0:
287*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(
288*cda5da8dSAndroid Build Coastguard Worker                f"ssl_shutdown_timeout should be a positive number, "
289*cda5da8dSAndroid Build Coastguard Worker                f"got {ssl_shutdown_timeout}")
290*cda5da8dSAndroid Build Coastguard Worker
291*cda5da8dSAndroid Build Coastguard Worker        if not sslcontext:
292*cda5da8dSAndroid Build Coastguard Worker            sslcontext = _create_transport_context(
293*cda5da8dSAndroid Build Coastguard Worker                server_side, server_hostname)
294*cda5da8dSAndroid Build Coastguard Worker
295*cda5da8dSAndroid Build Coastguard Worker        self._server_side = server_side
296*cda5da8dSAndroid Build Coastguard Worker        if server_hostname and not server_side:
297*cda5da8dSAndroid Build Coastguard Worker            self._server_hostname = server_hostname
298*cda5da8dSAndroid Build Coastguard Worker        else:
299*cda5da8dSAndroid Build Coastguard Worker            self._server_hostname = None
300*cda5da8dSAndroid Build Coastguard Worker        self._sslcontext = sslcontext
301*cda5da8dSAndroid Build Coastguard Worker        # SSL-specific extra info. More info are set when the handshake
302*cda5da8dSAndroid Build Coastguard Worker        # completes.
303*cda5da8dSAndroid Build Coastguard Worker        self._extra = dict(sslcontext=sslcontext)
304*cda5da8dSAndroid Build Coastguard Worker
305*cda5da8dSAndroid Build Coastguard Worker        # App data write buffering
306*cda5da8dSAndroid Build Coastguard Worker        self._write_backlog = collections.deque()
307*cda5da8dSAndroid Build Coastguard Worker        self._write_buffer_size = 0
308*cda5da8dSAndroid Build Coastguard Worker
309*cda5da8dSAndroid Build Coastguard Worker        self._waiter = waiter
310*cda5da8dSAndroid Build Coastguard Worker        self._loop = loop
311*cda5da8dSAndroid Build Coastguard Worker        self._set_app_protocol(app_protocol)
312*cda5da8dSAndroid Build Coastguard Worker        self._app_transport = None
313*cda5da8dSAndroid Build Coastguard Worker        self._app_transport_created = False
314*cda5da8dSAndroid Build Coastguard Worker        # transport, ex: SelectorSocketTransport
315*cda5da8dSAndroid Build Coastguard Worker        self._transport = None
316*cda5da8dSAndroid Build Coastguard Worker        self._ssl_handshake_timeout = ssl_handshake_timeout
317*cda5da8dSAndroid Build Coastguard Worker        self._ssl_shutdown_timeout = ssl_shutdown_timeout
318*cda5da8dSAndroid Build Coastguard Worker        # SSL and state machine
319*cda5da8dSAndroid Build Coastguard Worker        self._incoming = ssl.MemoryBIO()
320*cda5da8dSAndroid Build Coastguard Worker        self._outgoing = ssl.MemoryBIO()
321*cda5da8dSAndroid Build Coastguard Worker        self._state = SSLProtocolState.UNWRAPPED
322*cda5da8dSAndroid Build Coastguard Worker        self._conn_lost = 0  # Set when connection_lost called
323*cda5da8dSAndroid Build Coastguard Worker        if call_connection_made:
324*cda5da8dSAndroid Build Coastguard Worker            self._app_state = AppProtocolState.STATE_INIT
325*cda5da8dSAndroid Build Coastguard Worker        else:
326*cda5da8dSAndroid Build Coastguard Worker            self._app_state = AppProtocolState.STATE_CON_MADE
327*cda5da8dSAndroid Build Coastguard Worker        self._sslobj = self._sslcontext.wrap_bio(
328*cda5da8dSAndroid Build Coastguard Worker            self._incoming, self._outgoing,
329*cda5da8dSAndroid Build Coastguard Worker            server_side=self._server_side,
330*cda5da8dSAndroid Build Coastguard Worker            server_hostname=self._server_hostname)
331*cda5da8dSAndroid Build Coastguard Worker
332*cda5da8dSAndroid Build Coastguard Worker        # Flow Control
333*cda5da8dSAndroid Build Coastguard Worker
334*cda5da8dSAndroid Build Coastguard Worker        self._ssl_writing_paused = False
335*cda5da8dSAndroid Build Coastguard Worker
336*cda5da8dSAndroid Build Coastguard Worker        self._app_reading_paused = False
337*cda5da8dSAndroid Build Coastguard Worker
338*cda5da8dSAndroid Build Coastguard Worker        self._ssl_reading_paused = False
339*cda5da8dSAndroid Build Coastguard Worker        self._incoming_high_water = 0
340*cda5da8dSAndroid Build Coastguard Worker        self._incoming_low_water = 0
341*cda5da8dSAndroid Build Coastguard Worker        self._set_read_buffer_limits()
342*cda5da8dSAndroid Build Coastguard Worker        self._eof_received = False
343*cda5da8dSAndroid Build Coastguard Worker
344*cda5da8dSAndroid Build Coastguard Worker        self._app_writing_paused = False
345*cda5da8dSAndroid Build Coastguard Worker        self._outgoing_high_water = 0
346*cda5da8dSAndroid Build Coastguard Worker        self._outgoing_low_water = 0
347*cda5da8dSAndroid Build Coastguard Worker        self._set_write_buffer_limits()
348*cda5da8dSAndroid Build Coastguard Worker        self._get_app_transport()
349*cda5da8dSAndroid Build Coastguard Worker
350*cda5da8dSAndroid Build Coastguard Worker    def _set_app_protocol(self, app_protocol):
351*cda5da8dSAndroid Build Coastguard Worker        self._app_protocol = app_protocol
352*cda5da8dSAndroid Build Coastguard Worker        # Make fast hasattr check first
353*cda5da8dSAndroid Build Coastguard Worker        if (hasattr(app_protocol, 'get_buffer') and
354*cda5da8dSAndroid Build Coastguard Worker                isinstance(app_protocol, protocols.BufferedProtocol)):
355*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol_get_buffer = app_protocol.get_buffer
356*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol_buffer_updated = app_protocol.buffer_updated
357*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol_is_buffer = True
358*cda5da8dSAndroid Build Coastguard Worker        else:
359*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol_is_buffer = False
360*cda5da8dSAndroid Build Coastguard Worker
361*cda5da8dSAndroid Build Coastguard Worker    def _wakeup_waiter(self, exc=None):
362*cda5da8dSAndroid Build Coastguard Worker        if self._waiter is None:
363*cda5da8dSAndroid Build Coastguard Worker            return
364*cda5da8dSAndroid Build Coastguard Worker        if not self._waiter.cancelled():
365*cda5da8dSAndroid Build Coastguard Worker            if exc is not None:
366*cda5da8dSAndroid Build Coastguard Worker                self._waiter.set_exception(exc)
367*cda5da8dSAndroid Build Coastguard Worker            else:
368*cda5da8dSAndroid Build Coastguard Worker                self._waiter.set_result(None)
369*cda5da8dSAndroid Build Coastguard Worker        self._waiter = None
370*cda5da8dSAndroid Build Coastguard Worker
371*cda5da8dSAndroid Build Coastguard Worker    def _get_app_transport(self):
372*cda5da8dSAndroid Build Coastguard Worker        if self._app_transport is None:
373*cda5da8dSAndroid Build Coastguard Worker            if self._app_transport_created:
374*cda5da8dSAndroid Build Coastguard Worker                raise RuntimeError('Creating _SSLProtocolTransport twice')
375*cda5da8dSAndroid Build Coastguard Worker            self._app_transport = _SSLProtocolTransport(self._loop, self)
376*cda5da8dSAndroid Build Coastguard Worker            self._app_transport_created = True
377*cda5da8dSAndroid Build Coastguard Worker        return self._app_transport
378*cda5da8dSAndroid Build Coastguard Worker
379*cda5da8dSAndroid Build Coastguard Worker    def connection_made(self, transport):
380*cda5da8dSAndroid Build Coastguard Worker        """Called when the low-level connection is made.
381*cda5da8dSAndroid Build Coastguard Worker
382*cda5da8dSAndroid Build Coastguard Worker        Start the SSL handshake.
383*cda5da8dSAndroid Build Coastguard Worker        """
384*cda5da8dSAndroid Build Coastguard Worker        self._transport = transport
385*cda5da8dSAndroid Build Coastguard Worker        self._start_handshake()
386*cda5da8dSAndroid Build Coastguard Worker
387*cda5da8dSAndroid Build Coastguard Worker    def connection_lost(self, exc):
388*cda5da8dSAndroid Build Coastguard Worker        """Called when the low-level connection is lost or closed.
389*cda5da8dSAndroid Build Coastguard Worker
390*cda5da8dSAndroid Build Coastguard Worker        The argument is an exception object or None (the latter
391*cda5da8dSAndroid Build Coastguard Worker        meaning a regular EOF is received or the connection was
392*cda5da8dSAndroid Build Coastguard Worker        aborted or closed).
393*cda5da8dSAndroid Build Coastguard Worker        """
394*cda5da8dSAndroid Build Coastguard Worker        self._write_backlog.clear()
395*cda5da8dSAndroid Build Coastguard Worker        self._outgoing.read()
396*cda5da8dSAndroid Build Coastguard Worker        self._conn_lost += 1
397*cda5da8dSAndroid Build Coastguard Worker
398*cda5da8dSAndroid Build Coastguard Worker        # Just mark the app transport as closed so that its __dealloc__
399*cda5da8dSAndroid Build Coastguard Worker        # doesn't complain.
400*cda5da8dSAndroid Build Coastguard Worker        if self._app_transport is not None:
401*cda5da8dSAndroid Build Coastguard Worker            self._app_transport._closed = True
402*cda5da8dSAndroid Build Coastguard Worker
403*cda5da8dSAndroid Build Coastguard Worker        if self._state != SSLProtocolState.DO_HANDSHAKE:
404*cda5da8dSAndroid Build Coastguard Worker            if (
405*cda5da8dSAndroid Build Coastguard Worker                self._app_state == AppProtocolState.STATE_CON_MADE or
406*cda5da8dSAndroid Build Coastguard Worker                self._app_state == AppProtocolState.STATE_EOF
407*cda5da8dSAndroid Build Coastguard Worker            ):
408*cda5da8dSAndroid Build Coastguard Worker                self._app_state = AppProtocolState.STATE_CON_LOST
409*cda5da8dSAndroid Build Coastguard Worker                self._loop.call_soon(self._app_protocol.connection_lost, exc)
410*cda5da8dSAndroid Build Coastguard Worker        self._set_state(SSLProtocolState.UNWRAPPED)
411*cda5da8dSAndroid Build Coastguard Worker        self._transport = None
412*cda5da8dSAndroid Build Coastguard Worker        self._app_transport = None
413*cda5da8dSAndroid Build Coastguard Worker        self._app_protocol = None
414*cda5da8dSAndroid Build Coastguard Worker        self._wakeup_waiter(exc)
415*cda5da8dSAndroid Build Coastguard Worker
416*cda5da8dSAndroid Build Coastguard Worker        if self._shutdown_timeout_handle:
417*cda5da8dSAndroid Build Coastguard Worker            self._shutdown_timeout_handle.cancel()
418*cda5da8dSAndroid Build Coastguard Worker            self._shutdown_timeout_handle = None
419*cda5da8dSAndroid Build Coastguard Worker        if self._handshake_timeout_handle:
420*cda5da8dSAndroid Build Coastguard Worker            self._handshake_timeout_handle.cancel()
421*cda5da8dSAndroid Build Coastguard Worker            self._handshake_timeout_handle = None
422*cda5da8dSAndroid Build Coastguard Worker
423*cda5da8dSAndroid Build Coastguard Worker    def get_buffer(self, n):
424*cda5da8dSAndroid Build Coastguard Worker        want = n
425*cda5da8dSAndroid Build Coastguard Worker        if want <= 0 or want > self.max_size:
426*cda5da8dSAndroid Build Coastguard Worker            want = self.max_size
427*cda5da8dSAndroid Build Coastguard Worker        if len(self._ssl_buffer) < want:
428*cda5da8dSAndroid Build Coastguard Worker            self._ssl_buffer = bytearray(want)
429*cda5da8dSAndroid Build Coastguard Worker            self._ssl_buffer_view = memoryview(self._ssl_buffer)
430*cda5da8dSAndroid Build Coastguard Worker        return self._ssl_buffer_view
431*cda5da8dSAndroid Build Coastguard Worker
432*cda5da8dSAndroid Build Coastguard Worker    def buffer_updated(self, nbytes):
433*cda5da8dSAndroid Build Coastguard Worker        self._incoming.write(self._ssl_buffer_view[:nbytes])
434*cda5da8dSAndroid Build Coastguard Worker
435*cda5da8dSAndroid Build Coastguard Worker        if self._state == SSLProtocolState.DO_HANDSHAKE:
436*cda5da8dSAndroid Build Coastguard Worker            self._do_handshake()
437*cda5da8dSAndroid Build Coastguard Worker
438*cda5da8dSAndroid Build Coastguard Worker        elif self._state == SSLProtocolState.WRAPPED:
439*cda5da8dSAndroid Build Coastguard Worker            self._do_read()
440*cda5da8dSAndroid Build Coastguard Worker
441*cda5da8dSAndroid Build Coastguard Worker        elif self._state == SSLProtocolState.FLUSHING:
442*cda5da8dSAndroid Build Coastguard Worker            self._do_flush()
443*cda5da8dSAndroid Build Coastguard Worker
444*cda5da8dSAndroid Build Coastguard Worker        elif self._state == SSLProtocolState.SHUTDOWN:
445*cda5da8dSAndroid Build Coastguard Worker            self._do_shutdown()
446*cda5da8dSAndroid Build Coastguard Worker
447*cda5da8dSAndroid Build Coastguard Worker    def eof_received(self):
448*cda5da8dSAndroid Build Coastguard Worker        """Called when the other end of the low-level stream
449*cda5da8dSAndroid Build Coastguard Worker        is half-closed.
450*cda5da8dSAndroid Build Coastguard Worker
451*cda5da8dSAndroid Build Coastguard Worker        If this returns a false value (including None), the transport
452*cda5da8dSAndroid Build Coastguard Worker        will close itself.  If it returns a true value, closing the
453*cda5da8dSAndroid Build Coastguard Worker        transport is up to the protocol.
454*cda5da8dSAndroid Build Coastguard Worker        """
455*cda5da8dSAndroid Build Coastguard Worker        self._eof_received = True
456*cda5da8dSAndroid Build Coastguard Worker        try:
457*cda5da8dSAndroid Build Coastguard Worker            if self._loop.get_debug():
458*cda5da8dSAndroid Build Coastguard Worker                logger.debug("%r received EOF", self)
459*cda5da8dSAndroid Build Coastguard Worker
460*cda5da8dSAndroid Build Coastguard Worker            if self._state == SSLProtocolState.DO_HANDSHAKE:
461*cda5da8dSAndroid Build Coastguard Worker                self._on_handshake_complete(ConnectionResetError)
462*cda5da8dSAndroid Build Coastguard Worker
463*cda5da8dSAndroid Build Coastguard Worker            elif self._state == SSLProtocolState.WRAPPED:
464*cda5da8dSAndroid Build Coastguard Worker                self._set_state(SSLProtocolState.FLUSHING)
465*cda5da8dSAndroid Build Coastguard Worker                if self._app_reading_paused:
466*cda5da8dSAndroid Build Coastguard Worker                    return True
467*cda5da8dSAndroid Build Coastguard Worker                else:
468*cda5da8dSAndroid Build Coastguard Worker                    self._do_flush()
469*cda5da8dSAndroid Build Coastguard Worker
470*cda5da8dSAndroid Build Coastguard Worker            elif self._state == SSLProtocolState.FLUSHING:
471*cda5da8dSAndroid Build Coastguard Worker                self._do_write()
472*cda5da8dSAndroid Build Coastguard Worker                self._set_state(SSLProtocolState.SHUTDOWN)
473*cda5da8dSAndroid Build Coastguard Worker                self._do_shutdown()
474*cda5da8dSAndroid Build Coastguard Worker
475*cda5da8dSAndroid Build Coastguard Worker            elif self._state == SSLProtocolState.SHUTDOWN:
476*cda5da8dSAndroid Build Coastguard Worker                self._do_shutdown()
477*cda5da8dSAndroid Build Coastguard Worker
478*cda5da8dSAndroid Build Coastguard Worker        except Exception:
479*cda5da8dSAndroid Build Coastguard Worker            self._transport.close()
480*cda5da8dSAndroid Build Coastguard Worker            raise
481*cda5da8dSAndroid Build Coastguard Worker
482*cda5da8dSAndroid Build Coastguard Worker    def _get_extra_info(self, name, default=None):
483*cda5da8dSAndroid Build Coastguard Worker        if name in self._extra:
484*cda5da8dSAndroid Build Coastguard Worker            return self._extra[name]
485*cda5da8dSAndroid Build Coastguard Worker        elif self._transport is not None:
486*cda5da8dSAndroid Build Coastguard Worker            return self._transport.get_extra_info(name, default)
487*cda5da8dSAndroid Build Coastguard Worker        else:
488*cda5da8dSAndroid Build Coastguard Worker            return default
489*cda5da8dSAndroid Build Coastguard Worker
490*cda5da8dSAndroid Build Coastguard Worker    def _set_state(self, new_state):
491*cda5da8dSAndroid Build Coastguard Worker        allowed = False
492*cda5da8dSAndroid Build Coastguard Worker
493*cda5da8dSAndroid Build Coastguard Worker        if new_state == SSLProtocolState.UNWRAPPED:
494*cda5da8dSAndroid Build Coastguard Worker            allowed = True
495*cda5da8dSAndroid Build Coastguard Worker
496*cda5da8dSAndroid Build Coastguard Worker        elif (
497*cda5da8dSAndroid Build Coastguard Worker            self._state == SSLProtocolState.UNWRAPPED and
498*cda5da8dSAndroid Build Coastguard Worker            new_state == SSLProtocolState.DO_HANDSHAKE
499*cda5da8dSAndroid Build Coastguard Worker        ):
500*cda5da8dSAndroid Build Coastguard Worker            allowed = True
501*cda5da8dSAndroid Build Coastguard Worker
502*cda5da8dSAndroid Build Coastguard Worker        elif (
503*cda5da8dSAndroid Build Coastguard Worker            self._state == SSLProtocolState.DO_HANDSHAKE and
504*cda5da8dSAndroid Build Coastguard Worker            new_state == SSLProtocolState.WRAPPED
505*cda5da8dSAndroid Build Coastguard Worker        ):
506*cda5da8dSAndroid Build Coastguard Worker            allowed = True
507*cda5da8dSAndroid Build Coastguard Worker
508*cda5da8dSAndroid Build Coastguard Worker        elif (
509*cda5da8dSAndroid Build Coastguard Worker            self._state == SSLProtocolState.WRAPPED and
510*cda5da8dSAndroid Build Coastguard Worker            new_state == SSLProtocolState.FLUSHING
511*cda5da8dSAndroid Build Coastguard Worker        ):
512*cda5da8dSAndroid Build Coastguard Worker            allowed = True
513*cda5da8dSAndroid Build Coastguard Worker
514*cda5da8dSAndroid Build Coastguard Worker        elif (
515*cda5da8dSAndroid Build Coastguard Worker            self._state == SSLProtocolState.FLUSHING and
516*cda5da8dSAndroid Build Coastguard Worker            new_state == SSLProtocolState.SHUTDOWN
517*cda5da8dSAndroid Build Coastguard Worker        ):
518*cda5da8dSAndroid Build Coastguard Worker            allowed = True
519*cda5da8dSAndroid Build Coastguard Worker
520*cda5da8dSAndroid Build Coastguard Worker        if allowed:
521*cda5da8dSAndroid Build Coastguard Worker            self._state = new_state
522*cda5da8dSAndroid Build Coastguard Worker
523*cda5da8dSAndroid Build Coastguard Worker        else:
524*cda5da8dSAndroid Build Coastguard Worker            raise RuntimeError(
525*cda5da8dSAndroid Build Coastguard Worker                'cannot switch state from {} to {}'.format(
526*cda5da8dSAndroid Build Coastguard Worker                    self._state, new_state))
527*cda5da8dSAndroid Build Coastguard Worker
528*cda5da8dSAndroid Build Coastguard Worker    # Handshake flow
529*cda5da8dSAndroid Build Coastguard Worker
530*cda5da8dSAndroid Build Coastguard Worker    def _start_handshake(self):
531*cda5da8dSAndroid Build Coastguard Worker        if self._loop.get_debug():
532*cda5da8dSAndroid Build Coastguard Worker            logger.debug("%r starts SSL handshake", self)
533*cda5da8dSAndroid Build Coastguard Worker            self._handshake_start_time = self._loop.time()
534*cda5da8dSAndroid Build Coastguard Worker        else:
535*cda5da8dSAndroid Build Coastguard Worker            self._handshake_start_time = None
536*cda5da8dSAndroid Build Coastguard Worker
537*cda5da8dSAndroid Build Coastguard Worker        self._set_state(SSLProtocolState.DO_HANDSHAKE)
538*cda5da8dSAndroid Build Coastguard Worker
539*cda5da8dSAndroid Build Coastguard Worker        # start handshake timeout count down
540*cda5da8dSAndroid Build Coastguard Worker        self._handshake_timeout_handle = \
541*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_later(self._ssl_handshake_timeout,
542*cda5da8dSAndroid Build Coastguard Worker                                  lambda: self._check_handshake_timeout())
543*cda5da8dSAndroid Build Coastguard Worker
544*cda5da8dSAndroid Build Coastguard Worker        self._do_handshake()
545*cda5da8dSAndroid Build Coastguard Worker
546*cda5da8dSAndroid Build Coastguard Worker    def _check_handshake_timeout(self):
547*cda5da8dSAndroid Build Coastguard Worker        if self._state == SSLProtocolState.DO_HANDSHAKE:
548*cda5da8dSAndroid Build Coastguard Worker            msg = (
549*cda5da8dSAndroid Build Coastguard Worker                f"SSL handshake is taking longer than "
550*cda5da8dSAndroid Build Coastguard Worker                f"{self._ssl_handshake_timeout} seconds: "
551*cda5da8dSAndroid Build Coastguard Worker                f"aborting the connection"
552*cda5da8dSAndroid Build Coastguard Worker            )
553*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(ConnectionAbortedError(msg))
554*cda5da8dSAndroid Build Coastguard Worker
555*cda5da8dSAndroid Build Coastguard Worker    def _do_handshake(self):
556*cda5da8dSAndroid Build Coastguard Worker        try:
557*cda5da8dSAndroid Build Coastguard Worker            self._sslobj.do_handshake()
558*cda5da8dSAndroid Build Coastguard Worker        except SSLAgainErrors:
559*cda5da8dSAndroid Build Coastguard Worker            self._process_outgoing()
560*cda5da8dSAndroid Build Coastguard Worker        except ssl.SSLError as exc:
561*cda5da8dSAndroid Build Coastguard Worker            self._on_handshake_complete(exc)
562*cda5da8dSAndroid Build Coastguard Worker        else:
563*cda5da8dSAndroid Build Coastguard Worker            self._on_handshake_complete(None)
564*cda5da8dSAndroid Build Coastguard Worker
565*cda5da8dSAndroid Build Coastguard Worker    def _on_handshake_complete(self, handshake_exc):
566*cda5da8dSAndroid Build Coastguard Worker        if self._handshake_timeout_handle is not None:
567*cda5da8dSAndroid Build Coastguard Worker            self._handshake_timeout_handle.cancel()
568*cda5da8dSAndroid Build Coastguard Worker            self._handshake_timeout_handle = None
569*cda5da8dSAndroid Build Coastguard Worker
570*cda5da8dSAndroid Build Coastguard Worker        sslobj = self._sslobj
571*cda5da8dSAndroid Build Coastguard Worker        try:
572*cda5da8dSAndroid Build Coastguard Worker            if handshake_exc is None:
573*cda5da8dSAndroid Build Coastguard Worker                self._set_state(SSLProtocolState.WRAPPED)
574*cda5da8dSAndroid Build Coastguard Worker            else:
575*cda5da8dSAndroid Build Coastguard Worker                raise handshake_exc
576*cda5da8dSAndroid Build Coastguard Worker
577*cda5da8dSAndroid Build Coastguard Worker            peercert = sslobj.getpeercert()
578*cda5da8dSAndroid Build Coastguard Worker        except Exception as exc:
579*cda5da8dSAndroid Build Coastguard Worker            self._set_state(SSLProtocolState.UNWRAPPED)
580*cda5da8dSAndroid Build Coastguard Worker            if isinstance(exc, ssl.CertificateError):
581*cda5da8dSAndroid Build Coastguard Worker                msg = 'SSL handshake failed on verifying the certificate'
582*cda5da8dSAndroid Build Coastguard Worker            else:
583*cda5da8dSAndroid Build Coastguard Worker                msg = 'SSL handshake failed'
584*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(exc, msg)
585*cda5da8dSAndroid Build Coastguard Worker            self._wakeup_waiter(exc)
586*cda5da8dSAndroid Build Coastguard Worker            return
587*cda5da8dSAndroid Build Coastguard Worker
588*cda5da8dSAndroid Build Coastguard Worker        if self._loop.get_debug():
589*cda5da8dSAndroid Build Coastguard Worker            dt = self._loop.time() - self._handshake_start_time
590*cda5da8dSAndroid Build Coastguard Worker            logger.debug("%r: SSL handshake took %.1f ms", self, dt * 1e3)
591*cda5da8dSAndroid Build Coastguard Worker
592*cda5da8dSAndroid Build Coastguard Worker        # Add extra info that becomes available after handshake.
593*cda5da8dSAndroid Build Coastguard Worker        self._extra.update(peercert=peercert,
594*cda5da8dSAndroid Build Coastguard Worker                           cipher=sslobj.cipher(),
595*cda5da8dSAndroid Build Coastguard Worker                           compression=sslobj.compression(),
596*cda5da8dSAndroid Build Coastguard Worker                           ssl_object=sslobj)
597*cda5da8dSAndroid Build Coastguard Worker        if self._app_state == AppProtocolState.STATE_INIT:
598*cda5da8dSAndroid Build Coastguard Worker            self._app_state = AppProtocolState.STATE_CON_MADE
599*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol.connection_made(self._get_app_transport())
600*cda5da8dSAndroid Build Coastguard Worker        self._wakeup_waiter()
601*cda5da8dSAndroid Build Coastguard Worker        self._do_read()
602*cda5da8dSAndroid Build Coastguard Worker
603*cda5da8dSAndroid Build Coastguard Worker    # Shutdown flow
604*cda5da8dSAndroid Build Coastguard Worker
605*cda5da8dSAndroid Build Coastguard Worker    def _start_shutdown(self):
606*cda5da8dSAndroid Build Coastguard Worker        if (
607*cda5da8dSAndroid Build Coastguard Worker            self._state in (
608*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.FLUSHING,
609*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.SHUTDOWN,
610*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.UNWRAPPED
611*cda5da8dSAndroid Build Coastguard Worker            )
612*cda5da8dSAndroid Build Coastguard Worker        ):
613*cda5da8dSAndroid Build Coastguard Worker            return
614*cda5da8dSAndroid Build Coastguard Worker        if self._app_transport is not None:
615*cda5da8dSAndroid Build Coastguard Worker            self._app_transport._closed = True
616*cda5da8dSAndroid Build Coastguard Worker        if self._state == SSLProtocolState.DO_HANDSHAKE:
617*cda5da8dSAndroid Build Coastguard Worker            self._abort()
618*cda5da8dSAndroid Build Coastguard Worker        else:
619*cda5da8dSAndroid Build Coastguard Worker            self._set_state(SSLProtocolState.FLUSHING)
620*cda5da8dSAndroid Build Coastguard Worker            self._shutdown_timeout_handle = self._loop.call_later(
621*cda5da8dSAndroid Build Coastguard Worker                self._ssl_shutdown_timeout,
622*cda5da8dSAndroid Build Coastguard Worker                lambda: self._check_shutdown_timeout()
623*cda5da8dSAndroid Build Coastguard Worker            )
624*cda5da8dSAndroid Build Coastguard Worker            self._do_flush()
625*cda5da8dSAndroid Build Coastguard Worker
626*cda5da8dSAndroid Build Coastguard Worker    def _check_shutdown_timeout(self):
627*cda5da8dSAndroid Build Coastguard Worker        if (
628*cda5da8dSAndroid Build Coastguard Worker            self._state in (
629*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.FLUSHING,
630*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.SHUTDOWN
631*cda5da8dSAndroid Build Coastguard Worker            )
632*cda5da8dSAndroid Build Coastguard Worker        ):
633*cda5da8dSAndroid Build Coastguard Worker            self._transport._force_close(
634*cda5da8dSAndroid Build Coastguard Worker                exceptions.TimeoutError('SSL shutdown timed out'))
635*cda5da8dSAndroid Build Coastguard Worker
636*cda5da8dSAndroid Build Coastguard Worker    def _do_flush(self):
637*cda5da8dSAndroid Build Coastguard Worker        self._do_read()
638*cda5da8dSAndroid Build Coastguard Worker        self._set_state(SSLProtocolState.SHUTDOWN)
639*cda5da8dSAndroid Build Coastguard Worker        self._do_shutdown()
640*cda5da8dSAndroid Build Coastguard Worker
641*cda5da8dSAndroid Build Coastguard Worker    def _do_shutdown(self):
642*cda5da8dSAndroid Build Coastguard Worker        try:
643*cda5da8dSAndroid Build Coastguard Worker            if not self._eof_received:
644*cda5da8dSAndroid Build Coastguard Worker                self._sslobj.unwrap()
645*cda5da8dSAndroid Build Coastguard Worker        except SSLAgainErrors:
646*cda5da8dSAndroid Build Coastguard Worker            self._process_outgoing()
647*cda5da8dSAndroid Build Coastguard Worker        except ssl.SSLError as exc:
648*cda5da8dSAndroid Build Coastguard Worker            self._on_shutdown_complete(exc)
649*cda5da8dSAndroid Build Coastguard Worker        else:
650*cda5da8dSAndroid Build Coastguard Worker            self._process_outgoing()
651*cda5da8dSAndroid Build Coastguard Worker            self._call_eof_received()
652*cda5da8dSAndroid Build Coastguard Worker            self._on_shutdown_complete(None)
653*cda5da8dSAndroid Build Coastguard Worker
654*cda5da8dSAndroid Build Coastguard Worker    def _on_shutdown_complete(self, shutdown_exc):
655*cda5da8dSAndroid Build Coastguard Worker        if self._shutdown_timeout_handle is not None:
656*cda5da8dSAndroid Build Coastguard Worker            self._shutdown_timeout_handle.cancel()
657*cda5da8dSAndroid Build Coastguard Worker            self._shutdown_timeout_handle = None
658*cda5da8dSAndroid Build Coastguard Worker
659*cda5da8dSAndroid Build Coastguard Worker        if shutdown_exc:
660*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(shutdown_exc)
661*cda5da8dSAndroid Build Coastguard Worker        else:
662*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_soon(self._transport.close)
663*cda5da8dSAndroid Build Coastguard Worker
664*cda5da8dSAndroid Build Coastguard Worker    def _abort(self):
665*cda5da8dSAndroid Build Coastguard Worker        self._set_state(SSLProtocolState.UNWRAPPED)
666*cda5da8dSAndroid Build Coastguard Worker        if self._transport is not None:
667*cda5da8dSAndroid Build Coastguard Worker            self._transport.abort()
668*cda5da8dSAndroid Build Coastguard Worker
669*cda5da8dSAndroid Build Coastguard Worker    # Outgoing flow
670*cda5da8dSAndroid Build Coastguard Worker
671*cda5da8dSAndroid Build Coastguard Worker    def _write_appdata(self, list_of_data):
672*cda5da8dSAndroid Build Coastguard Worker        if (
673*cda5da8dSAndroid Build Coastguard Worker            self._state in (
674*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.FLUSHING,
675*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.SHUTDOWN,
676*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.UNWRAPPED
677*cda5da8dSAndroid Build Coastguard Worker            )
678*cda5da8dSAndroid Build Coastguard Worker        ):
679*cda5da8dSAndroid Build Coastguard Worker            if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
680*cda5da8dSAndroid Build Coastguard Worker                logger.warning('SSL connection is closed')
681*cda5da8dSAndroid Build Coastguard Worker            self._conn_lost += 1
682*cda5da8dSAndroid Build Coastguard Worker            return
683*cda5da8dSAndroid Build Coastguard Worker
684*cda5da8dSAndroid Build Coastguard Worker        for data in list_of_data:
685*cda5da8dSAndroid Build Coastguard Worker            self._write_backlog.append(data)
686*cda5da8dSAndroid Build Coastguard Worker            self._write_buffer_size += len(data)
687*cda5da8dSAndroid Build Coastguard Worker
688*cda5da8dSAndroid Build Coastguard Worker        try:
689*cda5da8dSAndroid Build Coastguard Worker            if self._state == SSLProtocolState.WRAPPED:
690*cda5da8dSAndroid Build Coastguard Worker                self._do_write()
691*cda5da8dSAndroid Build Coastguard Worker
692*cda5da8dSAndroid Build Coastguard Worker        except Exception as ex:
693*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(ex, 'Fatal error on SSL protocol')
694*cda5da8dSAndroid Build Coastguard Worker
695*cda5da8dSAndroid Build Coastguard Worker    def _do_write(self):
696*cda5da8dSAndroid Build Coastguard Worker        try:
697*cda5da8dSAndroid Build Coastguard Worker            while self._write_backlog:
698*cda5da8dSAndroid Build Coastguard Worker                data = self._write_backlog[0]
699*cda5da8dSAndroid Build Coastguard Worker                count = self._sslobj.write(data)
700*cda5da8dSAndroid Build Coastguard Worker                data_len = len(data)
701*cda5da8dSAndroid Build Coastguard Worker                if count < data_len:
702*cda5da8dSAndroid Build Coastguard Worker                    self._write_backlog[0] = data[count:]
703*cda5da8dSAndroid Build Coastguard Worker                    self._write_buffer_size -= count
704*cda5da8dSAndroid Build Coastguard Worker                else:
705*cda5da8dSAndroid Build Coastguard Worker                    del self._write_backlog[0]
706*cda5da8dSAndroid Build Coastguard Worker                    self._write_buffer_size -= data_len
707*cda5da8dSAndroid Build Coastguard Worker        except SSLAgainErrors:
708*cda5da8dSAndroid Build Coastguard Worker            pass
709*cda5da8dSAndroid Build Coastguard Worker        self._process_outgoing()
710*cda5da8dSAndroid Build Coastguard Worker
711*cda5da8dSAndroid Build Coastguard Worker    def _process_outgoing(self):
712*cda5da8dSAndroid Build Coastguard Worker        if not self._ssl_writing_paused:
713*cda5da8dSAndroid Build Coastguard Worker            data = self._outgoing.read()
714*cda5da8dSAndroid Build Coastguard Worker            if len(data):
715*cda5da8dSAndroid Build Coastguard Worker                self._transport.write(data)
716*cda5da8dSAndroid Build Coastguard Worker        self._control_app_writing()
717*cda5da8dSAndroid Build Coastguard Worker
718*cda5da8dSAndroid Build Coastguard Worker    # Incoming flow
719*cda5da8dSAndroid Build Coastguard Worker
720*cda5da8dSAndroid Build Coastguard Worker    def _do_read(self):
721*cda5da8dSAndroid Build Coastguard Worker        if (
722*cda5da8dSAndroid Build Coastguard Worker            self._state not in (
723*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.WRAPPED,
724*cda5da8dSAndroid Build Coastguard Worker                SSLProtocolState.FLUSHING,
725*cda5da8dSAndroid Build Coastguard Worker            )
726*cda5da8dSAndroid Build Coastguard Worker        ):
727*cda5da8dSAndroid Build Coastguard Worker            return
728*cda5da8dSAndroid Build Coastguard Worker        try:
729*cda5da8dSAndroid Build Coastguard Worker            if not self._app_reading_paused:
730*cda5da8dSAndroid Build Coastguard Worker                if self._app_protocol_is_buffer:
731*cda5da8dSAndroid Build Coastguard Worker                    self._do_read__buffered()
732*cda5da8dSAndroid Build Coastguard Worker                else:
733*cda5da8dSAndroid Build Coastguard Worker                    self._do_read__copied()
734*cda5da8dSAndroid Build Coastguard Worker                if self._write_backlog:
735*cda5da8dSAndroid Build Coastguard Worker                    self._do_write()
736*cda5da8dSAndroid Build Coastguard Worker                else:
737*cda5da8dSAndroid Build Coastguard Worker                    self._process_outgoing()
738*cda5da8dSAndroid Build Coastguard Worker            self._control_ssl_reading()
739*cda5da8dSAndroid Build Coastguard Worker        except Exception as ex:
740*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(ex, 'Fatal error on SSL protocol')
741*cda5da8dSAndroid Build Coastguard Worker
742*cda5da8dSAndroid Build Coastguard Worker    def _do_read__buffered(self):
743*cda5da8dSAndroid Build Coastguard Worker        offset = 0
744*cda5da8dSAndroid Build Coastguard Worker        count = 1
745*cda5da8dSAndroid Build Coastguard Worker
746*cda5da8dSAndroid Build Coastguard Worker        buf = self._app_protocol_get_buffer(self._get_read_buffer_size())
747*cda5da8dSAndroid Build Coastguard Worker        wants = len(buf)
748*cda5da8dSAndroid Build Coastguard Worker
749*cda5da8dSAndroid Build Coastguard Worker        try:
750*cda5da8dSAndroid Build Coastguard Worker            count = self._sslobj.read(wants, buf)
751*cda5da8dSAndroid Build Coastguard Worker
752*cda5da8dSAndroid Build Coastguard Worker            if count > 0:
753*cda5da8dSAndroid Build Coastguard Worker                offset = count
754*cda5da8dSAndroid Build Coastguard Worker                while offset < wants:
755*cda5da8dSAndroid Build Coastguard Worker                    count = self._sslobj.read(wants - offset, buf[offset:])
756*cda5da8dSAndroid Build Coastguard Worker                    if count > 0:
757*cda5da8dSAndroid Build Coastguard Worker                        offset += count
758*cda5da8dSAndroid Build Coastguard Worker                    else:
759*cda5da8dSAndroid Build Coastguard Worker                        break
760*cda5da8dSAndroid Build Coastguard Worker                else:
761*cda5da8dSAndroid Build Coastguard Worker                    self._loop.call_soon(lambda: self._do_read())
762*cda5da8dSAndroid Build Coastguard Worker        except SSLAgainErrors:
763*cda5da8dSAndroid Build Coastguard Worker            pass
764*cda5da8dSAndroid Build Coastguard Worker        if offset > 0:
765*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol_buffer_updated(offset)
766*cda5da8dSAndroid Build Coastguard Worker        if not count:
767*cda5da8dSAndroid Build Coastguard Worker            # close_notify
768*cda5da8dSAndroid Build Coastguard Worker            self._call_eof_received()
769*cda5da8dSAndroid Build Coastguard Worker            self._start_shutdown()
770*cda5da8dSAndroid Build Coastguard Worker
771*cda5da8dSAndroid Build Coastguard Worker    def _do_read__copied(self):
772*cda5da8dSAndroid Build Coastguard Worker        chunk = b'1'
773*cda5da8dSAndroid Build Coastguard Worker        zero = True
774*cda5da8dSAndroid Build Coastguard Worker        one = False
775*cda5da8dSAndroid Build Coastguard Worker
776*cda5da8dSAndroid Build Coastguard Worker        try:
777*cda5da8dSAndroid Build Coastguard Worker            while True:
778*cda5da8dSAndroid Build Coastguard Worker                chunk = self._sslobj.read(self.max_size)
779*cda5da8dSAndroid Build Coastguard Worker                if not chunk:
780*cda5da8dSAndroid Build Coastguard Worker                    break
781*cda5da8dSAndroid Build Coastguard Worker                if zero:
782*cda5da8dSAndroid Build Coastguard Worker                    zero = False
783*cda5da8dSAndroid Build Coastguard Worker                    one = True
784*cda5da8dSAndroid Build Coastguard Worker                    first = chunk
785*cda5da8dSAndroid Build Coastguard Worker                elif one:
786*cda5da8dSAndroid Build Coastguard Worker                    one = False
787*cda5da8dSAndroid Build Coastguard Worker                    data = [first, chunk]
788*cda5da8dSAndroid Build Coastguard Worker                else:
789*cda5da8dSAndroid Build Coastguard Worker                    data.append(chunk)
790*cda5da8dSAndroid Build Coastguard Worker        except SSLAgainErrors:
791*cda5da8dSAndroid Build Coastguard Worker            pass
792*cda5da8dSAndroid Build Coastguard Worker        if one:
793*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol.data_received(first)
794*cda5da8dSAndroid Build Coastguard Worker        elif not zero:
795*cda5da8dSAndroid Build Coastguard Worker            self._app_protocol.data_received(b''.join(data))
796*cda5da8dSAndroid Build Coastguard Worker        if not chunk:
797*cda5da8dSAndroid Build Coastguard Worker            # close_notify
798*cda5da8dSAndroid Build Coastguard Worker            self._call_eof_received()
799*cda5da8dSAndroid Build Coastguard Worker            self._start_shutdown()
800*cda5da8dSAndroid Build Coastguard Worker
801*cda5da8dSAndroid Build Coastguard Worker    def _call_eof_received(self):
802*cda5da8dSAndroid Build Coastguard Worker        try:
803*cda5da8dSAndroid Build Coastguard Worker            if self._app_state == AppProtocolState.STATE_CON_MADE:
804*cda5da8dSAndroid Build Coastguard Worker                self._app_state = AppProtocolState.STATE_EOF
805*cda5da8dSAndroid Build Coastguard Worker                keep_open = self._app_protocol.eof_received()
806*cda5da8dSAndroid Build Coastguard Worker                if keep_open:
807*cda5da8dSAndroid Build Coastguard Worker                    logger.warning('returning true from eof_received() '
808*cda5da8dSAndroid Build Coastguard Worker                                   'has no effect when using ssl')
809*cda5da8dSAndroid Build Coastguard Worker        except (KeyboardInterrupt, SystemExit):
810*cda5da8dSAndroid Build Coastguard Worker            raise
811*cda5da8dSAndroid Build Coastguard Worker        except BaseException as ex:
812*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(ex, 'Error calling eof_received()')
813*cda5da8dSAndroid Build Coastguard Worker
814*cda5da8dSAndroid Build Coastguard Worker    # Flow control for writes from APP socket
815*cda5da8dSAndroid Build Coastguard Worker
816*cda5da8dSAndroid Build Coastguard Worker    def _control_app_writing(self):
817*cda5da8dSAndroid Build Coastguard Worker        size = self._get_write_buffer_size()
818*cda5da8dSAndroid Build Coastguard Worker        if size >= self._outgoing_high_water and not self._app_writing_paused:
819*cda5da8dSAndroid Build Coastguard Worker            self._app_writing_paused = True
820*cda5da8dSAndroid Build Coastguard Worker            try:
821*cda5da8dSAndroid Build Coastguard Worker                self._app_protocol.pause_writing()
822*cda5da8dSAndroid Build Coastguard Worker            except (KeyboardInterrupt, SystemExit):
823*cda5da8dSAndroid Build Coastguard Worker                raise
824*cda5da8dSAndroid Build Coastguard Worker            except BaseException as exc:
825*cda5da8dSAndroid Build Coastguard Worker                self._loop.call_exception_handler({
826*cda5da8dSAndroid Build Coastguard Worker                    'message': 'protocol.pause_writing() failed',
827*cda5da8dSAndroid Build Coastguard Worker                    'exception': exc,
828*cda5da8dSAndroid Build Coastguard Worker                    'transport': self._app_transport,
829*cda5da8dSAndroid Build Coastguard Worker                    'protocol': self,
830*cda5da8dSAndroid Build Coastguard Worker                })
831*cda5da8dSAndroid Build Coastguard Worker        elif size <= self._outgoing_low_water and self._app_writing_paused:
832*cda5da8dSAndroid Build Coastguard Worker            self._app_writing_paused = False
833*cda5da8dSAndroid Build Coastguard Worker            try:
834*cda5da8dSAndroid Build Coastguard Worker                self._app_protocol.resume_writing()
835*cda5da8dSAndroid Build Coastguard Worker            except (KeyboardInterrupt, SystemExit):
836*cda5da8dSAndroid Build Coastguard Worker                raise
837*cda5da8dSAndroid Build Coastguard Worker            except BaseException as exc:
838*cda5da8dSAndroid Build Coastguard Worker                self._loop.call_exception_handler({
839*cda5da8dSAndroid Build Coastguard Worker                    'message': 'protocol.resume_writing() failed',
840*cda5da8dSAndroid Build Coastguard Worker                    'exception': exc,
841*cda5da8dSAndroid Build Coastguard Worker                    'transport': self._app_transport,
842*cda5da8dSAndroid Build Coastguard Worker                    'protocol': self,
843*cda5da8dSAndroid Build Coastguard Worker                })
844*cda5da8dSAndroid Build Coastguard Worker
845*cda5da8dSAndroid Build Coastguard Worker    def _get_write_buffer_size(self):
846*cda5da8dSAndroid Build Coastguard Worker        return self._outgoing.pending + self._write_buffer_size
847*cda5da8dSAndroid Build Coastguard Worker
848*cda5da8dSAndroid Build Coastguard Worker    def _set_write_buffer_limits(self, high=None, low=None):
849*cda5da8dSAndroid Build Coastguard Worker        high, low = add_flowcontrol_defaults(
850*cda5da8dSAndroid Build Coastguard Worker            high, low, constants.FLOW_CONTROL_HIGH_WATER_SSL_WRITE)
851*cda5da8dSAndroid Build Coastguard Worker        self._outgoing_high_water = high
852*cda5da8dSAndroid Build Coastguard Worker        self._outgoing_low_water = low
853*cda5da8dSAndroid Build Coastguard Worker
854*cda5da8dSAndroid Build Coastguard Worker    # Flow control for reads to APP socket
855*cda5da8dSAndroid Build Coastguard Worker
856*cda5da8dSAndroid Build Coastguard Worker    def _pause_reading(self):
857*cda5da8dSAndroid Build Coastguard Worker        self._app_reading_paused = True
858*cda5da8dSAndroid Build Coastguard Worker
859*cda5da8dSAndroid Build Coastguard Worker    def _resume_reading(self):
860*cda5da8dSAndroid Build Coastguard Worker        if self._app_reading_paused:
861*cda5da8dSAndroid Build Coastguard Worker            self._app_reading_paused = False
862*cda5da8dSAndroid Build Coastguard Worker
863*cda5da8dSAndroid Build Coastguard Worker            def resume():
864*cda5da8dSAndroid Build Coastguard Worker                if self._state == SSLProtocolState.WRAPPED:
865*cda5da8dSAndroid Build Coastguard Worker                    self._do_read()
866*cda5da8dSAndroid Build Coastguard Worker                elif self._state == SSLProtocolState.FLUSHING:
867*cda5da8dSAndroid Build Coastguard Worker                    self._do_flush()
868*cda5da8dSAndroid Build Coastguard Worker                elif self._state == SSLProtocolState.SHUTDOWN:
869*cda5da8dSAndroid Build Coastguard Worker                    self._do_shutdown()
870*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_soon(resume)
871*cda5da8dSAndroid Build Coastguard Worker
872*cda5da8dSAndroid Build Coastguard Worker    # Flow control for reads from SSL socket
873*cda5da8dSAndroid Build Coastguard Worker
874*cda5da8dSAndroid Build Coastguard Worker    def _control_ssl_reading(self):
875*cda5da8dSAndroid Build Coastguard Worker        size = self._get_read_buffer_size()
876*cda5da8dSAndroid Build Coastguard Worker        if size >= self._incoming_high_water and not self._ssl_reading_paused:
877*cda5da8dSAndroid Build Coastguard Worker            self._ssl_reading_paused = True
878*cda5da8dSAndroid Build Coastguard Worker            self._transport.pause_reading()
879*cda5da8dSAndroid Build Coastguard Worker        elif size <= self._incoming_low_water and self._ssl_reading_paused:
880*cda5da8dSAndroid Build Coastguard Worker            self._ssl_reading_paused = False
881*cda5da8dSAndroid Build Coastguard Worker            self._transport.resume_reading()
882*cda5da8dSAndroid Build Coastguard Worker
883*cda5da8dSAndroid Build Coastguard Worker    def _set_read_buffer_limits(self, high=None, low=None):
884*cda5da8dSAndroid Build Coastguard Worker        high, low = add_flowcontrol_defaults(
885*cda5da8dSAndroid Build Coastguard Worker            high, low, constants.FLOW_CONTROL_HIGH_WATER_SSL_READ)
886*cda5da8dSAndroid Build Coastguard Worker        self._incoming_high_water = high
887*cda5da8dSAndroid Build Coastguard Worker        self._incoming_low_water = low
888*cda5da8dSAndroid Build Coastguard Worker
889*cda5da8dSAndroid Build Coastguard Worker    def _get_read_buffer_size(self):
890*cda5da8dSAndroid Build Coastguard Worker        return self._incoming.pending
891*cda5da8dSAndroid Build Coastguard Worker
892*cda5da8dSAndroid Build Coastguard Worker    # Flow control for writes to SSL socket
893*cda5da8dSAndroid Build Coastguard Worker
894*cda5da8dSAndroid Build Coastguard Worker    def pause_writing(self):
895*cda5da8dSAndroid Build Coastguard Worker        """Called when the low-level transport's buffer goes over
896*cda5da8dSAndroid Build Coastguard Worker        the high-water mark.
897*cda5da8dSAndroid Build Coastguard Worker        """
898*cda5da8dSAndroid Build Coastguard Worker        assert not self._ssl_writing_paused
899*cda5da8dSAndroid Build Coastguard Worker        self._ssl_writing_paused = True
900*cda5da8dSAndroid Build Coastguard Worker
901*cda5da8dSAndroid Build Coastguard Worker    def resume_writing(self):
902*cda5da8dSAndroid Build Coastguard Worker        """Called when the low-level transport's buffer drains below
903*cda5da8dSAndroid Build Coastguard Worker        the low-water mark.
904*cda5da8dSAndroid Build Coastguard Worker        """
905*cda5da8dSAndroid Build Coastguard Worker        assert self._ssl_writing_paused
906*cda5da8dSAndroid Build Coastguard Worker        self._ssl_writing_paused = False
907*cda5da8dSAndroid Build Coastguard Worker        self._process_outgoing()
908*cda5da8dSAndroid Build Coastguard Worker
909*cda5da8dSAndroid Build Coastguard Worker    def _fatal_error(self, exc, message='Fatal error on transport'):
910*cda5da8dSAndroid Build Coastguard Worker        if self._transport:
911*cda5da8dSAndroid Build Coastguard Worker            self._transport._force_close(exc)
912*cda5da8dSAndroid Build Coastguard Worker
913*cda5da8dSAndroid Build Coastguard Worker        if isinstance(exc, OSError):
914*cda5da8dSAndroid Build Coastguard Worker            if self._loop.get_debug():
915*cda5da8dSAndroid Build Coastguard Worker                logger.debug("%r: %s", self, message, exc_info=True)
916*cda5da8dSAndroid Build Coastguard Worker        elif not isinstance(exc, exceptions.CancelledError):
917*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_exception_handler({
918*cda5da8dSAndroid Build Coastguard Worker                'message': message,
919*cda5da8dSAndroid Build Coastguard Worker                'exception': exc,
920*cda5da8dSAndroid Build Coastguard Worker                'transport': self._transport,
921*cda5da8dSAndroid Build Coastguard Worker                'protocol': self,
922*cda5da8dSAndroid Build Coastguard Worker            })
923