Coverage for aiocoap/defaults.py: 89%
121 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 12:34 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 12:34 +0000
1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors
2#
3# SPDX-License-Identifier: MIT
5"""This module contains helpers that inspect available modules and platform
6specifics to give sane values to aiocoap defaults.
8All of this should eventually overridable by other libraries wrapping/using
9aiocoap and by applications using aiocoap; however, these overrides do not
10happen in the defaults module but where these values are actually accessed, so
11this module is considered internal to aiocoap and not part of the API.
13The ``_missing_modules`` functions are helpers for inspecting what is
14reasonable to expect to work. They can influence default values, but should not
15be used in the rest of the code for feature checking (just raise the
16ImportErrors) unless it's directly user-visible ("You configured OSCORE key
17material, but OSCORE needs the following unavailable modules") or in the test
18suite to decide which tests to skip.
19"""
21import os
22import socket
23import sys
24import warnings
26try:
27 import pyodide # noqa: F401
28 import js # noqa: F401
29except ImportError:
30 is_pyodide = False
31else:
32 is_pyodide = True
35def get_default_clienttransports(*, loop=None, use_env=True):
36 """Return a list of transports that should be connected when a client
37 context is created.
39 If an explicit ``AIOCOAP_CLIENT_TRANSPORT`` environment variable is set, it
40 is read as a colon separated list of transport names.
42 By default, a DTLS mechanism will be picked if the required modules are
43 available, and a UDP transport will be selected depending on whether the
44 full udp6 transport is known to work.
45 """
47 if use_env and "AIOCOAP_CLIENT_TRANSPORT" in os.environ:
48 yield from os.environ["AIOCOAP_CLIENT_TRANSPORT"].split(":")
49 return
51 if not oscore_missing_modules():
52 yield "oscore"
54 if not dtls_missing_modules():
55 yield "tinydtls"
57 yield "tcpclient"
58 yield "tlsclient"
59 if not ws_missing_modules():
60 yield "ws"
62 if sys.platform != "linux":
63 # udp6 was never reported to work on anything but linux; would happily
64 # add more platforms.
65 yield "simple6"
66 return
68 # on android it seems that it's only the AI_V4MAPPED that causes trouble,
69 # that should be managable in udp6 too.
70 yield "udp6"
71 return
74def get_default_servertransports(*, loop=None, use_env=True):
75 """Return a list of transports that should be connected when a server
76 context is created.
78 If an explicit ``AIOCOAP_SERVER_TRANSPORT`` environment variable is set, it
79 is read as a colon separated list of transport names.
81 By default, a DTLS mechanism will be picked if the required modules are
82 available, and a UDP transport will be selected depending on whether the
83 full udp6 transport is known to work. Both a simple6 and a simplesocketserver
84 will be selected when udp6 is not available, and the simple6 will be used
85 for any outgoing requests, which the simplesocketserver could serve but is worse
86 at.
87 """
89 if use_env and "AIOCOAP_SERVER_TRANSPORT" in os.environ:
90 yield from os.environ["AIOCOAP_SERVER_TRANSPORT"].split(":")
91 return
93 if not oscore_missing_modules():
94 yield "oscore"
96 if not dtls_missing_modules():
97 if "AIOCOAP_DTLSSERVER_ENABLED" in os.environ:
98 yield "tinydtls_server"
99 yield "tinydtls"
101 yield "tcpserver"
102 yield "tcpclient"
103 yield "tlsserver"
104 yield "tlsclient"
105 if not ws_missing_modules():
106 yield "ws"
108 if sys.platform != "linux":
109 # udp6 was never reported to work on anything but linux; would happily
110 # add more platforms.
111 yield "simple6"
112 yield "simplesocketserver"
113 return
115 # on android it seems that it's only the AI_V4MAPPED that causes trouble,
116 # that should be managable in udp6 too.
117 yield "udp6"
118 return
121def has_reuse_port(*, use_env=True):
122 """Return true if the platform indicates support for SO_REUSEPORT.
124 Can be overridden by explicitly setting ``AIOCOAP_REUSE_PORT`` to 1 or
125 0."""
127 if use_env and os.environ.get("AIOCOAP_REUSE_PORT"):
128 return bool(int(os.environ["AIOCOAP_REUSE_PORT"]))
130 return hasattr(socket, "SO_REUSEPORT")
133def use_ai_v4mapped_emulation():
134 """This used to indicate when ai_v4mapped emulation was used. Given it is
135 not used at all any more, the function is deprecated."""
136 warnings.warn(
137 "AI_V4MAPPED emulation is not used any more at all", warnings.DeprecationWarning
138 )
139 return False
142# FIXME: If there were a way to check for the extras defined in setup.py, or to link these lists to what is descibed there, that'd be great.
145def dtls_missing_modules():
146 """Return a list of modules that are missing in order to use the DTLS
147 transport, or a false value if everything is present"""
149 missing = []
151 try:
152 from DTLSSocket import dtls # noqa: F401
153 except ImportError:
154 missing.append("DTLSSocket")
156 return missing
159def oscore_missing_modules():
160 """Return a list of modules that are missing in order to use OSCORE, or a
161 false value if everything is present"""
162 missing = []
163 try:
164 import cbor2 # noqa: F401
165 except ImportError:
166 missing.append("cbor2")
167 try:
168 import cryptography # noqa: F401
169 except ImportError:
170 missing.append("cryptography")
171 else:
172 try:
173 from cryptography.hazmat.primitives.ciphers.aead import AESCCM
175 AESCCM(b"x" * 16, 8)
176 except (cryptography.exceptions.UnsupportedAlgorithm, ImportError):
177 missing.append("a version of OpenSSL that supports AES-CCM")
178 try:
179 import filelock # noqa: F401
180 except ImportError:
181 missing.append("filelock")
183 try:
184 import ge25519 # noqa: F401
185 except ImportError:
186 missing.append("ge25519")
188 try:
189 import lakers # noqa: F401
190 except ImportError:
191 missing.append("lakers-python")
193 return missing
196def ws_missing_modules():
197 """Return a list of modules that are missing in order to user CoAP-over-WS,
198 or a false value if everything is present"""
200 if is_pyodide:
201 return []
203 missing = []
204 try:
205 import websockets # noqa: F401
206 except ImportError:
207 missing.append("websockets")
209 return missing
212def linkheader_missing_modules():
213 """Return a list of moudles that are missing in order to use link_header
214 functionaity (eg. running a resource directory), of a false value if
215 everything is present."""
216 missing = []
217 # The link_header module is now provided in-tree
218 return missing
221def prettyprint_missing_modules():
222 """Return a list of modules that are missing in order to use pretty
223 printing (ie. full aiocoap-client)"""
224 missing = []
225 missing.extend(linkheader_missing_modules())
226 try:
227 import cbor2 # noqa: F401
228 except ImportError:
229 missing.append("cbor2")
230 try:
231 import pygments # noqa: F401
232 except ImportError:
233 missing.append("pygments")
234 try:
235 import cbor_diag # noqa: F401
236 except ImportError:
237 missing.append("cbor-diag")
238 return missing
241missing_module_functions = {
242 "dtls": dtls_missing_modules,
243 "oscore": oscore_missing_modules,
244 "linkheader": linkheader_missing_modules,
245 "prettyprint": prettyprint_missing_modules,
246 "ws": ws_missing_modules,
247}
249__all__ = [
250 "get_default_clienttransports",
251 "get_default_servertransports",
252 "has_reuse_port",
253 "dtls_missing_modules",
254 "oscore_missing_modules",
255 "ws_missing_modules",
256 "linkheader_missing_modules",
257 "prettyprint_missing_modules",
258 "missing_module_functions",
259]