A simple web server used for running Socket.IO servers in tests. :param sio: a Socket.IO server instance. Note 1: This class is not production-ready and is intended for testing. Note 2: This class only supports the "asgi" async_mode.
| 6 | |
| 7 | |
| 8 | class SocketIOWebServer: |
| 9 | """A simple web server used for running Socket.IO servers in tests. |
| 10 | |
| 11 | :param sio: a Socket.IO server instance. |
| 12 | |
| 13 | Note 1: This class is not production-ready and is intended for testing. |
| 14 | Note 2: This class only supports the "asgi" async_mode. |
| 15 | """ |
| 16 | def __init__(self, sio, on_shutdown=None): |
| 17 | if sio.async_mode != 'asgi': |
| 18 | raise ValueError('The async_mode must be "asgi"') |
| 19 | |
| 20 | async def http_app(scope, receive, send): |
| 21 | await send({'type': 'http.response.start', |
| 22 | 'status': 200, |
| 23 | 'headers': [('Content-Type', 'text/plain')]}) |
| 24 | await send({'type': 'http.response.body', |
| 25 | 'body': b'OK'}) |
| 26 | |
| 27 | self.sio = sio |
| 28 | self.app = socketio.ASGIApp(sio, http_app, on_shutdown=on_shutdown) |
| 29 | self.httpd = None |
| 30 | self.thread = None |
| 31 | |
| 32 | def start(self, port=8900): |
| 33 | """Start the web server. |
| 34 | |
| 35 | :param port: the port to listen on. Defaults to 8900. |
| 36 | |
| 37 | The server is started in a background thread. |
| 38 | """ |
| 39 | self.httpd = uvicorn.Server(config=uvicorn.Config(self.app, port=port)) |
| 40 | self.thread = threading.Thread(target=self.httpd.run) |
| 41 | self.thread.start() |
| 42 | |
| 43 | # wait for the server to start |
| 44 | while True: |
| 45 | try: |
| 46 | r = requests.get(f'http://localhost:{port}/') |
| 47 | r.raise_for_status() |
| 48 | if r.text == 'OK': |
| 49 | break |
| 50 | except: |
| 51 | time.sleep(0.1) |
| 52 | |
| 53 | def stop(self): |
| 54 | """Stop the web server.""" |
| 55 | self.httpd.should_exit = True |
| 56 | self.thread.join() |
| 57 | self.thread = None |