swadhinbiswas 🚀 release: 2.0.8 — write-path fix (ADR-080), fork-safe GIL pool (ADR-081), HTTP/3 (ADR-082), Security DI fix, docs honesty pass · 8/6/2026
731fffe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! HTTP/3 (QUIC) end-to-end integration test.
//!
//! Generates a self-signed certificate, starts an HTTP/3 server on an
//! ephemeral UDP port, and round-trips a request through the h3-quinn client.
#![cfg(feature = "http3")]
use std::net::SocketAddr;
use std::sync::Arc;
use bytes::{Buf, Bytes};
use http_body_util::Full;
use hyper::Request;
use justapi_core::http3::{serve_http3, Http3Config, Http3Handler};
use justapi_core::metrics::Metrics;
use tokio_util::sync::CancellationToken;
/// Write a self-signed cert/key pair (via rcgen) to temp files and return paths.
fn write_self_signed_cert(dir: &std::path::Path) -> (std::path::PathBuf, std::path::PathBuf) {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()]).unwrap();
let cert_path = dir.join("cert.pem");
let key_path = dir.join("key.pem");
std::fs::write(&cert_path, cert.cert.pem()).unwrap();
std::fs::write(&key_path, cert.key_pair.serialize_pem()).unwrap();
(cert_path, key_path)
}
/// Simple echo bridge handler: returns `{"echo": "<path>"}`.
fn echo_handler() -> Http3Handler {
Arc::new(|req: Request<Full<Bytes>>| {
Box::pin(async move {
let path = req.uri().path().to_string();
let body = format!(r#"{{"echo": "{}"}}"#, path).into_bytes();
Ok::<_, anyhow::Error>((
200,
vec![("content-type".to_string(), "application/json".to_string())],
body,
))
})
})
}
#[tokio::test]
async fn http3_roundtrip() {
let _ = tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.with_test_writer()
.try_init();
let dir = tempfile::tempdir().unwrap();
let (cert_path, key_path) = write_self_signed_cert(dir.path());
let cfg = Http3Config {
cert_path: cert_path.display().to_string(),
key_path: key_path.display().to_string(),
};
let cancel = CancellationToken::new();
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let bound =
serve_http3(addr, cfg, echo_handler(), Metrics::new(), cancel.clone()).await.unwrap();
// Trust the SAME certificate the server presents (read from the file).
let cert_pem = std::fs::read_to_string(&cert_path).unwrap();
let cert_der = rustls_pemfile::certs(&mut cert_pem.as_bytes()).next().unwrap().unwrap();
let mut roots = rustls::RootCertStore::empty();
roots.add(cert_der).unwrap();
let client_crypto = quinn::ClientConfig::with_root_certificates(Arc::new(roots)).unwrap();
let mut endpoint = quinn::Endpoint::client("127.0.0.1:0".parse().unwrap()).unwrap();
endpoint.set_default_client_config(client_crypto);
let conn = endpoint.connect(bound, "localhost").unwrap().await.unwrap();
let h3_conn = h3_quinn::Connection::new(conn);
let (mut driver, mut send_request) = h3::client::new(h3_conn).await.unwrap();
// Drive the client connection concurrently (required for the request
// to complete), mirroring the h3 example's `tokio::join!` pattern.
let drive = async move {
Err::<(), h3::error::ConnectionError>(
futures::future::poll_fn(|cx| driver.poll_close(cx)).await,
)
};
let request = async {
let req = Request::builder().method("GET").uri("https://localhost/hello").body(()).unwrap();
let mut stream = send_request.send_request(req).await.unwrap();
// Signal end of request body — otherwise the server's body read never
// returns `None`.
stream.finish().await.unwrap();
let resp = stream.recv_response().await.unwrap();
assert_eq!(resp.status(), 200);
let mut body = Vec::new();
while let Some(chunk) = stream.recv_data().await.unwrap() {
body.extend_from_slice(chunk.chunk());
}
String::from_utf8(body).unwrap()
};
let (body, _drive_res) = tokio::join!(request, drive);
assert!(body.contains(r#""echo": "/hello""#), "body was: {body}");
cancel.cancel();
}
#[tokio::test]
#[ignore = "requires the justapi-py wheel with http3 feature in the dev venv"]
async fn http3_python_native_pipeline() {
// Spawn a real JustAPI Python app with enable_http3 and verify a QUIC
// request reaches the Python handler through the native pipeline.
let dir = tempfile::tempdir().unwrap();
let cert_path = dir.path().join("cert.pem");
let key_path = dir.path().join("key.pem");
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()]).unwrap();
std::fs::write(&cert_path, cert.cert.pem()).unwrap();
std::fs::write(&key_path, cert.key_pair.serialize_pem()).unwrap();
let app_src = format!(
r#"
from justapi import JustAPIApp
app = JustAPIApp()
app.enable_http3(cert_path={cert:?}, key_path={key:?})
@app.get("/native")
def native(request):
return {{"handler": "python", "route": request.get("path")}}
app.run("127.0.0.1:8195")
"#,
cert = cert_path.display().to_string(),
key = key_path.display().to_string(),
);
let app_file = dir.path().join("app.py");
std::fs::write(&app_file, app_src).unwrap();
let repo_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.find(|p| p.join("Cargo.toml").exists() && p.join("AGENTS.md").exists())
.unwrap_or_else(|| std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap())
.to_path_buf();
let python = std::env::var("JUSTAPI_TEST_PYTHON").unwrap_or_else(|_| {
repo_root.join("crates/justapi-py/.venv/bin/python").display().to_string()
});
eprintln!("[h3-py-test] python={} repo_root={}", python, repo_root.display());
let mut child = tokio::process::Command::new(&python)
.arg(&app_file)
.current_dir(repo_root)
.kill_on_drop(true)
.spawn()
.expect("spawn python app");
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
// QUIC client trusting the same cert.
let cert_pem = std::fs::read_to_string(&cert_path).unwrap();
let cert_der = rustls_pemfile::certs(&mut cert_pem.as_bytes()).next().unwrap().unwrap();
let mut roots = rustls::RootCertStore::empty();
roots.add(cert_der).unwrap();
let client_crypto = quinn::ClientConfig::with_root_certificates(Arc::new(roots)).unwrap();
let mut endpoint = quinn::Endpoint::client("127.0.0.1:0".parse().unwrap()).unwrap();
endpoint.set_default_client_config(client_crypto);
let conn = endpoint
.connect("127.0.0.1:8195".parse().unwrap(), "localhost")
.unwrap()
.await
.expect("QUIC connect to python app");
let h3_conn = h3_quinn::Connection::new(conn);
let (mut driver, mut send_request) = h3::client::new(h3_conn).await.unwrap();
let drive = async move {
Err::<(), h3::error::ConnectionError>(
futures::future::poll_fn(|cx| driver.poll_close(cx)).await,
)
};
let request = async {
let req =
Request::builder().method("GET").uri("https://localhost/native").body(()).unwrap();
let mut stream = send_request.send_request(req).await.unwrap();
stream.finish().await.unwrap();
let resp = stream.recv_response().await.unwrap();
assert_eq!(resp.status(), 200);
let mut body = Vec::new();
while let Some(chunk) = stream.recv_data().await.unwrap() {
body.extend_from_slice(chunk.chunk());
}
String::from_utf8(body).unwrap()
};
let (body, _) = tokio::join!(request, drive);
// The response must come from the PYTHON handler (native pipeline),
// not a core static route.
assert!(body.contains(r#""handler":"python""#), "body was: {body}");
let _ = child.kill().await;
}