diff options
| author | 2022-11-18 13:42:59 +0100 | |
|---|---|---|
| committer | 2022-11-18 13:42:59 +0100 | |
| commit | d487ef52521646df62f442d214d62f9ef4696cd0 (patch) | |
| tree | c0b01ce29a6f823085a0ac8ec437539a9c5687ee | |
| parent | f6efa377a68f4997b7b4bdd1bd739c2a29562b31 (diff) | |
| download | client-d487ef52521646df62f442d214d62f9ef4696cd0.tar.gz client-d487ef52521646df62f442d214d62f9ef4696cd0.tar.bz2 client-d487ef52521646df62f442d214d62f9ef4696cd0.zip | |
Handle client connections gracefully
| -rw-r--r-- | .pre-commit-config.yaml | 6 | ||||
| -rw-r--r-- | ttun/client.py | 51 |
2 files changed, 34 insertions, 23 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c5c9a1e..5cecfd3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml | |||
| @@ -6,19 +6,19 @@ repos: | |||
| 6 | - id: end-of-file-fixer | 6 | - id: end-of-file-fixer |
| 7 | - id: trailing-whitespace | 7 | - id: trailing-whitespace |
| 8 | - repo: https://github.com/psf/black | 8 | - repo: https://github.com/psf/black |
| 9 | rev: 21.12b0 | 9 | rev: 22.10.0 |
| 10 | hooks: | 10 | hooks: |
| 11 | - id: black | 11 | - id: black |
| 12 | types_or: | 12 | types_or: |
| 13 | - python | 13 | - python |
| 14 | - repo: https://github.com/asottile/reorder_python_imports | 14 | - repo: https://github.com/asottile/reorder_python_imports |
| 15 | rev: v2.7.1 | 15 | rev: v3.9.0 |
| 16 | hooks: | 16 | hooks: |
| 17 | - id: reorder-python-imports | 17 | - id: reorder-python-imports |
| 18 | types_or: | 18 | types_or: |
| 19 | - python | 19 | - python |
| 20 | - repo: https://github.com/pre-commit/mirrors-prettier | 20 | - repo: https://github.com/pre-commit/mirrors-prettier |
| 21 | rev: 'v2.5.1' | 21 | rev: v2.7.1 |
| 22 | hooks: | 22 | hooks: |
| 23 | - id: prettier | 23 | - id: prettier |
| 24 | types_or: | 24 | types_or: |
diff --git a/ttun/client.py b/ttun/client.py index 671ac59..6d05da7 100644 --- a/ttun/client.py +++ b/ttun/client.py | |||
| @@ -11,6 +11,8 @@ from typing import Optional | |||
| 11 | from uuid import uuid4 | 11 | from uuid import uuid4 |
| 12 | 12 | ||
| 13 | import websockets | 13 | import websockets |
| 14 | from aiohttp import ClientConnectionError | ||
| 15 | from aiohttp import ClientError | ||
| 14 | from aiohttp import ClientSession | 16 | from aiohttp import ClientSession |
| 15 | from aiohttp import DummyCookieJar | 17 | from aiohttp import DummyCookieJar |
| 16 | from websockets import WebSocketClientProtocol | 18 | from websockets import WebSocketClientProtocol |
| @@ -66,14 +68,14 @@ class Client: | |||
| 66 | while True: | 68 | while True: |
| 67 | try: | 69 | try: |
| 68 | request: RequestData = await self.receive() | 70 | request: RequestData = await self.receive() |
| 69 | await self.proxyRequest( | 71 | await self.proxy_request( |
| 70 | request=request, on_response=lambda response: self.send(response) | 72 | request=request, on_response=lambda response: self.send(response) |
| 71 | ) | 73 | ) |
| 72 | 74 | ||
| 73 | except ConnectionClosed: | 75 | except ConnectionClosed: |
| 74 | break | 76 | break |
| 75 | 77 | ||
| 76 | async def proxyRequest( | 78 | async def proxy_request( |
| 77 | self, | 79 | self, |
| 78 | request: RequestData, | 80 | request: RequestData, |
| 79 | on_response: Callable[[ResponseData], Awaitable] = None, | 81 | on_response: Callable[[ResponseData], Awaitable] = None, |
| @@ -92,24 +94,33 @@ class Client: | |||
| 92 | ) | 94 | ) |
| 93 | 95 | ||
| 94 | start = perf_counter() | 96 | start = perf_counter() |
| 95 | response = await session.request( | 97 | try: |
| 96 | method=request["method"], | 98 | response = await session.request( |
| 97 | url=f'http://localhost:{self.port}{request["path"]}', | 99 | method=request["method"], |
| 98 | headers=request["headers"], | 100 | url=f'http://localhost:{self.port}{request["path"]}', |
| 99 | data=b64decode(request["body"].encode()), | 101 | headers=request["headers"], |
| 100 | allow_redirects=False, | 102 | data=b64decode(request["body"].encode()), |
| 101 | ) | 103 | allow_redirects=False, |
| 102 | end = perf_counter() | 104 | ) |
| 103 | 105 | end = perf_counter() | |
| 104 | response_data = ResponseData( | 106 | |
| 105 | status=response.status, | 107 | response_data = ResponseData( |
| 106 | headers=[ | 108 | status=response.status, |
| 107 | (key, value) | 109 | headers=[ |
| 108 | for key, value in response.headers.items() | 110 | (key, value) |
| 109 | if key.lower() not in ["transfer-encoding", "content-encoding"] | 111 | for key, value in response.headers.items() |
| 110 | ], | 112 | if key.lower() not in ["transfer-encoding", "content-encoding"] |
| 111 | body=b64encode(await response.read()).decode(), | 113 | ], |
| 112 | ) | 114 | body=b64encode(await response.read()).decode(), |
| 115 | ) | ||
| 116 | except ClientError as e: | ||
| 117 | end = perf_counter() | ||
| 118 | |||
| 119 | response_data = ResponseData( | ||
| 120 | status=(504 if isinstance(e, ClientConnectionError) else 502), | ||
| 121 | headers=[("content-type", "text/plain")], | ||
| 122 | body=b64encode(str(e).encode()).decode(), | ||
| 123 | ) | ||
| 113 | 124 | ||
| 114 | if on_response is not None: | 125 | if on_response is not None: |
| 115 | await on_response(response_data) | 126 | await on_response(response_data) |
