GNServer 0.0.0.0.29__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 -194
- GNServer/_client.py +90 -105
- GNServer/_crt.py +26 -0
- {gnserver-0.0.0.0.29.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.29.dist-info/RECORD +0 -9
- {gnserver-0.0.0.0.29.dist-info → gnserver-0.0.0.0.31.dist-info}/WHEEL +0 -0
- {gnserver-0.0.0.0.29.dist-info → gnserver-0.0.0.0.31.dist-info}/licenses/LICENSE +0 -0
- {gnserver-0.0.0.0.29.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,115 +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
|
-
|
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
52
|
|
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
|
-
|
95
|
-
class NotFound(_BaseEXception):
|
96
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
97
|
-
"""
|
98
|
-
# Ресурс не найден
|
99
|
-
"""
|
100
|
-
super().__init__('404', "Not Found", message=message, payload=payload)
|
101
|
-
|
102
|
-
|
103
|
-
class MethodNotAllowed(_BaseEXception):
|
104
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
105
|
-
"""
|
106
|
-
# Метод запроса не поддерживается данным ресурсом
|
107
|
-
"""
|
108
|
-
super().__init__('405', "Method Not Allowed", message=message, payload=payload)
|
109
|
-
|
110
|
-
|
111
|
-
class Conflict(_BaseEXception):
|
112
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
113
|
-
"""
|
114
|
-
# Конфликт состояния ресурса (например, дубликат)
|
115
|
-
"""
|
116
|
-
super().__init__('409', "Conflict", message=message, payload=payload)
|
117
|
-
|
118
|
-
|
119
|
-
class InternalServerError(_BaseEXception):
|
120
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
121
|
-
"""
|
122
|
-
# Внутренняя ошибка сервера
|
123
|
-
"""
|
124
|
-
super().__init__('500', "Internal Server Error", message=message, payload=payload)
|
125
|
-
|
126
|
-
|
127
|
-
class NotImplemented(_BaseEXception):
|
128
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
129
|
-
"""
|
130
|
-
# Метод или функционал ещё не реализован
|
131
|
-
"""
|
132
|
-
super().__init__('501', "Not Implemented", message=message, payload=payload)
|
133
|
-
|
134
|
-
|
135
|
-
class BadGateway(_BaseEXception):
|
136
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
137
|
-
"""
|
138
|
-
# Ошибка шлюза или прокси при обращении к апстриму
|
139
|
-
"""
|
140
|
-
super().__init__('502', "Bad Gateway", message=message, payload=payload)
|
141
|
-
|
142
|
-
|
143
|
-
class ServiceUnavailable(_BaseEXception):
|
144
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
145
|
-
"""
|
146
|
-
# Сервис временно недоступен
|
147
|
-
"""
|
148
|
-
super().__init__('503', "Service Unavailable", message=message, payload=payload)
|
149
|
-
|
150
|
-
|
151
|
-
class GatewayTimeout(_BaseEXception):
|
152
|
-
def __init__(self, message: Optional[str] = None, payload: PayloadType = None):
|
153
|
-
"""
|
154
|
-
# Таймаут при обращении к апстриму
|
155
|
-
"""
|
156
|
-
super().__init__('504', "Gateway Timeout", message=message, payload=payload)
|
157
53
|
|
158
54
|
def guess_type(filename: str) -> str:
|
159
55
|
"""
|
@@ -404,7 +300,7 @@ class Route:
|
|
404
300
|
param_types: dict[str, Callable[[str], Any]]
|
405
301
|
handler: Callable[..., Any]
|
406
302
|
name: str
|
407
|
-
cors: Optional[
|
303
|
+
cors: Optional[CORSObject]
|
408
304
|
|
409
305
|
_PARAM_REGEX: dict[str, str] = {
|
410
306
|
"str": r"[^/]+",
|
@@ -495,12 +391,12 @@ def _ensure_async(fn: Callable[..., Any]) -> Callable[..., Any]:
|
|
495
391
|
class App:
|
496
392
|
def __init__(self):
|
497
393
|
self._routes: List[Route] = []
|
498
|
-
self._cors: Optional[
|
499
|
-
self._events: Dict[str, Dict[str, Union[
|
394
|
+
self._cors: Optional[CORSObject] = None
|
395
|
+
self._events: Dict[str, List[Dict[str, Union[Any, Callable]]]] = {}
|
500
396
|
|
501
|
-
self.domain: str = None
|
397
|
+
self.domain: str = None # type: ignore
|
502
398
|
|
503
|
-
def route(self, method: str, path: str, cors: Optional[
|
399
|
+
def route(self, method: str, path: str, cors: Optional[CORSObject] = None):
|
504
400
|
if path == '/':
|
505
401
|
path = ''
|
506
402
|
def decorator(fn: Callable[..., Any]):
|
@@ -519,25 +415,25 @@ class App:
|
|
519
415
|
return fn
|
520
416
|
return decorator
|
521
417
|
|
522
|
-
def get(self, path: str, *, cors: Optional[
|
418
|
+
def get(self, path: str, *, cors: Optional[CORSObject] = None):
|
523
419
|
return self.route("GET", path, cors)
|
524
420
|
|
525
|
-
def post(self, path: str, *, cors: Optional[
|
421
|
+
def post(self, path: str, *, cors: Optional[CORSObject] = None):
|
526
422
|
return self.route("POST", path, cors)
|
527
423
|
|
528
|
-
def put(self, path: str, *, cors: Optional[
|
424
|
+
def put(self, path: str, *, cors: Optional[CORSObject] = None):
|
529
425
|
return self.route("PUT", path, cors)
|
530
426
|
|
531
|
-
def delete(self, path: str, *, cors: Optional[
|
427
|
+
def delete(self, path: str, *, cors: Optional[CORSObject] = None):
|
532
428
|
return self.route("DELETE", path, cors)
|
533
429
|
|
534
430
|
|
535
|
-
def setRouteCors(self, cors: Optional[
|
431
|
+
def setRouteCors(self, cors: Optional[CORSObject] = None):
|
536
432
|
self._cors = cors
|
537
433
|
|
538
434
|
|
539
435
|
def addEventListener(self, name: str):
|
540
|
-
def decorator(fn: Callable[Optional[dict], Any]):
|
436
|
+
def decorator(fn: Callable[[Optional[dict]], Any]):
|
541
437
|
events = self._events.get(name, [])
|
542
438
|
events.append({
|
543
439
|
'func': fn,
|
@@ -557,12 +453,12 @@ class App:
|
|
557
453
|
is_async = data['async']
|
558
454
|
|
559
455
|
if not is_async:
|
560
|
-
if payload in data['parameters']:
|
456
|
+
if payload in data['parameters']: # type: ignore
|
561
457
|
func(payload=payload)
|
562
458
|
else:
|
563
459
|
func()
|
564
460
|
else:
|
565
|
-
if payload in data['parameters']:
|
461
|
+
if payload in data['parameters']: # type: ignore
|
566
462
|
await func(payload=payload)
|
567
463
|
else:
|
568
464
|
await func()
|
@@ -573,8 +469,8 @@ class App:
|
|
573
469
|
|
574
470
|
|
575
471
|
async def dispatchRequest(
|
576
|
-
self, request:
|
577
|
-
) -> Union[
|
472
|
+
self, request: GNRequest
|
473
|
+
) -> Union[GNResponse, AsyncGenerator[GNResponse, None]]:
|
578
474
|
path = request.url.path
|
579
475
|
method = request.method.upper()
|
580
476
|
cand = {path, path.rstrip("/") or "/", f"{path}/"}
|
@@ -589,13 +485,13 @@ class App:
|
|
589
485
|
if r.method != method:
|
590
486
|
continue
|
591
487
|
|
592
|
-
if r.cors is not None and r.cors.
|
488
|
+
if r.cors is not None and r.cors.allow_origins is not None:
|
593
489
|
if request._origin is None:
|
594
|
-
return
|
595
|
-
if not resolve_cors(request._origin, r.cors.
|
596
|
-
return
|
597
|
-
if request.method not in r.cors.
|
598
|
-
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'})
|
599
495
|
|
600
496
|
sig = inspect.signature(r.handler)
|
601
497
|
def _ann(name: str):
|
@@ -607,7 +503,7 @@ class App:
|
|
607
503
|
for name, val in m.groupdict().items()
|
608
504
|
}
|
609
505
|
|
610
|
-
for qn, qvals in
|
506
|
+
for qn, qvals in request.url.params.items():
|
611
507
|
if qn in kw:
|
612
508
|
continue
|
613
509
|
raw = qvals if len(qvals) > 1 else qvals[0]
|
@@ -623,21 +519,21 @@ class App:
|
|
623
519
|
return r.handler(**kw)
|
624
520
|
|
625
521
|
result = await r.handler(**kw)
|
626
|
-
if isinstance(result,
|
522
|
+
if isinstance(result, GNResponse):
|
627
523
|
if r.cors is None:
|
628
524
|
if result._cors is None:
|
629
525
|
result._cors = self._cors
|
630
526
|
else:
|
631
527
|
result._cors = r.cors
|
632
528
|
|
633
|
-
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:
|
634
530
|
if request._origin is None:
|
635
|
-
print(result._cors.
|
636
|
-
return
|
637
|
-
if not resolve_cors(request._origin, result._cors.
|
638
|
-
return
|
639
|
-
if request.method not in result._cors.
|
640
|
-
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'})
|
641
537
|
return result
|
642
538
|
else:
|
643
539
|
raise TypeError(
|
@@ -645,11 +541,11 @@ class App:
|
|
645
541
|
)
|
646
542
|
|
647
543
|
if allowed:
|
648
|
-
raise
|
649
|
-
raise
|
544
|
+
raise AllGNFastCommands.MethodNotAllowed()
|
545
|
+
raise AllGNFastCommands.NotFound()
|
650
546
|
|
651
547
|
|
652
|
-
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):
|
653
549
|
@self.get(path)
|
654
550
|
async def r_static():
|
655
551
|
nonlocal file_path
|
@@ -657,13 +553,13 @@ class App:
|
|
657
553
|
file_path = file_path[:-1]
|
658
554
|
|
659
555
|
if not os.path.isfile(file_path):
|
660
|
-
raise
|
556
|
+
raise AllGNFastCommands.NotFound()
|
661
557
|
|
662
|
-
fileObject =
|
663
|
-
return
|
558
|
+
fileObject = FileObject(file_path, template)
|
559
|
+
return GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
664
560
|
|
665
561
|
|
666
|
-
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):
|
667
563
|
@self.get(f"{path}/{{_path:path}}")
|
668
564
|
async def r_static(_path: str):
|
669
565
|
file_path = os.path.join(dir_path, _path)
|
@@ -672,21 +568,21 @@ class App:
|
|
672
568
|
file_path = file_path[:-1]
|
673
569
|
|
674
570
|
if not os.path.isfile(file_path):
|
675
|
-
raise
|
571
|
+
raise AllGNFastCommands.NotFound()
|
676
572
|
|
677
|
-
fileObject =
|
678
|
-
return
|
573
|
+
fileObject = FileObject(file_path, template)
|
574
|
+
return GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
679
575
|
|
680
576
|
|
681
577
|
|
682
578
|
|
683
579
|
def _init_sys_routes(self):
|
684
|
-
@self.post('/!gn-vm-host/ping', cors=
|
685
|
-
async def r_ping(request:
|
580
|
+
@self.post('/!gn-vm-host/ping', cors=CORSObject())
|
581
|
+
async def r_ping(request: GNRequest):
|
686
582
|
|
687
583
|
if request.client.ip != '127.0.0.1':
|
688
|
-
raise
|
689
|
-
return
|
584
|
+
raise AllGNFastCommands.Forbidden()
|
585
|
+
return GNResponse('ok', {'time': datetime.datetime.now(datetime.UTC).isoformat()})
|
690
586
|
|
691
587
|
|
692
588
|
|
@@ -695,7 +591,7 @@ class App:
|
|
695
591
|
super().__init__(*a, **kw)
|
696
592
|
self._api = api
|
697
593
|
self._buffer: Dict[int, bytearray] = {}
|
698
|
-
self._streams: Dict[int, Tuple[asyncio.Queue[Optional[
|
594
|
+
self._streams: Dict[int, Tuple[asyncio.Queue[Optional[GNRequest]], bool]] = {}
|
699
595
|
|
700
596
|
def quic_event_received(self, event: QuicEvent):
|
701
597
|
if isinstance(event, StreamDataReceived):
|
@@ -710,7 +606,7 @@ class App:
|
|
710
606
|
|
711
607
|
|
712
608
|
# получаем длинну пакета
|
713
|
-
mode, stream, lenght =
|
609
|
+
mode, stream, lenght = GNRequest.type(buf)
|
714
610
|
|
715
611
|
if mode not in (1, 2): # не наш пакет
|
716
612
|
logger.debug(f'Пакет отклонен: mode пакета {mode}. Разрешен 1, 2')
|
@@ -718,13 +614,13 @@ class App:
|
|
718
614
|
|
719
615
|
if not stream: # если не стрим, то ждем конец quic стрима и запускаем обработку ответа
|
720
616
|
if event.end_stream:
|
721
|
-
request =
|
617
|
+
request = GNRequest.deserialize(buf, mode)
|
722
618
|
# request.stream_id = event.stream_id
|
723
619
|
# loop = asyncio.get_event_loop()
|
724
620
|
# request.fut = loop.create_future()
|
725
621
|
|
726
622
|
|
727
|
-
request.stream_id = event.stream_id
|
623
|
+
request.stream_id = event.stream_id # type: ignore
|
728
624
|
asyncio.create_task(self._handle_request(request, mode))
|
729
625
|
logger.debug(f'Отправлена задача разрешения пакета {request} route -> {request.route}')
|
730
626
|
|
@@ -744,11 +640,11 @@ class App:
|
|
744
640
|
del buf[:lenght]
|
745
641
|
|
746
642
|
# формируем запрос
|
747
|
-
request =
|
643
|
+
request = GNRequest.deserialize(data, mode)
|
748
644
|
|
749
645
|
logger.debug(request, f'event.stream_id -> {event.stream_id}')
|
750
646
|
|
751
|
-
request.stream_id = event.stream_id
|
647
|
+
request.stream_id = event.stream_id # type: ignore
|
752
648
|
|
753
649
|
queue, inapi = self._streams.setdefault(event.stream_id, (asyncio.Queue(), False))
|
754
650
|
|
@@ -780,10 +676,10 @@ class App:
|
|
780
676
|
break
|
781
677
|
yield chunk
|
782
678
|
|
783
|
-
request._stream = w
|
679
|
+
request._stream = w # type: ignore
|
784
680
|
asyncio.create_task(self._handle_request(request, mode))
|
785
681
|
|
786
|
-
async def _handle_request(self, request:
|
682
|
+
async def _handle_request(self, request: GNRequest, mode: int):
|
787
683
|
|
788
684
|
request.client._data['remote_addr'] = self._quic._network_paths[0].addr
|
789
685
|
|
@@ -794,43 +690,36 @@ class App:
|
|
794
690
|
if inspect.isasyncgen(response):
|
795
691
|
async for chunk in response: # type: ignore[misc]
|
796
692
|
chunk._stream = True
|
797
|
-
|
798
|
-
self._quic.send_stream_data(request.stream_id, chunk.serialize(mode), end_stream=False)
|
799
|
-
self.transmit()
|
693
|
+
await self.sendResponse(request, chunk, mode, False)
|
800
694
|
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
self.
|
805
|
-
self.transmit()
|
695
|
+
resp = GNResponse('gn:end-stream')
|
696
|
+
resp._stream = True
|
697
|
+
|
698
|
+
await self.sendResponse(request, resp, mode)
|
806
699
|
return
|
807
700
|
|
701
|
+
if not isinstance(response, GNResponse):
|
702
|
+
await self.sendResponse(request, AllGNFastCommands.InternalServerError(), mode)
|
703
|
+
return
|
808
704
|
|
809
|
-
|
810
|
-
|
811
|
-
logger.debug(f'Отправлен на сервер ответ -> {response.command} {response.payload if response.payload and len((response.payload)) < 200 else ''}')
|
812
|
-
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 ''}')
|
813
707
|
except Exception as e:
|
814
|
-
if isinstance(e,
|
815
|
-
|
816
|
-
r = e.assembly()
|
817
|
-
return r
|
708
|
+
if isinstance(e, GNFastCommand):
|
709
|
+
await self.sendResponse(request, e, mode)
|
818
710
|
else:
|
819
711
|
logger.error('GNServer: error\n' + traceback.format_exc())
|
820
712
|
|
821
|
-
|
822
|
-
self._quic.send_stream_data(request.stream_id, response.serialize(mode), end_stream=True)
|
823
|
-
self.transmit()
|
713
|
+
await self.sendResponse(request, AllGNFastCommands.InternalServerError(), mode)
|
824
714
|
|
825
|
-
async def resolve_response(self, response: gn.GNResponse) -> gn.GNResponse:
|
826
|
-
await response.assembly()
|
827
|
-
|
828
|
-
return response
|
829
715
|
|
830
716
|
|
831
|
-
|
717
|
+
async def sendResponse(self, request: GNRequest, response: GNResponse, mode: int, end_stream: bool = True):
|
718
|
+
await response.assembly()
|
832
719
|
|
833
720
|
|
721
|
+
self._quic.send_stream_data(request.stream_id, response.serialize(mode), end_stream=end_stream) # type: ignore
|
722
|
+
self.transmit()
|
834
723
|
|
835
724
|
def run(
|
836
725
|
self,
|
@@ -858,7 +747,7 @@ class App:
|
|
858
747
|
cfg = QuicConfiguration(
|
859
748
|
alpn_protocols=["gn:backend"], is_client=False, idle_timeout=idle_timeout
|
860
749
|
)
|
861
|
-
cfg.load_cert_chain(cert_path, key_path)
|
750
|
+
cfg.load_cert_chain(cert_path, key_path) # type: ignore
|
862
751
|
|
863
752
|
async def _main():
|
864
753
|
|