borecli 1.0.0__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.
- bore/__init__.py +0 -0
- bore/auth.py +104 -0
- bore/cli.py +378 -0
- bore/config.py +132 -0
- bore/frames.py +376 -0
- bore/handlers/__init__.py +0 -0
- bore/handlers/error_pages.py +223 -0
- bore/handlers/http_handler.py +694 -0
- bore/handlers/websocket_handler.py +975 -0
- bore/parse_duration.py +32 -0
- bore/protocol.py +72 -0
- bore/tunnel/__init__.py +0 -0
- bore/tunnel/client.py +311 -0
- bore/tunnel/dispatcher.py +277 -0
- bore/tunnel/heartbeat.py +154 -0
- bore/tunnel.py +43 -0
- bore/websocket/__init__.py +0 -0
- bore/websocket/connection.py +108 -0
- bore/websocket/frames.py +162 -0
- bore/websocket/manager.py +174 -0
- bore/websocket/proxy.py +147 -0
- bore/websocket_client.py +338 -0
- borecli-1.0.0.dist-info/METADATA +231 -0
- borecli-1.0.0.dist-info/RECORD +28 -0
- borecli-1.0.0.dist-info/WHEEL +5 -0
- borecli-1.0.0.dist-info/entry_points.txt +2 -0
- borecli-1.0.0.dist-info/licenses/LICENSE +0 -0
- borecli-1.0.0.dist-info/top_level.txt +1 -0
bore/frames.py
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# borecli/bore/frames.py
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
8
|
+
from websockets.exceptions import ConnectionClosed
|
|
9
|
+
|
|
10
|
+
from .protocol import (
|
|
11
|
+
PROTOCOL_VERSION,
|
|
12
|
+
MessageType,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ============================================================
|
|
20
|
+
# Base Frame
|
|
21
|
+
# ============================================================
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def make_frame(
|
|
25
|
+
message_type,
|
|
26
|
+
**payload,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Create a BoreHook protocol frame.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
"protocol": PROTOCOL_VERSION,
|
|
34
|
+
"frame_id": uuid.uuid4().hex,
|
|
35
|
+
"timestamp": int(time.time() * 1000),
|
|
36
|
+
"type": message_type,
|
|
37
|
+
**payload,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ============================================================
|
|
43
|
+
# Tunnel Registration
|
|
44
|
+
# ============================================================
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def make_tunnel_register(
|
|
48
|
+
*,
|
|
49
|
+
tunnel_id,
|
|
50
|
+
client="borecli",
|
|
51
|
+
version="1.0",
|
|
52
|
+
):
|
|
53
|
+
|
|
54
|
+
return make_frame(
|
|
55
|
+
|
|
56
|
+
MessageType.TUNNEL_REGISTER,
|
|
57
|
+
|
|
58
|
+
tunnel_id=tunnel_id,
|
|
59
|
+
|
|
60
|
+
client=client,
|
|
61
|
+
|
|
62
|
+
version=version,
|
|
63
|
+
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ============================================================
|
|
69
|
+
# HTTP Frames
|
|
70
|
+
# ============================================================
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def make_http_request(
|
|
74
|
+
**payload,
|
|
75
|
+
):
|
|
76
|
+
|
|
77
|
+
return make_frame(
|
|
78
|
+
|
|
79
|
+
MessageType.HTTP_REQUEST,
|
|
80
|
+
|
|
81
|
+
**payload,
|
|
82
|
+
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def make_http_response(
|
|
89
|
+
*,
|
|
90
|
+
request_id,
|
|
91
|
+
status,
|
|
92
|
+
headers,
|
|
93
|
+
body,
|
|
94
|
+
binary=False,
|
|
95
|
+
):
|
|
96
|
+
|
|
97
|
+
return make_frame(
|
|
98
|
+
|
|
99
|
+
MessageType.HTTP_RESPONSE,
|
|
100
|
+
|
|
101
|
+
request_id=request_id,
|
|
102
|
+
|
|
103
|
+
status=status,
|
|
104
|
+
|
|
105
|
+
headers=headers,
|
|
106
|
+
|
|
107
|
+
body=body,
|
|
108
|
+
|
|
109
|
+
binary=binary,
|
|
110
|
+
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def make_http_chunk(
|
|
117
|
+
*,
|
|
118
|
+
request_id,
|
|
119
|
+
chunk_index,
|
|
120
|
+
total_chunks,
|
|
121
|
+
body,
|
|
122
|
+
binary=False,
|
|
123
|
+
status=None,
|
|
124
|
+
headers=None,
|
|
125
|
+
):
|
|
126
|
+
|
|
127
|
+
frame = make_frame(
|
|
128
|
+
|
|
129
|
+
MessageType.HTTP_RESPONSE_CHUNK,
|
|
130
|
+
|
|
131
|
+
request_id=request_id,
|
|
132
|
+
|
|
133
|
+
chunk_index=chunk_index,
|
|
134
|
+
|
|
135
|
+
total_chunks=total_chunks,
|
|
136
|
+
|
|
137
|
+
body=body,
|
|
138
|
+
|
|
139
|
+
binary=binary,
|
|
140
|
+
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if status is not None:
|
|
145
|
+
|
|
146
|
+
frame["status"] = status
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if headers is not None:
|
|
150
|
+
|
|
151
|
+
frame["headers"] = headers
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
return frame
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ============================================================
|
|
159
|
+
# WebSocket Frames
|
|
160
|
+
# ============================================================
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def make_ws_connect(
|
|
164
|
+
**payload,
|
|
165
|
+
):
|
|
166
|
+
|
|
167
|
+
return make_frame(
|
|
168
|
+
MessageType.WS_CONNECT,
|
|
169
|
+
**payload,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def make_ws_message(
|
|
175
|
+
**payload,
|
|
176
|
+
):
|
|
177
|
+
|
|
178
|
+
return make_frame(
|
|
179
|
+
MessageType.WS_MESSAGE,
|
|
180
|
+
**payload,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def make_ws_close(
|
|
186
|
+
**payload,
|
|
187
|
+
):
|
|
188
|
+
|
|
189
|
+
return make_frame(
|
|
190
|
+
MessageType.WS_CLOSE,
|
|
191
|
+
**payload,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ============================================================
|
|
197
|
+
# Heartbeat
|
|
198
|
+
# ============================================================
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def make_ping():
|
|
202
|
+
|
|
203
|
+
return make_frame(
|
|
204
|
+
MessageType.PING
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def make_pong():
|
|
210
|
+
|
|
211
|
+
return make_frame(
|
|
212
|
+
MessageType.PONG
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# ============================================================
|
|
218
|
+
# Errors / Metrics
|
|
219
|
+
# ============================================================
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def make_error(
|
|
223
|
+
message,
|
|
224
|
+
):
|
|
225
|
+
|
|
226
|
+
return make_frame(
|
|
227
|
+
|
|
228
|
+
MessageType.ERROR,
|
|
229
|
+
|
|
230
|
+
message=message,
|
|
231
|
+
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def make_metrics(
|
|
237
|
+
**payload,
|
|
238
|
+
):
|
|
239
|
+
|
|
240
|
+
return make_frame(
|
|
241
|
+
|
|
242
|
+
MessageType.METRICS,
|
|
243
|
+
|
|
244
|
+
**payload,
|
|
245
|
+
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# ============================================================
|
|
251
|
+
# Transport
|
|
252
|
+
# ============================================================
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
async def send_frame(
|
|
256
|
+
websocket,
|
|
257
|
+
frame,
|
|
258
|
+
):
|
|
259
|
+
|
|
260
|
+
"""
|
|
261
|
+
Send protocol frame.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
payload = json.dumps(
|
|
265
|
+
frame
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
await websocket.send(
|
|
269
|
+
payload
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
async def receive_frame(
|
|
275
|
+
websocket,
|
|
276
|
+
):
|
|
277
|
+
|
|
278
|
+
"""
|
|
279
|
+
Receive one protocol frame.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
|
|
284
|
+
message = await websocket.recv()
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
except ConnectionClosed:
|
|
288
|
+
|
|
289
|
+
logger.warning(
|
|
290
|
+
"WebSocket closed."
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
except Exception:
|
|
297
|
+
|
|
298
|
+
logger.exception(
|
|
299
|
+
"Receive frame failed."
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
if isinstance(
|
|
307
|
+
message,
|
|
308
|
+
bytes,
|
|
309
|
+
):
|
|
310
|
+
|
|
311
|
+
message = message.decode(
|
|
312
|
+
"utf-8"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
|
|
319
|
+
frame = json.loads(
|
|
320
|
+
message
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
except json.JSONDecodeError:
|
|
325
|
+
|
|
326
|
+
logger.warning(
|
|
327
|
+
"Invalid JSON received:"
|
|
328
|
+
" %s",
|
|
329
|
+
message,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
return None
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
if not isinstance(
|
|
337
|
+
frame,
|
|
338
|
+
dict,
|
|
339
|
+
):
|
|
340
|
+
|
|
341
|
+
logger.warning(
|
|
342
|
+
"Invalid frame type"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
return None
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
protocol = frame.get(
|
|
350
|
+
"protocol"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
if protocol != PROTOCOL_VERSION:
|
|
355
|
+
|
|
356
|
+
logger.warning(
|
|
357
|
+
"Protocol mismatch:"
|
|
358
|
+
" %s",
|
|
359
|
+
protocol,
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
return None
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
if "type" not in frame:
|
|
367
|
+
|
|
368
|
+
logger.warning(
|
|
369
|
+
"Frame missing type"
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
return None
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
return frame
|
|
File without changes
|