pymammotion 0.4.0b7__py3-none-any.whl → 0.4.0b8__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.
- pymammotion/mqtt/linkkit/__init__.py +5 -0
- pymammotion/mqtt/linkkit/h2client.py +585 -0
- pymammotion/mqtt/linkkit/linkkit.py +3020 -0
- pymammotion/mqtt/mammotion_mqtt.py +1 -1
- {pymammotion-0.4.0b7.dist-info → pymammotion-0.4.0b8.dist-info}/METADATA +2 -3
- {pymammotion-0.4.0b7.dist-info → pymammotion-0.4.0b8.dist-info}/RECORD +8 -5
- {pymammotion-0.4.0b7.dist-info → pymammotion-0.4.0b8.dist-info}/LICENSE +0 -0
- {pymammotion-0.4.0b7.dist-info → pymammotion-0.4.0b8.dist-info}/WHEEL +0 -0
@@ -0,0 +1,585 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014-2018 Alibaba Group. All rights reserved.
|
3
|
+
# License-Identifier: Apache-2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
13
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
#
|
18
|
+
|
19
|
+
import concurrent.futures
|
20
|
+
import hashlib
|
21
|
+
import hmac
|
22
|
+
import logging
|
23
|
+
import os
|
24
|
+
import ssl
|
25
|
+
import threading
|
26
|
+
import time
|
27
|
+
from types import TracebackType
|
28
|
+
|
29
|
+
import crcmod
|
30
|
+
import hyper
|
31
|
+
|
32
|
+
|
33
|
+
def _assert_value(condition, error_msg):
|
34
|
+
if not condition:
|
35
|
+
raise ValueError(error_msg)
|
36
|
+
|
37
|
+
|
38
|
+
_H2_OPT_HEART_BEAT_TIME_DEFAULT = 25
|
39
|
+
_H2_OPT_PORT_DEFAULT = 443
|
40
|
+
_H2_MAX_FILE_SIZE = 1024 * 1024 * 1024
|
41
|
+
|
42
|
+
|
43
|
+
def h2_set_option(opt, value) -> None:
|
44
|
+
if opt == "heart_beat_interval":
|
45
|
+
global _H2_OPT_HEART_BEAT_TIME_DEFAULT
|
46
|
+
_H2_OPT_HEART_BEAT_TIME_DEFAULT = value
|
47
|
+
elif opt == "port":
|
48
|
+
global _H2_OPT_PORT_DEFAULT
|
49
|
+
_H2_OPT_PORT_DEFAULT = value
|
50
|
+
elif opt == "max_file_size":
|
51
|
+
global _H2_MAX_FILE_SIZE
|
52
|
+
_H2_MAX_FILE_SIZE = value
|
53
|
+
|
54
|
+
|
55
|
+
class StreamHandler:
|
56
|
+
def __init__(self) -> None:
|
57
|
+
pass
|
58
|
+
|
59
|
+
def __enter__(self) -> None:
|
60
|
+
pass
|
61
|
+
|
62
|
+
def __exit__(
|
63
|
+
self, type: type[BaseException] | None, value: BaseException | None, trace: TracebackType | None
|
64
|
+
) -> None:
|
65
|
+
pass
|
66
|
+
|
67
|
+
def get_content_length(self):
|
68
|
+
return None
|
69
|
+
|
70
|
+
def next(self):
|
71
|
+
return None
|
72
|
+
|
73
|
+
def has_next(self) -> bool:
|
74
|
+
return False
|
75
|
+
|
76
|
+
|
77
|
+
class FileStreamHandler(StreamHandler):
|
78
|
+
def __init__(self, filename, block_size=512 * 1024, opt_crc64=False) -> None:
|
79
|
+
self.__filename = filename
|
80
|
+
self.__block_size = block_size
|
81
|
+
self.__size = os.stat(filename).st_size
|
82
|
+
self.__opt_crc64 = opt_crc64
|
83
|
+
self.__last_crc = 0
|
84
|
+
self.__read_size = 0
|
85
|
+
|
86
|
+
def get_content_length(self):
|
87
|
+
return self.__size
|
88
|
+
|
89
|
+
def __enter__(self) -> None:
|
90
|
+
logging.debug("open the file, filename:%s" % self.__filename)
|
91
|
+
self.__f = open(self.__filename, "rb")
|
92
|
+
self.__read_size = 0
|
93
|
+
|
94
|
+
def __exit__(
|
95
|
+
self, type: type[BaseException] | None, value: BaseException | None, trace: TracebackType | None
|
96
|
+
) -> None:
|
97
|
+
if self.__f:
|
98
|
+
self.__f.close()
|
99
|
+
self.__f = None
|
100
|
+
|
101
|
+
def next(self):
|
102
|
+
if not self.__f or self.__read_size >= self.__size:
|
103
|
+
return None
|
104
|
+
data = self.__f.read(self.__block_size)
|
105
|
+
if data:
|
106
|
+
self.__read_size += len(data)
|
107
|
+
if self.__opt_crc64:
|
108
|
+
do_crc64 = crcmod.mkCrcFun(
|
109
|
+
0x142F0E1EBA9EA3693, initCrc=self.__last_crc, xorOut=0xFFFFFFFFFFFFFFFF, rev=True
|
110
|
+
)
|
111
|
+
self.__last_crc = do_crc64(data)
|
112
|
+
return data
|
113
|
+
|
114
|
+
def has_next(self):
|
115
|
+
return self.__f.tell() < self.__size
|
116
|
+
|
117
|
+
def get_crc64(self):
|
118
|
+
return self.__last_crc
|
119
|
+
|
120
|
+
def get_read_size(self):
|
121
|
+
return self.__read_size
|
122
|
+
|
123
|
+
|
124
|
+
class H2Exception(Exception):
|
125
|
+
def __init__(self, code, msg) -> None:
|
126
|
+
Exception.__init__(self, msg)
|
127
|
+
self.__code = code
|
128
|
+
self.__msg = msg
|
129
|
+
|
130
|
+
def get_code(self):
|
131
|
+
return self.__code
|
132
|
+
|
133
|
+
def get_msg(self):
|
134
|
+
return self.__msg
|
135
|
+
|
136
|
+
def __name__(self) -> str:
|
137
|
+
return "H2Exception"
|
138
|
+
|
139
|
+
|
140
|
+
class UploadFileInfo:
|
141
|
+
def __init__(self, local_filename, remote_filename=None, overwrite=True) -> None:
|
142
|
+
self.local_filename = local_filename
|
143
|
+
self.opt_overwrite = overwrite
|
144
|
+
if not remote_filename:
|
145
|
+
self.remote_filename = os.path.basename(local_filename)
|
146
|
+
else:
|
147
|
+
self.remote_filename = remote_filename
|
148
|
+
|
149
|
+
def __name__(self) -> str:
|
150
|
+
return "UploadFileInfo"
|
151
|
+
|
152
|
+
|
153
|
+
class UploadFileResult:
|
154
|
+
def __init__(self, code=None, exception=None, upload_size=None, total_size=None, file_store_id=None) -> None:
|
155
|
+
self.upload_size = upload_size
|
156
|
+
self.total_size = total_size
|
157
|
+
self.file_store_id = file_store_id
|
158
|
+
self.code = code
|
159
|
+
self.exception = exception
|
160
|
+
|
161
|
+
def __name__(self) -> str:
|
162
|
+
return "UploadFileResult"
|
163
|
+
|
164
|
+
|
165
|
+
class H2FileUploadSink:
|
166
|
+
def on_file_upload_start(self, id, upload_file_info, user_data) -> None:
|
167
|
+
pass
|
168
|
+
|
169
|
+
def on_file_upload_end(self, id, upload_file_info, upload_file_result, user_data) -> None:
|
170
|
+
pass
|
171
|
+
|
172
|
+
def on_file_upload_progress(self, id, upload_file_info, upload_file_result, user_data) -> None:
|
173
|
+
pass
|
174
|
+
|
175
|
+
|
176
|
+
class H2FileTask:
|
177
|
+
def __init__(self, id, file_info, future_result) -> None:
|
178
|
+
self.__file_info = file_info
|
179
|
+
self.__future_result = future_result
|
180
|
+
self.__id = id
|
181
|
+
|
182
|
+
def get_file_info(self):
|
183
|
+
return self.__file_info
|
184
|
+
|
185
|
+
def get_future_result(self):
|
186
|
+
return self.__future_result
|
187
|
+
|
188
|
+
def result(self, timeout=None):
|
189
|
+
return self.__future_result.result(timeout)
|
190
|
+
|
191
|
+
def cancel(self) -> None:
|
192
|
+
self.__future_result.call()
|
193
|
+
|
194
|
+
def get_id(self):
|
195
|
+
return self.__id
|
196
|
+
|
197
|
+
def __name__(self) -> str:
|
198
|
+
return "H2FileTask"
|
199
|
+
|
200
|
+
|
201
|
+
class H2Stream:
|
202
|
+
def __init__(self, client, id) -> None:
|
203
|
+
self.__client = client
|
204
|
+
self.__conn = None
|
205
|
+
# self.__length = None
|
206
|
+
self.__total_sent_size = 0
|
207
|
+
self.__path = None
|
208
|
+
self.__id = id
|
209
|
+
self.__stream_id = None
|
210
|
+
self.__x_request_id = None
|
211
|
+
self.__x_data_stream_id = None
|
212
|
+
|
213
|
+
def __name__(self) -> str:
|
214
|
+
return "H2Stream"
|
215
|
+
|
216
|
+
def get_id(self):
|
217
|
+
return self.__id
|
218
|
+
|
219
|
+
def open(self, path, header):
|
220
|
+
_assert_value(path, "path is required")
|
221
|
+
|
222
|
+
with self.__client._get_auth_lock():
|
223
|
+
url = "/stream/open" + path
|
224
|
+
self.__conn = self.__client.get_connect()
|
225
|
+
|
226
|
+
# self.__length = length
|
227
|
+
self.__total_sent_size = 0
|
228
|
+
self.__path = path
|
229
|
+
|
230
|
+
logging.debug("request url: %s" % url)
|
231
|
+
|
232
|
+
# open the stream
|
233
|
+
conn_header = self.__client.get_default_header()
|
234
|
+
if header:
|
235
|
+
conn_header.update(header)
|
236
|
+
|
237
|
+
req_id = self.__conn.request("GET", url, None, conn_header)
|
238
|
+
response = self.__conn.get_response(req_id)
|
239
|
+
|
240
|
+
self.__check_response(response)
|
241
|
+
self.__x_request_id = response.headers["x-request-id"][0]
|
242
|
+
self.__x_data_stream_id = response.headers["x-data-stream-id"][0]
|
243
|
+
|
244
|
+
logging.debug("x_request_id: %s" % self.__x_request_id)
|
245
|
+
logging.debug("x_data_stream_id: %s" % self.__x_data_stream_id)
|
246
|
+
|
247
|
+
return response
|
248
|
+
|
249
|
+
def close(self, header):
|
250
|
+
logging.debug("close the stream")
|
251
|
+
final_header = {"x-request-id": self.__x_request_id, "x-data-stream-id": self.__x_data_stream_id}
|
252
|
+
final_header.update(header)
|
253
|
+
req_id = self.__conn.request("GET", "/stream/close/" + self.__path, None, final_header)
|
254
|
+
response = self.__conn.get_response(req_id)
|
255
|
+
self.__check_response(response)
|
256
|
+
return response
|
257
|
+
|
258
|
+
def send(self, headers, data_handler):
|
259
|
+
# prepare for sending
|
260
|
+
with self.__client._get_auth_lock():
|
261
|
+
url = "/stream/send" + self.__path
|
262
|
+
logging.debug("request url: %s" % url)
|
263
|
+
self.__stream_id = self.__conn.putrequest("GET", url)
|
264
|
+
self.__conn.putheader("x-request-id", self.__x_request_id, stream_id=self.__stream_id)
|
265
|
+
self.__conn.putheader("x-data-stream-id", self.__x_data_stream_id, stream_id=self.__stream_id)
|
266
|
+
content_length = data_handler.get_content_length()
|
267
|
+
if content_length:
|
268
|
+
self.__conn.putheader("content-length", "%s" % (content_length), self.__stream_id)
|
269
|
+
for k, v in headers.items():
|
270
|
+
self.__conn.putheader(k, v, self.__stream_id)
|
271
|
+
self.__conn.endheaders(stream_id=self.__stream_id)
|
272
|
+
|
273
|
+
with data_handler:
|
274
|
+
final = False
|
275
|
+
while not final:
|
276
|
+
data = data_handler.next()
|
277
|
+
if data == None or len(data) == 0:
|
278
|
+
break
|
279
|
+
final = not data_handler.has_next()
|
280
|
+
self.__conn.send(data, final, stream_id=self.__stream_id)
|
281
|
+
|
282
|
+
response = self.__conn.get_response(self.__stream_id)
|
283
|
+
# response.read()
|
284
|
+
self.__check_response(response)
|
285
|
+
return response
|
286
|
+
|
287
|
+
def __check_response(self, response, msg=None):
|
288
|
+
if response.status != 200:
|
289
|
+
raise H2Exception(response.status, msg if msg else "fail to request http/2, code:%d" % (response.status))
|
290
|
+
|
291
|
+
def __str__(self) -> str:
|
292
|
+
return "H2Stream(id=%s,stream_x_id=%s,x_request_id=%s,x_data_stream_id:%s" % (
|
293
|
+
self.__id,
|
294
|
+
self.__stream_id,
|
295
|
+
self.__x_request_id,
|
296
|
+
self.__x_data_stream_id,
|
297
|
+
)
|
298
|
+
|
299
|
+
|
300
|
+
class H2Client:
|
301
|
+
def __init__(
|
302
|
+
self, region, product_key, device_name, device_secret, client_id=None, opt_max_thread_num=4, endpoint=None
|
303
|
+
) -> None:
|
304
|
+
_assert_value(region, "region is not empty")
|
305
|
+
_assert_value(product_key, "product_key is not empty")
|
306
|
+
_assert_value(device_name, "device_name is not empty")
|
307
|
+
|
308
|
+
self.__product_key = product_key
|
309
|
+
self.__device_name = device_name
|
310
|
+
self.__client_id = client_id
|
311
|
+
self.__device_secret = device_secret
|
312
|
+
self.__region = region
|
313
|
+
self.__endpoint = endpoint
|
314
|
+
self.__opt_free_idle_connect = False
|
315
|
+
self.__connected = False
|
316
|
+
self.__port = _H2_OPT_PORT_DEFAULT
|
317
|
+
self.__conn = None
|
318
|
+
self.__opt_heart_beat_time = _H2_OPT_HEART_BEAT_TIME_DEFAULT
|
319
|
+
self.__conn_lock = threading.RLock()
|
320
|
+
self.__lock = threading.RLock()
|
321
|
+
self.__stream_list = []
|
322
|
+
self.__stream_list_lock = threading.RLock()
|
323
|
+
self.__thread_executor = concurrent.futures.ThreadPoolExecutor(max_workers=opt_max_thread_num)
|
324
|
+
self.__auth_lock = threading.RLock()
|
325
|
+
self.__id = 0
|
326
|
+
self.__heart_beat_lock = threading.RLock()
|
327
|
+
self.__timer = None
|
328
|
+
|
329
|
+
def get_endpoint(self):
|
330
|
+
return self.__endpoint
|
331
|
+
|
332
|
+
def get_actual_endpoint(self):
|
333
|
+
return self.__generate_endpoint()
|
334
|
+
|
335
|
+
def __generate_endpoint(self):
|
336
|
+
if self.__endpoint:
|
337
|
+
return self.__endpoint
|
338
|
+
else:
|
339
|
+
return self.__product_key + ".iot-as-http2.%s.aliyuncs.com" % (self.__region)
|
340
|
+
|
341
|
+
def open(self):
|
342
|
+
with self.__conn_lock:
|
343
|
+
if self.__conn:
|
344
|
+
logging.info("the client is opened")
|
345
|
+
return -1
|
346
|
+
return self.__connect()
|
347
|
+
|
348
|
+
def close(self):
|
349
|
+
with self.__conn_lock:
|
350
|
+
return self.__close_connect()
|
351
|
+
self.__close_all_streams()
|
352
|
+
|
353
|
+
def upload_file_async(
|
354
|
+
self, local_filename, remote_filename=None, over_write=True, upload_file_sink=None, upload_sink_user_data=None
|
355
|
+
):
|
356
|
+
_assert_value(local_filename, "local_filename is required")
|
357
|
+
self.__check_file(local_filename)
|
358
|
+
|
359
|
+
file_info = UploadFileInfo(local_filename, remote_filename, over_write)
|
360
|
+
|
361
|
+
future_result = self.__thread_executor.submit(
|
362
|
+
self.__post_file_task, file_info, upload_file_sink, upload_sink_user_data
|
363
|
+
)
|
364
|
+
return H2FileTask(id, file_info, future_result)
|
365
|
+
|
366
|
+
def upload_file_sync(
|
367
|
+
self,
|
368
|
+
local_filename,
|
369
|
+
remote_filename=None,
|
370
|
+
over_write=True,
|
371
|
+
timeout=None,
|
372
|
+
upload_file_sink=None,
|
373
|
+
upload_sink_user_data=None,
|
374
|
+
):
|
375
|
+
self.__check_file(local_filename)
|
376
|
+
f = self.upload_file_async(local_filename, remote_filename, over_write, upload_file_sink, upload_sink_user_data)
|
377
|
+
return f.result(timeout)
|
378
|
+
|
379
|
+
def __create_stream_id(self):
|
380
|
+
with self.__lock:
|
381
|
+
self.__id += 1
|
382
|
+
return self.__id
|
383
|
+
|
384
|
+
def new_stream(self):
|
385
|
+
return H2Stream(self, self.__create_stream_id())
|
386
|
+
|
387
|
+
def _get_auth_lock(self):
|
388
|
+
return self.__auth_lock
|
389
|
+
|
390
|
+
def __crc_equal(self, value1, value2):
|
391
|
+
if value1 == value2:
|
392
|
+
return True
|
393
|
+
return self.__to_unsign(value1) == self.__to_unsign(value2)
|
394
|
+
|
395
|
+
def __to_unsign(self, value):
|
396
|
+
return value if value > 0 else (0xFFFFFFFFFFFFFFFF + 1 + value)
|
397
|
+
|
398
|
+
def __check_file(self, path):
|
399
|
+
stat_info = os.stat(path)
|
400
|
+
if stat_info.st_size >= _H2_MAX_FILE_SIZE:
|
401
|
+
raise ValueError("maximum file size exceeded")
|
402
|
+
|
403
|
+
def __post_file_task(self, file_info, sink=None, user_data=None):
|
404
|
+
local_filename = file_info.local_filename
|
405
|
+
remote_filename = file_info.remote_filename
|
406
|
+
over_write = file_info.opt_overwrite
|
407
|
+
fs = None
|
408
|
+
file_store_id = None
|
409
|
+
exception = None
|
410
|
+
code = 0
|
411
|
+
x_file_upload_id = None
|
412
|
+
|
413
|
+
stream = self.new_stream()
|
414
|
+
self.__on_new_stream(stream)
|
415
|
+
try:
|
416
|
+
logging.info(
|
417
|
+
"start to post file, local_filename:%s, remote:%s, over_write:%d"
|
418
|
+
% (local_filename, remote_filename, over_write)
|
419
|
+
)
|
420
|
+
|
421
|
+
# callback
|
422
|
+
if sink:
|
423
|
+
sink.on_file_upload_start(stream.get_id(), file_info, user_data)
|
424
|
+
|
425
|
+
# open stream
|
426
|
+
header = {"x-file-name": remote_filename, "x-file-overwrite": "1" if over_write else "0"}
|
427
|
+
response = stream.open("/c/iot/sys/thing/file/upload", header)
|
428
|
+
x_file_upload_id = response.headers["x-file-upload-id"][0]
|
429
|
+
|
430
|
+
# send stream
|
431
|
+
header = {"x-file-upload-id": x_file_upload_id}
|
432
|
+
fs = FileStreamHandler(local_filename, opt_crc64=True)
|
433
|
+
stream.send(header, fs)
|
434
|
+
|
435
|
+
# close stream
|
436
|
+
response = stream.close(header)
|
437
|
+
remote_crc64 = int(response.headers["x-file-crc64ecma"][0])
|
438
|
+
logging.info("crc64, local:%ld, remote:%ld" % (fs.get_crc64(), remote_crc64))
|
439
|
+
if not self.__crc_equal(fs.get_crc64(), remote_crc64):
|
440
|
+
raise Exception("fail to check crc64, local:%ld, remote:%ld" % (fs.get_crc64(), remote_crc64))
|
441
|
+
file_store_id = response.headers["x-file-store-id"][0]
|
442
|
+
logging.info(
|
443
|
+
"finish uploading file, local_filename:%s, remote:%s, over_write:%d, file_store_id:%s"
|
444
|
+
% (local_filename, remote_filename, over_write, file_store_id)
|
445
|
+
)
|
446
|
+
|
447
|
+
return UploadFileResult(code, exception, fs.get_read_size(), fs.get_content_length, file_store_id)
|
448
|
+
except H2Exception as e:
|
449
|
+
logging.error(
|
450
|
+
"fail to upload the file, local_filename:%s, remote:%s, over_write:%d, x_file_upload_id:%s, stream:%s, code:%s, error:%s"
|
451
|
+
% (local_filename, remote_filename, over_write, x_file_upload_id, stream, e.get_code(), e)
|
452
|
+
)
|
453
|
+
return UploadFileResult(
|
454
|
+
e.get_code(),
|
455
|
+
exception,
|
456
|
+
(fs.get_read_size() if fs else -1),
|
457
|
+
(fs.get_content_length() if fs else -1),
|
458
|
+
file_store_id,
|
459
|
+
)
|
460
|
+
except Exception as e:
|
461
|
+
logging.error(
|
462
|
+
"fail to upload the file, local_filename:%s, remote:%s, over_write:%d, x_file_upload_id:%s, stream:%s, error:%s"
|
463
|
+
% (local_filename, remote_filename, over_write, x_file_upload_id, stream, e)
|
464
|
+
)
|
465
|
+
return UploadFileResult(
|
466
|
+
-1,
|
467
|
+
exception,
|
468
|
+
(fs.get_read_size() if fs else -1),
|
469
|
+
(fs.get_content_length() if fs else -1),
|
470
|
+
file_store_id,
|
471
|
+
)
|
472
|
+
# raise e
|
473
|
+
finally:
|
474
|
+
self.__on_free_stream(stream)
|
475
|
+
if sink:
|
476
|
+
result = UploadFileResult(
|
477
|
+
code,
|
478
|
+
exception,
|
479
|
+
(fs.get_read_size() if fs else -1),
|
480
|
+
(fs.get_content_length() if fs else -1),
|
481
|
+
file_store_id,
|
482
|
+
)
|
483
|
+
sink.on_file_upload_end(stream.get_id(), file_info, result, user_data)
|
484
|
+
|
485
|
+
def __connect(self) -> int:
|
486
|
+
with self.__conn_lock:
|
487
|
+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
488
|
+
h2_endpoint = self.__generate_endpoint()
|
489
|
+
logging.debug("http/2 endpoint:%s" % (h2_endpoint))
|
490
|
+
self.__conn = hyper.HTTP20Connection(
|
491
|
+
h2_endpoint, port=self.__port, force_proto=hyper.tls.NPN_PROTOCOL, ssl_context=ctx
|
492
|
+
)
|
493
|
+
return 0
|
494
|
+
|
495
|
+
def get_connect(self):
|
496
|
+
with self.__conn_lock:
|
497
|
+
if self.__conn:
|
498
|
+
return self.__conn
|
499
|
+
return self.__connect()
|
500
|
+
|
501
|
+
def __fill_auth_header(self, header):
|
502
|
+
client_id = self.__client_id or self.__device_name
|
503
|
+
timestamp = str(int(time.time() * 1000))
|
504
|
+
sign_content = (
|
505
|
+
"clientId"
|
506
|
+
+ client_id
|
507
|
+
+ "deviceName"
|
508
|
+
+ self.__device_name
|
509
|
+
+ "productKey"
|
510
|
+
+ self.__product_key
|
511
|
+
+ "timestamp"
|
512
|
+
+ timestamp
|
513
|
+
)
|
514
|
+
sign = hmac.new(self.__device_secret.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha256).hexdigest()
|
515
|
+
header["x-auth-param-timestamp"] = timestamp
|
516
|
+
header["x-auth-param-signmethod"] = "hmacsha256"
|
517
|
+
header["x-auth-param-sign"] = sign
|
518
|
+
header["x-auth-param-product-key"] = self.__product_key
|
519
|
+
header["x-auth-param-device-name"] = self.__device_name
|
520
|
+
header["x-auth-param-client-id"] = client_id
|
521
|
+
header["x-auth-name"] = "devicename"
|
522
|
+
return header
|
523
|
+
|
524
|
+
def __fill_sdk_header(self, header):
|
525
|
+
header["x-sdk-version"] = "1.2.0"
|
526
|
+
header["x-sdk-version-name"] = "1.2.0"
|
527
|
+
header["x-sdk-platform"] = "python"
|
528
|
+
return header
|
529
|
+
|
530
|
+
def get_default_header(self):
|
531
|
+
header = {}
|
532
|
+
self.__fill_auth_header(header)
|
533
|
+
self.__fill_sdk_header(header)
|
534
|
+
return header
|
535
|
+
|
536
|
+
def __close_connect(self) -> int:
|
537
|
+
with self.__conn_lock:
|
538
|
+
if self.__conn:
|
539
|
+
self.__conn.close(0)
|
540
|
+
return 0
|
541
|
+
|
542
|
+
def __close_all_streams(self) -> None:
|
543
|
+
with self.__stream_list_lock:
|
544
|
+
self.__stream_list.clear()
|
545
|
+
self.__stream_list = None
|
546
|
+
self.__stop_heart_beat()
|
547
|
+
|
548
|
+
def __on_new_stream(self, stream) -> None:
|
549
|
+
with self.__stream_list_lock:
|
550
|
+
self.__stream_list.append(stream)
|
551
|
+
|
552
|
+
if len(self.__stream_list) == 1:
|
553
|
+
self.__start_heart_beat()
|
554
|
+
|
555
|
+
def __on_free_stream(self, stream) -> None:
|
556
|
+
with self.__stream_list_lock:
|
557
|
+
self.__stream_list.remove(stream)
|
558
|
+
|
559
|
+
if len(self.__stream_list) == 0:
|
560
|
+
self.__stop_heart_beat()
|
561
|
+
|
562
|
+
def __start_heart_beat(self) -> None:
|
563
|
+
logging.debug("start heart_beat")
|
564
|
+
self.__schedule_heart_beat()
|
565
|
+
|
566
|
+
def __handle_heart_beat(self) -> None:
|
567
|
+
logging.debug("heart...")
|
568
|
+
self.__conn.ping(b"PINGPONG")
|
569
|
+
self.__schedule_heart_beat()
|
570
|
+
|
571
|
+
def __stop_heart_beat(self) -> None:
|
572
|
+
logging.debug("stop heart")
|
573
|
+
self.__cancel_heart_beat()
|
574
|
+
|
575
|
+
def __schedule_heart_beat(self) -> None:
|
576
|
+
with self.__heart_beat_lock:
|
577
|
+
if self.__opt_heart_beat_time and self.__opt_heart_beat_time > 0:
|
578
|
+
self.__timer = threading.Timer(self.__opt_heart_beat_time, self.__handle_heart_beat)
|
579
|
+
self.__timer.start()
|
580
|
+
|
581
|
+
def __cancel_heart_beat(self) -> None:
|
582
|
+
with self.__heart_beat_lock:
|
583
|
+
if self.__timer:
|
584
|
+
self.__timer.cancel()
|
585
|
+
self.__timer = None
|