Skip to content

Commit fabe279

Browse files
authored
Merge pull request #65 from brentru/update-cpython-example
Fix CPython/Legacy Socket SSL Problems
2 parents b0b4418 + c9ab6ab commit fabe279

File tree

3 files changed

+42
-81
lines changed

3 files changed

+42
-81
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ jobs:
5454
run: |
5555
pylint $( find . -path './adafruit*.py' )
5656
([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace $( find . -path "./examples/*.py" ))
57+
([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace $( find . -path "./examples/*/*.py" ))
5758
- name: Build assets
5859
run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
5960
- name: Archive bundles

adafruit_minimqtt/adafruit_minimqtt.py

Lines changed: 32 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
const(0x05): "Connection Refused - Unauthorized",
6262
}
6363

64-
_the_interface = None # pylint: disable=invalid-name
65-
_the_sock = None # pylint: disable=invalid-name
64+
_default_sock = None # pylint: disable=invalid-name
65+
_fake_context = None # pylint: disable=invalid-name
6666

6767

6868
class MMQTTException(Exception):
@@ -74,17 +74,17 @@ class MMQTTException(Exception):
7474

7575
# Legacy ESP32SPI Socket API
7676
def set_socket(sock, iface=None):
77-
"""Legacy API for setting the socket and network interface, use a Session instead.
78-
77+
"""Legacy API for setting the socket and network interface.
7978
:param sock: socket object.
8079
:param iface: internet interface object
80+
8181
"""
82-
global _the_sock # pylint: disable=invalid-name, global-statement
83-
_the_sock = sock
82+
global _default_sock # pylint: disable=invalid-name, global-statement
83+
global _fake_context # pylint: disable=invalid-name, global-statement
84+
_default_sock = sock
8485
if iface:
85-
global _the_interface # pylint: disable=invalid-name, global-statement
86-
_the_interface = iface
87-
_the_sock.set_interface(iface)
86+
_default_sock.set_interface(iface)
87+
_fake_context = _FakeSSLContext(iface)
8888

8989

9090
class _FakeSSLSocket:
@@ -144,18 +144,7 @@ def __init__(
144144
):
145145

146146
self._socket_pool = socket_pool
147-
# Legacy API - if we do not have a socket pool, use default socket
148-
if self._socket_pool is None:
149-
self._socket_pool = _the_sock
150-
151147
self._ssl_context = ssl_context
152-
# Legacy API - if we do not have SSL context, fake it
153-
if self._ssl_context is None:
154-
self._ssl_context = _FakeSSLContext(_the_interface)
155-
156-
# Hang onto open sockets so that we can reuse them
157-
self._socket_free = {}
158-
self._open_sockets = {}
159148
self._sock = None
160149
self._backwards_compatible_sock = False
161150

@@ -214,62 +203,37 @@ def __init__(
214203
self.on_subscribe = None
215204
self.on_unsubscribe = None
216205

217-
# Socket helpers
218-
def _free_socket(self, socket):
219-
"""Frees a socket for re-use."""
220-
if socket not in self._open_sockets.values():
221-
raise RuntimeError("Socket not from MQTT client.")
222-
self._socket_free[socket] = True
223-
224-
def _close_socket(self, socket):
225-
"""Closes a slocket."""
226-
socket.close()
227-
del self._socket_free[socket]
228-
key = None
229-
for k in self._open_sockets:
230-
if self._open_sockets[k] == socket:
231-
key = k
232-
break
233-
if key:
234-
del self._open_sockets[key]
235-
236-
def _free_sockets(self):
237-
"""Closes all free sockets."""
238-
free_sockets = []
239-
for sock in self._socket_free:
240-
if self._socket_free[sock]:
241-
free_sockets.append(sock)
242-
for sock in free_sockets:
243-
self._close_socket(sock)
244-
245-
# pylint: disable=too-many-branches
246-
def _get_socket(self, host, port, *, timeout=1):
247-
key = (host, port)
248-
if key in self._open_sockets:
249-
sock = self._open_sockets[key]
250-
if self._socket_free[sock]:
251-
self._socket_free[sock] = False
252-
return sock
206+
def _get_connect_socket(self, host, port, *, timeout=1):
207+
"""Obtains a new socket and connects to a broker.
208+
:param str host: Desired broker hostname
209+
:param int port: Desired broker port
210+
:param int timeout: Desired socket timeout
211+
"""
212+
# For reconnections - check if we're using a socket already and close it
213+
if self._sock:
214+
self._sock.close()
215+
self._sock = None
216+
217+
# Legacy API - use the interface's socket instead of a passed socket pool
218+
if self._socket_pool is None:
219+
self._socket_pool = _default_sock
220+
221+
# Legacy API - fake the ssl context
222+
if self._ssl_context is None:
223+
self._ssl_context = _fake_context
224+
253225
if port == 8883 and not self._ssl_context:
254226
raise RuntimeError(
255227
"ssl_context must be set before using adafruit_mqtt for secure MQTT."
256228
)
257229

258-
# Legacy API - use a default socket instead of socket pool
259-
if self._socket_pool is None:
260-
self._socket_pool = _the_sock
261-
262230
addr_info = self._socket_pool.getaddrinfo(
263231
host, port, 0, self._socket_pool.SOCK_STREAM
264232
)[0]
265-
retry_count = 0
233+
266234
sock = None
235+
retry_count = 0
267236
while retry_count < 5 and sock is None:
268-
if retry_count > 0:
269-
if any(self._socket_free.items()):
270-
self._free_sockets()
271-
else:
272-
raise RuntimeError("Sending request failed")
273237
retry_count += 1
274238

275239
try:
@@ -298,9 +262,6 @@ def _get_socket(self, host, port, *, timeout=1):
298262
raise RuntimeError("Repeated socket failures")
299263

300264
self._backwards_compatible_sock = not hasattr(sock, "recv_into")
301-
302-
self._open_sockets[key] = sock
303-
self._socket_free[sock] = False
304265
return sock
305266

306267
def __enter__(self):
@@ -463,8 +424,8 @@ def connect(self, clean_session=True, host=None, port=None, keep_alive=None):
463424
if self.logger:
464425
self.logger.debug("Attempting to establish MQTT connection...")
465426

466-
# Attempt to get a new socket
467-
self._sock = self._get_socket(self.broker, self.port)
427+
# Get a new socket
428+
self._sock = self._get_connect_socket(self.broker, self.port)
468429

469430
# Fixed Header
470431
fixed_header = bytearray([0x10])

examples/cpython/minimqtt_simpletest_cpython.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
22
# SPDX-License-Identifier: MIT
33

4+
import ssl
45
import socket
56
import adafruit_minimqtt.adafruit_minimqtt as MQTT
67

7-
### Secrets File Setup ###
8-
8+
# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and
9+
# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other
10+
# source control.
11+
# pylint: disable=no-name-in-module,wrong-import-order
912
try:
1013
from secrets import secrets
1114
except ImportError:
12-
print("Connection secrets are kept in secrets.py, please add them there!")
15+
print("WiFi secrets are kept in secrets.py, please add them there!")
1316
raise
1417

1518
### Topic Setup ###
1619

1720
# MQTT Topic
1821
# Use this topic if you'd like to connect to a standard MQTT broker
19-
# mqtt_topic = "test/topic"
22+
mqtt_topic = "test/topic"
2023

2124
# Adafruit IO-style Topic
2225
# Use this topic if you'd like to connect to io.adafruit.com
23-
mqtt_topic = secrets["aio_username"] + "/feeds/temperature"
26+
# mqtt_topic = secrets["aio_username"] + "/feeds/temperature"
2427

2528
### Code ###
26-
27-
# Keep track of client connection state
28-
disconnect_client = False
29-
3029
# Define callback methods which are called when events occur
3130
# pylint: disable=unused-argument, redefined-outer-name
3231
def connect(mqtt_client, userdata, flags, rc):
@@ -65,10 +64,10 @@ def message(client, topic, message):
6564
# Set up a MiniMQTT Client
6665
mqtt_client = MQTT.MQTT(
6766
broker=secrets["broker"],
68-
port=1883,
6967
username=secrets["aio_username"],
7068
password=secrets["aio_key"],
7169
socket_pool=socket,
70+
ssl_context=ssl.create_default_context(),
7271
)
7372

7473
# Connect callback handlers to mqtt_client

0 commit comments

Comments
 (0)