feat: publish Dever core with project scaffolding and versioned AI skills
This commit is contained in:
1 parent
70fa73cdc2
commit
ac458ffa7f
960 files changed
+221319
-1163
No files matched your search
@@ -0,0 +1,566 @@
|
||||
use std::future::{Future, poll_fn};
|
||||
use std::time::Duration;
|
||||
|
||||
use dever_runtime::{
|
||||
async_stream::AsyncStream, bytes::Bytes, channel::Channel, http, net, task, tls,
|
||||
};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
const ROOT: &[u8] = include_bytes!("../fixtures/tls/root.pem");
|
||||
const CERT: &[u8] = include_bytes!("../fixtures/tls/server.pem");
|
||||
const KEY: &[u8] = include_bytes!("../fixtures/tls/server-key.pem");
|
||||
|
||||
fn bytes(value: &[u8]) -> Bytes {
|
||||
Bytes::new(value.to_vec())
|
||||
}
|
||||
|
||||
fn limits(connections: i64, streams: i64) -> http::Limits {
|
||||
http::Limits {
|
||||
header_bytes: 8192,
|
||||
body_bytes: 65536,
|
||||
timeout_ms: 1000,
|
||||
connections,
|
||||
http2: Some(http::Http2Limits {
|
||||
streams,
|
||||
stream_window_bytes: 65535,
|
||||
connection_window_bytes: 65535,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn pool_limits() -> http::PoolLimits {
|
||||
http::PoolLimits {
|
||||
idle_ms: 1000,
|
||||
chunk_bytes: 4,
|
||||
read_ms: 500,
|
||||
}
|
||||
}
|
||||
|
||||
fn request(target: &str) -> http::Request {
|
||||
http::Request {
|
||||
method: "GET".into(),
|
||||
target: target.into(),
|
||||
headers: vec![],
|
||||
body: bytes(b""),
|
||||
}
|
||||
}
|
||||
|
||||
fn trust() -> tls::ClientTls {
|
||||
tls::client(&bytes(ROOT)).unwrap()
|
||||
}
|
||||
|
||||
fn identity() -> tls::ServerTls {
|
||||
tls::server(&bytes(CERT), &bytes(KEY)).unwrap()
|
||||
}
|
||||
|
||||
fn run(future: impl Future<Output = Result<(), String>>) {
|
||||
task::run_entry_with(
|
||||
task::RuntimeConfig {
|
||||
worker_threads: 1,
|
||||
max_blocking_threads: 2,
|
||||
task_capacity: 64,
|
||||
},
|
||||
async {
|
||||
tokio::time::timeout(Duration::from_secs(8), future)
|
||||
.await
|
||||
.map_err(|_| "HTTP/2 client test deadline exceeded".to_owned())?
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn h2_error(error: impl std::fmt::Display) -> String {
|
||||
error.to_string()
|
||||
}
|
||||
|
||||
async fn next_request(
|
||||
requests: &mut mpsc::Receiver<(
|
||||
hyper::Request<h2::RecvStream>,
|
||||
h2::server::SendResponse<bytes::Bytes>,
|
||||
)>,
|
||||
) -> Result<
|
||||
(
|
||||
hyper::Request<h2::RecvStream>,
|
||||
h2::server::SendResponse<bytes::Bytes>,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
requests
|
||||
.recv()
|
||||
.await
|
||||
.ok_or("HTTP/2 connection closed before request".into())
|
||||
}
|
||||
|
||||
fn response() -> hyper::Response<()> {
|
||||
hyper::Response::builder().status(200).body(()).unwrap()
|
||||
}
|
||||
|
||||
async fn start_peer(
|
||||
socket: tokio::net::TcpStream,
|
||||
) -> Result<
|
||||
(
|
||||
mpsc::Receiver<(
|
||||
hyper::Request<h2::RecvStream>,
|
||||
h2::server::SendResponse<bytes::Bytes>,
|
||||
)>,
|
||||
oneshot::Sender<oneshot::Sender<()>>,
|
||||
task::Task<()>,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
let mut connection = h2::server::handshake(socket).await.map_err(h2_error)?;
|
||||
let (requests, receiver) = mpsc::channel(4);
|
||||
let (shutdown, mut shutdown_request) = oneshot::channel::<oneshot::Sender<()>>();
|
||||
let driver = task::run(async move {
|
||||
let mut graceful = false;
|
||||
loop {
|
||||
let request = if graceful {
|
||||
connection.accept().await
|
||||
} else {
|
||||
tokio::select! {
|
||||
request = connection.accept() => request,
|
||||
command = &mut shutdown_request => {
|
||||
if let Ok(acknowledge) = command {
|
||||
connection.graceful_shutdown();
|
||||
graceful = true;
|
||||
let _ = acknowledge.send(());
|
||||
continue;
|
||||
}
|
||||
connection.accept().await
|
||||
}
|
||||
}
|
||||
};
|
||||
let Some(request) = request else {
|
||||
return Ok(());
|
||||
};
|
||||
requests
|
||||
.send(request.map_err(h2_error)?)
|
||||
.await
|
||||
.map_err(|_| "HTTP/2 peer request receiver closed".to_owned())?;
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
Ok((receiver, shutdown, driver))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_physical_connection_multiplexes_upload_and_streaming_download() {
|
||||
run(async {
|
||||
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0))
|
||||
.await
|
||||
.map_err(h2_error)?;
|
||||
let port = i64::from(listener.local_addr().map_err(h2_error)?.port());
|
||||
let peer = task::run(async move {
|
||||
let (socket, _) = listener.accept().await.map_err(h2_error)?;
|
||||
let (mut requests, _shutdown, driver) = start_peer(socket).await?;
|
||||
|
||||
let (first, mut first_reply) = next_request(&mut requests).await?;
|
||||
assert_eq!(first.method(), hyper::Method::POST);
|
||||
assert_eq!(first.uri().scheme_str(), Some("http"));
|
||||
let authority = format!("localhost:{port}");
|
||||
assert_eq!(
|
||||
first.uri().authority().map(|value| value.as_str()),
|
||||
Some(authority.as_str())
|
||||
);
|
||||
assert_eq!(first.uri().path(), "/upload");
|
||||
let mut upload = first.into_body();
|
||||
let mut uploaded = Vec::new();
|
||||
while let Some(chunk) = upload.data().await {
|
||||
let chunk = chunk.map_err(h2_error)?;
|
||||
uploaded.extend_from_slice(&chunk);
|
||||
}
|
||||
assert_eq!(uploaded, b"abcdefgh");
|
||||
let mut first_body = first_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
first_body
|
||||
.send_data(bytes::Bytes::from_static(b"first"), false)
|
||||
.map_err(h2_error)?;
|
||||
|
||||
let (second, mut second_reply) = next_request(&mut requests).await?;
|
||||
assert_eq!(second.uri().path(), "/second");
|
||||
let mut second_body = second_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
second_body
|
||||
.send_data(bytes::Bytes::from_static(b"second"), true)
|
||||
.map_err(h2_error)?;
|
||||
first_body
|
||||
.send_data(bytes::Bytes::from_static(b"last"), true)
|
||||
.map_err(h2_error)?;
|
||||
|
||||
task::wait(driver).await?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
let client = http::client(
|
||||
&format!("http://localhost:{port}"),
|
||||
tls::system(),
|
||||
limits(1, 2),
|
||||
pool_limits(),
|
||||
)
|
||||
.await?;
|
||||
let first = http::open_stream(
|
||||
&client,
|
||||
http::StreamRequest {
|
||||
method: "POST".into(),
|
||||
target: "/upload".into(),
|
||||
headers: vec![],
|
||||
body: AsyncStream::from_values([Ok(bytes(b"abcd")), Ok(bytes(b"efgh"))]),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(first.body.pull().await.unwrap()?.values(), b"firs");
|
||||
assert_eq!(first.body.pull().await.unwrap()?.values(), b"t");
|
||||
assert_eq!(
|
||||
http::request(&client, request("/second"))
|
||||
.await?
|
||||
.body
|
||||
.values(),
|
||||
b"second"
|
||||
);
|
||||
let mut tail = Vec::new();
|
||||
while let Some(chunk) = first.body.pull().await {
|
||||
tail.extend_from_slice(chunk?.values());
|
||||
}
|
||||
assert_eq!(tail, b"last");
|
||||
http::close_client(&client).await?;
|
||||
task::wait(peer).await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn canceling_one_response_resets_only_its_stream() {
|
||||
run(async {
|
||||
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0))
|
||||
.await
|
||||
.map_err(h2_error)?;
|
||||
let port = i64::from(listener.local_addr().map_err(h2_error)?.port());
|
||||
let peer = task::run(async move {
|
||||
let (socket, _) = listener.accept().await.map_err(h2_error)?;
|
||||
let (mut requests, _shutdown, driver) = start_peer(socket).await?;
|
||||
let (_, mut first_reply) = next_request(&mut requests).await?;
|
||||
let mut first_body = first_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
first_body
|
||||
.send_data(bytes::Bytes::from_static(b"open"), false)
|
||||
.map_err(h2_error)?;
|
||||
|
||||
let (_, mut neighbor_reply) = next_request(&mut requests).await?;
|
||||
let mut neighbor_body = neighbor_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
neighbor_body
|
||||
.send_data(bytes::Bytes::from_static(b"neighbor"), true)
|
||||
.map_err(h2_error)?;
|
||||
|
||||
let reason = poll_fn(|context| first_body.poll_reset(context))
|
||||
.await
|
||||
.map_err(h2_error)?;
|
||||
assert_eq!(reason, h2::Reason::CANCEL);
|
||||
|
||||
let (_, mut final_reply) = next_request(&mut requests).await?;
|
||||
let mut final_body = final_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
final_body
|
||||
.send_data(bytes::Bytes::from_static(b"still-open"), true)
|
||||
.map_err(h2_error)?;
|
||||
task::wait(driver).await?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
let client = http::client(
|
||||
&format!("http://localhost:{port}"),
|
||||
tls::system(),
|
||||
limits(1, 2),
|
||||
pool_limits(),
|
||||
)
|
||||
.await?;
|
||||
let first = http::open(&client, request("/cancel")).await?;
|
||||
assert_eq!(first.body.pull().await.unwrap()?.values(), b"open");
|
||||
assert_eq!(
|
||||
http::request(&client, request("/neighbor"))
|
||||
.await?
|
||||
.body
|
||||
.values(),
|
||||
b"neighbor"
|
||||
);
|
||||
first.body.close();
|
||||
assert_eq!(
|
||||
http::request(&client, request("/after-reset"))
|
||||
.await?
|
||||
.body
|
||||
.values(),
|
||||
b"still-open"
|
||||
);
|
||||
http::close_client(&client).await?;
|
||||
task::wait(peer).await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_limit_blocks_without_expiring_an_active_body_then_reaps_at_eof() {
|
||||
run(async {
|
||||
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0))
|
||||
.await
|
||||
.map_err(h2_error)?;
|
||||
let port = i64::from(listener.local_addr().map_err(h2_error)?.port());
|
||||
let finish_first = Channel::new(1)?;
|
||||
let finish = finish_first.clone();
|
||||
let first_connection_closed = Channel::new(1)?;
|
||||
let closed = first_connection_closed.clone();
|
||||
let peer = task::run(async move {
|
||||
let (first_socket, _) = listener.accept().await.map_err(h2_error)?;
|
||||
let (mut first_requests, _shutdown, first_driver) = start_peer(first_socket).await?;
|
||||
let (_, mut first_reply) = next_request(&mut first_requests).await?;
|
||||
let mut first_body = first_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
first_body
|
||||
.send_data(bytes::Bytes::from_static(b"open"), false)
|
||||
.map_err(h2_error)?;
|
||||
finish.receive().await?;
|
||||
assert!(matches!(
|
||||
first_requests.try_recv(),
|
||||
Err(mpsc::error::TryRecvError::Empty)
|
||||
));
|
||||
first_body
|
||||
.send_data(bytes::Bytes::from_static(b"done"), true)
|
||||
.map_err(h2_error)?;
|
||||
task::wait(first_driver).await?;
|
||||
closed.send(()).await?;
|
||||
|
||||
let (second_socket, _) = listener.accept().await.map_err(h2_error)?;
|
||||
let (mut second_requests, _shutdown, second_driver) = start_peer(second_socket).await?;
|
||||
let (_, mut second_reply) = next_request(&mut second_requests).await?;
|
||||
let mut second_body = second_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
second_body
|
||||
.send_data(bytes::Bytes::from_static(b"new"), true)
|
||||
.map_err(h2_error)?;
|
||||
task::wait(second_driver).await?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut budget = limits(1, 1);
|
||||
budget.timeout_ms = 60;
|
||||
let mut pool = pool_limits();
|
||||
pool.idle_ms = 30;
|
||||
let client = http::client(
|
||||
&format!("http://localhost:{port}"),
|
||||
tls::system(),
|
||||
budget,
|
||||
pool,
|
||||
)
|
||||
.await?;
|
||||
let first = http::open(&client, request("/active")).await?;
|
||||
assert_eq!(first.body.pull().await.unwrap()?.values(), b"open");
|
||||
task::sleep(80).await?;
|
||||
assert!(
|
||||
http::request(&client, request("/blocked"))
|
||||
.await
|
||||
.unwrap_err()
|
||||
.contains("timed out")
|
||||
);
|
||||
finish_first.send(()).await?;
|
||||
assert_eq!(first.body.pull().await.unwrap()?.values(), b"done");
|
||||
assert!(first.body.pull().await.is_none());
|
||||
first_connection_closed.receive().await?;
|
||||
assert_eq!(
|
||||
http::request(&client, request("/after-idle"))
|
||||
.await?
|
||||
.body
|
||||
.values(),
|
||||
b"new"
|
||||
);
|
||||
http::close_client(&client).await?;
|
||||
task::wait(peer).await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goaway_replaces_the_connection_within_the_physical_budget() {
|
||||
run(async {
|
||||
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0))
|
||||
.await
|
||||
.map_err(h2_error)?;
|
||||
let port = i64::from(listener.local_addr().map_err(h2_error)?.port());
|
||||
let goaway = Channel::new(1)?;
|
||||
let goaway_sent = goaway.clone();
|
||||
let finish_old_stream = Channel::new(1)?;
|
||||
let finish = finish_old_stream.clone();
|
||||
let peer = task::run(async move {
|
||||
let (first_socket, _) = listener.accept().await.map_err(h2_error)?;
|
||||
let (mut first_requests, shutdown, first_driver) = start_peer(first_socket).await?;
|
||||
let (_, mut first_reply) = next_request(&mut first_requests).await?;
|
||||
let mut first_body = first_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
first_body
|
||||
.send_data(bytes::Bytes::from_static(b"open"), false)
|
||||
.map_err(h2_error)?;
|
||||
let (acknowledge, acknowledged) = oneshot::channel();
|
||||
shutdown
|
||||
.send(acknowledge)
|
||||
.map_err(|_| "HTTP/2 peer driver closed before GOAWAY".to_owned())?;
|
||||
acknowledged
|
||||
.await
|
||||
.map_err(|_| "HTTP/2 peer did not begin GOAWAY".to_owned())?;
|
||||
task::sleep(100).await?;
|
||||
goaway_sent.send(()).await?;
|
||||
finish.receive().await?;
|
||||
assert!(matches!(
|
||||
first_requests.try_recv(),
|
||||
Err(mpsc::error::TryRecvError::Empty)
|
||||
));
|
||||
first_body
|
||||
.send_data(bytes::Bytes::from_static(b"done"), true)
|
||||
.map_err(h2_error)?;
|
||||
task::wait(first_driver).await?;
|
||||
|
||||
let (second_socket, _) = listener.accept().await.map_err(h2_error)?;
|
||||
let (mut second_requests, _shutdown, second_driver) = start_peer(second_socket).await?;
|
||||
let (_, mut second_reply) = next_request(&mut second_requests).await?;
|
||||
let mut second_body = second_reply
|
||||
.send_response(response(), false)
|
||||
.map_err(h2_error)?;
|
||||
second_body
|
||||
.send_data(bytes::Bytes::from_static(b"second"), true)
|
||||
.map_err(h2_error)?;
|
||||
task::wait(second_driver).await?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut budget = limits(1, 2);
|
||||
budget.timeout_ms = 80;
|
||||
let client = http::client(
|
||||
&format!("http://localhost:{port}"),
|
||||
tls::system(),
|
||||
budget,
|
||||
pool_limits(),
|
||||
)
|
||||
.await?;
|
||||
let first = http::open(&client, request("/before-goaway")).await?;
|
||||
assert_eq!(first.body.pull().await.unwrap()?.values(), b"open");
|
||||
goaway.receive().await?;
|
||||
let blocked = http::request(&client, request("/must-wait"))
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(blocked.contains("timed out"), "{blocked}");
|
||||
finish_old_stream.send(()).await?;
|
||||
assert_eq!(first.body.pull().await.unwrap()?.values(), b"done");
|
||||
assert!(first.body.pull().await.is_none());
|
||||
assert_eq!(
|
||||
http::request(&client, request("/after-goaway"))
|
||||
.await?
|
||||
.body
|
||||
.values(),
|
||||
b"second"
|
||||
);
|
||||
http::close_client(&client).await?;
|
||||
task::wait(peer).await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn https_requires_h2_alpn_without_protocol_fallback() {
|
||||
run(async {
|
||||
let listener = net::listen("127.0.0.1", 0).await?;
|
||||
let port = net::port(&listener)?;
|
||||
let mut http1 = limits(1, 1);
|
||||
http1.http2 = None;
|
||||
let server_listener = listener.clone();
|
||||
let server = task::run(async move {
|
||||
http::serve_tls(
|
||||
|_| async {
|
||||
Ok(http::Response {
|
||||
status: 200,
|
||||
headers: vec![],
|
||||
body: bytes(b"unexpected"),
|
||||
})
|
||||
},
|
||||
server_listener,
|
||||
http1,
|
||||
identity(),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await?;
|
||||
let client = http::client(
|
||||
&format!("https://localhost:{port}"),
|
||||
trust(),
|
||||
limits(1, 1),
|
||||
pool_limits(),
|
||||
)
|
||||
.await?;
|
||||
let error = http::request(&client, request("/alpn")).await.unwrap_err();
|
||||
assert!(error.contains("NoApplicationProtocol"), "{error}");
|
||||
http::close_client(&client).await?;
|
||||
task::stop(server).await?;
|
||||
listener.close()?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_http2_budgets_and_outgoing_headers_fail_before_connecting() {
|
||||
run(async {
|
||||
let base = limits(1, 2);
|
||||
let mut cases = Vec::new();
|
||||
for streams in [0, 65537] {
|
||||
let mut budget = base;
|
||||
budget.http2.as_mut().unwrap().streams = streams;
|
||||
cases.push((budget, "connections * streams"));
|
||||
}
|
||||
let mut multiplied = base;
|
||||
multiplied.connections = 32769;
|
||||
cases.push((multiplied, "connections * streams"));
|
||||
for window in [0, i64::from(i32::MAX) + 1] {
|
||||
let mut budget = base;
|
||||
budget.http2.as_mut().unwrap().stream_window_bytes = window;
|
||||
cases.push((budget, "stream_window_bytes"));
|
||||
}
|
||||
for window in [65534, i64::from(i32::MAX) + 1] {
|
||||
let mut budget = base;
|
||||
budget.http2.as_mut().unwrap().connection_window_bytes = window;
|
||||
cases.push((budget, "connection_window_bytes"));
|
||||
}
|
||||
for (budget, expected) in cases {
|
||||
let failure = http::client("http://127.0.0.1:1", tls::system(), budget, pool_limits())
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(failure.contains(expected), "{failure}");
|
||||
}
|
||||
|
||||
let client = http::client("http://127.0.0.1:1", tls::system(), base, pool_limits()).await?;
|
||||
for (name, value) in [
|
||||
("Connection", "close"),
|
||||
("Keep-Alive", "timeout=5"),
|
||||
("TE", "gzip"),
|
||||
] {
|
||||
let mut outgoing = request("/");
|
||||
outgoing.headers.push(http::Header {
|
||||
name: name.into(),
|
||||
value: bytes(value.as_bytes()),
|
||||
});
|
||||
let failure = http::request(&client, outgoing).await.unwrap_err();
|
||||
assert!(
|
||||
failure.contains("HTTP/2 forbids connection-specific header"),
|
||||
"{failure}"
|
||||
);
|
||||
}
|
||||
http::close_client(&client).await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user