Hi everyone,
I am trying to initiate an SSH connection via a non-standard transport: HTTP Connect. One of the issue that I have noticed and Ron pointed out is:
"set_protocol() call on transports doesn’t seem to unhook the old protocol object from the transport completely, and some new code in Python 3.8 that tries to clean up streams when they are garbage collected incorrectly causes the connection to be closed even though the transport is no longer associated with those stream objects."
Some working ideas:
------------------------------------------------------------------------------------------------
class HTTPConnector:
def __init__(self, proxy_host, proxy_port) -> None:
self._proxy_host = proxy_host
self._proxy_port = proxy_port
async def create_connection(self, protocol_factory, host, port) -> None:
"""Return a channel and transport to run SSH over"""
reader, writer = await asyncio.open_connection(
self._proxy_host,
self._proxy_port,
)
# the http connect request to the target host.
cmd_connect = f"CONNECT {host}:{port} HTTP/1.1\r\n\r\n".encode("ASCII")
writer.write(cmd_connect)
transport = writer.transport
transport.get_protocol()._transport = None
protocol = protocol_factory()
transport.set_protocol(protocol)
protocol.connection_made(transport)
return transport, protocol
async def run_client() -> None:
tunnel = HTTPConnector("proxy_host", proxy_port)
async with asyncssh.connect(host, tunnel=tunnel) as conn:
result = await conn.run("ls abc")
------------------------------------------------------------------------------------------------
Subclassing StreamReaderProtocol:
class StreamReaderProtocolSubClass(StreamReaderProtocol):
def __init__(self, stream_reader, client_connected_cb=None, loop=None):
super().__init__(stream_reader, client_connected_cb, loop)
def connection_made(self, transport):
# this is the condition added
if not transport:
self._stream_reader.set_transport(transport)
self._over_ssl = transport.get_extra_info("sslcontext") is not None
if self._client_connected_cb is not None:
self._stream_writer = StreamWriter(
transport, self, self._stream_reader, self._loop
)
res = self._client_connected_cb(self._stream_reader, self._stream_writer)
if coroutines.iscoroutine(res):
self._loop.create_task(res)
What do you all think would be a better option and is it possible to upstream this change to asyncssh lib for general use.
Thank you,
Nalin Goel