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.
@@ -29,7 +29,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29
29
  DEALINGS IN THE SOFTWARE.
30
30
  """
31
31
 
32
- from ._app import App
32
+ from ._app import App, GNExceptions
33
33
  from KeyisBClient.gn import GNRequest, GNResponse, CORSObject, FileObject, TemplateObject
34
34
 
35
35
 
@@ -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
- - "https://*.site.tld" -> с проверкой схемы
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
- async def dispatch(
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
- return gn.GNResponse("gn:backend:405", {'error': 'Method Not Allowed'})
505
- return gn.GNResponse("gn:backend:404", {'error': 'Not Found'})
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
- return gn.GNResponse('gn:backend:404', cors=cors)
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
- return gn.GNResponse('gn:backend:404', cors=cors)
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._client_ip != '127.0.0.1':
544
- return gn.GNResponse('gn:backend:403', {'error': 'Forbidden'})
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._client_ip = self._quic._network_paths[0].addr[0]
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
- logger.error('GNServer: error\n' + traceback.format_exc())
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
- response = gn.GNResponse('gn:backend:500')
673
- self._quic.send_stream_data(request.stream_id, response.serialize(mode), end_stream=True)
674
- self.transmit()
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()
@@ -110,7 +110,6 @@ class AsyncClient:
110
110
  self.__dns_core__ipv4 = '51.250.85.38:52943'
111
111
  self.__dns_gn__ipv4 = None
112
112
 
113
- self.__user = {}
114
113
  self.__current_session = {}
115
114
  self.__request_callbacks = {}
116
115
  self.__response_callbacks = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GNServer
3
- Version: 0.0.0.0.19
3
+ Version: 0.0.0.0.21
4
4
  Summary: GNServer
5
5
  Home-page: https://github.com/KeyisB/libs/tree/main/GNServer
6
6
  Author: KeyisB
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GNServer
3
- Version: 0.0.0.0.19
3
+ Version: 0.0.0.0.21
4
4
  Summary: GNServer
5
5
  Home-page: https://github.com/KeyisB/libs/tree/main/GNServer
6
6
  Author: KeyisB
@@ -5,7 +5,7 @@ filesName = 'GNServer'
5
5
 
6
6
  setup(
7
7
  name=name,
8
- version='0.0.0.0.19',
8
+ version='0.0.0.0.21',
9
9
  author="KeyisB",
10
10
  author_email="keyisb.pip@gmail.com",
11
11
  description=name,
File without changes
File without changes
File without changes