Beispiel #1
0
def open_proxy_backend(backend,
                       target,
                       name,
                       client,
                       use_ssl=False,
                       ssl_opts=None):
    proxy = eventlet.connect(backend)
    if use_ssl:
        ssl_opts = ssl_opts or {}
        proxy = eventlet.wrap_ssl(proxy, server_side=False, **ssl_opts)
    proxy.sendall(protocol.version)
    protocol.send_message(proxy,
                          protocol.proxy_request(
                              name=name,
                              client=client,
                          ))
    reply = protocol.recv_message(proxy)
    if reply and 'proxy' in reply:
        try:
            local = eventlet.connect(target)
            util.join_sockets(proxy, local)
        except IOError:
            proxy.close()
    elif reply and 'error' in reply:
        print "  ERROR: {0}".format(reply['error'])
        return
    else:
        pass
Beispiel #2
0
def handle_proxy_request(socket, request):
    try:
        tunnel = Tunnel.get_by_proxy_request(request)
    except RuntimeError, e:
        protocol.send_message(socket, protocol.error_reply('notavailable'))
        socket.close()
        return
Beispiel #3
0
def connection_handler(socket, address):
    hostname = peek_http_host(socket)
    if not hostname:
        send_http_error(socket, 'No hostname', '400 Bad Request')
        return

    if hostname == Tunnel.domain_suffix:
        meta.server.process_request((socket, address))
        return

    tunnel = Tunnel.get_by_hostname(hostname)
    if not tunnel:
        send_http_error(socket, 'No tunnel for {0}'.format(hostname),
                        '410 Gone')
        return

    conn, proxy_used = tunnel.pop_proxy_conn(timeout=2)
    if not conn:
        send_http_error(socket, 'No proxy connections', '502 Bad Gateway')
        return

    protocol.send_message(conn, protocol.proxy_reply())
    pool = util.join_sockets(conn, socket)
    proxy_used.send(pool)
    logging.debug("popped connection:\"{0}\" for frontend:\"{1}\"".format(
        tunnel.name, hostname))
    pool.waitall()
Beispiel #4
0
def connection_handler(socket, address):
    hostname = peek_http_host(socket)
    if not hostname:
        send_http_error(socket, "No hostname", "400 Bad Request")
        return

    if hostname == Tunnel.domain_suffix:
        meta.server.process_request((socket, address))
        return

    tunnel = Tunnel.get_by_hostname(hostname)
    if not tunnel:
        send_http_error(socket, "No tunnel for {0}".format(hostname), "410 Gone")
        return

    conn, proxy_used = tunnel.pop_proxy_conn(timeout=2)
    if not conn:
        send_http_error(socket, "No proxy connections", "502 Bad Gateway")
        return

    protocol.send_message(conn, protocol.proxy_reply())
    pool = util.join_sockets(conn, socket)
    proxy_used.send(pool)
    logging.debug('popped connection:"{0}" for frontend:"{1}"'.format(tunnel.name, hostname))
    pool.waitall()
Beispiel #5
0
def handle_proxy(socket, request):
    try:
        tunnel = Tunnel.get_by_proxy_request(request)
    except RuntimeError, e:
        protocol.send_message(socket, protocol.error_reply('notavailable'))
        socket.close()
        return
Beispiel #6
0
def start_client(**kwargs):
    host = kwargs['host']
    backend_port = kwargs.get('backend_port')

    if not backend_port:
        try:
            backend_port = util.discover_backend_port(host)
        except:
            print "  ERROR: Unable to connect to service."
            sys.exit(0)

    frontend_ip = socket.gethostbyname(host.split(':')[0])
    frontend_address, frontend_hostname = util.parse_address(host,
            default_ip=frontend_ip)
    backend = (frontend_address[0], backend_port)

    name = kwargs['name']

    client = util.client_name()
    target = util.parse_address(kwargs['target'])[0]
    try:
        control = eventlet.connect(backend)
        control.sendall(protocol.version)
        protocol.send_message(control,
            protocol.control_request(
                name=name,
                client=client,
        ))
        reply = protocol.recv_message(control)
        if reply and 'control' in reply:
            reply = reply['control']

            def maintain_proxy_backend_pool():
                pool = eventlet.greenpool.GreenPool(reply['concurrency'])
                while True:
                    pool.spawn_n(open_proxy_backend,
                            backend, target, name, client)
            proxying = eventlet.spawn(maintain_proxy_backend_pool)

            print "  {0}".format(reply['banner'])
            print "  Port {0} is now accessible from http://{1} ...\n".format(
                    target[1], reply['host'])

            try:
                while True:
                    message = protocol.recv_message(control)
                    assert message == protocol.control_ping()
                    protocol.send_message(control, protocol.control_pong())
            except (IOError, AssertionError):
                proxying.kill()

        elif reply and 'error' in reply:
            print "  ERROR: {0}".format(reply['message'])
        else:
            print "  ERROR: Unexpected server reply."
            print "         Make sure you have the latest version of the client."
    except KeyboardInterrupt:
        pass
Beispiel #7
0
def start_client(**kwargs):
    host = kwargs['host']

    try:
        backend_port = util.discover_backend_port(host)
    except:
        print "  ERROR: Unable to connect to service."
        sys.exit(0)
    frontend_ip = socket.gethostbyname(host.split(':')[0])
    frontend_address, frontend_hostname = util.parse_address(
        host, default_ip=frontend_ip)
    backend = (frontend_address[0], backend_port)

    name = kwargs['name']

    client = util.client_name()
    target = util.parse_address(kwargs['target'])[0]
    try:
        control = eventlet.connect(backend)
        control.sendall(protocol.version)
        protocol.send_message(
            control, protocol.control_request(
                name=name,
                client=client,
            ))
        reply = protocol.recv_message(control)
        if reply and 'control' in reply:
            reply = reply['control']

            def maintain_proxy_backend_pool():
                pool = eventlet.greenpool.GreenPool(reply['concurrency'])
                while True:
                    pool.spawn_n(open_proxy_backend, backend, target, name,
                                 client)

            proxying = eventlet.spawn(maintain_proxy_backend_pool)

            print "  {0}".format(reply['banner'])
            print "  Port {0} is now accessible from http://{1} ...\n".format(
                target[1], reply['host'])

            try:
                while True:
                    message = protocol.recv_message(control)
                    assert message == protocol.control_ping()
                    protocol.send_message(control, protocol.control_pong())
            except (IOError, AssertionError):
                proxying.kill()

        elif reply and 'error' in reply:
            print "  ERROR: {0}".format(reply['message'])
        else:
            print "  ERROR: Unexpected server reply."
            print "         Make sure you have the latest version of the client."
    except KeyboardInterrupt:
        pass
Beispiel #8
0
def start_client(**kwargs):
    host = kwargs["host"]
    backend_port = kwargs.get("backend_port")
    use_ssl = kwargs.get("use_ssl", False)
    ssl_opts = kwargs.get("ssl_opts", {})

    if not backend_port:
        try:
            backend_port = util.discover_backend_port(host)
        except:
            print "  ERROR: Unable to connect to service."
            sys.exit(0)

    frontend_ip = socket.gethostbyname(host.split(":")[0])
    frontend_address, frontend_hostname = util.parse_address(host, default_ip=frontend_ip)
    backend = (frontend_address[0], backend_port)

    name = kwargs["name"]

    client = util.client_name()
    target = util.parse_address(kwargs["target"])[0]
    try:
        control = eventlet.connect(backend)
        if use_ssl:
            control = eventlet.wrap_ssl(control, server_side=False, **ssl_opts)
        control.sendall(protocol.version)
        protocol.send_message(control, protocol.control_request(name=name, client=client))
        reply = protocol.recv_message(control)
        if reply and "control" in reply:
            reply = reply["control"]

            def maintain_proxy_backend_pool():
                pool = eventlet.greenpool.GreenPool(reply["concurrency"])
                while True:
                    pool.spawn_n(open_proxy_backend, backend, target, name, client, use_ssl, ssl_opts)

            proxying = eventlet.spawn(maintain_proxy_backend_pool)

            print "  {0}".format(reply["banner"])
            print "  Port {0} is now accessible from http://{1} ...\n".format(target[1], reply["host"])

            try:
                while True:
                    message = protocol.recv_message(control)
                    assert message == protocol.control_ping()
                    protocol.send_message(control, protocol.control_pong())
            except (IOError, AssertionError):
                proxying.kill()

        elif reply and "error" in reply:
            print "  ERROR: {0}".format(reply["message"])
        else:
            print "  ERROR: Unexpected server reply."
            print "         Make sure you have the latest version of the client."
    except KeyboardInterrupt:
        pass
Beispiel #9
0
def frontend_handler(socket, address):
    hostname = ''
    hostheader = re.compile('host: ([^\(\);:,<>]+)', re.I)
    # Peek up to 512 bytes into data for the Host header
    for n in [128, 256, 512]:
        bytes = socket.recv(n, MSG_PEEK)
        if not bytes:
            socket.close()
            return
        for line in bytes.split('\r\n'):
            match = hostheader.match(line)
            if match:
                hostname = match.group(1)
        if hostname:
            break
    hostname = hostname.split(':')[0]
    if not hostname:
        logging.debug("!frontend: no hostname, closing")
        socket.close()
        return
    if hostname.startswith('_version.'):
        data = """HTTP/1.1 200 OK\r\nContent-Length: {0}\r\nConnection: close\r\n\r\n{1}
               """.format(len(VERSION), VERSION).strip()
        socket.sendall(data)
        socket.close()
        logging.debug("version request")
        return
    if hostname.startswith('_backend.'):
        port = os.environ.get('DOTCLOUD_SERVER_BACKEND_PORT',
                              Tunnel.backend_port)
        data = """HTTP/1.1 200 OK\r\nContent-Length: {0}\r\nConnection: close\r\n\r\n{1}
               """.format(len(str(port)), port).strip()
        socket.sendall(data)
        socket.close()
        return
    tunnel = Tunnel.get_by_hostname(hostname)
    if not tunnel:
        logging.debug("!frontend: no tunnel, closing ({0})".format(hostname))
        socket.close()
        return
    conn = tunnel.pop_proxy_backend(timeout=2)
    if not conn:
        logging.debug("!frontend: no backend, closing")
        socket.close()
        return
    protocol.send_message(conn, protocol.proxy_reply())
    pool = util.join_sockets(conn, socket)
    logging.debug("popped backend:\"{0}\" for frontend:\"{1}\"".format(
        tunnel.name, hostname))
    pool.waitall()
Beispiel #10
0
def frontend_handler(socket, address):
    hostname = ''
    hostheader = re.compile('host: ([^\(\);:,<>]+)', re.I)
    # Peek up to 512 bytes into data for the Host header
    for n in [128, 256, 512]:
        bytes = socket.recv(n, MSG_PEEK)
        if not bytes:
            socket.close()
            return
        for line in bytes.split('\r\n'):
            match = hostheader.match(line)
            if match:
                hostname = match.group(1)
        if hostname:
            break
    hostname = hostname.split(':')[0]
    if not hostname:
        logging.debug("!frontend: no hostname, closing")
        socket.close()
        return
    if hostname.startswith('_version.'):
        data = """HTTP/1.1 200 OK\r\nContent-Length: {0}\r\nConnection: close\r\n\r\n{1}
               """.format(len(VERSION), VERSION).strip()
        socket.sendall(data)
        socket.close()
        logging.debug("version request")
        return
    if hostname.startswith('_backend.'):
        port = os.environ.get('DOTCLOUD_SERVER_BACKEND_PORT', Tunnel.backend_port)
        data = """HTTP/1.1 200 OK\r\nContent-Length: {0}\r\nConnection: close\r\n\r\n{1}
               """.format(len(str(port)), port).strip()
        socket.sendall(data)
        socket.close()
        return
    tunnel = Tunnel.get_by_hostname(hostname)
    if not tunnel:
        logging.debug("!frontend: no tunnel, closing ({0})".format(
            hostname))
        socket.close()
        return
    conn = tunnel.pop_proxy_backend(timeout=2)
    if not conn:
        logging.debug("!frontend: no backend, closing")
        socket.close()
        return
    protocol.send_message(conn, protocol.proxy_reply())
    pool = util.join_sockets(conn, socket)
    logging.debug("popped backend:\"{0}\" for frontend:\"{1}\"".format(
                tunnel.name, hostname))
    pool.waitall()
Beispiel #11
0
def connection_handler(socket, address):
    host = peek_http_host(socket)
    hostname = host.split(':')[0]
    if not hostname:
        logging.debug("!no hostname, closing")
        socket.close()
        return

    if hostname.startswith('_version.'):
        send_http_response(socket, __version__)
        socket.close()
        logging.debug("version request from {0}".format(address[0]))
        return

    if hostname.startswith('_backend.'):
        port = os.environ.get('DOTCLOUD_SERVER_BACKEND_PORT',
                              Tunnel.backend_port)
        send_http_response(socket, port)
        socket.close()
        return

    if hostname.startswith('_metrics.'):
        content = json.dumps(metrics.dump_metrics(),
                             sort_keys=True,
                             indent=2,
                             separators=(',', ': '))
        send_http_response(socket, content)
        socket.close()
        logging.debug("metrics request from {0}".format(address[0]))
        return

    tunnel = Tunnel.get_by_hostname(hostname)
    if not tunnel:
        logging.debug("!no tunnel, closing ({0})".format(hostname))
        socket.close()
        return

    conn, proxy_used = tunnel.pop_proxy_conn(timeout=2)
    if not conn:
        logging.debug("!no proxy connection, closing")
        socket.close()
        return

    protocol.send_message(conn, protocol.proxy_reply())
    pool = util.join_sockets(conn, socket)
    proxy_used.send(pool)
    logging.debug("popped connection:\"{0}\" for frontend:\"{1}\"".format(
        tunnel.name, hostname))
    pool.waitall()
Beispiel #12
0
def connection_handler(socket, address):
    host = peek_http_host(socket)
    hostname = host.split(':')[0]
    if not hostname:
        logging.debug("!no hostname, closing")
        socket.close()
        return

    if hostname.startswith('_version.'):
        send_http_response(socket, __version__)
        socket.close()
        logging.debug("version request from {0}".format(address[0]))
        return

    if hostname.startswith('_backend.'):
        port = os.environ.get('DOTCLOUD_SERVER_BACKEND_PORT', 
                    Tunnel.backend_port)
        send_http_response(socket, port)
        socket.close()
        return

    if hostname.startswith('_metrics.'):
        content = json.dumps(metrics.dump_metrics(),
                    sort_keys=True, indent=2, separators=(',', ': '))
        send_http_response(socket, content)
        socket.close()
        logging.debug("metrics request from {0}".format(address[0]))
        return

    tunnel = Tunnel.get_by_hostname(hostname)
    if not tunnel:
        logging.debug("!no tunnel, closing ({0})".format(
            hostname))
        socket.close()
        return

    conn, proxy_used = tunnel.pop_proxy_conn(timeout=2)
    if not conn:
        logging.debug("!no proxy connection, closing")
        socket.close()
        return

    protocol.send_message(conn, protocol.proxy_reply())
    pool = util.join_sockets(conn, socket)
    proxy_used.send(pool)
    logging.debug("popped connection:\"{0}\" for frontend:\"{1}\"".format(
                tunnel.name, hostname))
    pool.waitall()
Beispiel #13
0
def open_proxy_backend(backend, target, name, client, use_ssl=False, ssl_opts=None):
    proxy = eventlet.connect(backend)
    if use_ssl:
        ssl_opts = ssl_opts or {}
        proxy = eventlet.wrap_ssl(proxy, server_side=False, **ssl_opts)
    proxy.sendall(protocol.version)
    protocol.send_message(proxy, protocol.proxy_request(name=name, client=client))
    reply = protocol.recv_message(proxy)
    if reply and "proxy" in reply:
        try:
            local = eventlet.connect(target)
            util.join_sockets(proxy, local)
        except IOError:
            proxy.close()
    elif reply and "error" in reply:
        print "  ERROR: {0}".format(reply["error"])
        return
    else:
        pass
Beispiel #14
0
def open_proxy_backend(backend, port, name, client):
    proxy = eventlet.connect(backend)
    proxy.sendall(protocol.version)
    protocol.send_message(proxy, 
        protocol.proxy_request(
            name=name, 
            client=client,
    ))
    reply = protocol.recv_message(proxy)
    if reply and 'proxy' in reply:
        try:
            local = eventlet.connect(('0.0.0.0', port))
            util.join_sockets(proxy, local)
        except IOError:
            proxy.close()
    elif reply and 'error' in reply:
        print "  ERROR: {0}".format(reply['error'])
        return
    else:
        pass
Beispiel #15
0
def open_proxy_backend(backend, target, name, client):
    proxy = eventlet.connect(backend)
    proxy.sendall(protocol.version)
    protocol.send_message(proxy,
        protocol.proxy_request(
            name=name,
            client=client,
    ))
    reply = protocol.recv_message(proxy)
    if reply and 'proxy' in reply:
        try:
            local = eventlet.connect(target)
            util.join_sockets(proxy, local)
        except IOError:
            proxy.close()
    elif reply and 'error' in reply:
        print "  ERROR: {0}".format(reply['error'])
        return
    else:
        pass
Beispiel #16
0
    except AssertionError:
        logging.debug("!backend: invalid protocol, closing")


@metrics.time_calls(name="control_conn")
def handle_control_request(socket, request):
    try:
        tunnel = Tunnel.get_by_control_request(request)
    except RuntimeError, e:
        protocol.send_message(socket, protocol.error_reply("notavailable"))
        socket.close()
        return
    protocol.send_message(
        socket,
        protocol.control_reply(
            host=HOST_TEMPLATE.format(tunnel.name, Tunnel.domain_suffix),
            banner=BANNER,
            concurrency=Tunnel.max_pool_size,
        ),
    )
    logging.info('created tunnel:"{0}" by client:"{1}"'.format(tunnel.name, tunnel.client))

    try:
        while True:
            eventlet.sleep(HEARTBEAT_INTERVAL)
            protocol.send_message(socket, protocol.control_ping())
            with Timeout(HEARTBEAT_INTERVAL):
                message = protocol.recv_message(socket)
                assert message == protocol.control_pong()
    except (IOError, AssertionError, Timeout):
        logging.debug('expiring tunnel:"{0}"'.format(tunnel.name))
        tunnel.destroy()
Beispiel #17
0
        else:
            logging.debug("!backend: no request message, closing")
    except AssertionError:
        logging.debug("!backend: invalid protocol, closing")

@metrics.time_calls(name='control_conn')
def handle_control_request(socket, request):
    try:
        tunnel = Tunnel.get_by_control_request(request)
    except RuntimeError, e:
        protocol.send_message(socket, error_reply('notavailable'))
        socket.close()
        return
    protocol.send_message(socket, protocol.control_reply(
        host=HOST_TEMPLATE.format(tunnel.name),
        banner=BANNER,
        concurrency=Tunnel.max_pool_size,
    ))
    logging.info("created tunnel:\"{0}\" by client:\"{1}\"".format(
            tunnel.name, tunnel.client))

    try:
        while True:
            eventlet.sleep(HEARTBEAT_INTERVAL)
            protocol.send_message(socket, protocol.control_ping())
            with Timeout(HEARTBEAT_INTERVAL):
                message = protocol.recv_message(socket)
                assert message == protocol.control_pong()
    except (IOError, AssertionError, Timeout):
        logging.debug("expiring tunnel:\"{0}\"".format(tunnel.name))
        tunnel.destroy()
Beispiel #18
0
def run():
    parser = argparse.ArgumentParser(
                description='Open a public HTTP tunnel to a local server')
    parser.add_argument('-s', dest='host', metavar='address',
                default='v2.localtunnel.com',
                help='localtunnel server address (default: v2.localtunnel.com)')
    parser.add_argument('--version', action='store_true',
                help='show version information for client and server')
    parser.add_argument('-m', action='store_true',
                help='show server metrics and exit')

    
    if '--version' in sys.argv:
        args = parser.parse_args()
        print "client: {}".format(__version__)
        try:
            server_version = util.lookup_server_version(args.host)
        except:
            server_version = '??'
        print "server: {} ({})".format(server_version, args.host)
        sys.exit(0)
    elif '-m' in sys.argv:
        args = parser.parse_args()
        util.print_server_metrics(args.host)
        sys.exit(0)
    
    parser.add_argument('-n', dest='name', metavar='name',
                default=str(uuid.uuid4()).split('-')[-1], 
                help='name of the tunnel (default: randomly generate)')
    parser.add_argument('-c', dest='concurrency', type=int,
                metavar='concurrency', default=3,
                help='number of concurrent backend connections')
    parser.add_argument('port', metavar='port', type=int,
                help='local port of server to tunnel to')
    args = parser.parse_args()

        
    host = args.host.split(':')
    if len(host) == 1:
        backend_port = util.discover_backend_port(host[0])
    else:
        backend_port = util.discover_backend_port(host[0], int(host[1]))
    backend = (host[0], backend_port)

    name = args.name
    client = util.client_name()
    port = args.port

    try:
        control = eventlet.connect(backend)
        control.sendall(protocol.version)
        protocol.send_message(control, 
            protocol.control_request(
                name=name, 
                client=client,
        ))
        reply = protocol.recv_message(control)
        if reply and 'control' in reply:
            reply = reply['control']

            def maintain_proxy_backend_pool():
                pool = eventlet.greenpool.GreenPool(reply['concurrency'])
                while True:
                    pool.spawn_n(open_proxy_backend, 
                            backend, port, name, client)
            proxying = eventlet.spawn(maintain_proxy_backend_pool)

            print "  {0}".format(reply['banner'])
            print "  Port {0} is now accessible from http://{1} ...\n".format(
                    port, reply['host'])

            try:
                while True:
                    message = protocol.recv_message(control)
                    assert message == protocol.control_ping()
                    protocol.send_message(control, protocol.control_pong())
            except (IOError, AssertionError):
                proxying.kill()
            
        elif reply and 'error' in reply:
            print "  ERROR: {0}".format(reply['message'])
        else:
            print "  ERROR: Unexpected server reply."
            print "         Make sure you have the latest version of the client."
    except KeyboardInterrupt:
        pass
Beispiel #19
0
            logging.debug("!backend: no request message, closing")
    except AssertionError:
        logging.debug("!backend: invalid protocol, closing")


def handle_control(socket, request):
    try:
        tunnel = Tunnel.get_by_control_request(request)
    except RuntimeError, e:
        protocol.send_message(socket, error_reply('notavailable'))
        socket.close()
        return
    protocol.send_message(
        socket,
        protocol.control_reply(
            host=HOST_TEMPLATE.format(tunnel.name),
            banner=BANNER,
            concurrency=Tunnel.max_pool_size,
        ))
    logging.info("created tunnel:\"{0}\" by client:\"{1}\"".format(
        tunnel.name, tunnel.client))

    try:
        while True:
            eventlet.sleep(HEARTBEAT_INTERVAL)
            protocol.send_message(socket, protocol.control_ping())
            with Timeout(HEARTBEAT_INTERVAL):
                message = protocol.recv_message(socket)
                assert message == protocol.control_pong()
    except (IOError, AssertionError, Timeout):
        logging.debug("expiring tunnel:\"{0}\"".format(tunnel.name))
Beispiel #20
0
def run():
    parser = argparse.ArgumentParser(
                description='Open a public HTTP tunnel to a local server')
    parser.add_argument('-s', dest='host', metavar='address',
                default='v2.localtunnel.com',
                help='localtunnel server address (default: v2.localtunnel.com)')
    parser.add_argument('--version', action='store_true',
                help='show version information for client and server')
    parser.add_argument('-m', action='store_true',
                help='show server metrics and exit')


    if '--version' in sys.argv:
        args = parser.parse_args()
        print "client: {}".format(__version__)
        try:
            server_version = util.lookup_server_version(args.host)
        except:
            server_version = '??'
        print "server: {} ({})".format(server_version, args.host)
        sys.exit(0)
    elif '-m' in sys.argv:
        args = parser.parse_args()
        util.print_server_metrics(args.host)
        sys.exit(0)

    parser.add_argument('-n', dest='name', metavar='name',
                default=str(uuid.uuid4()).split('-')[-1],
                help='name of the tunnel (default: randomly generate)')
    parser.add_argument('-c', dest='concurrency', type=int,
                metavar='concurrency', default=3,
                help='number of concurrent backend connections')
    parser.add_argument('target', metavar='target', type=str,
                help='local target port or address of server to tunnel to')
    args = parser.parse_args()


    backend_port = util.discover_backend_port(args.host)
    frontend_address, frontend_hostname = util.parse_address(args.host)
    backend = (frontend_address[0], backend_port)

    name = args.name
    client = util.client_name()
    target = util.parse_address(args.target)[0]

    try:
        control = eventlet.connect(backend)
        control.sendall(protocol.version)
        protocol.send_message(control,
            protocol.control_request(
                name=name,
                client=client,
        ))
        reply = protocol.recv_message(control)
        if reply and 'control' in reply:
            reply = reply['control']

            def maintain_proxy_backend_pool():
                pool = eventlet.greenpool.GreenPool(reply['concurrency'])
                while True:
                    pool.spawn_n(open_proxy_backend,
                            backend, target, name, client)
            proxying = eventlet.spawn(maintain_proxy_backend_pool)

            print "  {0}".format(reply['banner'])
            print "  Port {0} is now accessible from http://{1} ...\n".format(
                    target[1], reply['host'])

            try:
                while True:
                    message = protocol.recv_message(control)
                    assert message == protocol.control_ping()
                    protocol.send_message(control, protocol.control_pong())
            except (IOError, AssertionError):
                proxying.kill()

        elif reply and 'error' in reply:
            print "  ERROR: {0}".format(reply['message'])
        else:
            print "  ERROR: Unexpected server reply."
            print "         Make sure you have the latest version of the client."
    except KeyboardInterrupt:
        pass