GNServer 0.0.0.0.19__tar.gz → 0.0.0.0.21__tar.gz
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-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer/__init__.py +1 -1
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer/_app.py +157 -17
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer/_client.py +0 -1
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer.egg-info/PKG-INFO +1 -1
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/PKG-INFO +1 -1
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/setup.py +1 -1
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer/___client.py +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer.egg-info/SOURCES.txt +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer.egg-info/dependency_links.txt +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer.egg-info/requires.txt +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/GNServer.egg-info/top_level.txt +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/LICENSE +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/GNServer/mmbConfig.json +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/LICENSE +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/MANIFEST.in +0 -0
- {gnserver-0.0.0.0.19 → gnserver-0.0.0.0.21}/setup.cfg +0 -0
@@ -46,9 +46,103 @@ console.setFormatter(logging.Formatter("[GNServer] %(name)s: %(levelname)s: %(me
|
|
46
46
|
|
47
47
|
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
class _BaseEXception(Exception):
|
50
|
+
def __init__(self, code: str, message=""):
|
51
|
+
self._code = code
|
52
|
+
self._message = message
|
53
|
+
|
54
|
+
def assembly(self):
|
55
|
+
"""
|
56
|
+
Собирает ошибку в ответ типа `GNResponse`
|
57
|
+
"""
|
58
|
+
return gn.GNResponse(f'gn:error:{self._code}', payload={'msg': self._message})
|
59
|
+
|
60
|
+
|
61
|
+
class GNExceptions:
|
62
|
+
class UnprocessableEntity(_BaseEXception):
|
63
|
+
def __init__(self):
|
64
|
+
"""
|
65
|
+
# Некорректные данные
|
66
|
+
"""
|
67
|
+
super().__init__('422', "Unprocessable Entity")
|
68
|
+
|
69
|
+
class BadRequest(_BaseEXception):
|
70
|
+
def __init__(self):
|
71
|
+
"""
|
72
|
+
# неправильного синтаксис url или параметров
|
73
|
+
"""
|
74
|
+
super().__init__('400', "Bad Request")
|
75
|
+
|
76
|
+
class Forbidden(_BaseEXception):
|
77
|
+
def __init__(self):
|
78
|
+
"""
|
79
|
+
# Доступ запрещён, даже при наличии авторизации
|
80
|
+
"""
|
81
|
+
super().__init__('403', "Forbidden")
|
82
|
+
|
83
|
+
|
84
|
+
class NotFound(_BaseEXception):
|
85
|
+
def __init__(self):
|
86
|
+
"""
|
87
|
+
# Ресурс не найден
|
88
|
+
"""
|
89
|
+
super().__init__('404', "Not Found")
|
90
|
+
|
91
|
+
|
92
|
+
class MethodNotAllowed(_BaseEXception):
|
93
|
+
def __init__(self):
|
94
|
+
"""
|
95
|
+
# Метод запроса не поддерживается данным ресурсом
|
96
|
+
"""
|
97
|
+
super().__init__('405', "Method Not Allowed")
|
98
|
+
|
99
|
+
|
100
|
+
class Conflict(_BaseEXception):
|
101
|
+
def __init__(self):
|
102
|
+
"""
|
103
|
+
# Конфликт состояния ресурса (например, дубликат)
|
104
|
+
"""
|
105
|
+
super().__init__('409', "Conflict")
|
106
|
+
|
107
|
+
|
108
|
+
class InternalServerError(_BaseEXception):
|
109
|
+
def __init__(self):
|
110
|
+
"""
|
111
|
+
# Внутренняя ошибка сервера
|
112
|
+
"""
|
113
|
+
super().__init__('500', "Internal Server Error")
|
114
|
+
|
115
|
+
|
116
|
+
class NotImplemented(_BaseEXception):
|
117
|
+
def __init__(self):
|
118
|
+
"""
|
119
|
+
# Метод или функционал ещё не реализован
|
120
|
+
"""
|
121
|
+
super().__init__('501', "Not Implemented")
|
122
|
+
|
123
|
+
|
124
|
+
class BadGateway(_BaseEXception):
|
125
|
+
def __init__(self):
|
126
|
+
"""
|
127
|
+
# Ошибка шлюза или прокси при обращении к апстриму
|
128
|
+
"""
|
129
|
+
super().__init__('502', "Bad Gateway")
|
130
|
+
|
131
|
+
|
132
|
+
class ServiceUnavailable(_BaseEXception):
|
133
|
+
def __init__(self):
|
134
|
+
"""
|
135
|
+
# Сервис временно недоступен
|
136
|
+
"""
|
137
|
+
super().__init__('503', "Service Unavailable")
|
138
|
+
|
139
|
+
|
140
|
+
class GatewayTimeout(_BaseEXception):
|
141
|
+
def __init__(self):
|
142
|
+
"""
|
143
|
+
# Таймаут при обращении к апстриму
|
144
|
+
"""
|
145
|
+
super().__init__('504', "Gateway Timeout")
|
52
146
|
|
53
147
|
def guess_type(filename: str) -> str:
|
54
148
|
"""
|
@@ -169,7 +263,7 @@ def resolve_cors(origin_url: str, rules: list[str]) -> bool:
|
|
169
263
|
- "*.example.com" -> wildcard (одна метка)
|
170
264
|
- "**.example.com" -> globstar (0+ меток)
|
171
265
|
- "pages.*.core.gn" -> смешанное
|
172
|
-
- "
|
266
|
+
- "gn://*.site.tld" -> с проверкой схемы
|
173
267
|
- "!<regex>" -> полное соответствие по regex к origin_url
|
174
268
|
"""
|
175
269
|
|
@@ -391,6 +485,7 @@ class App:
|
|
391
485
|
def __init__(self):
|
392
486
|
self._routes: List[Route] = []
|
393
487
|
self._cors: Optional[gn.CORSObject] = None
|
488
|
+
self._events: Dict[str, Dict[str, Union[str, Callable]]] = {}
|
394
489
|
|
395
490
|
self.domain: str = None
|
396
491
|
|
@@ -430,8 +525,42 @@ class App:
|
|
430
525
|
self._cors = cors
|
431
526
|
|
432
527
|
|
528
|
+
def addEventListener(self, name: str):
|
529
|
+
def decorator(fn: Callable[Optional[dict], Any]):
|
530
|
+
events = self._events.get(name, [])
|
531
|
+
events.append({
|
532
|
+
'func': fn,
|
533
|
+
'async': inspect.iscoroutinefunction(fn),
|
534
|
+
'parameters': inspect.signature(fn).parameters
|
535
|
+
})
|
536
|
+
|
537
|
+
return fn
|
538
|
+
return decorator
|
539
|
+
|
540
|
+
async def dispatchEvent(self, name: str, payload: Optional[str] = None) -> None:
|
541
|
+
data_list = self._events.get(name, None)
|
542
|
+
if data_list:
|
543
|
+
for data in data_list:
|
544
|
+
func: Callable = data['func']
|
545
|
+
is_async = data['async']
|
546
|
+
|
547
|
+
if not is_async:
|
548
|
+
if payload in data['parameters']:
|
549
|
+
func(payload=payload)
|
550
|
+
else:
|
551
|
+
func()
|
552
|
+
else:
|
553
|
+
if payload in data['parameters']:
|
554
|
+
await func(payload=payload)
|
555
|
+
else:
|
556
|
+
await func()
|
557
|
+
|
558
|
+
|
559
|
+
|
560
|
+
|
433
561
|
|
434
|
-
|
562
|
+
|
563
|
+
async def dispatchRequest(
|
435
564
|
self, request: gn.GNRequest
|
436
565
|
) -> Union[gn.GNResponse, AsyncGenerator[gn.GNResponse, None]]:
|
437
566
|
path = request.url.path
|
@@ -501,8 +630,8 @@ class App:
|
|
501
630
|
)
|
502
631
|
|
503
632
|
if allowed:
|
504
|
-
|
505
|
-
|
633
|
+
raise GNExceptions.MethodNotAllowed()
|
634
|
+
raise GNExceptions.NotFound()
|
506
635
|
|
507
636
|
|
508
637
|
def fastFile(self, path: str, file_path: str, cors: Optional[gn.CORSObject] = None, template: Optional[gn.TemplateObject] = None, payload: Optional[dict] = None):
|
@@ -513,7 +642,7 @@ class App:
|
|
513
642
|
file_path = file_path[:-1]
|
514
643
|
|
515
644
|
if not os.path.isfile(file_path):
|
516
|
-
|
645
|
+
raise GNExceptions.NotFound()
|
517
646
|
|
518
647
|
fileObject = gn.FileObject(file_path, template)
|
519
648
|
return gn.GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
@@ -528,7 +657,7 @@ class App:
|
|
528
657
|
file_path = file_path[:-1]
|
529
658
|
|
530
659
|
if not os.path.isfile(file_path):
|
531
|
-
|
660
|
+
raise GNExceptions.NotFound()
|
532
661
|
|
533
662
|
fileObject = gn.FileObject(file_path, template)
|
534
663
|
return gn.GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
@@ -540,8 +669,8 @@ class App:
|
|
540
669
|
@self.post('/!gn-vm-host/ping', cors=gn.CORSObject(None))
|
541
670
|
async def r_ping(request: gn.GNRequest):
|
542
671
|
|
543
|
-
if request.
|
544
|
-
|
672
|
+
if request.client.remote_addr != '127.0.0.1':
|
673
|
+
raise GNExceptions.Forbidden()
|
545
674
|
return gn.GNResponse('ok', {'time': datetime.datetime.now(datetime.UTC).isoformat()})
|
546
675
|
|
547
676
|
|
@@ -641,7 +770,7 @@ class App:
|
|
641
770
|
|
642
771
|
async def _handle_request(self, request: gn.GNRequest, mode: int):
|
643
772
|
|
644
|
-
request.
|
773
|
+
request.client._data['remote_addr'] = self._quic._network_paths[0].addr[0]
|
645
774
|
|
646
775
|
try:
|
647
776
|
|
@@ -667,12 +796,17 @@ class App:
|
|
667
796
|
logger.debug(f'Отправлен на сервер ответ -> {response.command} {response.payload if response.payload and len((response.payload)) < 200 else ''}')
|
668
797
|
self.transmit()
|
669
798
|
except Exception as e:
|
670
|
-
|
799
|
+
if e.__qualname__.startswith("GNExceptions."):
|
800
|
+
e: GNExceptions.UnprocessableEntity = e
|
801
|
+
r = e.assembly()
|
802
|
+
return r
|
803
|
+
else:
|
804
|
+
logger.error('GNServer: error\n' + traceback.format_exc())
|
671
805
|
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
806
|
+
response = gn.GNResponse('gn:backend:500')
|
807
|
+
self._quic.send_stream_data(request.stream_id, response.serialize(mode), end_stream=True)
|
808
|
+
self.transmit()
|
809
|
+
|
676
810
|
async def resolve_response(self, response: gn.GNResponse) -> gn.GNResponse:
|
677
811
|
await response.assembly()
|
678
812
|
|
@@ -707,6 +841,9 @@ class App:
|
|
707
841
|
cfg.load_cert_chain(cert_path, key_path)
|
708
842
|
|
709
843
|
async def _main():
|
844
|
+
|
845
|
+
await self.dispatchEvent('before-run')
|
846
|
+
|
710
847
|
await serve(
|
711
848
|
host,
|
712
849
|
port,
|
@@ -718,6 +855,9 @@ class App:
|
|
718
855
|
if run is not None:
|
719
856
|
await run()
|
720
857
|
|
858
|
+
await self.dispatchEvent('run')
|
859
|
+
|
860
|
+
|
721
861
|
|
722
862
|
if wait:
|
723
863
|
await asyncio.Event().wait()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|