summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom van der Lee <tom@vanderlee.io>2026-06-10 08:31:30 +0200
committerGravatar Tom van der Lee <tom@vanderlee.io>2026-06-10 08:31:30 +0200
commitc4f33b3576e3a4a7f70b3d681fadae45f73ae31e (patch)
tree542ea2b5858fce5ae4f3c36706a73929db5a96e6
parentb8d5952b83e7601c5df646efd976879f0dbd30c2 (diff)
downloadserver-main.tar.gz
server-main.tar.bz2
server-main.zip
Added support for multiple domains within one sessionv2.3.0main
-rw-r--r--ttun_server/endpoints.py4
-rw-r--r--ttun_server/websockets.py52
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
7from starlette.responses import Response 7from starlette.responses import Response
8 8
9from ttun_server.proxy_queue import ProxyQueue 9from ttun_server.proxy_queue import ProxyQueue
10from ttun_server.types import HttpRequestData, Message, HttpMessageType, HttpMessage 10from ttun_server.types import HttpRequestData, HttpMessageType, HttpMessage
11 11
12logger = logging.getLogger(__name__) 12logger = 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()