async def test_http1_request(event_loop: asyncio.AbstractEventLoop) -> None: server = TCPServer( sanity_framework, event_loop, Config(), WorkerContext(), MemoryReader(), MemoryWriter() # type: ignore # noqa: E501 ) task = event_loop.create_task(server.run()) client = h11.Connection(h11.CLIENT) await server.reader.send( # type: ignore client.send( h11.Request( method="POST", target="/", headers=[ (b"host", b"hypercorn"), (b"connection", b"close"), (b"content-length", b"%d" % len(SANITY_BODY)), ], ))) await server.reader.send(client.send(h11.Data(data=SANITY_BODY)) ) # type: ignore await server.reader.send(client.send(h11.EndOfMessage())) # type: ignore events = [] while True: event = client.next_event() if event == h11.NEED_DATA: data = await server.writer.receive() # type: ignore client.receive_data(data) elif isinstance(event, h11.ConnectionClosed): break else: events.append(event) assert events == [ h11.Response( status_code=200, headers=[ (b"content-length", b"15"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h11"), (b"connection", b"close"), ], http_version=b"1.1", reason=b"", ), h11.Data(data=b"Hello & Goodbye"), h11.EndOfMessage(headers=[]), # type: ignore ] server.reader.close() # type: ignore await task
async def test_spawn_app_error(event_loop: asyncio.AbstractEventLoop, http_scope: HTTPScope) -> None: async def _error_app(scope: Scope, receive: Callable, send: Callable) -> None: raise Exception() app_queue: asyncio.Queue = asyncio.Queue() async with TaskGroup(event_loop) as task_group: context = Context(task_group) await context.spawn_app(_error_app, Config(), http_scope, app_queue.put) assert (await app_queue.get()) is None
async def _make_upgrade_request( request: h11.Request) -> h11.InformationalResponse: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.socket = MockSocket() async with trio.open_nursery() as nursery: nursery.start_soon(serve_stream, echo_framework, Config(), server_stream) client = h11.Connection(h11.CLIENT) await client_stream.send_all(client.send(request)) client.receive_data(await client_stream.receive_some(2**16)) await client_stream.aclose() return client.next_event()
async def test_protocol_handle_max_incomplete(monkeypatch: MonkeyPatch) -> None: config = Config() config.h11_max_incomplete_size = 5 MockHTTPStream = AsyncMock() # noqa: N806 MockHTTPStream.return_value = AsyncMock(spec=HTTPStream) monkeypatch.setattr(hypercorn.protocol.h11, "HTTPStream", MockHTTPStream) MockEvent = AsyncMock() # noqa: N806 MockEvent.return_value = AsyncMock(spec=IOEvent) protocol = H11Protocol(config, False, None, None, CoroutineMock(), CoroutineMock(), MockEvent) await protocol.handle(RawData(data=b"GET / HTTP/1.1\r\nHost: hypercorn\r\n")) protocol.send.assert_called() assert protocol.send.call_args_list == [ call( RawData( data=b"HTTP/1.1 400 \r\ncontent-length: 0\r\nconnection: close\r\n" b"date: Thu, 01 Jan 1970 01:23:20 GMT\r\nserver: hypercorn-h11\r\n\r\n" ) ), call(RawData(data=b"")), call(Closed()), ]
async def test_asgi_send_invalid_message(data_bytes: Any, data_text: Any) -> None: server = WebsocketMixin() server.config = Config() server.start_time = 0.0 server.state = ASGIWebsocketState.CONNECTED with pytest.raises((TypeError, ValueError)): await server.asgi_send({ "type": "websocket.send", "bytes": data_bytes, "text": data_text })
def test_access_logger_init( target: Union[logging.Logger, str, None], expected_name: Optional[str], expected_handler_type: Optional[Type[logging.Handler]], ) -> None: config = Config() config.accesslog = target config.access_log_format = "%h" logger = Logger(config) assert logger.access_log_format == "%h" assert logger.getEffectiveLevel() == logging.INFO if target is None: assert logger.access_logger is None elif expected_name is None: assert logger.access_logger.handlers == [] else: assert logger.access_logger.name == expected_name if expected_handler_type is None: assert logger.access_logger.handlers == [] else: assert isinstance(logger.access_logger.handlers[0], expected_handler_type)
async def test_http2_request(event_loop: asyncio.AbstractEventLoop) -> None: server = TCPServer( sanity_framework, event_loop, Config(), MemoryReader(), # type: ignore MemoryWriter(http2=True), # type: ignore ) asyncio.ensure_future(server.run()) client = h2.connection.H2Connection() client.initiate_connection() await server.reader.send(client.data_to_send()) # type: ignore stream_id = client.get_next_available_stream_id() client.send_headers( stream_id, [ (":method", "POST"), (":path", "/"), (":authority", "hypercorn"), (":scheme", "https"), ("content-length", "%d" % len(SANITY_BODY)), ], ) client.send_data(stream_id, SANITY_BODY) client.end_stream(stream_id) await server.reader.send(client.data_to_send()) # type: ignore events = [] open_ = True while open_: data = await server.writer.receive() # type: ignore if data == b"": open_ = False h2_events = client.receive_data(data) for event in h2_events: if isinstance(event, h2.events.DataReceived): client.acknowledge_received_data(event.flow_controlled_length, event.stream_id) elif isinstance( event, (h2.events.ConnectionTerminated, h2.events.StreamEnded, h2.events.StreamReset), ): open_ = False break else: events.append(event) await server.reader.send(client.data_to_send()) # type: ignore assert isinstance(events[2], h2.events.ResponseReceived) assert events[2].headers == [ (b":status", b"200"), (b"content-length", b"15"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h2"), ]
async def test_spawn_app_cancelled(event_loop: asyncio.AbstractEventLoop, http_scope: HTTPScope) -> None: async def _error_app(scope: Scope, receive: Callable, send: Callable) -> None: raise asyncio.CancelledError() app_queue: asyncio.Queue = asyncio.Queue() with pytest.raises(asyncio.CancelledError): async with TaskGroup(event_loop) as task_group: await task_group.spawn_app(_error_app, Config(), http_scope, app_queue.put) assert (await app_queue.get()) is None
async def test_http2_websocket(event_loop: asyncio.AbstractEventLoop) -> None: server = TCPServer( # type: ignore sanity_framework, event_loop, Config(), MemoryReader(), MemoryWriter(http2=True)) asyncio.ensure_future(server.run()) h2_client = h2.connection.H2Connection() h2_client.initiate_connection() await server.reader.send(h2_client.data_to_send()) # type: ignore stream_id = h2_client.get_next_available_stream_id() h2_client.send_headers( stream_id, [ (":method", "CONNECT"), (":path", "/"), (":authority", "hypercorn"), (":scheme", "https"), ("sec-websocket-version", "13"), ], ) await server.reader.send(h2_client.data_to_send()) # type: ignore events = h2_client.receive_data(await server.writer.receive()) # type: ignore await server.reader.send(h2_client.data_to_send()) # type: ignore events = h2_client.receive_data(await server.writer.receive()) # type: ignore events = h2_client.receive_data(await server.writer.receive()) # type: ignore assert isinstance(events[0], h2.events.ResponseReceived) assert events[0].headers == [ (b":status", b"200"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h2"), ] client = wsproto.connection.Connection(wsproto.ConnectionType.CLIENT) h2_client.send_data( stream_id, client.send(wsproto.events.BytesMessage(data=SANITY_BODY))) await server.reader.send(h2_client.data_to_send()) # type: ignore events = h2_client.receive_data(await server.writer.receive()) # type: ignore client.receive_data(events[0].data) assert list(client.events()) == [ wsproto.events.TextMessage(data="Hello & Goodbye") ] h2_client.send_data(stream_id, client.send(wsproto.events.CloseConnection(code=1000))) await server.reader.send(h2_client.data_to_send()) # type: ignore events = h2_client.receive_data(await server.writer.receive()) # type: ignore client.receive_data(events[0].data) assert list(client.events()) == [ wsproto.events.CloseConnection(code=1000, reason="") ] await server.reader.send(b"") # type: ignore
async def test_httpx_starlette(unused_tcp_port: int, calculator_server_creator) -> None: """Test the httpx client/starlette server combo.""" serv: Server[Calculator, StarletteRequest] = calculator_server_creator( StarletteRequest) @serv.implement(Calculator.call_none) async def call_none(ctx: StarletteRequest) -> int: assert ctx.method == "POST" return 1 app = create_starlette_app(serv) config = Config() config.bind = [f"localhost:{unused_tcp_port}"] shutdown_event = Event() async def shutdown_trigger(): await shutdown_event.wait() task = create_task(serve(app, config, shutdown_trigger=shutdown_trigger)) from asyncio import sleep await sleep(0.1) # Wait for the server to start up. t = await create_client( Calculator, httpx_client_adapter(f"http://localhost:{unused_tcp_port}"), ) r = await t.add(1, 2) await t.call_none() assert r == 3 await close_client(t) shutdown_event.set() await task
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()
async def test_http2_websocket(nursery: trio._core._run.Nursery) -> None: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.transport_stream = Mock(return_value=PropertyMock( return_value=MockSocket())) server_stream.do_handshake = AsyncMock() server_stream.selected_alpn_protocol = Mock(return_value="h2") server = TCPServer(sanity_framework, Config(), WorkerContext(), server_stream) nursery.start_soon(server.run) h2_client = h2.connection.H2Connection() h2_client.initiate_connection() await client_stream.send_all(h2_client.data_to_send()) stream_id = h2_client.get_next_available_stream_id() h2_client.send_headers( stream_id, [ (":method", "CONNECT"), (":path", "/"), (":authority", "hypercorn"), (":scheme", "https"), ("sec-websocket-version", "13"), ], ) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) if not isinstance(events[-1], h2.events.ResponseReceived): events = h2_client.receive_data(await client_stream.receive_some(1024)) assert events[-1].headers == [ (b":status", b"200"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h2"), ] client = wsproto.connection.Connection(wsproto.ConnectionType.CLIENT) h2_client.send_data( stream_id, client.send(wsproto.events.BytesMessage(data=SANITY_BODY))) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) client.receive_data(events[0].data) assert list(client.events()) == [ wsproto.events.TextMessage(data="Hello & Goodbye") ] h2_client.send_data(stream_id, client.send(wsproto.events.CloseConnection(code=1000))) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) client.receive_data(events[0].data) assert list(client.events()) == [ wsproto.events.CloseConnection(code=1000, reason="") ] await client_stream.send_all(b"")
def run_http_app(app, host, port): from hypercorn.config import Config as HyperConfig from hypercorn.asyncio import serve from quart.logging import create_serving_logger config = HyperConfig() config.access_log_format = "%(h)s %(r)s %(s)s %(b)s %(D)s" config.access_logger = create_serving_logger() # type: ignore config.bind = [f"{host}:{port}"] config.ca_certs = None config.certfile = None # config.debug = True config.error_logger = config.access_logger # type: ignore config.keyfile = None config.use_reloader = False scheme = 'https' if config.ssl_enabled else 'http' print("Listening on {}://{}".format(scheme, config.bind[0])) return serve(app, config)
async def test_complets_on_half_close(event_loop: asyncio.AbstractEventLoop) -> None: server = TCPServer( echo_framework, event_loop, Config(), MemoryReader(), MemoryWriter() # type: ignore ) asyncio.ensure_future(server.run()) await server.reader.send(b"GET / HTTP/1.1\r\nHost: hypercorn\r\n\r\n") # type: ignore server.reader.close() # type: ignore await asyncio.sleep(0) data = await server.writer.receive() # type: ignore assert ( data == b"HTTP/1.1 200 \r\ncontent-length: 335\r\ndate: Thu, 01 Jan 1970 01:23:20 GMT\r\nserver: hypercorn-h11\r\n\r\n" # noqa: E501 )
def __init__( self, path: str, event_loop: asyncio.AbstractEventLoop, *, framework: Type[ASGIFramework] = EchoFramework, ) -> None: self.transport = MockTransport() self.server = WebsocketServer( # type: ignore framework, event_loop, Config(), self.transport) self.connection = WSConnection(ConnectionType.CLIENT) self.server.data_received( self.connection.send(Request(target=path, host="hypercorn")))
def start_app(self, debug): ssl_context = None self.logger.debug("Preparing to start rest-service") if self.get_use_ssl() and self.get_key_pem() is not None and \ self.get_cert_pem() is not None: self.logger.debug( "Preparing ssl_context with cert:{} and key:{}".format( self.get_cert_pem(), self.get_key_pem())) ssl_context = (self.get_cert_pem(), self.get_key_pem()) self.logger.info("Starting the application {}:{} using ssl? {}".format( self.get_listening_host(), self.get_listening_port(), ssl_context is None)) # looked at the hypercorn and quart Python project to figure out # how to start the application separately, without going through # the Quart.app.run APIs self.app.debug = debug config = HyperConfig() config.debug = debug config.access_log_format = "%(h)s %(r)s %(s)s %(b)s %(D)s" config.accesslog = self.logger.logger config.bind = [ "{host}:{port}".format( **{ 'host': self.get_listening_host(), 'port': self.get_listening_port() }) ] config.certfile = self.get_cert_pem() if self.get_use_ssl() else None config.keyfile = self.get_key_pem() if self.get_use_ssl() else None config.errorlog = config.accesslog config.use_reloader = True scheme = "https" if config.ssl_enabled else "http" self.logger.info("Running on {}://{} (CTRL + C to quit)".format( scheme, config.bind[0])) loop = asyncio.get_event_loop() if loop is not None: loop.set_debug(debug or False) loop.run_until_complete(serve(self.app, config)) else: asyncio.run(serve(self.app, config), debug=config.debug)
async def test_h2_prior_knowledge( event_loop: asyncio.AbstractEventLoop) -> None: client = h2.connection.H2Connection() client.initiate_connection() client.ping(b"12345678") server = Server(EchoFramework, event_loop, Config()) transport = MockTransport() server.connection_made(transport) # type: ignore server.data_received(client.data_to_send()) await transport.updated.wait() events = client.receive_data(transport.data) client.close_connection() server.data_received(client.data_to_send()) assert isinstance(events[-1], h2.events.PingAcknowledged)
async def test_h2_prior_knowledge() -> None: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.socket = MockSocket() async with trio.open_nursery() as nursery: nursery.start_soon(serve_stream, EchoFramework, Config(), server_stream) client = h2.connection.H2Connection() client.initiate_connection() client.ping(b"12345678") await client_stream.send_all(client.data_to_send()) events = client.receive_data(await client_stream.receive_some(2**16)) client.close_connection() await client_stream.send_all(client.data_to_send()) assert isinstance(events[-1], h2.events.PingAcknowledged)
def start_hypercorn(app: Application, app_config: Dict[str, Any]) -> None: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) shutdown_event = asyncio.Event() def _signal_handler(*_): shutdown_event.set() loop.add_signal_handler(signal.SIGTERM, _signal_handler) loop.add_signal_handler(signal.SIGINT, _signal_handler) host = app_config['host'] port = app_config['port'] config = Config() config.bind = [f"{host}:{port}"] loop.run_until_complete( serve( app, config, shutdown_trigger=shutdown_event.wait # type: ignore ))
def __init__(self, log_collector, broker, config_manager, config=dict()): self.log_collector = log_collector self.broker = broker self.config_manager = config_manager self.config = config self.log = self.log_collector.get_logger('web') self.host = config.get('host', '0.0.0.0') self.port = config.get('port', 80) # Hypercorn self.task = None self.shutdown_event = asyncio.Event() self.hypercorn_config = Config() self.hypercorn_config.bind = ['{}:{}'.format(self.host, self.port)] self.hypercorn_config.logconfig_dict = self.get_log_config() self.app = App(config_manager=self.config_manager)
def hypercorn_config_from_env() -> Config: """Fill in a hypercorn Config object with environment variables""" # this grabs all the attributes of the Config class used for configuration # skipping over methods and other unusable attributes config_attrs = [ config_attr for config_attr in dir(Config) if not config_attr.startswith("_") and not callable(getattr(Config, config_attr)) ] hypercorn_config = Config() for config_attr in config_attrs: if (config_value := os.getenv(config_attr.upper())) is not None: setattr(hypercorn_config, config_attr, config_value)
async def test_spawn_app(event_loop: asyncio.AbstractEventLoop) -> None: async def _echo_app(scope: dict, receive: Callable, send: Callable) -> None: while True: message = await receive() if message is None: return await send(message) app_queue: asyncio.Queue = asyncio.Queue() put = await spawn_app(_echo_app, event_loop, Config(), {"asgi": {}}, app_queue.put) await put({"type": "message"}) assert (await app_queue.get()) == {"type": "message"} await put(None)
def run(bind, port, shutdown_trigger: Optional[Event] = None, use_uvloop: bool = True): if use_uvloop: logger.debug('Using uvloop') asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) loop = asyncio.get_event_loop() shutdown_trigger = shutdown_trigger or asyncio.Event() def _signal_handler(*_): print('') logger.warning('Shutting down web server') shutdown_trigger.set() for signal in (SIGHUP, SIGTERM, SIGINT): loop.add_signal_handler(signal, _signal_handler) hyper_config = HypercornConfig() hyper_config.bind = [f'{bind}:{port}'] logger.info('Starting web server') loop.run_until_complete(hypercorn_serve( app, hyper_config, shutdown_trigger=shutdown_trigger.wait))
def run(campaign, session_manager, port=80): app = create_app(campaign, session_manager) shutdown_event = asyncio.Event() def _signal_handler(*_): shutdown_event.set() def _exception_handler(context): exception = context.get("exception") shutdown_event.set() raise (exception) loop = asyncio.get_event_loop() loop.set_exception_handler(_exception_handler) if is_posix(): loop.add_signal_handler(signal.SIGINT, _signal_handler) loop.add_signal_handler(signal.SIGTERM, _signal_handler) config = Config() config.bind = ["0.0.0.0:" + str(port)] loop.run_until_complete( serve(app, config, shutdown_trigger=shutdown_event.wait))
async def startQuart(si): print(f"{si['serviceName']}({si['instanceID']})(v{si['serviceVersion']}) running at {si['serviceIP']}:{si['servicePort']}") config = Config() config.bind = [f"{si['serviceIP']}:{si['servicePort']}"] # config.bind = [f"0.0.0.0:{si['servicePort']}"] config.access_log_format = '%(h)s %(r)s %(s)s %(b)s %(D)s' config.accesslog = create_serving_logger() config.errorlog = config.accesslog loop = asyncio.get_event_loop() await loop.create_task(serve(app, config))
async def test_http1_request(nursery: trio._core._run.Nursery) -> None: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.socket = MockSocket() server = TCPServer(sanity_framework, Config(), WorkerContext(), server_stream) nursery.start_soon(server.run) client = h11.Connection(h11.CLIENT) await client_stream.send_all( client.send( h11.Request( method="POST", target="/", headers=[ (b"host", b"hypercorn"), (b"connection", b"close"), (b"content-length", b"%d" % len(SANITY_BODY)), ], ))) await client_stream.send_all(client.send(h11.Data(data=SANITY_BODY))) await client_stream.send_all(client.send(h11.EndOfMessage())) events = [] while True: event = client.next_event() if event == h11.NEED_DATA: # bytes cast is key otherwise b"" is lost data = bytes(await client_stream.receive_some(1024)) client.receive_data(data) elif isinstance(event, h11.ConnectionClosed): break else: events.append(event) assert events == [ h11.Response( status_code=200, headers=[ (b"content-length", b"15"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h11"), (b"connection", b"close"), ], http_version=b"1.1", reason=b"", ), h11.Data(data=b"Hello & Goodbye"), h11.EndOfMessage(headers=[]), # type: ignore ]
async def test_asgi_send_invalid_http_message(status: Any, headers: Any, body: Any) -> None: server = WebsocketMixin() server.config = Config() server.start_time = 0.0 server.state = ASGIWebsocketState.HANDSHAKE server.scope = {"method": "GET"} with pytest.raises((TypeError, ValueError)): await server.asgi_send({ "type": "websocket.http.response.start", "headers": headers, "status": status }) await server.asgi_send({ "type": "websocket.http.response.body", "body": body })
async def test_http_server_drain(event_loop: asyncio.AbstractEventLoop) -> None: transport = MockTransport() server = HTTPServer(event_loop, Config(), transport, "") # type: ignore server.pause_writing() async def write() -> None: server.write(b"Pre drain") await server.drain() server.write(b"Post drain") asyncio.ensure_future(write()) await transport.updated.wait() assert bytes(transport.data) == b"Pre drain" transport.clear() server.resume_writing() await transport.updated.wait() assert bytes(transport.data) == b"Post drain"
def __init__( self, event_loop: asyncio.AbstractEventLoop, *, framework: Type[ASGIFramework] = WebsocketFramework, path: str = '/ws', ) -> None: self.transport = MockTransport() self.server = WebsocketServer( framework, event_loop, Config(), self.transport) # type: ignore # noqa: E501 self.connection = wsproto.connection.WSConnection( wsproto.connection.CLIENT, host='hypercorn.com', resource=path, ) self.server.data_received(self.connection.bytes_to_send())
async def test_spawn_app(event_loop: asyncio.AbstractEventLoop, http_scope: HTTPScope) -> None: async def _echo_app(scope: Scope, receive: Callable, send: Callable) -> None: while True: message = await receive() if message is None: return await send(message) app_queue: asyncio.Queue = asyncio.Queue() async with TaskGroup(event_loop) as task_group: put = await task_group.spawn_app(_echo_app, Config(), http_scope, app_queue.put) await put({"type": "http.disconnect"}) # type: ignore assert (await app_queue.get()) == {"type": "http.disconnect"} await put(None)