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
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
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()
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()
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
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
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
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
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()
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()
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()
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()
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
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
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
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()
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()
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
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))
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