Examples

Below are a few examples, there may be more in the examples folder of the library

Helper example

This example shows you how to use the adafruit_connection_manager helpers to help simplify code when writing it for multiple different boards

examples/connectionmanager_helpers.py
 1# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
 2#
 3# SPDX-License-Identifier: Unlicense
 4
 5import os
 6
 7import adafruit_requests
 8import wifi
 9
10import adafruit_connection_manager
11
12TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"
13
14wifi_ssid = os.getenv("CIRCUITPY_WIFI_SSID")
15wifi_password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
16
17radio = wifi.radio
18while not radio.connected:
19    radio.connect(wifi_ssid, wifi_password)
20
21# get the pool and ssl_context from the helpers:
22pool = adafruit_connection_manager.get_radio_socketpool(radio)
23ssl_context = adafruit_connection_manager.get_radio_ssl_context(radio)
24
25# get request session
26requests = adafruit_requests.Session(pool, ssl_context)
27connection_manager = adafruit_connection_manager.get_connection_manager(pool)
28print("-" * 40)
29print("Nothing yet opened")
30print(f"Managed Sockets: {connection_manager.managed_socket_count}")
31print(f"Available Managed Sockets: {connection_manager.available_socket_count}")
32
33# make request
34print("-" * 40)
35print(f"Fetching from {TEXT_URL} in a context handler")
36with requests.get(TEXT_URL) as response:
37    response_text = response.text
38    print(f"Text Response {response_text}")
39
40print("-" * 40)
41print("1 request, opened and closed")
42print(f"Managed Sockets: {connection_manager.managed_socket_count}")
43print(f"Available Managed Sockets: {connection_manager.available_socket_count}")
44
45print("-" * 40)
46print(f"Fetching from {TEXT_URL} not in a context handler")
47response = requests.get(TEXT_URL)
48
49print("-" * 40)
50print("1 request, opened but not closed")
51print(f"Managed Sockets: {connection_manager.managed_socket_count}")
52print(f"Available Managed Sockets: {connection_manager.available_socket_count}")
53
54print("-" * 40)
55print("Closing everything in the pool")
56adafruit_connection_manager.connection_manager_close_all(pool)
57
58print("-" * 40)
59print("Everything closed")
60print(f"Managed Sockets: {connection_manager.managed_socket_count}")
61print(f"Available Managed Sockets: {connection_manager.available_socket_count}")

SSL Test

This test runs across the common hosts found in the Adafruit Learning System Guides as well as tests created by badssl.com

examples/connectionmanager_ssltest.py
  1# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
  2#
  3# SPDX-License-Identifier: Unlicense
  4
  5import os
  6import time
  7
  8import adafruit_connection_manager
  9
 10try:
 11    import wifi
 12
 13    radio = wifi.radio
 14    onboard_wifi = True
 15except ImportError:
 16    import board
 17    import busio
 18    from adafruit_esp32spi import adafruit_esp32spi
 19    from digitalio import DigitalInOut
 20
 21    # esp32spi pins set based on Adafruit AirLift FeatherWing
 22    # if using a different setup, please change appropriately
 23    spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
 24    esp32_cs = DigitalInOut(board.D13)
 25    esp32_ready = DigitalInOut(board.D11)
 26    esp32_reset = DigitalInOut(board.D12)
 27    radio = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
 28    onboard_wifi = False
 29
 30
 31# built from:
 32#  https://github.com/adafruit/Adafruit_Learning_System_Guides
 33ADAFRUIT_GROUPS = [
 34    {
 35        "heading": "API hosts",
 36        "description": "These are common API hosts users hit.",
 37        "success": "yes",
 38        "fail": "no",
 39        "subdomains": [
 40            {"host": "api.coindesk.com"},
 41            {"host": "api.covidtracking.com"},
 42            {"host": "api.developer.lifx.com"},
 43            {"host": "api.fitbit.com"},
 44            {"host": "api.github.com"},
 45            {"host": "api.hackaday.io"},
 46            {"host": "api.hackster.io"},
 47            {"host": "api.met.no"},
 48            {"host": "api.nasa.gov"},
 49            {"host": "api.nytimes.com"},
 50            {"host": "api.open-meteo.com"},
 51            {"host": "api.openai.com"},
 52            {"host": "api.openweathermap.org"},
 53            {"host": "api.purpleair.com"},
 54            {"host": "api.spacexdata.com"},
 55            {"host": "api.thecatapi.com"},
 56            {"host": "api.thingiverse.com"},
 57            {"host": "api.thingspeak.com"},
 58            {"host": "api.tidesandcurrents.noaa.gov"},
 59            {"host": "api.twitter.com"},
 60            {"host": "api.wordnik.com"},
 61        ],
 62    },
 63    {
 64        "heading": "Common hosts",
 65        "description": "These are other common hosts users hit.",
 66        "success": "yes",
 67        "fail": "no",
 68        "subdomains": [
 69            {"host": "admiraltyapi.azure-api.net"},
 70            {"host": "aeroapi.flightaware.com"},
 71            {"host": "airnowapi.org"},
 72            {"host": "certification.oshwa.org"},
 73            {"host": "certificationapi.oshwa.org"},
 74            {"host": "chat.openai.com"},
 75            {"host": "covidtracking.com"},
 76            {"host": "discord.com"},
 77            {"host": "enviro.epa.gov"},
 78            {"host": "flightaware.com"},
 79            {"host": "hosted.weblate.org"},
 80            {"host": "id.twitch.tv"},
 81            {"host": "io.adafruit.com"},
 82            {"host": "jwst.nasa.gov"},
 83            {"host": "management.azure.com"},
 84            {"host": "na1.api.riotgames.com"},
 85            {"host": "oauth2.googleapis.com"},
 86            {"host": "opensky-network.org"},
 87            {"host": "opentdb.com"},
 88            {"host": "raw.githubusercontent.com"},
 89            {"host": "site.api.espn.com"},
 90            {"host": "spreadsheets.google.com"},
 91            {"host": "twitrss.me"},
 92            {"host": "www.adafruit.com"},
 93            {"host": "www.alphavantage.co"},
 94            {"host": "www.googleapis.com"},
 95            {"host": "www.nhc.noaa.gov"},
 96            {"host": "www.reddit.com"},
 97            {"host": "youtube.googleapis.com"},
 98        ],
 99    },
100    {
101        "heading": "Known problem hosts",
102        "description": "These are hosts we have run into problems in the past.",
103        "success": "yes",
104        "fail": "no",
105        "subdomains": [
106            {"host": "valid-isrgrootx2.letsencrypt.org"},
107            {"host": "openaccess-api.clevelandart.org"},
108        ],
109    },
110]
111
112# pulled from:
113#  https://github.com/chromium/badssl.com/blob/master/domains/misc/badssl.com/dashboard/sets.js
114BADSSL_GROUPS = [
115    {
116        "heading": "Certificate Validation (High Risk)",
117        "description": (
118            "If your browser connects to one of these sites, it could be very easy for an attacker "
119            "to see and modify everything on web sites that you visit."
120        ),
121        "success": "no",
122        "fail": "yes",
123        "subdomains": [
124            {"subdomain": "expired"},
125            {"subdomain": "wrong.host"},
126            {"subdomain": "self-signed"},
127            {"subdomain": "untrusted-root"},
128        ],
129    },
130    {
131        "heading": "Interception Certificates (High Risk)",
132        "description": (
133            "If your browser connects to one of these sites, it could be very easy for an attacker "
134            "to see and modify everything on web sites that you visit. This may be due to "
135            "interception software installed on your device."
136        ),
137        "success": "no",
138        "fail": "yes",
139        "subdomains": [
140            {"subdomain": "superfish"},
141            {"subdomain": "edellroot"},
142            {"subdomain": "dsdtestprovider"},
143            {"subdomain": "preact-cli"},
144            {"subdomain": "webpack-dev-server"},
145        ],
146    },
147    {
148        "heading": "Broken Cryptography (Medium Risk)",
149        "description": (
150            "If your browser connects to one of these sites, an attacker with enough resources may "
151            "be able to see and/or modify everything on web sites that you visit. This is because "
152            "your browser supports connections settings that are outdated and known to have "
153            "significant security flaws."
154        ),
155        "success": "no",
156        "fail": "yes",
157        "subdomains": [
158            {"subdomain": "rc4"},
159            {"subdomain": "rc4-md5"},
160            {"subdomain": "dh480"},
161            {"subdomain": "dh512"},
162            {"subdomain": "dh1024"},
163            {"subdomain": "null"},
164        ],
165    },
166    {
167        "heading": "Legacy Cryptography (Moderate Risk)",
168        "description": (
169            "If your browser connects to one of these sites, your web traffic is probably safe "
170            "from attackers in the near future. However, your connections to some sites might "
171            "not be using the strongest possible security. Your browser may use these settings in "
172            "order to connect to some older sites."
173        ),
174        "success": "maybe",
175        "fail": "yes",
176        "subdomains": [
177            {"subdomain": "tls-v1-0", "port": 1010},
178            {"subdomain": "tls-v1-1", "port": 1011},
179            {"subdomain": "cbc"},
180            {"subdomain": "3des"},
181            {"subdomain": "dh2048"},
182        ],
183    },
184    {
185        "heading": "Domain Security Policies",
186        "description": (
187            "These are special tests for some specific browsers. These tests may be able to tell "
188            "whether your browser uses advanced domain security policy mechanisms (HSTS, HPKP, SCT"
189            ") to detect illegitimate certificates."
190        ),
191        "success": "maybe",
192        "fail": "yes",
193        "subdomains": [
194            {"subdomain": "revoked"},
195            {"subdomain": "pinning-test"},
196            {"subdomain": "no-sct"},
197        ],
198    },
199    {
200        "heading": "Secure (Uncommon)",
201        "description": (
202            "These settings are secure. However, they are less common and even if your browser "
203            "doesn't support them you probably won't have issues with most sites."
204        ),
205        "success": "yes",
206        "fail": "maybe",
207        "subdomains": [
208            {"subdomain": "1000-sans"},
209            {"subdomain": "10000-sans"},
210            {"subdomain": "sha384"},
211            {"subdomain": "sha512"},
212            {"subdomain": "rsa8192"},
213            {"subdomain": "no-subject"},
214            {"subdomain": "no-common-name"},
215            {"subdomain": "incomplete-chain"},
216        ],
217    },
218    {
219        "heading": "Secure (Common)",
220        "description": (
221            "These settings are secure and commonly used by sites. Your browser will need to "
222            "support most of these in order to connect to sites securely."
223        ),
224        "success": "yes",
225        "fail": "no",
226        "subdomains": [
227            {"subdomain": "tls-v1-2", "port": 1012},
228            {"subdomain": "sha256"},
229            {"subdomain": "rsa2048"},
230            {"subdomain": "ecc256"},
231            {"subdomain": "ecc384"},
232            {"subdomain": "extended-validation"},
233            {"subdomain": "mozilla-modern"},
234        ],
235    },
236]
237
238COMMON_FAILURE_CODES = [
239    "Expected 01 but got 00",  # AirLift
240    "Failed SSL handshake",  # Espressif
241    "MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANG",  # mbedtls
242    "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE",  # mbedtls
243    "MBEDTLS_ERR_X509_CERT_VERIFY_FAILED",  # mbedtls
244    "MBEDTLS_ERR_X509_FATAL_ERROR",  # mbedtls
245]
246
247
248pool = adafruit_connection_manager.get_radio_socketpool(radio)
249ssl_context = adafruit_connection_manager.get_radio_ssl_context(radio)
250connection_manager = adafruit_connection_manager.get_connection_manager(pool)
251
252wifi_ssid = os.getenv("CIRCUITPY_WIFI_SSID")
253wifi_password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
254
255if onboard_wifi:
256    while not radio.connected:
257        radio.connect(wifi_ssid, wifi_password)
258else:
259    while not radio.is_connected:
260        try:
261            radio.connect_AP(wifi_ssid, wifi_password)
262        except OSError as os_exc:
263            print(f"could not connect to AP, retrying: {os_exc}")
264            continue
265
266
267def common_failure(exc):
268    text_value = str(exc)
269    for common_failures_code in COMMON_FAILURE_CODES:
270        if common_failures_code in text_value:
271            return True
272    return False
273
274
275def check_group(groups, group_name):
276    print(f"\nRunning {group_name}")
277    for group in groups:
278        print(f'\n - {group["heading"]}')
279        success = group["success"]
280        fail = group["fail"]
281        for subdomain in group["subdomains"]:
282            if "host" in subdomain:
283                host = subdomain["host"]
284            else:
285                host = f'{subdomain["subdomain"]}.badssl.com'
286            port = subdomain.get("port", 443)
287            exc = None
288            start_time = time.monotonic()
289            try:
290                socket = connection_manager.get_socket(
291                    host,
292                    port,
293                    "https:",
294                    is_ssl=True,
295                    ssl_context=ssl_context,
296                    timeout=10,
297                )
298                connection_manager.close_socket(socket)
299            except RuntimeError as e:
300                exc = e
301            duration = time.monotonic() - start_time
302
303            if fail == "yes" and exc and common_failure(exc):
304                result = "passed"
305            elif success == "yes" and exc is None:
306                result = "passed"
307            else:
308                result = f"error - success:{success}, fail:{fail}, exc:{exc}"
309
310            print(f"   - {host}:{port} took {duration:.2f} seconds | {result}")
311
312
313check_group(ADAFRUIT_GROUPS, "Adafruit")
314check_group(BADSSL_GROUPS, "BadSSL")