HTTP Connect Support for SSH Client

41 views
Skip to first unread message

Nalin Goel

unread,
Nov 3, 2021, 4:13:43 PM11/3/21
to asyncssh-users
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

Reply all
Reply to author
Forward
0 new messages