wsbuilder 1.2.9__tar.gz → 1.2.10__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.9/src/wsbuilder.egg-info → wsbuilder-1.2.10}/PKG-INFO +1 -1
  2. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/pyproject.toml +1 -1
  3. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/__init__.py +1 -1
  4. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/http3_server.py +7 -0
  5. {wsbuilder-1.2.9 → wsbuilder-1.2.10/src/wsbuilder.egg-info}/PKG-INFO +1 -1
  6. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_http3_streaming.py +59 -0
  7. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/LICENSE +0 -0
  8. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/MANIFEST.in +0 -0
  9. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/README.md +0 -0
  10. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/404.html +0 -0
  11. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/CNAME +0 -0
  12. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/advanced.md +0 -0
  13. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/ai-predict.md +0 -0
  14. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/api-map.md +0 -0
  15. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/app-http.md +0 -0
  16. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/architecture.md +0 -0
  17. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/assets/wsbuilder-mark.svg +0 -0
  18. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/beginners.md +0 -0
  19. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/cache-security.md +0 -0
  20. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/cache.md +0 -0
  21. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/capabilities.md +0 -0
  22. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/data-orm.md +0 -0
  23. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/dns.md +0 -0
  24. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/full-project.md +0 -0
  25. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/getting-started.md +0 -0
  26. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/help/index.md +0 -0
  27. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/http-versions.md +0 -0
  28. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/http.md +0 -0
  29. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/ia.md +0 -0
  30. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/index.md +0 -0
  31. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/install.md +0 -0
  32. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/intermediate.md +0 -0
  33. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/mdns.md +0 -0
  34. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/network-edge.md +0 -0
  35. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/observability-tasks.md +0 -0
  36. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/observability.md +0 -0
  37. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/persistence.md +0 -0
  38. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/proxy.md +0 -0
  39. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/reference/index.md +0 -0
  40. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/security.md +0 -0
  41. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/stylesheets/extra.css +0 -0
  42. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/tasks.md +0 -0
  43. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/tls-certificates.md +0 -0
  44. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/docs/websocket.md +0 -0
  45. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/examples/full-project/.gitignore +0 -0
  46. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/examples/full-project/README.md +0 -0
  47. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/examples/full-project/app.py +0 -0
  48. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/examples/full-project/pyproject.toml +0 -0
  49. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/examples/full-project/upstream.py +0 -0
  50. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/mkdocs.yml +0 -0
  51. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/setup.cfg +0 -0
  52. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/__main__.py +0 -0
  53. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/_coerce.py +0 -0
  54. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/app.py +0 -0
  55. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/cache.py +0 -0
  56. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/caches.py +0 -0
  57. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/constants.py +0 -0
  58. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/cookies.py +0 -0
  59. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/db_replicas.py +0 -0
  60. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/dns.py +0 -0
  61. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/framework.py +0 -0
  62. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/headers.py +0 -0
  63. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/hpack.py +0 -0
  64. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/http.py +0 -0
  65. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/http1.py +0 -0
  66. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/http2.py +0 -0
  67. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/http3.py +0 -0
  68. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/http3_websocket.py +0 -0
  69. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/ia.py +0 -0
  70. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/logs.py +0 -0
  71. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/mdns.py +0 -0
  72. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/metrics.py +0 -0
  73. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/orm.py +0 -0
  74. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/pki.py +0 -0
  75. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/predicts.py +0 -0
  76. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/proxyi.py +0 -0
  77. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/qpack.py +0 -0
  78. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/__init__.py +0 -0
  79. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/address.py +0 -0
  80. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/connection.py +0 -0
  81. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/crypto.py +0 -0
  82. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/frames.py +0 -0
  83. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/packet.py +0 -0
  84. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/recovery.py +0 -0
  85. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/tls.py +0 -0
  86. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/quic/varint.py +0 -0
  87. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/security.py +0 -0
  88. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/server.py +0 -0
  89. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/tasks.py +0 -0
  90. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder/ws.py +0 -0
  91. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder.egg-info/SOURCES.txt +0 -0
  92. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder.egg-info/dependency_links.txt +0 -0
  93. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder.egg-info/entry_points.txt +0 -0
  94. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder.egg-info/requires.txt +0 -0
  95. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/src/wsbuilder.egg-info/top_level.txt +0 -0
  96. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_app_threads.py +0 -0
  97. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_cache.py +0 -0
  98. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_caches.py +0 -0
  99. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_contributing_policy.py +0 -0
  100. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_demo_monitor.py +0 -0
  101. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_dns.py +0 -0
  102. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_docs.py +0 -0
  103. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_documentation_files.py +0 -0
  104. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_headers_cookies.py +0 -0
  105. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_hpack.py +0 -0
  106. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_http.py +0 -0
  107. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_http1.py +0 -0
  108. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_http2.py +0 -0
  109. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_http3.py +0 -0
  110. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_http3_server.py +0 -0
  111. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_http3_streaming_udp.py +0 -0
  112. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_ia.py +0 -0
  113. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_logs.py +0 -0
  114. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_mdns.py +0 -0
  115. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_metrics.py +0 -0
  116. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_orm.py +0 -0
  117. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_orm_replicas.py +0 -0
  118. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_pki.py +0 -0
  119. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_pr_identity_policy.py +0 -0
  120. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_predicts.py +0 -0
  121. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_proxyi.py +0 -0
  122. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_proxyi_routing.py +0 -0
  123. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_quic_address.py +0 -0
  124. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_quic_crypto.py +0 -0
  125. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_quic_protocol.py +0 -0
  126. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_quic_recovery.py +0 -0
  127. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_quic_tls.py +0 -0
  128. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_security.py +0 -0
  129. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_server.py +0 -0
  130. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_server_errors.py +0 -0
  131. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_tasks.py +0 -0
  132. {wsbuilder-1.2.9 → wsbuilder-1.2.10}/tests/test_ws.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wsbuilder
3
- Version: 1.2.9
3
+ Version: 1.2.10
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.9"
7
+ version = "1.2.10"
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.9"
129
+ __version__ = "1.2.10"
130
130
 
131
131
  __all__ = [
132
132
  "App",
@@ -367,6 +367,13 @@ class Http3Server:
367
367
  get_log(self.app).exception("http3", f"dispatch failed on stream {stream_id}: {e}", e)
368
368
  response = Response.text("Internal Server Error", status=500)
369
369
  if connection.closed:
370
+ if getattr(response, "is_stream", False):
371
+ try:
372
+ close = getattr(response.stream, "close", None)
373
+ if callable(close):
374
+ close()
375
+ except Exception as e:
376
+ get_log(self.app).exception("http3", f"streaming cleanup on stream {stream_id} failed: {e}", e)
370
377
  return
371
378
  try:
372
379
  self._send_response(connection, stream_id, response, send_body=request.method != "HEAD")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wsbuilder
3
- Version: 1.2.9
3
+ Version: 1.2.10
4
4
  Summary: Lightweight HTTP + WebSocket framework for Python.
5
5
  Author: wsbuilder Authors
6
6
  License-Expression: MIT
@@ -4,6 +4,7 @@ import socket
4
4
  import threading
5
5
  import unittest
6
6
  from types import SimpleNamespace
7
+ from unittest.mock import patch
7
8
 
8
9
  from wsbuilder import App, Response, http3
9
10
  from wsbuilder.http3_server import Http3Server
@@ -207,6 +208,64 @@ class TestHttp3BodyLimit(unittest.TestCase):
207
208
 
208
209
 
209
210
  class TestHttp3StreamCleanup(unittest.TestCase):
211
+ def test_closing_a_connection_during_dispatch_closes_the_unused_producer(self):
212
+ class Producer:
213
+ iterations = 0
214
+ close_calls = 0
215
+
216
+ def __iter__(self):
217
+ self.iterations += 1
218
+ return iter([b"chunk"])
219
+
220
+ class ClosingProducer(Producer):
221
+ def __init__(self, fail_close=False):
222
+ self.error = OSError("cleanup failed") if fail_close else None
223
+
224
+ def close(self):
225
+ self.close_calls += 1
226
+ if self.error is not None:
227
+ raise self.error
228
+
229
+ for producer in (ClosingProducer(), ClosingProducer(fail_close=True), Producer()):
230
+ with self.subTest(producer=type(producer).__name__, error=getattr(producer, "error", None)):
231
+ entered = threading.Event()
232
+ release = threading.Event()
233
+ queued = []
234
+
235
+ def dispatch(request):
236
+ entered.set()
237
+ if not release.wait(2.0):
238
+ raise TimeoutError("dispatch was never released")
239
+ return Response.stream(producer)
240
+
241
+ app = SimpleNamespace(dispatch=dispatch)
242
+ server = Http3Server("127.0.0.1", 0, app, None)
243
+ self.addCleanup(server._dispatch_pool.shutdown, wait=True)
244
+ connection = SimpleNamespace(
245
+ closed=False,
246
+ queue_stream_data=lambda *args, **kwargs: queued.append((args, kwargs)),
247
+ )
248
+ request = SimpleNamespace(method="GET")
249
+ with patch("wsbuilder.http3_server.get_log") as get_log:
250
+ future = server._dispatch_pool.submit(server._dispatch, connection, 12, request)
251
+ try:
252
+ self.assertTrue(entered.wait(2.0), "dispatch never started")
253
+ server._drop(connection)
254
+ finally:
255
+ release.set()
256
+ future.result(timeout=2.0)
257
+
258
+ self.assertEqual(producer.iterations, 0)
259
+ self.assertEqual(queued, [])
260
+ self.assertEqual(producer.close_calls, int(isinstance(producer, ClosingProducer)))
261
+ if getattr(producer, "error", None) is not None:
262
+ get_log.assert_called_once_with(app)
263
+ get_log.return_value.exception.assert_called_once_with(
264
+ "http3", "streaming cleanup on stream 12 failed: cleanup failed", producer.error,
265
+ )
266
+ else:
267
+ get_log.assert_not_called()
268
+
210
269
  def test_dropping_a_connection_wakes_all_websocket_readers(self):
211
270
  server = Http3Server("127.0.0.1", 0, None, None)
212
271
  connection = SimpleNamespace(
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