wsbuilder 1.0.2__tar.gz → 1.0.3__tar.gz
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.
- {wsbuilder-1.0.2/src/wsbuilder.egg-info → wsbuilder-1.0.3}/PKG-INFO +1 -1
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/pyproject.toml +1 -1
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/__init__.py +1 -1
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/app.py +1 -1
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/cache.py +2 -2
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/caches.py +2 -2
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/http.py +1 -1
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/metrics.py +2 -2
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/proxyi.py +2 -2
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/security.py +4 -4
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/server.py +6 -6
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/ws.py +1 -1
- {wsbuilder-1.0.2 → wsbuilder-1.0.3/src/wsbuilder.egg-info}/PKG-INFO +1 -1
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_http.py +17 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_security.py +18 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_ws.py +7 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/LICENSE +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/MANIFEST.in +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/README.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/404.html +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/CNAME +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/advanced.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/ai-predict.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/api-map.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/app-http.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/architecture.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/assets/wsbuilder-mark.svg +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/beginners.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/cache-security.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/cache.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/capabilities.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/data-orm.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/dns.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/full-project.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/getting-started.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/help/index.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/http-versions.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/http.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/ia.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/index.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/install.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/intermediate.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/mdns.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/network-edge.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/observability-tasks.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/observability.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/persistence.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/proxy.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/reference/index.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/security.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/stylesheets/extra.css +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/tasks.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/tls-certificates.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/docs/websocket.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/examples/full-project/.gitignore +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/examples/full-project/README.md +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/examples/full-project/app.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/examples/full-project/pyproject.toml +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/examples/full-project/upstream.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/mkdocs.yml +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/setup.cfg +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/__main__.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/constants.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/cookies.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/db_replicas.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/dns.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/framework.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/headers.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/hpack.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/http1.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/http2.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/http3.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/http3_server.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/http3_websocket.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/ia.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/logs.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/mdns.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/orm.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/pki.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/predicts.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/qpack.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/__init__.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/address.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/connection.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/crypto.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/frames.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/packet.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/recovery.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/tls.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/quic/varint.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/tasks.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder/ws_demo.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder.egg-info/SOURCES.txt +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder.egg-info/dependency_links.txt +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder.egg-info/entry_points.txt +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder.egg-info/requires.txt +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/src/wsbuilder.egg-info/top_level.txt +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_app_threads.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_cache.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_caches.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_contributing_policy.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_demo_monitor.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_dns.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_docs.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_documentation_files.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_headers_cookies.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_hpack.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_http1.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_http2.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_http3.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_http3_server.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_http3_streaming.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_http3_streaming_udp.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_ia.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_logs.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_mdns.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_metrics.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_orm.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_orm_replicas.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_pki.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_pr_identity_policy.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_predicts.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_proxyi.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_quic_address.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_quic_crypto.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_quic_protocol.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_quic_recovery.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_quic_tls.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_server.py +0 -0
- {wsbuilder-1.0.2 → wsbuilder-1.0.3}/tests/test_tasks.py +0 -0
|
@@ -16,14 +16,14 @@ DEFAULT_CLEANUP_INTERVAL_SECONDS = 30.0
|
|
|
16
16
|
def _safe_float(value, default):
|
|
17
17
|
try:
|
|
18
18
|
return float(value)
|
|
19
|
-
except
|
|
19
|
+
except (TypeError, ValueError, OverflowError):
|
|
20
20
|
return default
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _safe_int(value, default):
|
|
24
24
|
try:
|
|
25
25
|
return int(value)
|
|
26
|
-
except
|
|
26
|
+
except (TypeError, ValueError, OverflowError):
|
|
27
27
|
return default
|
|
28
28
|
|
|
29
29
|
|
|
@@ -39,14 +39,14 @@ _DROP_HEADERS = {
|
|
|
39
39
|
def _safe_float(value, default):
|
|
40
40
|
try:
|
|
41
41
|
return float(value)
|
|
42
|
-
except
|
|
42
|
+
except (TypeError, ValueError, OverflowError):
|
|
43
43
|
return default
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def _safe_int(value, default):
|
|
47
47
|
try:
|
|
48
48
|
return int(value)
|
|
49
|
-
except
|
|
49
|
+
except (TypeError, ValueError, OverflowError):
|
|
50
50
|
return default
|
|
51
51
|
|
|
52
52
|
|
|
@@ -19,14 +19,14 @@ def _iso_utc(ts):
|
|
|
19
19
|
def _safe_int(value, default):
|
|
20
20
|
try:
|
|
21
21
|
return int(value)
|
|
22
|
-
except
|
|
22
|
+
except (TypeError, ValueError, OverflowError):
|
|
23
23
|
return default
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _safe_float(value, default):
|
|
27
27
|
try:
|
|
28
28
|
return float(value)
|
|
29
|
-
except
|
|
29
|
+
except (TypeError, ValueError, OverflowError):
|
|
30
30
|
return default
|
|
31
31
|
|
|
32
32
|
|
|
@@ -99,14 +99,14 @@ _REDACTED_HEADER_VALUE = "[REDACTED]"
|
|
|
99
99
|
def _safe_int(value, default):
|
|
100
100
|
try:
|
|
101
101
|
return int(value)
|
|
102
|
-
except
|
|
102
|
+
except (TypeError, ValueError, OverflowError):
|
|
103
103
|
return default
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
def _safe_float(value, default):
|
|
107
107
|
try:
|
|
108
108
|
return float(value)
|
|
109
|
-
except
|
|
109
|
+
except (TypeError, ValueError, OverflowError):
|
|
110
110
|
return default
|
|
111
111
|
|
|
112
112
|
|
|
@@ -29,14 +29,14 @@ DEFAULT_MAX_TEMPORARY_BLOCKS = 20000
|
|
|
29
29
|
def _safe_int(value, default):
|
|
30
30
|
try:
|
|
31
31
|
return int(value)
|
|
32
|
-
except
|
|
32
|
+
except (TypeError, ValueError, OverflowError):
|
|
33
33
|
return default
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def _safe_float(value, default):
|
|
37
37
|
try:
|
|
38
38
|
return float(value)
|
|
39
|
-
except
|
|
39
|
+
except (TypeError, ValueError, OverflowError):
|
|
40
40
|
return default
|
|
41
41
|
|
|
42
42
|
|
|
@@ -46,7 +46,7 @@ def _to_ip(value):
|
|
|
46
46
|
return None
|
|
47
47
|
try:
|
|
48
48
|
return ipaddress.ip_address(text)
|
|
49
|
-
except
|
|
49
|
+
except ValueError:
|
|
50
50
|
return None
|
|
51
51
|
|
|
52
52
|
|
|
@@ -56,7 +56,7 @@ def _to_network(value):
|
|
|
56
56
|
return None
|
|
57
57
|
try:
|
|
58
58
|
return ipaddress.ip_network(text, strict=False)
|
|
59
|
-
except
|
|
59
|
+
except ValueError:
|
|
60
60
|
ip_obj = _to_ip(text)
|
|
61
61
|
if ip_obj is None:
|
|
62
62
|
return None
|
|
@@ -105,7 +105,7 @@ class HTTPServer:
|
|
|
105
105
|
if not acquired:
|
|
106
106
|
try:
|
|
107
107
|
conn.close()
|
|
108
|
-
except
|
|
108
|
+
except OSError:
|
|
109
109
|
pass
|
|
110
110
|
continue
|
|
111
111
|
t = threading.Thread(
|
|
@@ -172,7 +172,7 @@ class HTTPServer:
|
|
|
172
172
|
metrics.error("tls_context", e)
|
|
173
173
|
try:
|
|
174
174
|
conn.close()
|
|
175
|
-
except
|
|
175
|
+
except OSError:
|
|
176
176
|
pass
|
|
177
177
|
return
|
|
178
178
|
tls_meta = {
|
|
@@ -192,14 +192,14 @@ class HTTPServer:
|
|
|
192
192
|
print(f"[tls] handshake error from {addr}: {e}")
|
|
193
193
|
try:
|
|
194
194
|
conn.close()
|
|
195
|
-
except
|
|
195
|
+
except OSError:
|
|
196
196
|
pass
|
|
197
197
|
return
|
|
198
198
|
except Exception as e:
|
|
199
199
|
print(f"[tls] wrap error from {addr}: {e}")
|
|
200
200
|
try:
|
|
201
201
|
conn.close()
|
|
202
|
-
except
|
|
202
|
+
except OSError:
|
|
203
203
|
pass
|
|
204
204
|
return
|
|
205
205
|
with conn:
|
|
@@ -214,7 +214,7 @@ class HTTPServer:
|
|
|
214
214
|
# first read, so a reused connection waits less.
|
|
215
215
|
try:
|
|
216
216
|
conn.settimeout(self.KEEPALIVE_TIMEOUT_SECONDS)
|
|
217
|
-
except
|
|
217
|
+
except OSError:
|
|
218
218
|
pass
|
|
219
219
|
try:
|
|
220
220
|
keep_alive = self._serve_one_request(reader, conn, addr, tls_meta)
|
|
@@ -274,7 +274,7 @@ class HTTPServer:
|
|
|
274
274
|
security = getattr(self.app, "security", None)
|
|
275
275
|
try:
|
|
276
276
|
conn.settimeout(self.REQUEST_READ_TIMEOUT_SECONDS)
|
|
277
|
-
except
|
|
277
|
+
except OSError:
|
|
278
278
|
pass
|
|
279
279
|
try:
|
|
280
280
|
req = parse_http_request(
|
|
@@ -21,6 +21,23 @@ class BufferSocket:
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class TestHTTPCore(unittest.TestCase):
|
|
24
|
+
def test_request_json_returns_none_for_unparseable_bodies(self):
|
|
25
|
+
def request(body):
|
|
26
|
+
return Request(
|
|
27
|
+
method="POST",
|
|
28
|
+
path="/",
|
|
29
|
+
query_string="",
|
|
30
|
+
headers={},
|
|
31
|
+
body=body,
|
|
32
|
+
client=("127.0.0.1", 1234),
|
|
33
|
+
tls={},
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
self.assertEqual(request(b'{"ok": true}').json(), {"ok": True})
|
|
37
|
+
for body in (b"", b"{not json", b"[" * 100_000):
|
|
38
|
+
with self.subTest(body=body[:16]):
|
|
39
|
+
self.assertIsNone(request(body).json())
|
|
40
|
+
|
|
24
41
|
def test_query_string_decodes_standard_url_encoding(self):
|
|
25
42
|
parsed = parse_query_string(
|
|
26
43
|
"name=Ana+Mar%C3%ADa&empty=&flag&name=final"
|
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
import unittest
|
|
3
3
|
|
|
4
4
|
from wsbuilder import App, Request, SecurityPolicy
|
|
5
|
+
from wsbuilder.security import _safe_float, _safe_int, _to_ip, _to_network
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def _req(method, path, query_string="", client=("127.0.0.1", 1234), headers=None, tls=None):
|
|
@@ -152,6 +153,23 @@ if __name__ == "__main__":
|
|
|
152
153
|
unittest.main()
|
|
153
154
|
|
|
154
155
|
|
|
156
|
+
class TestSecurityParsing(unittest.TestCase):
|
|
157
|
+
def test_numeric_helpers_fall_back_on_unconvertible_values(self):
|
|
158
|
+
for value in (None, "", "abc", float("inf"), float("nan"), object()):
|
|
159
|
+
with self.subTest(value=value):
|
|
160
|
+
self.assertEqual(_safe_int(value, 7), 7)
|
|
161
|
+
self.assertEqual(_safe_int("12", 7), 12)
|
|
162
|
+
self.assertEqual(_safe_float(None, 1.5), 1.5)
|
|
163
|
+
self.assertEqual(_safe_float(10**400, 1.5), 1.5)
|
|
164
|
+
self.assertEqual(_safe_float("2.5", 1.5), 2.5)
|
|
165
|
+
|
|
166
|
+
def test_address_helpers_return_none_for_invalid_text(self):
|
|
167
|
+
self.assertIsNone(_to_ip("999.1.1.1"))
|
|
168
|
+
self.assertIsNone(_to_network("not-a-network"))
|
|
169
|
+
self.assertEqual(str(_to_network("10.0.0.0/8")), "10.0.0.0/8")
|
|
170
|
+
self.assertEqual(str(_to_ip("::1")), "::1")
|
|
171
|
+
|
|
172
|
+
|
|
155
173
|
class TestSecurityTrackingLimits(unittest.TestCase):
|
|
156
174
|
def test_client_tracking_maps_stop_growing_at_the_configured_ceiling(self):
|
|
157
175
|
policy = SecurityPolicy(max_tracked_clients=16)
|
|
@@ -8,6 +8,7 @@ from wsbuilder.ws import (
|
|
|
8
8
|
WebSocketFrame,
|
|
9
9
|
WebSocketProtocolError,
|
|
10
10
|
WebSocketReadTimeoutError,
|
|
11
|
+
_is_valid_websocket_key,
|
|
11
12
|
base64_encode,
|
|
12
13
|
handshake_websocket_with_options,
|
|
13
14
|
make_ws_frame_bytes,
|
|
@@ -332,6 +333,12 @@ if __name__ == "__main__":
|
|
|
332
333
|
|
|
333
334
|
|
|
334
335
|
class TestWebSocketPrimitives(unittest.TestCase):
|
|
336
|
+
def test_websocket_key_must_be_sixteen_base64_octets(self):
|
|
337
|
+
self.assertTrue(_is_valid_websocket_key("dGhlIHNhbXBsZSBub25jZQ=="))
|
|
338
|
+
for key in (None, "", "not base64!", "ñandú", "dGhlIHNhbXBsZQ=="):
|
|
339
|
+
with self.subTest(key=key):
|
|
340
|
+
self.assertFalse(_is_valid_websocket_key(key))
|
|
341
|
+
|
|
335
342
|
def test_unmask_payload_matches_the_per_byte_definition(self):
|
|
336
343
|
mask = b"\x37\xfa\x21\x3d"
|
|
337
344
|
for size in (0, 1, 2, 3, 4, 5, 7, 8, 1023, 4096):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|