diff options
| author | 2026-06-10 08:31:30 +0200 | |
|---|---|---|
| committer | 2026-06-10 08:31:30 +0200 | |
| commit | c4f33b3576e3a4a7f70b3d681fadae45f73ae31e (patch) | |
| tree | 542ea2b5858fce5ae4f3c36706a73929db5a96e6 | |
| parent | b8d5952b83e7601c5df646efd976879f0dbd30c2 (diff) | |
| download | server-c4f33b3576e3a4a7f70b3d681fadae45f73ae31e.tar.gz server-c4f33b3576e3a4a7f70b3d681fadae45f73ae31e.tar.bz2 server-c4f33b3576e3a4a7f70b3d681fadae45f73ae31e.zip | |
| -rw-r--r-- | ttun_server/endpoints.py | 4 | ||||
| -rw-r--r-- | ttun_server/websockets.py | 52 |
2 files changed, 34 insertions, 22 deletions
diff --git a/ttun_server/endpoints.py b/ttun_server/endpoints.py index eae0ebe..fa5e7e7 100644 --- a/ttun_server/endpoints.py +++ b/ttun_server/endpoints.py | |||
| @@ -7,7 +7,7 @@ from starlette.requests import Request | |||
| 7 | from starlette.responses import Response | 7 | from starlette.responses import Response |
| 8 | 8 | ||
| 9 | from ttun_server.proxy_queue import ProxyQueue | 9 | from ttun_server.proxy_queue import ProxyQueue |
| 10 | from ttun_server.types import HttpRequestData, Message, HttpMessageType, HttpMessage | 10 | from ttun_server.types import HttpRequestData, HttpMessageType, HttpMessage |
| 11 | 11 | ||
| 12 | logger = logging.getLogger(__name__) | 12 | logger = logging.getLogger(__name__) |
| 13 | 13 | ||
| @@ -29,7 +29,7 @@ class Proxy(HTTPEndpoint): | |||
| 29 | response = Response(content='Not Found', status_code=404) | 29 | response = Response(content='Not Found', status_code=404) |
| 30 | 30 | ||
| 31 | identifier = str(uuid4()) | 31 | identifier = str(uuid4()) |
| 32 | response_queue = await ProxyQueue.create_for_identifier(f'{subdomain}_{identifier}') | 32 | response_queue = await ProxyQueue.create_for_identifier(identifier) |
| 33 | 33 | ||
| 34 | try: | 34 | try: |
| 35 | 35 | ||
diff --git a/ttun_server/websockets.py b/ttun_server/websockets.py index f80359f..e828b8d 100644 --- a/ttun_server/websockets.py +++ b/ttun_server/websockets.py | |||
| @@ -40,7 +40,7 @@ class WebsocketProxy(WebSocketEndpoint): | |||
| 40 | await request_queue.enqueue(message) | 40 | await request_queue.enqueue(message) |
| 41 | 41 | ||
| 42 | if expect_ack: | 42 | if expect_ack: |
| 43 | response_queue = await ProxyQueue.create_for_identifier(f'{subdomain}_{message["identifier"]}') | 43 | response_queue = await ProxyQueue.create_for_identifier(message["identifier"]) |
| 44 | yield await response_queue.dequeue() | 44 | yield await response_queue.dequeue() |
| 45 | await response_queue.delete() | 45 | await response_queue.delete() |
| 46 | else: | 46 | else: |
| @@ -49,10 +49,7 @@ class WebsocketProxy(WebSocketEndpoint): | |||
| 49 | yield None | 49 | yield None |
| 50 | 50 | ||
| 51 | async def listen_for_messages(self, websocket: WebSocket): | 51 | async def listen_for_messages(self, websocket: WebSocket): |
| 52 | [subdomain, *_] = websocket.url.hostname.split('.') | 52 | response_queue = await ProxyQueue.create_for_identifier(self.id) |
| 53 | |||
| 54 | print('listen', self.id) | ||
| 55 | response_queue = await ProxyQueue.create_for_identifier(f'{subdomain}_{self.id}') | ||
| 56 | 53 | ||
| 57 | while True: | 54 | while True: |
| 58 | message: WebsocketMessage = await response_queue.dequeue() | 55 | message: WebsocketMessage = await response_queue.dequeue() |
| @@ -122,12 +119,14 @@ class Tunnel(WebSocketEndpoint): | |||
| 122 | 119 | ||
| 123 | def __init__(self, scope: Scope, receive: Receive, send: Send): | 120 | def __init__(self, scope: Scope, receive: Receive, send: Send): |
| 124 | super().__init__(scope, receive, send) | 121 | super().__init__(scope, receive, send) |
| 125 | self.request_task = None | 122 | self.request_tasks: dict[str, asyncio.Task] = {} |
| 126 | self.config: Optional[Config] = None | 123 | self.config: Optional[Config] = None |
| 124 | self.proxy_queues: dict[str, ProxyQueue] = {} | ||
| 125 | |||
| 126 | async def handle_requests(self, websocket: WebSocket, subdomain: str): | ||
| 127 | while request := await self.proxy_queues[subdomain].dequeue(): | ||
| 128 | task = asyncio.create_task(websocket.send_json(request), name=request['identifier']) | ||
| 127 | 129 | ||
| 128 | async def handle_requests(self, websocket: WebSocket): | ||
| 129 | while request := await self.proxy_queue.dequeue(): | ||
| 130 | create_task(websocket.send_json(request)) | ||
| 131 | 130 | ||
| 132 | async def on_connect(self, websocket: WebSocket) -> None: | 131 | async def on_connect(self, websocket: WebSocket) -> None: |
| 133 | await websocket.accept() | 132 | await websocket.accept() |
| @@ -146,32 +145,45 @@ class Tunnel(WebSocketEndpoint): | |||
| 146 | if client_major > server_major: | 145 | if client_major > server_major: |
| 147 | await websocket.close(4001, 'Your client is too new') | 146 | await websocket.close(4001, 'Your client is too new') |
| 148 | 147 | ||
| 148 | if 'subdomains' not in self.config: | ||
| 149 | self.config['subdomains'] = [self.config['subdomain']] | ||
| 150 | elif self.config['subdomains'] is None: | ||
| 151 | self.config['subdomains'] = [None] | ||
| 149 | 152 | ||
| 150 | if self.config['subdomain'] is None \ | 153 | for i, subdomain in enumerate(self.config['subdomains']): |
| 151 | or await ProxyQueue.has_connection(self.config['subdomain']): | 154 | if subdomain is None or await ProxyQueue.has_connection(subdomain): |
| 152 | self.config['subdomain'] = uuid4().hex | 155 | self.config['subdomains'][i] = uuid4().hex |
| 153 | 156 | ||
| 154 | 157 | for subdomain in self.config['subdomains']: | |
| 155 | self.proxy_queue = await ProxyQueue.create_for_identifier(self.config['subdomain']) | 158 | self.proxy_queues[subdomain] = await ProxyQueue.create_for_identifier(subdomain) |
| 156 | 159 | ||
| 157 | hostname = os.environ.get("TUNNEL_DOMAIN") | 160 | hostname = os.environ.get("TUNNEL_DOMAIN") |
| 158 | protocol = "https" if os.environ.get("SECURE", False) else "http" | 161 | protocol = "https" if os.environ.get("SECURE", False) else "http" |
| 159 | 162 | ||
| 163 | urls = [ | ||
| 164 | f'{protocol}://{subdomain}.{hostname}' | ||
| 165 | for subdomain in self.config['subdomains'] | ||
| 166 | ] | ||
| 167 | |||
| 160 | await websocket.send_json({ | 168 | await websocket.send_json({ |
| 161 | 'url': f'{protocol}://{self.config["subdomain"]}.{hostname}' | 169 | 'url': urls[0], |
| 170 | 'urls': urls, | ||
| 162 | }) | 171 | }) |
| 163 | 172 | ||
| 164 | self.request_task = asyncio.create_task(self.handle_requests(websocket)) | 173 | for subdomain in self.config['subdomains']: |
| 174 | self.request_tasks[subdomain] = asyncio.create_task(self.handle_requests(websocket, subdomain), name=subdomain) | ||
| 165 | 175 | ||
| 166 | async def on_receive(self, websocket: WebSocket, data: Message): | 176 | async def on_receive(self, websocket: WebSocket, data: Message): |
| 167 | try: | 177 | try: |
| 168 | response_queue = await ProxyQueue.get_for_identifier(f"{self.config['subdomain']}_{data['identifier']}") | 178 | data['type'] = MessageType(data['type']).value |
| 179 | response_queue = await ProxyQueue.get_for_identifier(data['identifier']) | ||
| 169 | await response_queue.enqueue(data) | 180 | await response_queue.enqueue(data) |
| 170 | except AssertionError: | 181 | except AssertionError: |
| 171 | pass | 182 | pass |
| 172 | 183 | ||
| 173 | async def on_disconnect(self, websocket: WebSocket, close_code: int): | 184 | async def on_disconnect(self, websocket: WebSocket, close_code: int): |
| 174 | await self.proxy_queue.delete() | 185 | for proxy_queue in self.proxy_queues.values(): |
| 186 | await proxy_queue.delete() | ||
| 175 | 187 | ||
| 176 | if self.request_task is not None: | 188 | for request_task in self.request_tasks.values(): |
| 177 | self.request_task.cancel() | 189 | request_task.cancel() |
