Skip to content

release: v0.2.0 hardening from dev - #69

Merged
ZhuchkaTriplesix merged 47 commits into
mainfrom
release-v0.2.0-hardening
Apr 27, 2026
Merged

ZhuchkaTriplesix merged 47 commits into
mainfrom
release-v0.2.0-hardening

Conversation

@ZhuchkaTriplesix

Copy link
Copy Markdown
Member

Summary

  • harden ASGI queue-drain cleanup so exceptional paths do not leak hanging drain tasks
  • move synchronous websocket handlers fully off the event loop thread
  • add regressions for ASGI drain shutdown behavior and sync websocket thread dispatch

Test plan

  • make test (maintainer run)
  • verify tests/test_asgi.py::test_asgi_queue_drain_shutdown_when_executor_path_raises
  • verify tests/test_websocket_asgi.py::test_asgi_websocket_sync_handler_runs_off_loop_thread

This PR is opened with base main as requested and includes current dev line + release hardening commits.

ZhuchkaTriplesix and others added 30 commits April 26, 2026 22:45
- Cursor rule: ordered steps including read issue, CI parity before push
- development-workflow: checklist; fix ci workflow link
- CONTRIBUTING: sync dev first, linters, pointer to workflow
- Add CompiledRouters (Arc) cloned from per-method matchit tables at freeze
- match_route and methods_matching_path use snapshot when set; mutex path before freeze
- Unit tests for match parity and methods_matching_path
Closes #4
…afe deadlock

- Run handle_rsgi via run_in_executor + asyncio.run on a worker loop; main ASGI
  loop remains free to drain run_coroutine_threadsafe from the RSGI protocol.
- Add 50-concurrent-GET stress test (httpx ASGITransport).
- Document bridge behavior and uvicorn single-worker note in docs/asgi.md.

Closes #17
fix(asgi): run handle_rsgi in executor to avoid run_coroutine_threads…
- Expose types.SimpleNamespace on App.state; document in class docstring and rsgi.md
- rsgi_lifespan_app: ThreadPoolExecutor + asyncio.Semaphore, safe shutdown in __rsgi_del__
- test: app.state is a mutable SimpleNamespace; refresh feature.md and PRIORITIES

Closes #18
feat(app): add per-process app.state; expand lifespan example (#18)
- Route flag read_form_body; inject form (dict) and files (list of part dicts)
- Parse with form_urlencoded crate and multer; 400/415 on bad input; 413 when
  body exceeds OXYROUTE_MAX_BODY_BYTES (default 8 MiB; 0 disables)
- Mutual exclusion with read_json_body in Rust and app._route
- Tests: httpx ASGI urlencoded, multipart upload, oversize 413
- docs/handlers.md + feature.md; PRIORITIES marks #47 done

Closes #47
feat: form bodies (urlencoded + multipart) with read_form_body (#47)
- Add oxyroute.exceptions.HTTPException and Rust dispatch path via
  _http_exception_payload + send_python_error before generic 500
- Tests: httpx ASGI 404/422/headers; ValueError still 500
- docs/handlers.md and feature.md; PRIORITIES: #23/#48 done

Closes #48
feat: HTTPException for non-500 JSON responses (#48)
- Add oxyroute.router: APIRouter (same route decorators as App), join_path,
  nested APIRouter.include_router
- App.include_router: merge default kwargs, filter by method signature, register
  on native app + OpenAPI
- tests, examples/routers_include_app.py, docs/routing.md; PRIORITIES + feature.md

Closes #46
feat: APIRouter and App.include_router (#46)
- Add oxyroute.cors with CORSConfig, preflightResponse, apply_cors(chain=)
- App.set_cors and exports; Rust merges response_header_pairs after handlers/middleware
- Fix Py<PyAny> clone under GIL, PyO3 0.25 extract + merge_cors_to_handler_map
- tests/test_cors.py, docs/cors.md, backlog and feature table updates
- Ensure .venv with PY_BOOTSTRAP; UV_PYTHON points at .venv to avoid PEP 668 on /usr
- One shot: sync, ruff, rustfmt+clippy, maturin develop --uv, pytest in temp dir
- AGENTS.md and git-workflow: prefer make test, human runs before push
- oxyroute: RUF022 __all__ order, ruff format cors.py
- Rust: state.security_headers, set_security_headers, merge if-absent then CORS replace
- oxyroute.security_headers: HSTS on https only, optional CSP/Permissions-Policy, extra
- tests, docs/security-headers.md, backlog; App.set_security_headers
- oxyroute.csrf: CSRFConfig.guard, constant-time compare, 403 JSON
- apply_csrf + csrf_layer for chain with apply_cors
- docs/csrf.md, tests, backlog; export in __init__
Clarify what OxyRoute guarantees for HTTP/2 versus what is owned by Granian/proxy TLS setup, and add a docs index entry for discoverability.
docs: document HTTP/2 deployment guarantees with Granian
ZhuchkaTriplesix and others added 15 commits April 27, 2026 09:45
Allow applications to register exact-path websocket handlers and dispatch them through the ASGI bridge as a practical spike for issue #52.
Add an end-to-end style websocket test for connect, receive, send, and close to validate the new ASGI websocket spike behavior.
Document the new websocket API, its current ASGI-only scope, and the split between Python spike implementation and future Rust/RSGI work.
Add the websocket page to the main documentation index so the new ASGI spike API is discoverable.
Mark websocket support as partial via the ASGI spike and clarify that RSGI-native websocket support is still pending.
feat: add ASGI websocket spike with docs and tests
Replace run_coroutine_threadsafe(...).result() with a thread-safe outgoing queue drained on the main ASGI loop to reduce deadlock risk under concurrent load.
Document the queue-based response bridge and align the deadlock explanation with the new non-blocking send scheduling model.
fix: harden ASGI bridge response scheduling under load
Ensure ASGI queue drain shutdown runs reliably in exception/cancellation paths and preserve the original request failure instead of leaking drain-task state.
Dispatch synchronous websocket handlers entirely in the executor so user sync code does not block the main ASGI event loop.
Cover executor failure behavior to verify the ASGI outgoing drain task is finalized without hanging tasks.
Add coverage that synchronous websocket handlers run outside the loop thread and still complete accept/close flow.
Synchronize Python package, Rust crate, lockfiles, and OpenAPI info version so release artifacts and runtime docs report 0.2.0 consistently.
Refresh development guide examples so the release tagging instructions match the current target version.

@ZhuchkaTriplesix ZhuchkaTriplesix left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

123

@ZhuchkaTriplesix
ZhuchkaTriplesix merged commit 92824fe into main Apr 27, 2026
16 checks passed
@ZhuchkaTriplesix
ZhuchkaTriplesix deleted the release-v0.2.0-hardening branch April 27, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment