The SSLTransport wraps an existing socket and establishes an SSL connection. Contrary to Python's implementation of SSLSocket, it allows you to chain multiple TLS connections together. It's particularly useful if you need to implement TLS within TLS. The class supports most of
| 20 | |
| 21 | |
| 22 | class SSLTransport: |
| 23 | """ |
| 24 | The SSLTransport wraps an existing socket and establishes an SSL connection. |
| 25 | |
| 26 | Contrary to Python's implementation of SSLSocket, it allows you to chain |
| 27 | multiple TLS connections together. It's particularly useful if you need to |
| 28 | implement TLS within TLS. |
| 29 | |
| 30 | The class supports most of the socket API operations. |
| 31 | """ |
| 32 | |
| 33 | @staticmethod |
| 34 | def _validate_ssl_context_for_tls_in_tls(ssl_context: ssl.SSLContext) -> None: |
| 35 | """ |
| 36 | Raises a ProxySchemeUnsupported if the provided ssl_context can't be used |
| 37 | for TLS in TLS. |
| 38 | |
| 39 | The only requirement is that the ssl_context provides the 'wrap_bio' |
| 40 | methods. |
| 41 | """ |
| 42 | |
| 43 | if not hasattr(ssl_context, "wrap_bio"): |
| 44 | raise ProxySchemeUnsupported( |
| 45 | "TLS in TLS requires SSLContext.wrap_bio() which isn't " |
| 46 | "available on non-native SSLContext" |
| 47 | ) |
| 48 | |
| 49 | def __init__( |
| 50 | self, |
| 51 | socket: socket.socket, |
| 52 | ssl_context: ssl.SSLContext, |
| 53 | server_hostname: str | None = None, |
| 54 | suppress_ragged_eofs: bool = True, |
| 55 | ) -> None: |
| 56 | """ |
| 57 | Create an SSLTransport around socket using the provided ssl_context. |
| 58 | """ |
| 59 | self.incoming = ssl.MemoryBIO() |
| 60 | self.outgoing = ssl.MemoryBIO() |
| 61 | |
| 62 | self.suppress_ragged_eofs = suppress_ragged_eofs |
| 63 | self.socket = socket |
| 64 | |
| 65 | self.sslobj = ssl_context.wrap_bio( |
| 66 | self.incoming, self.outgoing, server_hostname=server_hostname |
| 67 | ) |
| 68 | |
| 69 | # Perform initial handshake. |
| 70 | self._ssl_io_loop(self.sslobj.do_handshake) |
| 71 | |
| 72 | def __enter__(self) -> Self: |
| 73 | return self |
| 74 | |
| 75 | def __exit__(self, *_: typing.Any) -> None: |
| 76 | self.close() |
| 77 | |
| 78 | def fileno(self) -> int: |
| 79 | return self.socket.fileno() |
no outgoing calls
searching dependent graphs…