GNServer 0.0.0.0.30__py3-none-any.whl → 0.0.0.0.32__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/___client.py DELETED
@@ -1,628 +0,0 @@
1
-
2
- import os
3
- import httpx
4
- import asyncio
5
- import typing as _typing
6
- import logging as logging2
7
- from typing import Union, List, Dict, Tuple, Optional
8
- import datetime
9
- logging2.basicConfig(level=logging2.INFO)
10
-
11
- from KeyisBLogging import logging
12
- from typing import Dict, List, Tuple, Optional, cast, AsyncGenerator, Callable, Literal
13
- from itertools import count
14
- from aioquic.asyncio.client import connect
15
- 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
- from aioquic.quic.configuration import QuicConfiguration
19
- from aioquic.quic.events import QuicEvent
20
-
21
-
22
- import time
23
- import json, ssl, asyncio, struct, base64, hashlib
24
- from typing import Any, Dict, Optional
25
- import websockets
26
-
27
- from cryptography.hazmat.primitives.ciphers.aead import AESGCM
28
- import os
29
- import msgpack
30
- import logging
31
- from httpx import Request, Headers, URL
32
- logging.basicConfig(level=logging.DEBUG)
33
- logging.getLogger("websockets").setLevel(logging.DEBUG)
34
-
35
- import KeyisBClient
36
- from KeyisBClient import Url
37
- httpxAsyncClient = httpx.AsyncClient(verify=KeyisBClient.ssl_gw_crt_path, timeout=200)
38
-
39
- class GNExceptions:
40
- class ConnectionError:
41
- class openconnector():
42
- """Ошибка подключения к серверу openconnector.gn"""
43
-
44
- class connection(Exception):
45
- def __init__(self, message="Ошибка подключения к серверу openconnector.gn. Сервер не найден."):
46
- super().__init__(message)
47
-
48
- class timeout(Exception):
49
- def __init__(self, message="Ошибка подключения к серверу openconnector.gn. Проблема с сетью или сервер перегружен."):
50
- super().__init__(message)
51
-
52
- class data(Exception):
53
- def __init__(self, message="Ошибка подключения к серверу openconnector.gn. Сервер не подтвердил подключение."):
54
- super().__init__(message)
55
-
56
- class dns_core():
57
- """Ошибка подключения к серверу dns.core"""
58
- class connection(Exception):
59
- def __init__(self, message="Ошибка подключения к серверу dns.core Сервер не найден."):
60
- super().__init__(message)
61
-
62
- class timeout(Exception):
63
- def __init__(self, message="Ошибка подключения к серверу dns.core Проблема с сетью или сервер перегружен"):
64
- super().__init__(message)
65
-
66
- class data(Exception):
67
- def __init__(self, message="Ошибка подключения к серверу dns.core Сервер не подтвердил подключение."):
68
- super().__init__(message)
69
-
70
-
71
-
72
- class connector():
73
- """Ошибка подключения к серверу <?>~connector.gn"""
74
-
75
- class connection(Exception):
76
- def __init__(self, message="Ошибка подключения к серверу <?>~connector.gn. Сервер не найден."):
77
- super().__init__(message)
78
-
79
- class timeout(Exception):
80
- def __init__(self, message="Ошибка подключения к серверу <?>~connector.gn. Проблема с сетью или сервер перегружен"):
81
- super().__init__(message)
82
-
83
- class data(Exception):
84
- def __init__(self, message="Ошибка подключения к серверу <?>~connector.gn. Сервер не подтвердил подключение."):
85
- super().__init__(message)
86
-
87
-
88
-
89
- from KeyisBClient.gn import GNRequest, GNResponse, GNProtocol
90
-
91
-
92
-
93
-
94
- class AsyncClient:
95
- def __init__(self):
96
- self.__dns_core__ipv4 = '51.250.85.38:52943'
97
- self.__dns_gn__ipv4 = None
98
-
99
- self.__user = {}
100
- self.__current_session = {}
101
- self.__request_callbacks = {}
102
- self.__response_callbacks = {}
103
-
104
- self._active_connections: Dict[str, QuicClient] = {}
105
-
106
- async def _getCoreDNS(self, domain: str):
107
-
108
- if domain.split('.')[-1].split(':')[0].isdigit() and domain.split(':')[-1].isdigit():
109
- r2_data = {
110
- "ip": domain.split(':')[0],
111
- "port": int(domain.split(':')[-1])
112
- }
113
- return r2_data
114
-
115
- try:
116
- if self.__dns_gn__ipv4 is None:
117
- r1 = await httpxAsyncClient.request('GET', f'https://{self.__dns_core__ipv4}/gn/getIp?d=dns.gn')
118
- if r1.status_code != 200:
119
- raise GNExceptions.ConnectionError.dns_core.data
120
- r1_data = r1.json()
121
- self.__dns_gn__ipv4 = r1_data['ip'] + ':' + str(r1_data['port'])
122
-
123
-
124
- r2 = await httpxAsyncClient.request('GET', f'https://{self.__dns_gn__ipv4}/gn/getIp?d={domain}')
125
- except httpx.TimeoutException:
126
- raise GNExceptions.ConnectionError.dns_core.timeout
127
- except:
128
- raise GNExceptions.ConnectionError.dns_core.connection
129
-
130
- if r2.status_code != 200:
131
- raise GNExceptions.ConnectionError.dns_core.data
132
-
133
- r2_data = r2.json()
134
-
135
- return r2_data
136
-
137
- def addRequestCallback(self, callback: Callable, name: str):
138
- self.__request_callbacks[name] = callback
139
-
140
- def addResponseCallback(self, callback: Callable, name: str):
141
- self.__response_callbacks[name] = callback
142
-
143
-
144
- async def connect(self, domain: str, restart_connection: bool = False, reconnect_wait: float = 10) -> 'QuicClient':
145
- print('Запрос подключения')
146
- if not restart_connection and domain in self._active_connections:
147
- print('Подключение уже было')
148
- c = self._active_connections[domain]
149
- if c.status == 'connecting':
150
- if (c.connection_time + datetime.timedelta(seconds=11)) < datetime.datetime.now():
151
- print('ждем поделючения')
152
- try:
153
- await asyncio.wait_for(c.connect_future, reconnect_wait)
154
- print('дождались')
155
- return c
156
- except:
157
- print('Заново соеденяемся...')
158
-
159
- else:
160
- return c
161
-
162
- c = QuicClient()
163
- c.status = 'connecting'
164
- self._active_connections[domain] = c
165
-
166
- data = await self._getCoreDNS(domain)
167
-
168
-
169
-
170
- def f(domain):
171
- self._active_connections.pop(domain)
172
-
173
- c._disconnect_signal = f
174
- c._domain = domain
175
-
176
- await c.connect(data['ip'], data['port'])
177
- await c.connect_future
178
-
179
- return c
180
-
181
- async def disconnect(self, domain):
182
- if domain not in self._active_connections:
183
- return
184
-
185
- await self._active_connections[domain].disconnect()
186
-
187
-
188
- def _return_token(self, bigToken: str, s: bool = True) -> str:
189
- return bigToken[:128] if s else bigToken[128:]
190
-
191
-
192
- async def request(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]], restart_connection: bool = False, reconnect_wait: float = 10) -> GNResponse:
193
- if isinstance(request, GNRequest):
194
-
195
-
196
- c = await self.connect(request.url.hostname, restart_connection, reconnect_wait)
197
-
198
-
199
-
200
- for f in self.__request_callbacks.values():
201
- asyncio.create_task(f(request))
202
-
203
- r = await c.asyncRequest(request)
204
-
205
- for f in self.__response_callbacks.values():
206
- asyncio.create_task(f(r))
207
-
208
- return r
209
-
210
- # else:
211
- # async def wrapped(request) -> AsyncGenerator[GNRequest, None]:
212
- # async for req in request:
213
- # if req.gn_protocol is None:
214
- # req.setGNProtocol(self.__current_session['protocols'][0])
215
- # req._stream = True
216
-
217
- # for f in self.__request_callbacks.values():
218
- # asyncio.create_task(f(req))
219
-
220
- # yield req
221
- # r = await self.client.asyncRequest(wrapped(request))
222
-
223
- # for f in self.__response_callbacks.values():
224
- # asyncio.create_task(f(r))
225
-
226
- # return r
227
-
228
- async def requestStream(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]) -> AsyncGenerator[GNResponse, None]:
229
- """
230
- Build and send a async request.
231
- """
232
- if isinstance(request, GNRequest):
233
- if request.gn_protocol is None:
234
- request.setGNProtocol(self.__current_session['protocols'][0])
235
-
236
- for f in self.__request_callbacks.values():
237
- asyncio.create_task(f(request))
238
-
239
- async for response in self.client.asyncRequestStream(request):
240
-
241
- for f in self.__response_callbacks.values():
242
- asyncio.create_task(f(response))
243
-
244
- yield response
245
- else:
246
- async def wrapped(request) -> AsyncGenerator[GNRequest, None]:
247
- async for req in request:
248
- if req.gn_protocol is None:
249
- req.setGNProtocol(self.__current_session['protocols'][0])
250
-
251
- for f in self.__request_callbacks.values():
252
- asyncio.create_task(f(req))
253
-
254
- req._stream = True
255
- yield req
256
- async for response in self.client.asyncRequestStream(wrapped(request)):
257
-
258
- for f in self.__response_callbacks.values():
259
- asyncio.create_task(f(response))
260
-
261
- yield response
262
-
263
- from aioquic.asyncio.protocol import QuicConnectionProtocol
264
- from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset
265
- from aioquic.quic.connection import END_STATES
266
- import asyncio
267
- from collections import deque
268
- from typing import Dict, Deque, Tuple, Optional, List
269
-
270
- import asyncio
271
- import time
272
- from collections import deque
273
- from dataclasses import dataclass
274
- from itertools import count
275
- from typing import Deque, Dict, Optional, Tuple, Union
276
-
277
- from aioquic.quic.configuration import QuicConfiguration
278
- from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset, ConnectionTerminated
279
-
280
-
281
- class RawQuicClient(QuicConnectionProtocol):
282
-
283
- SYS_RATIO_NUM = 9 # SYS 9/10
284
- SYS_RATIO_DEN = 10
285
- KEEPALIVE_INTERVAL = 10 # сек
286
- KEEPALIVE_IDLE_TRIGGER = 30 # сек
287
-
288
- # ────────────────────────────────────────────────────────────────── init ─┐
289
- def __init__(self, *args, **kwargs):
290
- super().__init__(*args, **kwargs)
291
-
292
- self.quicClient: QuicClient = None
293
-
294
- self._sys_stream_id: Optional[int] = None
295
- self._queue_sys: Deque[Tuple[int, bytes, bool]] = deque()
296
- self._queue_user: Deque[Tuple[int, bytes, bool]] = deque()
297
-
298
- # <‑‑ Future | Queue[bytes | None]
299
- self._inflight: Dict[int, Union[asyncio.Future, asyncio.Queue[Optional[GNResponse]]]] = {}
300
- self._inflight_streams: Dict[int, bytearray] = {}
301
- self._sys_inflight: Dict[int, asyncio.Future] = {}
302
- self._buffer: Dict[Union[int, str], bytearray] = {}
303
-
304
- self._sys_budget = self.SYS_RATIO_NUM
305
- self._sys_id_gen = count(1) # int64 message‑id generator
306
-
307
- self._last_activity = time.time()
308
- self._running = True
309
- self._ping_id_gen = count(1) # int64 ping‑id generator
310
- asyncio.create_task(self._keepalive_loop())
311
-
312
- # ───────────────────────────────────────── private helpers ─┤
313
- def _activity(self):
314
- self._last_activity = time.time()
315
-
316
- async def _keepalive_loop(self):
317
- while self._running:
318
- await asyncio.sleep(self.KEEPALIVE_INTERVAL)
319
- idle_time = time.time() - self._last_activity
320
- if idle_time > self.KEEPALIVE_IDLE_TRIGGER:
321
- self._quic.send_ping(next(self._ping_id_gen))
322
- self.transmit()
323
- self._last_activity = time.time()
324
-
325
- def stop(self):
326
- self._running = False
327
-
328
- # ───────────────────────────────────────────── events ─┤
329
- def quic_event_received(self, event: QuicEvent) -> None: # noqa: C901
330
- # ─── DATA ───────────────────────────────────────────
331
- if isinstance(event, StreamDataReceived):
332
- #print(event)
333
- # SYS поток
334
- if event.stream_id == self._sys_stream_id:
335
- buf = self._buffer.setdefault("sys", bytearray())
336
- buf.extend(event.data)
337
- while True:
338
- if len(buf) < 12:
339
- break
340
- msg_id = int.from_bytes(buf[:8], "little")
341
- size = int.from_bytes(buf[8:12], "little")
342
- if len(buf) < 12 + size:
343
- break
344
- payload = bytes(buf[12 : 12 + size])
345
- del buf[: 12 + size]
346
- fut = self._sys_inflight.pop(msg_id, None) if msg_id else None
347
- if fut and not fut.done():
348
- fut.set_result(payload)
349
- # USER поток
350
- else:
351
- handler = self._inflight.get(event.stream_id)
352
- if handler is None:
353
- return
354
-
355
- # Чтение в зависимости от режима
356
- if isinstance(handler, asyncio.Queue): # стрим от сервера
357
- # получаем байты
358
-
359
- buf = self._buffer.setdefault(event.stream_id, bytearray())
360
- buf.extend(event.data)
361
-
362
- if len(buf) < 8: # не дошел даже frame пакета
363
- return
364
-
365
- # получаем длинну пакета
366
- mode, stream, lenght = GNResponse.type(buf)
367
-
368
- if mode != 4: # не наш пакет
369
- self._buffer.pop(event.stream_id)
370
- return
371
-
372
- if not stream: # клиент просил стрим, а сервер прислал один пакет
373
- self._buffer.pop(event.stream_id)
374
- return
375
-
376
- # читаем пакет
377
- if len(buf) < lenght: # если пакет не весь пришел, пропускаем
378
- return
379
-
380
- # пакет пришел весь
381
-
382
- # берем пакет
383
- data = buf[:lenght]
384
-
385
- # удаляем его из буфера
386
- del buf[:lenght]
387
-
388
-
389
- r = GNResponse.deserialize(data, 2)
390
- handler.put_nowait(r)
391
- if event.end_stream:
392
- handler.put_nowait(None)
393
- self._buffer.pop(event.stream_id)
394
- self._inflight.pop(event.stream_id, None)
395
-
396
-
397
-
398
- else: # Future
399
- buf = self._buffer.setdefault(event.stream_id, bytearray())
400
- buf.extend(event.data)
401
- if event.end_stream:
402
- self._inflight.pop(event.stream_id, None)
403
- data = bytes(self._buffer.pop(event.stream_id, b""))
404
- if not handler.done():
405
- handler.set_result(data)
406
-
407
- # ─── RESET ──────────────────────────────────────────
408
- elif isinstance(event, StreamReset):
409
- handler = self._inflight.pop(event.stream_id, None) or self._sys_inflight.pop(
410
- event.stream_id, None
411
- )
412
- if handler is None:
413
- return
414
- if isinstance(handler, asyncio.Queue):
415
- handler.put_nowait(None)
416
- else:
417
- if not handler.done():
418
- handler.set_exception(RuntimeError("stream reset"))
419
-
420
-
421
- elif isinstance(event, ConnectionTerminated):
422
- print("QUIC connection closed")
423
- print("Error code:", event.error_code)
424
- print("Reason:", event.reason_phrase)
425
- if self.quicClient is None:
426
- return
427
-
428
- asyncio.create_task(self.quicClient.disconnect())
429
-
430
-
431
- # ─────────────────────────────────────────── scheduler ─┤
432
- def _enqueue(self, sid: int, blob: bytes, end_stream: bool, is_sys: bool):
433
- (self._queue_sys if is_sys else self._queue_user).append((sid, blob, end_stream))
434
-
435
- def _schedule_flush(self):
436
- while (self._queue_sys or self._queue_user) and self._quic._close_event is None:
437
- q = None
438
- if self._queue_sys and (self._sys_budget > 0 or not self._queue_user):
439
- q = self._queue_sys
440
- self._sys_budget -= 1
441
- elif self._queue_user:
442
- q = self._queue_user
443
- self._sys_budget = self.SYS_RATIO_NUM
444
- if q is None:
445
- break
446
- sid, blob, end_stream = q.popleft()
447
- print(f'Отправка стрима {sid}')
448
- self._quic.send_stream_data(sid, blob, end_stream=end_stream)
449
- self.transmit()
450
- self._activity()
451
-
452
- # ─────────────────────────────────────────── public API ─┤
453
- async def ensure_sys_stream(self):
454
- if self._sys_stream_id is None:
455
- self._sys_stream_id = self._quic.get_next_available_stream_id()
456
- self._enqueue(self._sys_stream_id, b"", False, True) # dummy
457
- self._schedule_flush()
458
-
459
- async def send_sys(self, request: GNRequest, response: bool = False) -> Optional[bytes]:
460
- await self.ensure_sys_stream()
461
- if response:
462
- msg_id = next(self._sys_id_gen)
463
- blob = request.serialize(2)
464
- payload = (
465
- msg_id.to_bytes(8, "little") + len(blob).to_bytes(4, "little") + blob
466
- )
467
- fut = asyncio.get_running_loop().create_future()
468
- self._sys_inflight[msg_id] = fut
469
- self._enqueue(self._sys_stream_id, payload, False, True)
470
- self._schedule_flush()
471
- return await fut
472
- payload = (0).to_bytes(8, "little") + request.serialize(2)
473
- self._enqueue(self._sys_stream_id, payload, False, True)
474
- self._schedule_flush()
475
- return None
476
-
477
- async def request(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]):
478
- if isinstance(request, GNRequest):
479
- blob = request.serialize(2)
480
- sid = self._quic.get_next_available_stream_id()
481
- self._enqueue(sid, blob, True, False)
482
- self._schedule_flush()
483
-
484
-
485
- fut = asyncio.get_running_loop().create_future()
486
- self._inflight[sid] = fut
487
- return await fut
488
-
489
- else:
490
- sid = self._quic.get_next_available_stream_id()
491
- #if sid in self._quic._streams and not self._quic._streams[sid].is_finished:
492
-
493
- async def _stream_sender(sid, request: AsyncGenerator[GNRequest, Any]):
494
- _last = None
495
- async for req in request:
496
- _last = req
497
- blob = req.serialize(2)
498
- self._enqueue(sid, blob, False, False)
499
-
500
-
501
- self._schedule_flush()
502
-
503
- print(f'Отправлен stream запрос {req}')
504
-
505
-
506
- _last.setPayload(None)
507
- _last.setMethod('gn:end-stream')
508
- blob = _last.serialize(2)
509
- self._enqueue(sid, blob, True, False)
510
- self._schedule_flush()
511
-
512
- asyncio.create_task(_stream_sender(sid, request))
513
-
514
-
515
- fut = asyncio.get_running_loop().create_future()
516
- self._inflight[sid] = fut
517
- return await fut
518
-
519
- async def requestStream(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]) -> asyncio.Queue[GNResponse]:
520
- if isinstance(request, GNRequest):
521
- blob = request.serialize(2)
522
- sid = self._quic.get_next_available_stream_id()
523
- self._enqueue(sid, blob, False, False)
524
- self._schedule_flush()
525
-
526
-
527
- q = asyncio.Queue()
528
- self._inflight[sid] = q
529
- return q
530
-
531
- else:
532
- sid = self._quic.get_next_available_stream_id()
533
-
534
- async def _stream_sender(sid, request: AsyncGenerator[GNRequest, Any]):
535
- _last = None
536
- async for req in request:
537
- _last = req
538
- blob = req.serialize(2)
539
- self._enqueue(sid, blob, False, False)
540
-
541
-
542
- self._schedule_flush()
543
-
544
- print(f'Отправлен stream запрос {req}')
545
-
546
-
547
- _last.setPayload(None)
548
- _last.setMethod('gn:end-stream')
549
- blob = _last.serialize(2)
550
- self._enqueue(sid, blob, True, False)
551
- self._schedule_flush()
552
-
553
- asyncio.create_task(_stream_sender(sid, request))
554
-
555
-
556
- q = asyncio.Queue()
557
- self._inflight[sid] = q
558
- return q
559
-
560
-
561
-
562
- class QuicClient:
563
- """Обёртка‑фасад над RawQuicClient."""
564
-
565
- def __init__(self):
566
- self._quik_core: Optional[RawQuicClient] = None
567
- self._client_cm = None
568
- self._disconnect_signal = None
569
- self._domain = None
570
-
571
- self.status: Literal['active', 'connecting', 'disconnect']
572
-
573
- self.connect_future = asyncio.get_event_loop().create_future()
574
- self.connection_time: datetime.datetime = None
575
-
576
- async def connect(self, ip: str, port: int):
577
- self.status = 'connecting'
578
- self.connection_time = datetime.datetime.now()
579
- cfg = QuicConfiguration(is_client=True, alpn_protocols=["gn:backend"])
580
- cfg.load_verify_locations(KeyisBClient.ssl_gw_crt_path)
581
- cfg.idle_timeout = 10
582
-
583
- self._client_cm = connect(
584
- ip,
585
- port,
586
- configuration=cfg,
587
- create_protocol=RawQuicClient,
588
- wait_connected=True,
589
- )
590
- self._quik_core = await self._client_cm.__aenter__()
591
- self._quik_core.quicClient = self
592
-
593
- self.status = 'active'
594
- self.connect_future.set_result(True)
595
-
596
- async def disconnect(self):
597
- self.status = 'disconnect'
598
- if self._disconnect_signal is not None:
599
- self._disconnect_signal(self._domain)
600
-
601
- self._quik_core.close()
602
- await self._quik_core.wait_closed()
603
- self._quik_core = None
604
-
605
- def syncRequest(self, request: GNRequest):
606
- return asyncio.get_event_loop().run_until_complete(self.asyncRequest(request))
607
-
608
- async def asyncRequest(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]) -> GNResponse:
609
- if self._quik_core is None:
610
- raise RuntimeError("Not connected")
611
-
612
- resp = await self._quik_core.request(request)
613
- return GNResponse.deserialize(resp, 2)
614
-
615
- async def asyncRequestStream(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]) -> AsyncGenerator[GNResponse, None]:
616
-
617
- if self._quik_core is None:
618
- raise RuntimeError("Not connected")
619
-
620
- queue = await self._quik_core.requestStream(request)
621
-
622
- while True:
623
- chunk = await queue.get()
624
- if chunk is None or chunk.command == 'gn:end-stream':
625
- break
626
- yield chunk
627
-
628
-
@@ -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=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,,