A utility class to write to and read from a non-blocking SSL socket. If the socket passed to the constructor is already connected, it should be wrapped with:: ssl.SSLContext(...).wrap_socket(sock, do_handshake_on_connect=False, **kwargs) before constructing the `SSLIOStream`.
| 1313 | |
| 1314 | |
| 1315 | class SSLIOStream(IOStream): |
| 1316 | """A utility class to write to and read from a non-blocking SSL socket. |
| 1317 | |
| 1318 | If the socket passed to the constructor is already connected, |
| 1319 | it should be wrapped with:: |
| 1320 | |
| 1321 | ssl.SSLContext(...).wrap_socket(sock, do_handshake_on_connect=False, **kwargs) |
| 1322 | |
| 1323 | before constructing the `SSLIOStream`. Unconnected sockets will be |
| 1324 | wrapped when `IOStream.connect` is finished. |
| 1325 | """ |
| 1326 | |
| 1327 | socket = None # type: ssl.SSLSocket |
| 1328 | |
| 1329 | def __init__(self, *args: Any, **kwargs: Any) -> None: |
| 1330 | """The ``ssl_options`` keyword argument may either be an |
| 1331 | `ssl.SSLContext` object or a dictionary of keywords arguments |
| 1332 | for `ssl.SSLContext.wrap_socket` |
| 1333 | """ |
| 1334 | self._ssl_options = kwargs.pop("ssl_options", _client_ssl_defaults) |
| 1335 | super().__init__(*args, **kwargs) |
| 1336 | self._ssl_accepting = True |
| 1337 | self._handshake_reading = False |
| 1338 | self._handshake_writing = False |
| 1339 | self._server_hostname = None # type: Optional[str] |
| 1340 | |
| 1341 | # If the socket is already connected, attempt to start the handshake. |
| 1342 | try: |
| 1343 | self.socket.getpeername() |
| 1344 | except OSError: |
| 1345 | pass |
| 1346 | else: |
| 1347 | # Indirectly start the handshake, which will run on the next |
| 1348 | # IOLoop iteration and then the real IO state will be set in |
| 1349 | # _handle_events. |
| 1350 | self._add_io_state(self.io_loop.WRITE) |
| 1351 | |
| 1352 | def reading(self) -> bool: |
| 1353 | return self._handshake_reading or super().reading() |
| 1354 | |
| 1355 | def writing(self) -> bool: |
| 1356 | return self._handshake_writing or super().writing() |
| 1357 | |
| 1358 | def _do_ssl_handshake(self) -> None: |
| 1359 | # Based on code from test_ssl.py in the python stdlib |
| 1360 | try: |
| 1361 | self._handshake_reading = False |
| 1362 | self._handshake_writing = False |
| 1363 | self.socket.do_handshake() |
| 1364 | except ssl.SSLError as err: |
| 1365 | if err.args[0] == ssl.SSL_ERROR_WANT_READ: |
| 1366 | self._handshake_reading = True |
| 1367 | return |
| 1368 | elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: |
| 1369 | self._handshake_writing = True |
| 1370 | return |
| 1371 | elif err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN): |
| 1372 | return self.close(exc_info=err) |
no outgoing calls