GNServer 0.0.0.0.28__py3-none-any.whl → 0.0.0.0.30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- GNServer/_app.py +42 -26
- GNServer/_client.py +67 -56
- {gnserver-0.0.0.0.28.dist-info → gnserver-0.0.0.0.30.dist-info}/METADATA +1 -1
- gnserver-0.0.0.0.30.dist-info/RECORD +9 -0
- gnserver-0.0.0.0.28.dist-info/RECORD +0 -9
- {gnserver-0.0.0.0.28.dist-info → gnserver-0.0.0.0.30.dist-info}/WHEEL +0 -0
- {gnserver-0.0.0.0.28.dist-info → gnserver-0.0.0.0.30.dist-info}/licenses/LICENSE +0 -0
- {gnserver-0.0.0.0.28.dist-info → gnserver-0.0.0.0.30.dist-info}/top_level.txt +0 -0
GNServer/_app.py
CHANGED
@@ -45,105 +45,121 @@ console = logging.StreamHandler()
|
|
45
45
|
console.setLevel(logging.INFO)
|
46
46
|
console.setFormatter(logging.Formatter("[GNServer] %(name)s: %(levelname)s: %(message)s"))
|
47
47
|
|
48
|
-
|
48
|
+
PayloadType = Optional[Union[int, str, list, tuple, dict]]
|
49
49
|
|
50
50
|
class _BaseEXception(Exception):
|
51
|
-
def __init__(self, code: str,
|
51
|
+
def __init__(self, code: str, name="", message: Optional[str] = None, payload: PayloadType = None):
|
52
52
|
self._code = code
|
53
|
+
self._name = name
|
53
54
|
self._message = message
|
55
|
+
self._payload = payload
|
54
56
|
|
55
57
|
def assembly(self):
|
56
58
|
"""
|
57
59
|
Собирает ошибку в ответ типа `GNResponse`
|
58
60
|
"""
|
59
|
-
|
61
|
+
payload: dict = {'name': self._name}
|
62
|
+
|
63
|
+
if self._message is not None:
|
64
|
+
payload['message'] = self._message
|
65
|
+
|
66
|
+
if self._payload is not None:
|
67
|
+
payload['payload'] = self._payload
|
68
|
+
|
69
|
+
return gn.GNResponse(f'gn:error:{self._code}', payload=payload)
|
60
70
|
|
61
71
|
|
62
72
|
class GNExceptions:
|
63
73
|
class UnprocessableEntity(_BaseEXception):
|
64
|
-
def __init__(self):
|
74
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
65
75
|
"""
|
66
76
|
# Некорректные данные
|
67
77
|
"""
|
68
|
-
super().__init__('422', "Unprocessable Entity")
|
78
|
+
super().__init__('422', "Unprocessable Entity", message=message, payload=payload)
|
69
79
|
|
70
80
|
class BadRequest(_BaseEXception):
|
71
|
-
def __init__(self):
|
81
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
72
82
|
"""
|
73
83
|
# неправильного синтаксис url или параметров
|
74
84
|
"""
|
75
|
-
super().__init__('400', "Bad Request")
|
85
|
+
super().__init__('400', "Bad Request", message=message, payload=payload)
|
76
86
|
|
77
87
|
class Forbidden(_BaseEXception):
|
78
|
-
def __init__(self):
|
88
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
79
89
|
"""
|
80
90
|
# Доступ запрещён, даже при наличии авторизации
|
81
91
|
"""
|
82
|
-
super().__init__('403', "Forbidden")
|
83
|
-
|
92
|
+
super().__init__('403', "Forbidden", message=message, payload=payload)
|
93
|
+
|
94
|
+
class Unauthorized(_BaseEXception):
|
95
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
96
|
+
"""
|
97
|
+
# Требуется авторизация
|
98
|
+
"""
|
99
|
+
super().__init__('401', "Unauthorized", message=message, payload=payload)
|
84
100
|
|
85
101
|
class NotFound(_BaseEXception):
|
86
|
-
def __init__(self):
|
102
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
87
103
|
"""
|
88
104
|
# Ресурс не найден
|
89
105
|
"""
|
90
|
-
super().__init__('404', "Not Found")
|
106
|
+
super().__init__('404', "Not Found", message=message, payload=payload)
|
91
107
|
|
92
108
|
|
93
109
|
class MethodNotAllowed(_BaseEXception):
|
94
|
-
def __init__(self):
|
110
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
95
111
|
"""
|
96
112
|
# Метод запроса не поддерживается данным ресурсом
|
97
113
|
"""
|
98
|
-
super().__init__('405', "Method Not Allowed")
|
114
|
+
super().__init__('405', "Method Not Allowed", message=message, payload=payload)
|
99
115
|
|
100
116
|
|
101
117
|
class Conflict(_BaseEXception):
|
102
|
-
def __init__(self):
|
118
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
103
119
|
"""
|
104
120
|
# Конфликт состояния ресурса (например, дубликат)
|
105
121
|
"""
|
106
|
-
super().__init__('409', "Conflict")
|
122
|
+
super().__init__('409', "Conflict", message=message, payload=payload)
|
107
123
|
|
108
124
|
|
109
125
|
class InternalServerError(_BaseEXception):
|
110
|
-
def __init__(self):
|
126
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
111
127
|
"""
|
112
128
|
# Внутренняя ошибка сервера
|
113
129
|
"""
|
114
|
-
super().__init__('500', "Internal Server Error")
|
130
|
+
super().__init__('500', "Internal Server Error", message=message, payload=payload)
|
115
131
|
|
116
132
|
|
117
133
|
class NotImplemented(_BaseEXception):
|
118
|
-
def __init__(self):
|
134
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
119
135
|
"""
|
120
136
|
# Метод или функционал ещё не реализован
|
121
137
|
"""
|
122
|
-
super().__init__('501', "Not Implemented")
|
138
|
+
super().__init__('501', "Not Implemented", message=message, payload=payload)
|
123
139
|
|
124
140
|
|
125
141
|
class BadGateway(_BaseEXception):
|
126
|
-
def __init__(self):
|
142
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
127
143
|
"""
|
128
144
|
# Ошибка шлюза или прокси при обращении к апстриму
|
129
145
|
"""
|
130
|
-
super().__init__('502', "Bad Gateway")
|
146
|
+
super().__init__('502', "Bad Gateway", message=message, payload=payload)
|
131
147
|
|
132
148
|
|
133
149
|
class ServiceUnavailable(_BaseEXception):
|
134
|
-
def __init__(self):
|
150
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
135
151
|
"""
|
136
152
|
# Сервис временно недоступен
|
137
153
|
"""
|
138
|
-
super().__init__('503', "Service Unavailable")
|
154
|
+
super().__init__('503', "Service Unavailable", message=message, payload=payload)
|
139
155
|
|
140
156
|
|
141
157
|
class GatewayTimeout(_BaseEXception):
|
142
|
-
def __init__(self):
|
158
|
+
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
143
159
|
"""
|
144
160
|
# Таймаут при обращении к апстриму
|
145
161
|
"""
|
146
|
-
super().__init__('504', "Gateway Timeout")
|
162
|
+
super().__init__('504', "Gateway Timeout", message=message, payload=payload)
|
147
163
|
|
148
164
|
def guess_type(filename: str) -> str:
|
149
165
|
"""
|
GNServer/_client.py
CHANGED
@@ -1,34 +1,36 @@
|
|
1
|
-
|
2
|
-
import os
|
3
1
|
import httpx
|
4
2
|
import asyncio
|
5
|
-
import typing as _typing
|
6
3
|
import logging as logging2
|
7
4
|
from typing import Union, List, Dict, Tuple, Optional
|
8
5
|
import datetime
|
9
6
|
logging2.basicConfig(level=logging2.INFO)
|
10
|
-
|
11
7
|
from KeyisBLogging import logging
|
12
|
-
from typing import Dict, List, Tuple, Optional,
|
8
|
+
from typing import Dict, List, Tuple, Optional, AsyncGenerator, Callable, Literal, AsyncIterable
|
13
9
|
from itertools import count
|
14
10
|
from aioquic.asyncio.client import connect
|
15
11
|
from aioquic.asyncio.protocol import QuicConnectionProtocol
|
16
|
-
from aioquic.h3.connection import H3_ALPN, H3Connection
|
17
|
-
from aioquic.h3.events import DataReceived, DatagramReceived, H3Event, HeadersReceived
|
18
12
|
from aioquic.quic.configuration import QuicConfiguration
|
19
13
|
from aioquic.quic.events import QuicEvent
|
14
|
+
from aioquic.asyncio.protocol import QuicConnectionProtocol
|
15
|
+
from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset
|
16
|
+
import asyncio
|
17
|
+
from collections import deque
|
18
|
+
from typing import Dict, Deque, Tuple, Optional
|
19
|
+
|
20
|
+
import asyncio
|
21
|
+
import time
|
22
|
+
from collections import deque
|
23
|
+
from itertools import count
|
24
|
+
from typing import Deque, Dict, Optional, Tuple, Union
|
25
|
+
|
26
|
+
from aioquic.quic.configuration import QuicConfiguration
|
27
|
+
from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset, ConnectionTerminated
|
20
28
|
|
21
29
|
|
22
30
|
import time
|
23
|
-
import json, ssl, asyncio, struct, base64, hashlib
|
24
31
|
from typing import Any, Dict, Optional
|
25
|
-
import websockets
|
26
32
|
|
27
|
-
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
28
|
-
import os
|
29
|
-
import msgpack
|
30
33
|
import logging
|
31
|
-
from httpx import Request, Headers, URL
|
32
34
|
logging.basicConfig(level=logging.DEBUG)
|
33
35
|
logging.getLogger("websockets").setLevel(logging.DEBUG)
|
34
36
|
|
@@ -103,9 +105,15 @@ class GNExceptions:
|
|
103
105
|
from KeyisBClient.gn import GNRequest, GNResponse
|
104
106
|
from KeyisBTools import TTLDict
|
105
107
|
|
106
|
-
from KeyisBTools.cryptography.sign import
|
108
|
+
from KeyisBTools.cryptography.sign import S1
|
109
|
+
|
110
|
+
s1 = S1()
|
107
111
|
|
108
|
-
|
112
|
+
|
113
|
+
async def chain_async(first_item, rest: AsyncIterable) -> AsyncGenerator:
|
114
|
+
yield first_item
|
115
|
+
async for x in rest:
|
116
|
+
yield x
|
109
117
|
|
110
118
|
|
111
119
|
|
@@ -137,7 +145,7 @@ class AsyncClient:
|
|
137
145
|
self.__dns_client = AsyncClient()
|
138
146
|
|
139
147
|
if token is not None:
|
140
|
-
payload = {'sign': {'alg': 'KeyisB-c-s-m1', 'data':
|
148
|
+
payload = {'sign': {'alg': 'KeyisB-c-s-m1', 'data': s1.sign(token)}}
|
141
149
|
else:
|
142
150
|
payload = None
|
143
151
|
|
@@ -146,6 +154,9 @@ class AsyncClient:
|
|
146
154
|
if r1.command != 'ok':
|
147
155
|
raise GNExceptions.ConnectionError.dns.data
|
148
156
|
|
157
|
+
if r1.payload is None:
|
158
|
+
raise GNExceptions.ConnectionError.dns.data
|
159
|
+
|
149
160
|
r1_data = r1.payload
|
150
161
|
|
151
162
|
result = r1_data['ip'] + ':' + str(r1_data['port'])
|
@@ -176,7 +187,7 @@ class AsyncClient:
|
|
176
187
|
self.__dns_client = AsyncClient()
|
177
188
|
|
178
189
|
if token is not None:
|
179
|
-
payload = {'sign': {'alg': 'KeyisB-c-s-m1', 'data':
|
190
|
+
payload = {'sign': {'alg': 'KeyisB-c-s-m1', 'data': s1.sign(token)}}
|
180
191
|
else:
|
181
192
|
payload = None
|
182
193
|
|
@@ -185,6 +196,10 @@ class AsyncClient:
|
|
185
196
|
if r1.command != 'ok':
|
186
197
|
raise GNExceptions.ConnectionError.dns.data
|
187
198
|
|
199
|
+
|
200
|
+
if r1.payload is None:
|
201
|
+
raise GNExceptions.ConnectionError.dns.data
|
202
|
+
|
188
203
|
r1_data = r1.payload
|
189
204
|
|
190
205
|
result = r1_data['ip'] + ':' + str(r1_data['port'])
|
@@ -198,7 +213,7 @@ class AsyncClient:
|
|
198
213
|
except:
|
199
214
|
raise GNExceptions.ConnectionError.dns.connection
|
200
215
|
|
201
|
-
async def getDNS(self, domain: str, token: Optional[
|
216
|
+
async def getDNS(self, domain: str, token: Optional[bytes] = None, use_cache: bool = True, keep_alive: bool = False):
|
202
217
|
if domain.endswith(('.core', '.gw', '.gn', '.cdn', '.sys', '.gwis', '.abs')):
|
203
218
|
return await self.getCoreDNS(domain=domain, use_cache=use_cache, keep_alive=keep_alive)
|
204
219
|
else:
|
@@ -248,10 +263,10 @@ class AsyncClient:
|
|
248
263
|
def f(domain):
|
249
264
|
self._active_connections.pop(domain)
|
250
265
|
|
251
|
-
c._disconnect_signal = f
|
252
|
-
c._domain = domain
|
266
|
+
c._disconnect_signal = f # type: ignore
|
267
|
+
c._domain = domain # type: ignore
|
253
268
|
|
254
|
-
await c.connect(data[0], data[1], keep_alive=keep_alive)
|
269
|
+
await c.connect(data[0], int(data[1]), keep_alive=keep_alive)
|
255
270
|
await c.connect_future
|
256
271
|
|
257
272
|
return c
|
@@ -285,23 +300,37 @@ class AsyncClient:
|
|
285
300
|
|
286
301
|
return r
|
287
302
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
303
|
+
else:
|
304
|
+
c: Optional[QuicClient] = None
|
305
|
+
|
306
|
+
async def wrapped(request) -> AsyncGenerator[GNRequest, None]:
|
307
|
+
async for req in request:
|
308
|
+
if req.gn_protocol is None:
|
309
|
+
req.setGNProtocol(self.__current_session['protocols'][0])
|
310
|
+
req._stream = True
|
311
|
+
|
312
|
+
for f in self.__request_callbacks.values():
|
313
|
+
asyncio.create_task(f(req))
|
314
|
+
|
315
|
+
nonlocal c
|
316
|
+
if c is None: # инициализируем при первом req
|
317
|
+
c = await self.connect(request.url.hostname, restart_connection, reconnect_wait, keep_alive=keep_alive)
|
318
|
+
|
319
|
+
yield req
|
320
|
+
|
321
|
+
gen = wrapped(request)
|
322
|
+
first_req = await gen.__anext__()
|
323
|
+
|
324
|
+
if c is None:
|
325
|
+
raise GNExceptions.ConnectionError.client.data
|
326
|
+
|
327
|
+
r = await c.asyncRequest(chain_async(first_req, gen))
|
328
|
+
|
329
|
+
for f in self.__response_callbacks.values():
|
330
|
+
asyncio.create_task(f(r))
|
331
|
+
|
332
|
+
return r
|
333
|
+
|
305
334
|
|
306
335
|
async def requestStream(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]) -> AsyncGenerator[GNResponse, None]:
|
307
336
|
"""
|
@@ -338,24 +367,6 @@ class AsyncClient:
|
|
338
367
|
|
339
368
|
yield response
|
340
369
|
|
341
|
-
from aioquic.asyncio.protocol import QuicConnectionProtocol
|
342
|
-
from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset
|
343
|
-
from aioquic.quic.connection import END_STATES
|
344
|
-
import asyncio
|
345
|
-
from collections import deque
|
346
|
-
from typing import Dict, Deque, Tuple, Optional, List
|
347
|
-
|
348
|
-
import asyncio
|
349
|
-
import time
|
350
|
-
from collections import deque
|
351
|
-
from dataclasses import dataclass
|
352
|
-
from itertools import count
|
353
|
-
from typing import Deque, Dict, Optional, Tuple, Union
|
354
|
-
|
355
|
-
from aioquic.quic.configuration import QuicConfiguration
|
356
|
-
from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset, ConnectionTerminated
|
357
|
-
|
358
|
-
|
359
370
|
class RawQuicClient(QuicConnectionProtocol):
|
360
371
|
|
361
372
|
SYS_RATIO_NUM = 9 # SYS 9/10
|
@@ -0,0 +1,9 @@
|
|
1
|
+
GNServer/___client.py,sha256=hmeUL2Vqp-BnwJeRcLZAaIfRNVxBrRRB_AFk9ofkei4,25459
|
2
|
+
GNServer/__init__.py,sha256=V50sMYrrPdOGuI1iJm-SW7izhX-eggDH16AHvtIKjmM,1480
|
3
|
+
GNServer/_app.py,sha256=lP2YEChUHDWB8Ig3711hgsccwKTko8a3nlgT2EFmpwY,32494
|
4
|
+
GNServer/_client.py,sha256=7D4JVgjjUqFN7RVzcOltMN_KyqV6Zfhe7F0Hz3OCIPk,30182
|
5
|
+
gnserver-0.0.0.0.30.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
|
6
|
+
gnserver-0.0.0.0.30.dist-info/METADATA,sha256=8KCKYQXhaFF2d_P8Y-QK1dFDLjnus723FehM0b4N4r8,833
|
7
|
+
gnserver-0.0.0.0.30.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
+
gnserver-0.0.0.0.30.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
|
9
|
+
gnserver-0.0.0.0.30.dist-info/RECORD,,
|
@@ -1,9 +0,0 @@
|
|
1
|
-
GNServer/___client.py,sha256=hmeUL2Vqp-BnwJeRcLZAaIfRNVxBrRRB_AFk9ofkei4,25459
|
2
|
-
GNServer/__init__.py,sha256=V50sMYrrPdOGuI1iJm-SW7izhX-eggDH16AHvtIKjmM,1480
|
3
|
-
GNServer/_app.py,sha256=CZQ1EM-m0FsMTOVyjNIMeDvS0B1MtZMsGV0O1deMyTc,30778
|
4
|
-
GNServer/_client.py,sha256=uF5h5pu0noJ1uNWSdC-kxGvBUwUUmyyAQTt_altNJ_g,29861
|
5
|
-
gnserver-0.0.0.0.28.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
|
6
|
-
gnserver-0.0.0.0.28.dist-info/METADATA,sha256=lrvZv_aAc4ggrW0gIWvQerVUexDwivGvyVsNWoDr-cQ,833
|
7
|
-
gnserver-0.0.0.0.28.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
-
gnserver-0.0.0.0.28.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
|
9
|
-
gnserver-0.0.0.0.28.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|