GNServer 0.0.0.0.30__py3-none-any.whl → 0.0.0.0.31__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 +83 -200
- GNServer/_client.py +45 -71
- GNServer/_crt.py +26 -0
- {gnserver-0.0.0.0.30.dist-info → gnserver-0.0.0.0.31.dist-info}/METADATA +1 -1
- gnserver-0.0.0.0.31.dist-info/RECORD +9 -0
- GNServer/___client.py +0 -628
- gnserver-0.0.0.0.30.dist-info/RECORD +0 -9
- {gnserver-0.0.0.0.30.dist-info → gnserver-0.0.0.0.31.dist-info}/WHEEL +0 -0
- {gnserver-0.0.0.0.30.dist-info → gnserver-0.0.0.0.31.dist-info}/licenses/LICENSE +0 -0
- {gnserver-0.0.0.0.30.dist-info → gnserver-0.0.0.0.31.dist-info}/top_level.txt +0 -0
GNServer/_app.py
CHANGED
@@ -2,32 +2,36 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
import re
|
5
|
+
import os
|
5
6
|
import sys
|
6
7
|
import uuid
|
7
|
-
import anyio
|
8
8
|
import decimal
|
9
9
|
import asyncio
|
10
10
|
import inspect
|
11
11
|
import traceback
|
12
12
|
import datetime
|
13
|
-
import
|
14
|
-
from typing import Any, Awaitable, Callable, Dict, List, Optional, Pattern, Tuple, Union, AsyncGenerator
|
13
|
+
from typing import Any, Callable, Dict, List, Optional, Pattern, Tuple, Union, AsyncGenerator
|
15
14
|
from dataclasses import dataclass
|
16
15
|
from aioquic.asyncio.server import serve
|
17
16
|
from aioquic.asyncio.protocol import QuicConnectionProtocol
|
18
17
|
from aioquic.quic.configuration import QuicConfiguration
|
19
18
|
from aioquic.quic.events import QuicEvent, StreamDataReceived
|
20
19
|
from typing import Any, AsyncGenerator, Union, get_origin, get_args
|
21
|
-
from urllib.parse import parse_qs
|
22
20
|
|
23
|
-
|
24
|
-
import
|
25
|
-
import
|
26
|
-
|
21
|
+
|
22
|
+
from gnobjects.net.objects import GNRequest, GNResponse, FileObject, CORSObject, TemplateObject
|
23
|
+
from gnobjects.net.fastcommands import AllGNFastCommands, GNFastCommand
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
27
31
|
|
28
32
|
try:
|
29
33
|
if not sys.platform.startswith("win"):
|
30
|
-
import uvloop
|
34
|
+
import uvloop # type: ignore
|
31
35
|
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
32
36
|
except ImportError:
|
33
37
|
print("uvloop не установлен")
|
@@ -45,121 +49,7 @@ console = logging.StreamHandler()
|
|
45
49
|
console.setLevel(logging.INFO)
|
46
50
|
console.setFormatter(logging.Formatter("[GNServer] %(name)s: %(levelname)s: %(message)s"))
|
47
51
|
|
48
|
-
PayloadType = Optional[Union[int, str, list, tuple, dict]]
|
49
|
-
|
50
|
-
class _BaseEXception(Exception):
|
51
|
-
def __init__(self, code: str, name="", message: Optional[str] = None, payload: PayloadType = None):
|
52
|
-
self._code = code
|
53
|
-
self._name = name
|
54
|
-
self._message = message
|
55
|
-
self._payload = payload
|
56
52
|
|
57
|
-
def assembly(self):
|
58
|
-
"""
|
59
|
-
Собирает ошибку в ответ типа `GNResponse`
|
60
|
-
"""
|
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)
|
70
|
-
|
71
|
-
|
72
|
-
class GNExceptions:
|
73
|
-
class UnprocessableEntity(_BaseEXception):
|
74
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
75
|
-
"""
|
76
|
-
# Некорректные данные
|
77
|
-
"""
|
78
|
-
super().__init__('422', "Unprocessable Entity", message=message, payload=payload)
|
79
|
-
|
80
|
-
class BadRequest(_BaseEXception):
|
81
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
82
|
-
"""
|
83
|
-
# неправильного синтаксис url или параметров
|
84
|
-
"""
|
85
|
-
super().__init__('400', "Bad Request", message=message, payload=payload)
|
86
|
-
|
87
|
-
class Forbidden(_BaseEXception):
|
88
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
89
|
-
"""
|
90
|
-
# Доступ запрещён, даже при наличии авторизации
|
91
|
-
"""
|
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)
|
100
|
-
|
101
|
-
class NotFound(_BaseEXception):
|
102
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
103
|
-
"""
|
104
|
-
# Ресурс не найден
|
105
|
-
"""
|
106
|
-
super().__init__('404', "Not Found", message=message, payload=payload)
|
107
|
-
|
108
|
-
|
109
|
-
class MethodNotAllowed(_BaseEXception):
|
110
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
111
|
-
"""
|
112
|
-
# Метод запроса не поддерживается данным ресурсом
|
113
|
-
"""
|
114
|
-
super().__init__('405', "Method Not Allowed", message=message, payload=payload)
|
115
|
-
|
116
|
-
|
117
|
-
class Conflict(_BaseEXception):
|
118
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
119
|
-
"""
|
120
|
-
# Конфликт состояния ресурса (например, дубликат)
|
121
|
-
"""
|
122
|
-
super().__init__('409', "Conflict", message=message, payload=payload)
|
123
|
-
|
124
|
-
|
125
|
-
class InternalServerError(_BaseEXception):
|
126
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
127
|
-
"""
|
128
|
-
# Внутренняя ошибка сервера
|
129
|
-
"""
|
130
|
-
super().__init__('500', "Internal Server Error", message=message, payload=payload)
|
131
|
-
|
132
|
-
|
133
|
-
class NotImplemented(_BaseEXception):
|
134
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
135
|
-
"""
|
136
|
-
# Метод или функционал ещё не реализован
|
137
|
-
"""
|
138
|
-
super().__init__('501', "Not Implemented", message=message, payload=payload)
|
139
|
-
|
140
|
-
|
141
|
-
class BadGateway(_BaseEXception):
|
142
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
143
|
-
"""
|
144
|
-
# Ошибка шлюза или прокси при обращении к апстриму
|
145
|
-
"""
|
146
|
-
super().__init__('502', "Bad Gateway", message=message, payload=payload)
|
147
|
-
|
148
|
-
|
149
|
-
class ServiceUnavailable(_BaseEXception):
|
150
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
151
|
-
"""
|
152
|
-
# Сервис временно недоступен
|
153
|
-
"""
|
154
|
-
super().__init__('503', "Service Unavailable", message=message, payload=payload)
|
155
|
-
|
156
|
-
|
157
|
-
class GatewayTimeout(_BaseEXception):
|
158
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
159
|
-
"""
|
160
|
-
# Таймаут при обращении к апстриму
|
161
|
-
"""
|
162
|
-
super().__init__('504', "Gateway Timeout", message=message, payload=payload)
|
163
53
|
|
164
54
|
def guess_type(filename: str) -> str:
|
165
55
|
"""
|
@@ -410,7 +300,7 @@ class Route:
|
|
410
300
|
param_types: dict[str, Callable[[str], Any]]
|
411
301
|
handler: Callable[..., Any]
|
412
302
|
name: str
|
413
|
-
cors: Optional[
|
303
|
+
cors: Optional[CORSObject]
|
414
304
|
|
415
305
|
_PARAM_REGEX: dict[str, str] = {
|
416
306
|
"str": r"[^/]+",
|
@@ -501,12 +391,12 @@ def _ensure_async(fn: Callable[..., Any]) -> Callable[..., Any]:
|
|
501
391
|
class App:
|
502
392
|
def __init__(self):
|
503
393
|
self._routes: List[Route] = []
|
504
|
-
self._cors: Optional[
|
505
|
-
self._events: Dict[str, Dict[str, Union[
|
394
|
+
self._cors: Optional[CORSObject] = None
|
395
|
+
self._events: Dict[str, List[Dict[str, Union[Any, Callable]]]] = {}
|
506
396
|
|
507
|
-
self.domain: str = None
|
397
|
+
self.domain: str = None # type: ignore
|
508
398
|
|
509
|
-
def route(self, method: str, path: str, cors: Optional[
|
399
|
+
def route(self, method: str, path: str, cors: Optional[CORSObject] = None):
|
510
400
|
if path == '/':
|
511
401
|
path = ''
|
512
402
|
def decorator(fn: Callable[..., Any]):
|
@@ -525,25 +415,25 @@ class App:
|
|
525
415
|
return fn
|
526
416
|
return decorator
|
527
417
|
|
528
|
-
def get(self, path: str, *, cors: Optional[
|
418
|
+
def get(self, path: str, *, cors: Optional[CORSObject] = None):
|
529
419
|
return self.route("GET", path, cors)
|
530
420
|
|
531
|
-
def post(self, path: str, *, cors: Optional[
|
421
|
+
def post(self, path: str, *, cors: Optional[CORSObject] = None):
|
532
422
|
return self.route("POST", path, cors)
|
533
423
|
|
534
|
-
def put(self, path: str, *, cors: Optional[
|
424
|
+
def put(self, path: str, *, cors: Optional[CORSObject] = None):
|
535
425
|
return self.route("PUT", path, cors)
|
536
426
|
|
537
|
-
def delete(self, path: str, *, cors: Optional[
|
427
|
+
def delete(self, path: str, *, cors: Optional[CORSObject] = None):
|
538
428
|
return self.route("DELETE", path, cors)
|
539
429
|
|
540
430
|
|
541
|
-
def setRouteCors(self, cors: Optional[
|
431
|
+
def setRouteCors(self, cors: Optional[CORSObject] = None):
|
542
432
|
self._cors = cors
|
543
433
|
|
544
434
|
|
545
435
|
def addEventListener(self, name: str):
|
546
|
-
def decorator(fn: Callable[Optional[dict], Any]):
|
436
|
+
def decorator(fn: Callable[[Optional[dict]], Any]):
|
547
437
|
events = self._events.get(name, [])
|
548
438
|
events.append({
|
549
439
|
'func': fn,
|
@@ -563,12 +453,12 @@ class App:
|
|
563
453
|
is_async = data['async']
|
564
454
|
|
565
455
|
if not is_async:
|
566
|
-
if payload in data['parameters']:
|
456
|
+
if payload in data['parameters']: # type: ignore
|
567
457
|
func(payload=payload)
|
568
458
|
else:
|
569
459
|
func()
|
570
460
|
else:
|
571
|
-
if payload in data['parameters']:
|
461
|
+
if payload in data['parameters']: # type: ignore
|
572
462
|
await func(payload=payload)
|
573
463
|
else:
|
574
464
|
await func()
|
@@ -579,8 +469,8 @@ class App:
|
|
579
469
|
|
580
470
|
|
581
471
|
async def dispatchRequest(
|
582
|
-
self, request:
|
583
|
-
) -> Union[
|
472
|
+
self, request: GNRequest
|
473
|
+
) -> Union[GNResponse, AsyncGenerator[GNResponse, None]]:
|
584
474
|
path = request.url.path
|
585
475
|
method = request.method.upper()
|
586
476
|
cand = {path, path.rstrip("/") or "/", f"{path}/"}
|
@@ -595,13 +485,13 @@ class App:
|
|
595
485
|
if r.method != method:
|
596
486
|
continue
|
597
487
|
|
598
|
-
if r.cors is not None and r.cors.
|
488
|
+
if r.cors is not None and r.cors.allow_origins is not None:
|
599
489
|
if request._origin is None:
|
600
|
-
return
|
601
|
-
if not resolve_cors(request._origin, r.cors.
|
602
|
-
return
|
603
|
-
if request.method not in r.cors.
|
604
|
-
return
|
490
|
+
return GNResponse("gn:backend:801", {'error': 'Cors error. Route has cors but request has no origin url.'})
|
491
|
+
if not resolve_cors(request._origin, r.cors.allow_origins):
|
492
|
+
return GNResponse("gn:backend:802", {'error': 'Cors error: origin'})
|
493
|
+
if request.method not in r.cors.allow_methods and '*' not in r.cors.allow_methods:
|
494
|
+
return GNResponse("gn:backend:803", {'error': 'Cors error: method'})
|
605
495
|
|
606
496
|
sig = inspect.signature(r.handler)
|
607
497
|
def _ann(name: str):
|
@@ -613,7 +503,7 @@ class App:
|
|
613
503
|
for name, val in m.groupdict().items()
|
614
504
|
}
|
615
505
|
|
616
|
-
for qn, qvals in
|
506
|
+
for qn, qvals in request.url.params.items():
|
617
507
|
if qn in kw:
|
618
508
|
continue
|
619
509
|
raw = qvals if len(qvals) > 1 else qvals[0]
|
@@ -629,21 +519,21 @@ class App:
|
|
629
519
|
return r.handler(**kw)
|
630
520
|
|
631
521
|
result = await r.handler(**kw)
|
632
|
-
if isinstance(result,
|
522
|
+
if isinstance(result, GNResponse):
|
633
523
|
if r.cors is None:
|
634
524
|
if result._cors is None:
|
635
525
|
result._cors = self._cors
|
636
526
|
else:
|
637
527
|
result._cors = r.cors
|
638
528
|
|
639
|
-
if result._cors is not None and result._cors != r.cors and result._cors.
|
529
|
+
if result._cors is not None and result._cors != r.cors and result._cors.allow_origins is not None:
|
640
530
|
if request._origin is None:
|
641
|
-
print(result._cors.
|
642
|
-
return
|
643
|
-
if not resolve_cors(request._origin, result._cors.
|
644
|
-
return
|
645
|
-
if request.method not in result._cors.
|
646
|
-
return
|
531
|
+
print(result._cors.allow_origins)
|
532
|
+
return GNResponse("gn:backend:801", {'error': 'Cors error. Route has cors but request has no origin url. [2]'})
|
533
|
+
if not resolve_cors(request._origin, result._cors.allow_origins):
|
534
|
+
return GNResponse("gn:backend:802", {'error': 'Cors error: origin'})
|
535
|
+
if request.method not in result._cors.allow_methods and '*' not in result._cors.allow_methods:
|
536
|
+
return GNResponse("gn:backend:803", {'error': 'Cors error: method'})
|
647
537
|
return result
|
648
538
|
else:
|
649
539
|
raise TypeError(
|
@@ -651,11 +541,11 @@ class App:
|
|
651
541
|
)
|
652
542
|
|
653
543
|
if allowed:
|
654
|
-
raise
|
655
|
-
raise
|
544
|
+
raise AllGNFastCommands.MethodNotAllowed()
|
545
|
+
raise AllGNFastCommands.NotFound()
|
656
546
|
|
657
547
|
|
658
|
-
def fastFile(self, path: str, file_path: str, cors: Optional[
|
548
|
+
def fastFile(self, path: str, file_path: str, cors: Optional[CORSObject] = None, template: Optional[TemplateObject] = None, payload: Optional[dict] = None):
|
659
549
|
@self.get(path)
|
660
550
|
async def r_static():
|
661
551
|
nonlocal file_path
|
@@ -663,13 +553,13 @@ class App:
|
|
663
553
|
file_path = file_path[:-1]
|
664
554
|
|
665
555
|
if not os.path.isfile(file_path):
|
666
|
-
raise
|
556
|
+
raise AllGNFastCommands.NotFound()
|
667
557
|
|
668
|
-
fileObject =
|
669
|
-
return
|
558
|
+
fileObject = FileObject(file_path, template)
|
559
|
+
return GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
670
560
|
|
671
561
|
|
672
|
-
def static(self, path: str, dir_path: str, cors: Optional[
|
562
|
+
def static(self, path: str, dir_path: str, cors: Optional[CORSObject] = None, template: Optional[TemplateObject] = None, payload: Optional[dict] = None):
|
673
563
|
@self.get(f"{path}/{{_path:path}}")
|
674
564
|
async def r_static(_path: str):
|
675
565
|
file_path = os.path.join(dir_path, _path)
|
@@ -678,21 +568,21 @@ class App:
|
|
678
568
|
file_path = file_path[:-1]
|
679
569
|
|
680
570
|
if not os.path.isfile(file_path):
|
681
|
-
raise
|
571
|
+
raise AllGNFastCommands.NotFound()
|
682
572
|
|
683
|
-
fileObject =
|
684
|
-
return
|
573
|
+
fileObject = FileObject(file_path, template)
|
574
|
+
return GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
685
575
|
|
686
576
|
|
687
577
|
|
688
578
|
|
689
579
|
def _init_sys_routes(self):
|
690
|
-
@self.post('/!gn-vm-host/ping', cors=
|
691
|
-
async def r_ping(request:
|
580
|
+
@self.post('/!gn-vm-host/ping', cors=CORSObject())
|
581
|
+
async def r_ping(request: GNRequest):
|
692
582
|
|
693
583
|
if request.client.ip != '127.0.0.1':
|
694
|
-
raise
|
695
|
-
return
|
584
|
+
raise AllGNFastCommands.Forbidden()
|
585
|
+
return GNResponse('ok', {'time': datetime.datetime.now(datetime.UTC).isoformat()})
|
696
586
|
|
697
587
|
|
698
588
|
|
@@ -701,7 +591,7 @@ class App:
|
|
701
591
|
super().__init__(*a, **kw)
|
702
592
|
self._api = api
|
703
593
|
self._buffer: Dict[int, bytearray] = {}
|
704
|
-
self._streams: Dict[int, Tuple[asyncio.Queue[Optional[
|
594
|
+
self._streams: Dict[int, Tuple[asyncio.Queue[Optional[GNRequest]], bool]] = {}
|
705
595
|
|
706
596
|
def quic_event_received(self, event: QuicEvent):
|
707
597
|
if isinstance(event, StreamDataReceived):
|
@@ -716,7 +606,7 @@ class App:
|
|
716
606
|
|
717
607
|
|
718
608
|
# получаем длинну пакета
|
719
|
-
mode, stream, lenght =
|
609
|
+
mode, stream, lenght = GNRequest.type(buf)
|
720
610
|
|
721
611
|
if mode not in (1, 2): # не наш пакет
|
722
612
|
logger.debug(f'Пакет отклонен: mode пакета {mode}. Разрешен 1, 2')
|
@@ -724,13 +614,13 @@ class App:
|
|
724
614
|
|
725
615
|
if not stream: # если не стрим, то ждем конец quic стрима и запускаем обработку ответа
|
726
616
|
if event.end_stream:
|
727
|
-
request =
|
617
|
+
request = GNRequest.deserialize(buf, mode)
|
728
618
|
# request.stream_id = event.stream_id
|
729
619
|
# loop = asyncio.get_event_loop()
|
730
620
|
# request.fut = loop.create_future()
|
731
621
|
|
732
622
|
|
733
|
-
request.stream_id = event.stream_id
|
623
|
+
request.stream_id = event.stream_id # type: ignore
|
734
624
|
asyncio.create_task(self._handle_request(request, mode))
|
735
625
|
logger.debug(f'Отправлена задача разрешения пакета {request} route -> {request.route}')
|
736
626
|
|
@@ -750,11 +640,11 @@ class App:
|
|
750
640
|
del buf[:lenght]
|
751
641
|
|
752
642
|
# формируем запрос
|
753
|
-
request =
|
643
|
+
request = GNRequest.deserialize(data, mode)
|
754
644
|
|
755
645
|
logger.debug(request, f'event.stream_id -> {event.stream_id}')
|
756
646
|
|
757
|
-
request.stream_id = event.stream_id
|
647
|
+
request.stream_id = event.stream_id # type: ignore
|
758
648
|
|
759
649
|
queue, inapi = self._streams.setdefault(event.stream_id, (asyncio.Queue(), False))
|
760
650
|
|
@@ -786,10 +676,10 @@ class App:
|
|
786
676
|
break
|
787
677
|
yield chunk
|
788
678
|
|
789
|
-
request._stream = w
|
679
|
+
request._stream = w # type: ignore
|
790
680
|
asyncio.create_task(self._handle_request(request, mode))
|
791
681
|
|
792
|
-
async def _handle_request(self, request:
|
682
|
+
async def _handle_request(self, request: GNRequest, mode: int):
|
793
683
|
|
794
684
|
request.client._data['remote_addr'] = self._quic._network_paths[0].addr
|
795
685
|
|
@@ -800,43 +690,36 @@ class App:
|
|
800
690
|
if inspect.isasyncgen(response):
|
801
691
|
async for chunk in response: # type: ignore[misc]
|
802
692
|
chunk._stream = True
|
803
|
-
|
804
|
-
self._quic.send_stream_data(request.stream_id, chunk.serialize(mode), end_stream=False)
|
805
|
-
self.transmit()
|
693
|
+
await self.sendResponse(request, chunk, mode, False)
|
806
694
|
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
self.
|
811
|
-
self.transmit()
|
695
|
+
resp = GNResponse('gn:end-stream')
|
696
|
+
resp._stream = True
|
697
|
+
|
698
|
+
await self.sendResponse(request, resp, mode)
|
812
699
|
return
|
813
700
|
|
701
|
+
if not isinstance(response, GNResponse):
|
702
|
+
await self.sendResponse(request, AllGNFastCommands.InternalServerError(), mode)
|
703
|
+
return
|
814
704
|
|
815
|
-
|
816
|
-
|
817
|
-
logger.debug(f'Отправлен на сервер ответ -> {response.command} {response.payload if response.payload and len((response.payload)) < 200 else ''}')
|
818
|
-
self.transmit()
|
705
|
+
await self.sendResponse(request, response, mode)
|
706
|
+
logger.debug(f'Отправлен на сервер ответ -> {response.command} {response.payload if response.payload and len(str(response.payload)) < 200 else ''}')
|
819
707
|
except Exception as e:
|
820
|
-
if isinstance(e,
|
821
|
-
|
822
|
-
r = e.assembly()
|
823
|
-
return r
|
708
|
+
if isinstance(e, GNFastCommand):
|
709
|
+
await self.sendResponse(request, e, mode)
|
824
710
|
else:
|
825
711
|
logger.error('GNServer: error\n' + traceback.format_exc())
|
826
712
|
|
827
|
-
|
828
|
-
self._quic.send_stream_data(request.stream_id, response.serialize(mode), end_stream=True)
|
829
|
-
self.transmit()
|
713
|
+
await self.sendResponse(request, AllGNFastCommands.InternalServerError(), mode)
|
830
714
|
|
831
|
-
async def resolve_response(self, response: gn.GNResponse) -> gn.GNResponse:
|
832
|
-
await response.assembly()
|
833
|
-
|
834
|
-
return response
|
835
715
|
|
836
716
|
|
837
|
-
|
717
|
+
async def sendResponse(self, request: GNRequest, response: GNResponse, mode: int, end_stream: bool = True):
|
718
|
+
await response.assembly()
|
838
719
|
|
839
720
|
|
721
|
+
self._quic.send_stream_data(request.stream_id, response.serialize(mode), end_stream=end_stream) # type: ignore
|
722
|
+
self.transmit()
|
840
723
|
|
841
724
|
def run(
|
842
725
|
self,
|
@@ -864,7 +747,7 @@ class App:
|
|
864
747
|
cfg = QuicConfiguration(
|
865
748
|
alpn_protocols=["gn:backend"], is_client=False, idle_timeout=idle_timeout
|
866
749
|
)
|
867
|
-
cfg.load_cert_chain(cert_path, key_path)
|
750
|
+
cfg.load_cert_chain(cert_path, key_path) # type: ignore
|
868
751
|
|
869
752
|
async def _main():
|
870
753
|
|