def _do_start_ssl_proxy(port: int, target: PortOrUrl, target_ssl=False): import pproxy from localstack.services.generic_proxy import GenericProxy if ":" not in str(target): target = "127.0.0.1:%s" % target LOG.debug("Starting SSL proxy server %s -> %s", port, target) # create server and remote connection server = pproxy.Server("secure+tunnel://0.0.0.0:%s" % port) target_proto = "ssl+tunnel" if target_ssl else "tunnel" remote = pproxy.Connection("%s://%s" % (target_proto, target)) args = dict(rserver=[remote], verbose=print) # set SSL contexts _, cert_file_name, key_file_name = GenericProxy.create_ssl_cert() for context in pproxy.server.sslcontexts: context.load_cert_chain(cert_file_name, key_file_name) loop = ensure_event_loop() handler = loop.run_until_complete(server.start_server(args)) try: loop.run_forever() except KeyboardInterrupt: print("exit!") handler.close() loop.run_until_complete(handler.wait_closed()) loop.run_until_complete(loop.shutdown_asyncgens()) loop.close()
def run_app_sync(*args, loop=None, shutdown_event=None): kwargs = {} config = Config() cert_file_name, key_file_name = ssl_creds or (None, None) if cert_file_name: kwargs["certfile"] = cert_file_name config.certfile = cert_file_name if key_file_name: kwargs["keyfile"] = key_file_name config.keyfile = key_file_name setup_quart_logging() config.bind = ["%s:%s" % (bind_address, port)] loop = loop or ensure_event_loop() run_kwargs = {} if shutdown_event: run_kwargs["shutdown_trigger"] = shutdown_event.wait try: try: return loop.run_until_complete(serve(app, config, **run_kwargs)) except Exception as e: LOG.info( "Error running server event loop on port %s: %s %s", port, e, traceback.format_exc(), ) if "SSL" in str(e): c_exists = os.path.exists(cert_file_name) k_exists = os.path.exists(key_file_name) c_size = len(load_file(cert_file_name)) if c_exists else 0 k_size = len(load_file(key_file_name)) if k_exists else 0 LOG.warning( "Unable to create SSL context. Cert files exist: %s %s (%sB), %s %s (%sB)", cert_file_name, c_exists, c_size, key_file_name, k_exists, k_size, ) raise finally: try: _cancel_all_tasks(loop) loop.run_until_complete(loop.shutdown_asyncgens()) finally: asyncio.set_event_loop(None) loop.close()
def run_proxy(self, *args): self.loop = ensure_event_loop() self.shutdown_event = asyncio.Event() run_app_sync(loop=self.loop, shutdown_event=self.shutdown_event)
def run_server(port, bind_address, handler=None, asynchronous=True, ssl_creds=None): ensure_event_loop() app = Quart(__name__) app.config[ "MAX_CONTENT_LENGTH"] = 256 * 1024 * 1024 # 256 MB request payload limit @app.route("/", methods=HTTP_METHODS, defaults={"path": ""}) @app.route("/<path:path>", methods=HTTP_METHODS) async def index(path=None): response = await make_response("{}") if handler: data = await request.get_data() try: result = await run_sync(handler, request, data) if isinstance(result, Exception): raise result except Exception as e: LOG.warning( "Error in proxy handler for request %s %s: %s %s" % (request.method, request.url, e, traceback.format_exc())) response.status_code = 500 if isinstance(e, HTTPErrorResponse): response.status_code = e.code or response.status_code return response if result is not None: # check if this is an async generator (for HTTP2 push event responses) async_gen = get_async_generator_result(result) if async_gen: return async_gen # prepare and return regular response is_chunked = uses_chunked_encoding(result) result_content = result.content or "" response = await make_response(result_content) response.status_code = result.status_code if is_chunked: response.headers.pop("Content-Length", None) result.headers.pop("Server", None) result.headers.pop("Date", None) headers = { k: str(v).replace("\n", r"\n") for k, v in result.headers.items() } response.headers.update(headers) # set multi-value headers multi_value_headers = getattr(result, "multi_value_headers", {}) for key, values in multi_value_headers.items(): for value in values: response.headers.add_header(key, value) # set default headers, if required if not is_chunked and request.method not in [ "OPTIONS", "HEAD" ]: response_data = await response.get_data() response.headers["Content-Length"] = str( len(response_data or "")) if "Connection" not in response.headers: response.headers["Connection"] = "close" # fix headers for OPTIONS requests (possible fix for Firefox requests) if request.method == "OPTIONS": response.headers.pop("Content-Type", None) if not response.headers.get("Cache-Control"): response.headers["Cache-Control"] = "no-cache" return response def run_app_sync(*args, loop=None, shutdown_event=None): kwargs = {} config = Config() cert_file_name, key_file_name = ssl_creds or (None, None) if cert_file_name: kwargs["certfile"] = cert_file_name config.certfile = cert_file_name if key_file_name: kwargs["keyfile"] = key_file_name config.keyfile = key_file_name setup_quart_logging() config.bind = ["%s:%s" % (bind_address, port)] loop = loop or ensure_event_loop() run_kwargs = {} if shutdown_event: run_kwargs["shutdown_trigger"] = shutdown_event.wait try: try: return loop.run_until_complete(serve(app, config, **run_kwargs)) except Exception as e: LOG.info("Error running server event loop on port %s: %s %s" % (port, e, traceback.format_exc())) if "SSL" in str(e): c_exists = os.path.exists(cert_file_name) k_exists = os.path.exists(key_file_name) c_size = len(load_file(cert_file_name)) if c_exists else 0 k_size = len(load_file(key_file_name)) if k_exists else 0 LOG.warning( "Unable to create SSL context. Cert files exist: %s %s (%sB), %s %s (%sB)" % ( cert_file_name, c_exists, c_size, key_file_name, k_exists, k_size, )) raise finally: try: _cancel_all_tasks(loop) loop.run_until_complete(loop.shutdown_asyncgens()) finally: asyncio.set_event_loop(None) loop.close() class ProxyThread(FuncThread): def __init__(self): FuncThread.__init__(self, self.run_proxy, None) self.shutdown_event = None self.loop = None def run_proxy(self, *args): self.loop = ensure_event_loop() self.shutdown_event = asyncio.Event() run_app_sync(loop=self.loop, shutdown_event=self.shutdown_event) def stop(self, quiet=None): event = self.shutdown_event async def set_event(): event.set() run_coroutine(set_event(), self.loop) super().stop(quiet) def run_in_thread(): thread = ProxyThread() thread.start() TMP_THREADS.append(thread) return thread if asynchronous: return run_in_thread() return run_app_sync()
def run_server(port, handler=None, asynchronous=True, ssl_creds=None): ensure_event_loop() app = Quart(__name__) app.config[ 'MAX_CONTENT_LENGTH'] = 256 * 1024 * 1024 # 256 MB request payload limit @app.route('/', methods=HTTP_METHODS, defaults={'path': ''}) @app.route('/<path:path>', methods=HTTP_METHODS) async def index(path=None): response = await make_response('{}') if handler: data = await request.get_data() try: result = await run_sync(handler, request, data) except Exception as e: LOG.warning( 'Error in proxy handler for request %s %s: %s %s' % (request.method, request.url, e, traceback.format_exc())) response.status_code = 500 return response if result is not None: is_chunked = uses_chunked_encoding(result) result_content = result.content or '' response = await make_response(result_content) response.status_code = result.status_code if is_chunked: response.headers.pop('Content-Length', None) result.headers.pop('Server', None) result.headers.pop('Date', None) response.headers.update(dict(result.headers)) # set multi-value headers multi_value_headers = getattr(result, 'multi_value_headers', {}) for key, values in multi_value_headers.items(): for value in values: response.headers.add_header(key, value) # set default headers, if required if 'Content-Length' not in response.headers and not is_chunked: response.headers['Content-Length'] = str( len(result_content) if result_content else 0) if 'Connection' not in response.headers: response.headers['Connection'] = 'close' return response def run_app_sync(*args, loop=None, shutdown_event=None): kwargs = {} config = Config() cert_file_name, key_file_name = ssl_creds or (None, None) if cert_file_name: kwargs['certfile'] = cert_file_name config.certfile = cert_file_name if key_file_name: kwargs['keyfile'] = key_file_name config.keyfile = key_file_name setup_quart_logging() config.bind = ['0.0.0.0:%s' % port] loop = loop or ensure_event_loop() run_kwargs = {} if shutdown_event: run_kwargs['shutdown_trigger'] = shutdown_event.wait try: try: return loop.run_until_complete(serve(app, config, **run_kwargs)) except Exception as e: LOG.info('Error running server event loop on port %s: %s %s' % (port, e, traceback.format_exc())) if 'SSL' in str(e): c_exists = os.path.exists(cert_file_name) k_exists = os.path.exists(key_file_name) c_size = len(load_file(cert_file_name)) if c_exists else 0 k_size = len(load_file(key_file_name)) if k_exists else 0 LOG.warning( 'Unable to create SSL context. Cert files exist: %s %s (%sB), %s %s (%sB)' % (cert_file_name, c_exists, c_size, key_file_name, k_exists, k_size)) raise finally: try: _cancel_all_tasks(loop) loop.run_until_complete(loop.shutdown_asyncgens()) finally: asyncio.set_event_loop(None) loop.close() class ProxyThread(FuncThread): def __init__(self): FuncThread.__init__(self, self.run_proxy, None) def run_proxy(self, *args): loop = ensure_event_loop() self.shutdown_event = asyncio.Event() run_app_sync(loop=loop, shutdown_event=self.shutdown_event) def stop(self, quiet=None): self.shutdown_event.set() def run_in_thread(): thread = ProxyThread() thread.start() TMP_THREADS.append(thread) return thread if asynchronous: return run_in_thread() return run_app_sync()