diff options
| -rw-r--r-- | .gitignore | 98 | ||||
| -rw-r--r-- | ttun/__main__.py | 85 |
2 files changed, 173 insertions, 10 deletions
| @@ -1,6 +1,98 @@ | |||
| 1 | 1 | ||
| 2 | # Created by https://www.toptal.com/developers/gitignore/api/python | 2 | # Created by https://www.toptal.com/developers/gitignore/api/python,pycharm+all |
| 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python | 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm+all |
| 4 | |||
| 5 | ### PyCharm+all ### | ||
| 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider | ||
| 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | ||
| 8 | |||
| 9 | # User-specific stuff | ||
| 10 | .idea/**/workspace.xml | ||
| 11 | .idea/**/tasks.xml | ||
| 12 | .idea/**/usage.statistics.xml | ||
| 13 | .idea/**/dictionaries | ||
| 14 | .idea/**/shelf | ||
| 15 | |||
| 16 | # AWS User-specific | ||
| 17 | .idea/**/aws.xml | ||
| 18 | |||
| 19 | # Generated files | ||
| 20 | .idea/**/contentModel.xml | ||
| 21 | |||
| 22 | # Sensitive or high-churn files | ||
| 23 | .idea/**/dataSources/ | ||
| 24 | .idea/**/dataSources.ids | ||
| 25 | .idea/**/dataSources.local.xml | ||
| 26 | .idea/**/sqlDataSources.xml | ||
| 27 | .idea/**/dynamic.xml | ||
| 28 | .idea/**/uiDesigner.xml | ||
| 29 | .idea/**/dbnavigator.xml | ||
| 30 | |||
| 31 | # Gradle | ||
| 32 | .idea/**/gradle.xml | ||
| 33 | .idea/**/libraries | ||
| 34 | |||
| 35 | # Gradle and Maven with auto-import | ||
| 36 | # When using Gradle or Maven with auto-import, you should exclude module files, | ||
| 37 | # since they will be recreated, and may cause churn. Uncomment if using | ||
| 38 | # auto-import. | ||
| 39 | # .idea/artifacts | ||
| 40 | # .idea/compiler.xml | ||
| 41 | # .idea/jarRepositories.xml | ||
| 42 | # .idea/modules.xml | ||
| 43 | # .idea/*.iml | ||
| 44 | # .idea/modules | ||
| 45 | # *.iml | ||
| 46 | # *.ipr | ||
| 47 | |||
| 48 | # CMake | ||
| 49 | cmake-build-*/ | ||
| 50 | |||
| 51 | # Mongo Explorer plugin | ||
| 52 | .idea/**/mongoSettings.xml | ||
| 53 | |||
| 54 | # File-based project format | ||
| 55 | *.iws | ||
| 56 | |||
| 57 | # IntelliJ | ||
| 58 | out/ | ||
| 59 | |||
| 60 | # mpeltonen/sbt-idea plugin | ||
| 61 | .idea_modules/ | ||
| 62 | |||
| 63 | # JIRA plugin | ||
| 64 | atlassian-ide-plugin.xml | ||
| 65 | |||
| 66 | # Cursive Clojure plugin | ||
| 67 | .idea/replstate.xml | ||
| 68 | |||
| 69 | # Crashlytics plugin (for Android Studio and IntelliJ) | ||
| 70 | com_crashlytics_export_strings.xml | ||
| 71 | crashlytics.properties | ||
| 72 | crashlytics-build.properties | ||
| 73 | fabric.properties | ||
| 74 | |||
| 75 | # Editor-based Rest Client | ||
| 76 | .idea/httpRequests | ||
| 77 | |||
| 78 | # Android studio 3.1+ serialized cache file | ||
| 79 | .idea/caches/build_file_checksums.ser | ||
| 80 | |||
| 81 | ### PyCharm+all Patch ### | ||
| 82 | # Ignores the whole .idea folder and all .iml files | ||
| 83 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 | ||
| 84 | |||
| 85 | .idea/ | ||
| 86 | |||
| 87 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 | ||
| 88 | |||
| 89 | *.iml | ||
| 90 | modules.xml | ||
| 91 | .idea/misc.xml | ||
| 92 | *.ipr | ||
| 93 | |||
| 94 | # Sonarlint plugin | ||
| 95 | .idea/sonarlint | ||
| 4 | 96 | ||
| 5 | ### Python ### | 97 | ### Python ### |
| 6 | # Byte-compiled / optimized / DLL files | 98 | # Byte-compiled / optimized / DLL files |
| @@ -142,4 +234,4 @@ dmypy.json | |||
| 142 | # Cython debug symbols | 234 | # Cython debug symbols |
| 143 | cython_debug/ | 235 | cython_debug/ |
| 144 | 236 | ||
| 145 | # End of https://www.toptal.com/developers/gitignore/api/python | 237 | # End of https://www.toptal.com/developers/gitignore/api/python,pycharm+all |
diff --git a/ttun/__main__.py b/ttun/__main__.py index 36e074e..5984286 100644 --- a/ttun/__main__.py +++ b/ttun/__main__.py | |||
| @@ -1,17 +1,88 @@ | |||
| 1 | import asyncio | 1 | import asyncio |
| 2 | import json | 2 | import json |
| 3 | from dataclasses import dataclass | ||
| 4 | from functools import wraps | ||
| 5 | from typing import Coroutine, Callable, TypedDict | ||
| 3 | 6 | ||
| 4 | import websockets | 7 | import websockets |
| 5 | from websockets import WebSocketClientProtocol | 8 | from websockets import WebSocketClientProtocol |
| 9 | from websockets.exceptions import ConnectionClosed | ||
| 6 | 10 | ||
| 7 | async def test(): | ||
| 8 | async with websockets.connect('ws://localhost:8000/tunnel/') as websocket: | ||
| 9 | ws: WebSocketClientProtocol = websocket | ||
| 10 | 11 | ||
| 11 | await ws.send(json.dumps({ 'Lol': 'Test' })) | 12 | class Message(TypedDict): |
| 13 | type: str | ||
| 12 | 14 | ||
| 15 | |||
| 16 | class Request(TypedDict): | ||
| 17 | path: str | ||
| 18 | |||
| 19 | |||
| 20 | class Reply(TypedDict): | ||
| 21 | path: str | ||
| 22 | |||
| 23 | |||
| 24 | @dataclass | ||
| 25 | class Client: | ||
| 26 | connection = WebSocketClientProtocol | ||
| 27 | |||
| 28 | async def send(self, data: dict): | ||
| 29 | await self.connection.send(json.dumps(data)) | ||
| 30 | |||
| 31 | async def receive(self) -> dict: | ||
| 32 | return json.loads(await self.connection.recv()) | ||
| 33 | |||
| 34 | @staticmethod | ||
| 35 | def loop(sleep: int = None): | ||
| 36 | async def wrapper(callback: Callable[[], Coroutine]): | ||
| 37 | while True: | ||
| 38 | try: | ||
| 39 | await callback() | ||
| 40 | |||
| 41 | if sleep is not None: | ||
| 42 | await asyncio.sleep(sleep) | ||
| 43 | |||
| 44 | except ConnectionClosed: | ||
| 45 | break | ||
| 46 | return wrapper | ||
| 47 | |||
| 48 | async def connect(self) -> WebSocketClientProtocol: | ||
| 49 | self.connection = await websockets.connect('ws://localhost:8000/tunnel/') | ||
| 50 | |||
| 51 | await self.send({ | ||
| 52 | 'type': 'config', | ||
| 53 | 'config': { | ||
| 54 | 'subdomain': 'test' | ||
| 55 | } | ||
| 56 | }) | ||
| 57 | |||
| 58 | config = await self.receive() | ||
| 59 | print(config) | ||
| 60 | |||
| 61 | if self.connection.open: | ||
| 62 | return self.connection | ||
| 63 | |||
| 64 | async def handle_messages(self): | ||
| 13 | while True: | 65 | while True: |
| 14 | data = await ws.recv() | 66 | try: |
| 15 | print(data) | 67 | message: Request = await self.receive() |
| 68 | print(message) | ||
| 69 | await self.connection.send(json.dumps({ | ||
| 70 | 'type': 'response', | ||
| 71 | 'response': { | ||
| 72 | 'test': 'Test' | ||
| 73 | } | ||
| 74 | })) | ||
| 75 | |||
| 76 | except ConnectionClosed: | ||
| 77 | break | ||
| 78 | |||
| 79 | client = Client() | ||
| 80 | loop = asyncio.get_event_loop() | ||
| 81 | |||
| 82 | connection = loop.run_until_complete(client.connect()) | ||
| 83 | |||
| 84 | loop.run_until_complete(asyncio.wait([ | ||
| 85 | asyncio.ensure_future(client.handle_messages()), | ||
| 86 | # asyncio.ensure_future(client.heartbeat()) | ||
| 87 | ])) | ||
| 16 | 88 | ||
| 17 | asyncio.run(test()) | ||
