-
Notifications
You must be signed in to change notification settings - Fork 214
/
Copy pathmqtt5_client_builder.py
787 lines (608 loc) · 37.8 KB
/
mqtt5_client_builder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
"""
Builder functions to create a :class:`awscrt.mqtt5.Client`, configured for use with AWS IoT Core.
The following keyword arguments are common to all builder functions:
Required Keyword Arguments:
**endpoint** (`str`): Host name of AWS IoT server.
Optional Keyword Arguments (omit, or set `None` to get default value):
**client_options** (:class:`awscrt.mqtt5.ClientOptions`): This dataclass can be used to to apply all
configuration options for Client creation. Any options set within will supercede defaults
assigned by the builder. Any omitted arguments within this class will be filled by additional
keyword arguments provided to the builder or be set to their default values.
**connect_options** (:class:`awscrt.mqtt5.ConnectPacket`): This dataclass can be used to apply connection
options for the client. Any options set within will supercede defaults assigned by the builder but
will not overwrite options set by connect_options included within a client_options keyword argument.
Any omitted arguments within this class will be assigned values of keyword arguments provided to
the builder.
**client_id** (`str`): ID to place in CONNECT packet. Must be unique across all devices/clients.
If an ID is already in use, the other client will be disconnected. If one is not provided,
AWS IoT server will assign a unique ID for use and return it in the CONNACK packet.
**port** (`int`): Override default server port.
Default port is 443 if system supports ALPN or websockets are being used.
Otherwise, default port is 8883.
**client_bootstrap** (:class:`awscrt.io.ClientBootstrap`): Client bootstrap used to establish connection.
The ClientBootstrap will default to the static default (Io.ClientBootstrap.get_or_create_static_default)
if the argument is omitted or set to 'None'.
**http_proxy_options** (:class:`awscrt.http.HttpProxyOptions`): HTTP proxy options to use
**keep_alive_interval_sec** (`int`): The maximum time interval, in seconds, that is permitted to elapse
between the point at which the client finishes transmitting one MQTT packet and the point it starts
sending the next. The client will use PINGREQ packets to maintain this property. If the responding
CONNACK contains a keep alive property value, then that is the negotiated keep alive value. Otherwise,
the keep alive sent by the client is the negotiated value. keep_alive_interval_sec must be set to at
least 1 second greater than ping_timeout_ms (default 30,000 ms) or it will fail validation.
**username** (`str`): Username to connect with.
**password** (`str`): Password to connect with.
**session_expiry_interval_sec** (`int`): A time interval, in seconds, that the client requests the server
to persist this connection's MQTT session state for. Has no meaning if the client has not been
configured to rejoin sessions. Must be non-zero in order to successfully rejoin a session. If the
responding CONNACK contains a session expiry property value, then that is the negotiated session
expiry value. Otherwise, the session expiry sent by the client is the negotiated value.
**request_response_information** (`bool`): If true, requests that the server send response information in
the subsequent CONNACK. This response information may be used to set up request-response implementations
over MQTT, but doing so is outside the scope of the MQTT5 spec and client.
**request_problem_information** (`bool`): If true, requests that the server send additional diagnostic
information (via response string or user properties) in DISCONNECT or CONNACK packets from the server.
**receive_maximum** (`int`): Notifies the server of the maximum number of in-flight QoS 1 and 2 messages the
client is willing to handle. If omitted or null, then no limit is requested.
**maximum_packet_size** (`int`): Notifies the server of the maximum packet size the client is willing to handle.
If omitted or null, then no limit beyond the natural limits of MQTT packet size is requested.
**will_delay_interval_sec** (`int`): A time interval, in seconds, that the server should wait (for a session
reconnection) before sending the will message associated with the connection's session. If omitted or
null, the server will send the will when the associated session is destroyed. If the session is destroyed
before a will delay interval has elapsed, then the will must be sent at the time of session destruction.
**will** (:class:`awscrt.mqtt5.PublishPacket`): The definition of a message to be published when the connection's
session is destroyed by the server or when the will delay interval has elapsed, whichever comes first. If
null, then nothing will be sent.
**user_properties** (`Sequence` [:class:`awscrt.mqtt5.UserProperty`]): List of MQTT5 user properties included
with the packet.
**session_behavior** (:class:`awscrt.mqtt5.ClientSessionBehaviorType`): How the MQTT5 client should behave with
respect to MQTT sessions.
**extended_validation_and_flow_control_options** (:class:`awscrt.mqtt5.ExtendedValidationAndFlowControlOptions`):
The additional controls for client behavior with respect to operation validation and flow control; these
checks go beyond the base MQTT5 spec to respect limits of specific MQTT brokers. If argument is omitted or null,
then set to AWS_IOT_CORE_DEFAULTS.
**offline_queue_behavior** (:class:`awscrt.mqtt5.ClientOperationQueueBehaviorType`): Returns how disconnects
affect the queued and in-progress operations tracked by the client. Also controls how new operations are
handled while the client is not connected. In particular, if the client is not connected, then any operation
that would be failed on disconnect (according to these rules) will also be rejected.
**topic_aliasing_options** (:class:`awscrt.mqtt5.TopicAliasingOptions`): Configuration options for how the client
should use the topic aliasing features of MQTT5
**retry_jitter_mode** (:class:`awscrt.mqtt5.ExponentialBackoffJitterMode`): How the reconnect delay is modified
in order to smooth out the distribution of reconnection attempt timepoints for a large set of reconnecting
clients.
**min_reconnect_delay_ms** (`int`): The minimum amount of time to wait to reconnect after a disconnect.
Exponential backoff is performed with jitter after each connection failure.
**max_reconnect_delay_ms** (`int`): The maximum amount of time to wait to reconnect after a disconnect.
Exponential backoff is performed with jitter after each connection failure.
**min_connected_time_to_reset_reconnect_delay_ms** (`int`): The amount of time that must elapse with an
established connection before the reconnect delay is reset to the minimum. This helps alleviate
bandwidth-waste in fast reconnect cycles due to permission failures on operations.
**ping_timeout_ms** (`int`): The time interval to wait after sending a PINGREQ for a PINGRESP to arrive. If one
does not arrive, the client will close the current connection.
**connack_timeout_ms** (`int`): The time interval to wait after sending a CONNECT request for a CONNACK to arrive.
If one does not arrive, the connection will be shut down.
**ack_timeout_sec** (`int`): The time interval to wait for an ack after sending a QoS 1+ PUBLISH, SUBSCRIBE,
or UNSUBSCRIBE before failing the operation.
**on_publish_received** (`Callable`): Callback invoked for all publish packets received by client.
The function should take the following arguments and return nothing:
* `publish_received_data` (:class:`awscrt.mqtt5.PublishReceivedData`): Dataclass
containing the following:
* `publish_packet`: (:class:`awscrt.mqtt5.PublishPacket`): Data model of an `MQTT5 PUBLISH <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901100>` _ packet.
**on_lifecycle_stopped** (`Callable`): Callback invoked for Lifecycle Event Stopped.
The function should take the following arguments and return nothing:
* `lifecycle_stopped_data` (:class:`awscrt.mqtt5.LifecycleStoppedData`): Currently unused dataclass.
**on_lifecycle_attempting_connect** (`Callable`): Callback invoked for Lifecycle Event Attempting Connect.
The function should take the following arguments and return nothing:
* `lifecycle_attempting_connect_data` (:class:`awscrt.mqtt5.LifecycleAttemptingConnectData`): Currently
unused dataclass.
**on_lifecycle_connection_success** (`Callable`): Callback invoked for Lifecycle Event Connection Success.
The function should take the following arguments and return nothing:
* `lifecycle_connect_success_data` (:class:`awscrt.mqtt5.LifecycleConnectSuccessData`): Dataclass
containing the following:
* `connack_packet` (:class:`awscrt.mqtt5.ConnackPacket`): Data model of an `MQTT5 CONNACK <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074>`_ packet.
* `negotiated_settings` (:class:`awscrt.mqtt5.NegotiatedSettings`): Mqtt behavior settings that have been dynamically negotiated as part of the CONNECT/CONNACK exchange.
**on_lifecycle_connection_failure** (`Callable`): Callback invoked for Lifecycle Event Connection Failure.
The function should take the following arguments and return nothing:
* `lifecycle_connection_failure_data` (:class:`awscrt.mqtt5.LifecycleConnectFailureData`): Dataclass
containing the following:
* `connack_packet` (:class:`awscrt.mqtt5.ConnackPacket`): Data model of an `MQTT5 CONNACK <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074>`_ packet.
* `error_code` (`int`): Exception which caused connection failure.
**on_lifecycle_disconnection** (`Callable`): Callback invoked for Lifecycle Event Disconnection.
The function should take the following arguments and return nothing:
* `lifecycle_disconnect_data` (:class:`awscrt.mqtt5.LifecycleDisconnectData`): Dataclass
containing the following:
* `disconnect_packet` (:class:`awscrt.mqtt5.DisconnectPacket`): Data model of an `MQTT5 DISCONNECT <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205>`_ packet.
* `error_code` (`int`): Exception which caused disconnection.
**ca_filepath** (`str`): Override default trust store with CA certificates from this PEM formatted file.
**ca_dirpath** (`str`): Override default trust store with CA certificates loaded from this directory (Unix only).
**ca_bytes** (`bytes`): Override default trust store with CA certificates from these PEM formatted bytes.
**enable_metrics_collection** (`bool`): Whether to send the SDK version number in the CONNECT packet.
Default is True.
"""
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0.
import awscrt.auth
import awscrt.io
import awscrt.mqtt5
import urllib.parse
DEFAULT_WEBSOCKET_MQTT_PORT = 443
DEFAULT_DIRECT_MQTT_PORT = 8883
DEFAULT_KEEP_ALIVE = 1200
def _check_required_kwargs(**kwargs):
for required in ['endpoint']:
if not kwargs.get(required):
raise TypeError("Builder needs keyword-only argument '{}'".format(required))
def _get(kwargs, name, default=None):
"""
Returns kwargs['name'] if it exists and is not None.
Otherwise returns default.
This function exists so users can pass some_arg=None to get its default
value, instead of literally passing None.
"""
val = kwargs.get(name)
if val is None:
val = default
return val
_metrics_str = None
def _get_metrics_str(current_username=""):
global _metrics_str
username_has_query = False
if current_username.find("?") != -1:
username_has_query = True
if _metrics_str is None:
try:
import pkg_resources
try:
version = pkg_resources.get_distribution("awsiotsdk").version
_metrics_str = "SDK=PythonV2&Version={}".format(version)
except pkg_resources.DistributionNotFound:
_metrics_str = "SDK=PythonV2&Version=dev"
except BaseException:
_metrics_str = ""
if not _metrics_str == "":
if username_has_query:
return "&" + _metrics_str
else:
return "?" + _metrics_str
else:
return ""
def _builder(
tls_ctx_options,
use_websockets=False,
websocket_handshake_transform=None,
use_custom_authorizer=False,
**kwargs):
username = _get(kwargs, 'username', '')
if _get(kwargs, 'enable_metrics_collection', True):
username += _get_metrics_str(username)
client_options = _get(kwargs, 'client_options')
if client_options is None:
client_options = awscrt.mqtt5.ClientOptions(
host_name=_get(kwargs, 'endpoint')
)
if client_options.connect_options is None:
client_options.connect_options = _get(kwargs, 'connect_options', awscrt.mqtt5.ConnectPacket())
# Client Options
if client_options.port is None:
client_options.port = _get(kwargs, 'port')
if client_options.bootstrap is None:
client_options.bootstrap = _get(kwargs, 'client_bootstrap')
if client_options.socket_options is None:
client_options.socket_options = _get(kwargs, 'socket_options')
if client_options.http_proxy_options is None:
client_options.http_proxy_options = kwargs.get(
'http_proxy_options', kwargs.get(
'websocket_proxy_options', None))
if client_options.session_behavior is None:
client_options.session_behavior = _get(kwargs, 'session_behavior')
if client_options.extended_validation_and_flow_control_options is None:
client_options.extended_validation_and_flow_control_options = _get(
kwargs,
'extended_validation_and_flow_control_options',
default=awscrt.mqtt5.ExtendedValidationAndFlowControlOptions.AWS_IOT_CORE_DEFAULTS)
if client_options.offline_queue_behavior is None:
client_options.offline_queue_behavior = _get(kwargs, 'offline_queue_behavior')
if client_options.retry_jitter_mode is None:
client_options.retry_jitter_mode = _get(kwargs, 'retry_jitter_mode')
if client_options.min_reconnect_delay_ms is None:
client_options.min_reconnect_delay_ms = _get(kwargs, 'min_reconnect_delay_ms')
if client_options.max_reconnect_delay_ms is None:
client_options.max_reconnect_delay_ms = _get(kwargs, 'max_reconnect_delay_ms')
if client_options.min_connected_time_to_reset_reconnect_delay_ms is None:
client_options.min_connected_time_to_reset_reconnect_delay_ms = _get(
kwargs, 'min_connected_time_to_reset_reconnect_delay_ms')
if client_options.ping_timeout_ms is None:
client_options.ping_timeout_ms = _get(kwargs, 'ping_timeout_ms')
if client_options.connack_timeout_ms is None:
client_options.connack_timeout_ms = _get(kwargs, 'connack_timeout_ms')
if client_options.ack_timeout_sec is None:
client_options.ack_timeout_sec = _get(kwargs, 'ack_timeout_sec')
if client_options.websocket_handshake_transform is None:
client_options.websocket_handshake_transform = websocket_handshake_transform
if client_options.topic_aliasing_options is None:
client_options.topic_aliasing_options = _get(kwargs, 'topic_aliasing_options')
# Connect Options
if client_options.connect_options.client_id is None:
client_options.connect_options.client_id = _get(kwargs, 'client_id')
if client_options.connect_options.keep_alive_interval_sec is None:
client_options.connect_options.keep_alive_interval_sec = _get(
kwargs, 'keep_alive_interval_sec', DEFAULT_KEEP_ALIVE)
client_options.connect_options.username = username
if client_options.connect_options.password is None:
client_options.connect_options.password = _get(kwargs, 'password')
if client_options.connect_options.session_expiry_interval_sec is None:
client_options.connect_options.session_expiry_interval_sec = _get(kwargs, 'session_expiry_interval_sec')
if client_options.connect_options.request_response_information is None:
client_options.connect_options.request_response_information = _get(kwargs, 'request_response_information')
if client_options.connect_options.request_problem_information is None:
client_options.connect_options.request_problem_information = _get(kwargs, 'request_problem_information')
if client_options.connect_options.receive_maximum is None:
client_options.connect_options.receive_maximum = _get(kwargs, 'receive_maximum')
if client_options.connect_options.maximum_packet_size is None:
client_options.connect_options.maximum_packet_size = _get(kwargs, 'maximum_packet_size')
if client_options.connect_options.will_delay_interval_sec is None:
client_options.connect_options.will_delay_interval_sec = _get(kwargs, 'will_delay_interval_sec')
if client_options.connect_options.will is None:
client_options.connect_options.will = _get(kwargs, 'will')
if client_options.connect_options.user_properties is None:
client_options.connect_options.user_properties = _get(kwargs, 'user_properties')
# Callbacks
if client_options.on_publish_callback_fn is None:
client_options.on_publish_callback_fn = _get(kwargs, 'on_publish_received')
if client_options.on_lifecycle_event_stopped_fn is None:
client_options.on_lifecycle_event_stopped_fn = _get(kwargs, 'on_lifecycle_stopped')
if client_options.on_lifecycle_event_attempting_connect_fn is None:
client_options.on_lifecycle_event_attempting_connect_fn = _get(kwargs, 'on_lifecycle_attempting_connect')
if client_options.on_lifecycle_event_connection_success_fn is None:
client_options.on_lifecycle_event_connection_success_fn = _get(kwargs, 'on_lifecycle_connection_success')
if client_options.on_lifecycle_event_connection_failure_fn is None:
client_options.on_lifecycle_event_connection_failure_fn = _get(kwargs, 'on_lifecycle_connection_failure')
if client_options.on_lifecycle_event_disconnection_fn is None:
client_options.on_lifecycle_event_disconnection_fn = _get(kwargs, 'on_lifecycle_disconnection')
ca_bytes = _get(kwargs, 'ca_bytes')
ca_filepath = _get(kwargs, 'ca_filepath')
ca_dirpath = _get(kwargs, 'ca_dirpath')
if ca_bytes:
tls_ctx_options.override_default_trust_store(ca_bytes)
elif ca_filepath or ca_dirpath:
tls_ctx_options.override_default_trust_store_from_path(ca_dirpath, ca_filepath)
if client_options.port is None:
# prefer 443, even for direct MQTT connections, since it's less likely to be blocked by firewalls
if use_websockets or awscrt.io.is_alpn_available():
client_options.port = DEFAULT_WEBSOCKET_MQTT_PORT
else:
client_options.port = DEFAULT_DIRECT_MQTT_PORT
if client_options.port == 443 and awscrt.io.is_alpn_available() and use_custom_authorizer is False:
tls_ctx_options.alpn_list = ['http/1.1'] if use_websockets else ['x-amzn-mqtt-ca']
tls_ctx = awscrt.io.ClientTlsContext(tls_ctx_options)
client_options.tls_ctx = tls_ctx
client = awscrt.mqtt5.Client(client_options=client_options)
return client
def mtls_from_path(cert_filepath, pri_key_filepath, **kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an mTLS MQTT5 Client to AWS IoT.
TLS arguments are passed as filepaths.
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Keyword Args:
cert_filepath (str): Path to certificate file.
pri_key_filepath (str): Path to private key file.
"""
_check_required_kwargs(**kwargs)
tls_ctx_options = awscrt.io.TlsContextOptions.create_client_with_mtls_from_path(cert_filepath, pri_key_filepath)
return _builder(tls_ctx_options, **kwargs)
def mtls_from_bytes(cert_bytes, pri_key_bytes, **kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an mTLS MQTT5 Client to AWS IoT.
TLS arguments are passed as in-memory bytes.
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Keyword Args:
cert_bytes (bytes): Certificate file bytes.
pri_key_bytes (bytes): Private key bytes.
"""
_check_required_kwargs(**kwargs)
tls_ctx_options = awscrt.io.TlsContextOptions.create_client_with_mtls(cert_bytes, pri_key_bytes)
return _builder(tls_ctx_options, **kwargs)
def mtls_with_pkcs11(*,
pkcs11_lib: awscrt.io.Pkcs11Lib,
user_pin: str,
slot_id: int = None,
token_label: str = None,
private_key_label: str = None,
cert_filepath: str = None,
cert_bytes=None,
**kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an mTLS MQTT connection to AWS IoT,
using a PKCS#11 library for private key operations.
NOTE: Unix only
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Args:
pkcs11_lib: Use this PKCS#11 library
user_pin: User PIN, for logging into the PKCS#11 token.
Pass `None` to log into a token with a "protected authentication path".
slot_id: ID of slot containing PKCS#11 token.
If not specified, the token will be chosen based on other criteria (such as token label).
token_label: Label of the PKCS#11 token to use.
If not specified, the token will be chosen based on other criteria (such as slot ID).
private_key_label: Label of private key object on PKCS#11 token.
If not specified, the key will be chosen based on other criteria
(such as being the only available private key on the token).
cert_filepath: Use this X.509 certificate (file on disk).
The certificate must be PEM-formatted. The certificate may be
specified by other means instead (ex: `cert_bytes`)
cert_bytes (Optional[Union[str, bytes, bytearray]]):
Use this X.509 certificate (contents in memory).
The certificate must be PEM-formatted. The certificate may be
specified by other means instead (ex: `cert_filepath`)
"""
_check_required_kwargs(**kwargs)
tls_ctx_options = awscrt.io.TlsContextOptions.create_client_with_mtls_pkcs11(
pkcs11_lib=pkcs11_lib,
user_pin=user_pin,
slot_id=slot_id,
token_label=token_label,
private_key_label=private_key_label,
cert_file_path=cert_filepath,
cert_file_contents=cert_bytes)
return _builder(tls_ctx_options, **kwargs)
def mtls_with_pkcs12(*,
pkcs12_filepath: str,
pkcs12_password: str,
**kwargs) -> awscrt.mqtt.Connection:
"""
This builder creates an :class:`awscrt.mqtt.Connection`, configured for an mTLS MQTT connection to AWS IoT,
using a PKCS#12 certificate.
NOTE: MacOS only
This function takes all :mod:`common arguments<awsiot.mqtt_connection_builder>`
described at the top of this doc, as well as...
Args:
pkcs12_filepath: Path to the PKCS12 file to use
pkcs12_password: The password for the PKCS12 file.
"""
_check_required_kwargs(**kwargs)
tls_ctx_options = awscrt.io.TlsContextOptions.create_client_with_mtls_pkcs12(
pkcs12_filepath=pkcs12_filepath,
pkcs12_password=pkcs12_password)
return _builder(tls_ctx_options, **kwargs)
def mtls_with_windows_cert_store_path(*,
cert_store_path: str,
**kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an mTLS MQTT5 Client to AWS IoT,
using a client certificate in a Windows certificate store.
NOTE: Windows only
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Args:
cert_store_path: Path to certificate in a Windows certificate store.
The path must use backslashes and end with the certificate's thumbprint.
Example: ``CurrentUser\\MY\\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6``
"""
_check_required_kwargs(**kwargs)
tls_ctx_options = awscrt.io.TlsContextOptions.create_client_with_mtls_windows_cert_store_path(cert_store_path)
return _builder(tls_ctx_options, **kwargs)
def websockets_with_default_aws_signing(
region,
credentials_provider,
websocket_proxy_options=None,
**kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an MQTT5 Client over websockets to AWS IoT.
The websocket handshake is signed using credentials from the credentials_provider.
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Keyword Args:
region (str): AWS region to use when signing.
credentials_provider (awscrt.auth.AwsCredentialsProvider): Source of AWS credentials to use when signing.
websocket_proxy_options (awscrt.http.HttpProxyOptions): Deprecated,
for proxy settings use `http_proxy_options` (described in
:mod:`common arguments<awsiot.mqtt5_client_builder>`)
"""
_check_required_kwargs(**kwargs)
def _sign_websocket_handshake_request(transform_args, **kwargs):
# transform_args need to know when transform is done
try:
signing_config = awscrt.auth.AwsSigningConfig(
algorithm=awscrt.auth.AwsSigningAlgorithm.V4,
signature_type=awscrt.auth.AwsSignatureType.HTTP_REQUEST_QUERY_PARAMS,
credentials_provider=credentials_provider,
region=region,
service='iotdevicegateway',
omit_session_token=True, # IoT is weird and does not sign X-Amz-Security-Token
)
signing_future = awscrt.auth.aws_sign_request(transform_args.http_request, signing_config)
signing_future.add_done_callback(lambda x: transform_args.set_done(x.exception()))
except Exception as e:
transform_args.set_done(e)
return websockets_with_custom_handshake(_sign_websocket_handshake_request, websocket_proxy_options, **kwargs)
def websockets_with_custom_handshake(
websocket_handshake_transform,
websocket_proxy_options=None,
**kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an MQTT5 Client over websockets,
with a custom function to transform the websocket handshake request before it is sent to the server.
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Keyword Args:
websocket_handshake_transform (Callable): Function to transform websocket handshake request.
If provided, function is called each time a websocket connection is attempted.
The function may modify the HTTP request before it is sent to the server.
See :class:`awscrt.mqtt.WebsocketHandshakeTransformArgs` for more info.
Function should take the following arguments and return nothing:
* `transform_args` (:class:`awscrt.mqtt5.WebsocketHandshakeTransformArgs`):
Contains HTTP request to be transformed. Function must call
`transform_args.done()` when complete.
* `**kwargs` (dict): Forward-compatibility kwargs.
websocket_proxy_options (awscrt.http.HttpProxyOptions): Deprecated,
for proxy settings use `http_proxy_options` (described in
:mod:`common arguments<awsiot.mqtt5_client_builder>`)
"""
_check_required_kwargs(**kwargs)
tls_ctx_options = awscrt.io.TlsContextOptions()
return _builder(tls_ctx_options=tls_ctx_options,
use_websockets=True,
websocket_handshake_transform=websocket_handshake_transform,
websocket_proxy_options=websocket_proxy_options,
**kwargs)
def _add_to_username_parameter(input_string, parameter_value, parameter_pretext):
"""
Helper function to add parameters to the username in the direct_with_custom_authorizer function
"""
return_string = input_string
if return_string.find("?") != -1:
return_string += "&"
else:
return_string += "?"
if parameter_value.find(parameter_pretext) != -1:
return return_string + parameter_value
else:
return return_string + parameter_pretext + parameter_value
def direct_with_custom_authorizer(
auth_username=None,
auth_authorizer_name=None,
auth_authorizer_signature=None,
auth_password=None,
auth_token_key_name=None,
auth_token_value=None,
**kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an MQTT5 Client using a custom
authorizer. This function will set the username, port, and TLS options.
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Keyword Args:
auth_username (`str`): The username to use with the custom authorizer.
If provided, the username given will be passed when connecting to the custom authorizer.
If not provided, it will check to see if a username has already been set (via username="example")
and will use that instead. Custom authentication parameters will be appended as appropriate
to any supplied username value.
auth_password (`str`): The password to use with the custom authorizer.
If not provided, then no password will be sent in the initial CONNECT packet.
auth_authorizer_name (`str`): Name of the custom authorizer to use.
Required if the endpoint does not have a default custom authorizer associated with it. It is strongly
suggested to URL-encode this value; the SDK will not do so for you.
auth_authorizer_signature (`str`): The digital signature of the token value in the `auth_token_value`
parameter. The signature must be based on the private key associated with the custom authorizer. The
signature must be base64 encoded.
Required if the custom authorizer has signing enabled.
auth_token_key_name (`str`): Key used to extract the custom authorizer token from MQTT username query-string
properties.
Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode
this value; the SDK will not do so for you.
auth_token_value (`str`): An opaque token value. This value must be signed by the private key associated with
the custom authorizer and the result passed in via the `auth_authorizer_signature` parameter.
Required if the custom authorizer has signing enabled.
"""
_check_required_kwargs(**kwargs)
username_string = ""
if auth_username is None:
if not _get(kwargs, "username") is None:
username_string += _get(kwargs, "username")
else:
username_string += auth_username
if auth_authorizer_name is not None:
username_string = _add_to_username_parameter(
username_string, auth_authorizer_name, "x-amz-customauthorizer-name=")
if auth_authorizer_signature is not None:
encoded_signature = auth_authorizer_signature
if "%" not in encoded_signature:
encoded_signature = urllib.parse.quote(encoded_signature)
username_string = _add_to_username_parameter(
username_string, encoded_signature, "x-amz-customauthorizer-signature=")
if auth_token_key_name is not None and auth_token_value is not None:
username_string = _add_to_username_parameter(username_string, auth_token_value, auth_token_key_name + "=")
kwargs["username"] = username_string
kwargs["password"] = auth_password
tls_ctx_options = awscrt.io.TlsContextOptions()
tls_ctx_options.alpn_list = ["mqtt"]
return _builder(tls_ctx_options=tls_ctx_options,
use_websockets=False,
use_custom_authorizer=True,
**kwargs)
def websockets_with_custom_authorizer(
auth_username=None,
auth_authorizer_name=None,
auth_authorizer_signature=None,
auth_password=None,
websocket_proxy_options=None,
auth_token_key_name=None,
auth_token_value=None,
**kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, configured for an MQTT5 Client using a custom
authorizer through websockets. This function will set the username, port, and TLS options.
This function takes all :mod:`common arguments<awsiot.mqtt5_client_builder>`
described at the top of this doc, as well as...
Keyword Args:
auth_username (`str`): The username to use with the custom authorizer.
If provided, the username given will be passed when connecting to the custom authorizer.
If not provided, it will check to see if a username has already been set (via username="example")
and will use that instead. Custom authentication parameters will be appended as appropriate
to any supplied username value.
auth_password (`str`): The password to use with the custom authorizer.
If not provided, then no password will be sent in the initial CONNECT packet.
auth_authorizer_name (`str`): Name of the custom authorizer to use.
Required if the endpoint does not have a default custom authorizer associated with it. It is strongly
suggested to URL-encode this value; the SDK will not do so for you.
auth_authorizer_signature (`str`): The digital signature of the token value in the `auth_token_value`
parameter. The signature must be based on the private key associated with the custom authorizer. The
signature must be base64 encoded.
Required if the custom authorizer has signing enabled.
auth_token_key_name (`str`): Key used to extract the custom authorizer token from MQTT username query-string
properties.
Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode
this value; the SDK will not do so for you.
auth_token_value (`str`): An opaque token value. This value must be signed by the private key associated with
the custom authorizer and the result passed in via the `auth_authorizer_signature` parameter.
Required if the custom authorizer has signing enabled.
websocket_proxy_options (awscrt.http.HttpProxyOptions): Deprecated,
for proxy settings use `http_proxy_options` (described in
:mod:`common arguments<awsiot.mqtt5_client_builder>`)
"""
_check_required_kwargs(**kwargs)
username_string = ""
if auth_username is None:
if not _get(kwargs, "username") is None:
username_string += _get(kwargs, "username")
else:
username_string += auth_username
if auth_authorizer_name is not None:
username_string = _add_to_username_parameter(
username_string, auth_authorizer_name, "x-amz-customauthorizer-name=")
if auth_authorizer_signature is not None:
encoded_signature = auth_authorizer_signature
if "%" not in encoded_signature:
encoded_signature = urllib.parse.quote(encoded_signature)
username_string = _add_to_username_parameter(
username_string, encoded_signature, "x-amz-customauthorizer-signature=")
if auth_token_key_name is not None and auth_token_value is not None:
username_string = _add_to_username_parameter(username_string, auth_token_value, auth_token_key_name + "=")
kwargs["username"] = username_string
kwargs["password"] = auth_password
tls_ctx_options = awscrt.io.TlsContextOptions()
def _sign_websocket_handshake_request(transform_args, **kwargs):
# transform_args need to know when transform is done
try:
transform_args.set_done()
except Exception as e:
transform_args.set_done(e)
return _builder(tls_ctx_options=tls_ctx_options,
use_websockets=True,
use_custom_authorizer=True,
websocket_handshake_transform=_sign_websocket_handshake_request,
websocket_proxy_options=websocket_proxy_options,
**kwargs)
def new_default_builder(**kwargs) -> awscrt.mqtt5.Client:
"""
This builder creates an :class:`awscrt.mqtt5.Client`, without any configuration besides the default TLS context options.
This requires setting the client details manually by passing all the necessary data
in :mod:`common arguments<awsiot.mqtt5_client_builder>` to make a connection
"""
_check_required_kwargs(**kwargs)
tls_ctx_options = awscrt.io.TlsContextOptions()
return _builder(tls_ctx_options=tls_ctx_options,
use_websockets=False,
**kwargs)