wsbuilder 1.2.5__tar.gz → 1.2.7__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.
Files changed (132) hide show
  1. {wsbuilder-1.2.5/src/wsbuilder.egg-info → wsbuilder-1.2.7}/PKG-INFO +1 -1
  2. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/pyproject.toml +1 -1
  3. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/__init__.py +1 -1
  4. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/proxyi.py +3 -1
  5. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/server.py +6 -0
  6. {wsbuilder-1.2.5 → wsbuilder-1.2.7/src/wsbuilder.egg-info}/PKG-INFO +1 -1
  7. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_proxyi.py +16 -0
  8. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_server.py +37 -0
  9. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/LICENSE +0 -0
  10. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/MANIFEST.in +0 -0
  11. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/README.md +0 -0
  12. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/404.html +0 -0
  13. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/CNAME +0 -0
  14. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/advanced.md +0 -0
  15. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/ai-predict.md +0 -0
  16. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/api-map.md +0 -0
  17. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/app-http.md +0 -0
  18. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/architecture.md +0 -0
  19. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/assets/wsbuilder-mark.svg +0 -0
  20. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/beginners.md +0 -0
  21. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/cache-security.md +0 -0
  22. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/cache.md +0 -0
  23. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/capabilities.md +0 -0
  24. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/data-orm.md +0 -0
  25. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/dns.md +0 -0
  26. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/full-project.md +0 -0
  27. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/getting-started.md +0 -0
  28. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/help/index.md +0 -0
  29. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/http-versions.md +0 -0
  30. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/http.md +0 -0
  31. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/ia.md +0 -0
  32. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/index.md +0 -0
  33. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/install.md +0 -0
  34. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/intermediate.md +0 -0
  35. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/mdns.md +0 -0
  36. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/network-edge.md +0 -0
  37. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/observability-tasks.md +0 -0
  38. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/observability.md +0 -0
  39. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/persistence.md +0 -0
  40. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/proxy.md +0 -0
  41. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/reference/index.md +0 -0
  42. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/security.md +0 -0
  43. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/stylesheets/extra.css +0 -0
  44. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/tasks.md +0 -0
  45. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/tls-certificates.md +0 -0
  46. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/docs/websocket.md +0 -0
  47. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/examples/full-project/.gitignore +0 -0
  48. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/examples/full-project/README.md +0 -0
  49. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/examples/full-project/app.py +0 -0
  50. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/examples/full-project/pyproject.toml +0 -0
  51. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/examples/full-project/upstream.py +0 -0
  52. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/mkdocs.yml +0 -0
  53. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/setup.cfg +0 -0
  54. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/__main__.py +0 -0
  55. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/_coerce.py +0 -0
  56. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/app.py +0 -0
  57. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/cache.py +0 -0
  58. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/caches.py +0 -0
  59. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/constants.py +0 -0
  60. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/cookies.py +0 -0
  61. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/db_replicas.py +0 -0
  62. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/dns.py +0 -0
  63. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/framework.py +0 -0
  64. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/headers.py +0 -0
  65. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/hpack.py +0 -0
  66. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/http.py +0 -0
  67. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/http1.py +0 -0
  68. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/http2.py +0 -0
  69. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/http3.py +0 -0
  70. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/http3_server.py +0 -0
  71. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/http3_websocket.py +0 -0
  72. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/ia.py +0 -0
  73. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/logs.py +0 -0
  74. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/mdns.py +0 -0
  75. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/metrics.py +0 -0
  76. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/orm.py +0 -0
  77. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/pki.py +0 -0
  78. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/predicts.py +0 -0
  79. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/qpack.py +0 -0
  80. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/__init__.py +0 -0
  81. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/address.py +0 -0
  82. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/connection.py +0 -0
  83. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/crypto.py +0 -0
  84. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/frames.py +0 -0
  85. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/packet.py +0 -0
  86. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/recovery.py +0 -0
  87. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/tls.py +0 -0
  88. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/quic/varint.py +0 -0
  89. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/security.py +0 -0
  90. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/tasks.py +0 -0
  91. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder/ws.py +0 -0
  92. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder.egg-info/SOURCES.txt +0 -0
  93. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder.egg-info/dependency_links.txt +0 -0
  94. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder.egg-info/entry_points.txt +0 -0
  95. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder.egg-info/requires.txt +0 -0
  96. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/src/wsbuilder.egg-info/top_level.txt +0 -0
  97. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_app_threads.py +0 -0
  98. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_cache.py +0 -0
  99. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_caches.py +0 -0
  100. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_contributing_policy.py +0 -0
  101. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_demo_monitor.py +0 -0
  102. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_dns.py +0 -0
  103. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_docs.py +0 -0
  104. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_documentation_files.py +0 -0
  105. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_headers_cookies.py +0 -0
  106. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_hpack.py +0 -0
  107. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_http.py +0 -0
  108. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_http1.py +0 -0
  109. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_http2.py +0 -0
  110. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_http3.py +0 -0
  111. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_http3_server.py +0 -0
  112. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_http3_streaming.py +0 -0
  113. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_http3_streaming_udp.py +0 -0
  114. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_ia.py +0 -0
  115. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_logs.py +0 -0
  116. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_mdns.py +0 -0
  117. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_metrics.py +0 -0
  118. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_orm.py +0 -0
  119. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_orm_replicas.py +0 -0
  120. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_pki.py +0 -0
  121. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_pr_identity_policy.py +0 -0
  122. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_predicts.py +0 -0
  123. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_proxyi_routing.py +0 -0
  124. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_quic_address.py +0 -0
  125. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_quic_crypto.py +0 -0
  126. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_quic_protocol.py +0 -0
  127. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_quic_recovery.py +0 -0
  128. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_quic_tls.py +0 -0
  129. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_security.py +0 -0
  130. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_server_errors.py +0 -0
  131. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_tasks.py +0 -0
  132. {wsbuilder-1.2.5 → wsbuilder-1.2.7}/tests/test_ws.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wsbuilder
3
- Version: 1.2.5
3
+ Version: 1.2.7
4
4
  Summary: Lightweight HTTP + WebSocket framework for Python.
5
5
  Author: wsbuilder Authors
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wsbuilder"
7
- version = "1.2.5"
7
+ version = "1.2.7"
8
8
  description = "Lightweight HTTP + WebSocket framework for Python."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -126,7 +126,7 @@ def __getattr__(name):
126
126
  def __dir__():
127
127
  return sorted(set(globals()) | _PKI_EXPORTS)
128
128
 
129
- __version__ = "1.2.5"
129
+ __version__ = "1.2.7"
130
130
 
131
131
  __all__ = [
132
132
  "App",
@@ -869,7 +869,9 @@ class ProxyRule:
869
869
  def choose_target(self, request, *, proxy=None):
870
870
  targets = self.enabled_targets()
871
871
  if not targets:
872
- return None
872
+ # Every upstream of this rule is disabled: the proxy's default
873
+ # rule, when there is one, still answers the request.
874
+ return proxy._fallback_target(request) if proxy is not None else None
873
875
  mode = normalize_balance_mode(self.balance or BALANCING_ROUND_ROBIN)
874
876
  if mode == BALANCING_ROUND_ROBIN:
875
877
  target = self._round_robin(targets)
@@ -276,6 +276,12 @@ class HTTPServer:
276
276
  except OSError:
277
277
  pass
278
278
  return
279
+ # Accepted sockets block forever by default; bound the handshake and
280
+ # the HTTP/2 preface sniff the same way as the first request read.
281
+ try:
282
+ conn.settimeout(self.REQUEST_READ_TIMEOUT_SECONDS)
283
+ except OSError:
284
+ pass
279
285
  tls_meta = {
280
286
  "enabled": context is not None,
281
287
  "peer_cert": None,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wsbuilder
3
- Version: 1.2.5
3
+ Version: 1.2.7
4
4
  Summary: Lightweight HTTP + WebSocket framework for Python.
5
5
  Author: wsbuilder Authors
6
6
  License-Expression: MIT
@@ -204,6 +204,22 @@ class TestProxyI(unittest.TestCase):
204
204
  self.assertIsNone(rule.choose_target(request))
205
205
  self.assertEqual(proxy.dispatch(request).status, 502)
206
206
 
207
+ def test_a_rule_with_only_disabled_targets_falls_back_to_the_default_rule(self):
208
+ proxy = ProxyI(name="edge")
209
+ proxy.default({"url": f"http://127.0.0.1:{self.backend_port}", "name": "fallback"})
210
+ rule = (
211
+ proxy.route(name="api", path_prefix="/api")
212
+ .upstream("http://127.0.0.1:9001", enabled=False)
213
+ .build()
214
+ )
215
+ request = self._request(path="/api/users")
216
+
217
+ self.assertEqual(rule.choose_target(request, proxy=proxy).name, "fallback")
218
+ self.assertIsNone(rule.choose_target(request))
219
+ response = proxy.dispatch(request)
220
+ self.assertEqual(response.status, 200)
221
+ self.assertEqual(response.headers["X-ProxyI-Target"], "fallback")
222
+
207
223
  def test_snapshot_does_not_advance_round_robin(self):
208
224
  proxy = ProxyI(name="edge")
209
225
  rule = (
@@ -2,6 +2,7 @@ import io
2
2
  import os
3
3
  import signal
4
4
  import socket
5
+ import ssl
5
6
  import threading
6
7
  import unittest
7
8
  from unittest.mock import patch
@@ -512,6 +513,42 @@ class TestHTTPServerLifecycle(unittest.TestCase):
512
513
  self.assertFalse(thread.is_alive())
513
514
 
514
515
 
516
+ def test_silent_client_is_dropped_after_the_read_timeout(self):
517
+ server = HTTPServer("127.0.0.1", 0, self._ping_app(), request_read_timeout=0.2)
518
+ self._serve(server)
519
+
520
+ for opening in (b"", b"P"):
521
+ with self.subTest(opening=opening):
522
+ with socket.create_connection(server.server_address, timeout=5.0) as sock:
523
+ if opening:
524
+ sock.sendall(opening)
525
+ received = b""
526
+ while True:
527
+ chunk = sock.recv(4096)
528
+ if not chunk:
529
+ break
530
+ received += chunk
531
+ self.assertNotIn(b"200 OK", received)
532
+
533
+ def test_tls_handshake_runs_under_the_read_timeout(self):
534
+ seen = []
535
+
536
+ class RecordingContext:
537
+ def wrap_socket(self, conn, server_side):
538
+ seen.append(conn.gettimeout())
539
+ raise ssl.SSLError("stop after recording")
540
+
541
+ server = HTTPServer(
542
+ "127.0.0.1", 0, self._ping_app(), ssl_context=RecordingContext(), request_read_timeout=0.2
543
+ )
544
+ left, right = socket.socketpair()
545
+ self.addCleanup(right.close)
546
+ left.setblocking(True)
547
+ server.handle_conn(left, ("127.0.0.1", 0))
548
+
549
+ self.assertEqual(seen, [0.2])
550
+
551
+
515
552
  class TestHTTPServerLimits(unittest.TestCase):
516
553
  def test_constructor_limits_override_only_this_instance(self):
517
554
  server = HTTPServer(
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