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