Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,264 @@
|
||||
use super::*;
|
||||
use dever_runtime::{http::PoolLimits, tls};
|
||||
|
||||
fn message() -> Request {
|
||||
Request {
|
||||
method: "POST".into(),
|
||||
target: "/echo".into(),
|
||||
headers: vec![],
|
||||
body: b"pooled".to_vec().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn origins_reject_invalid_ports_before_connecting() {
|
||||
runtime()
|
||||
.run(async |scope| {
|
||||
for host in ["127.0.0.1", "[::1]", "localhost"] {
|
||||
for port in ["99999", "65536", "abc", "-1", ""] {
|
||||
for scheme in ["http", "https"] {
|
||||
assert!(
|
||||
http::client(
|
||||
&format!("{scheme}://{host}:{port}"),
|
||||
&tls::system(),
|
||||
&Limits::default(),
|
||||
&PoolLimits::default(),
|
||||
scope
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
for scheme in ["ws", "wss"] {
|
||||
let error = http::websocket::open(
|
||||
&format!("{scheme}://{host}:{port}/"),
|
||||
&tls::system(),
|
||||
&http::websocket::Limits::default(),
|
||||
scope,
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(
|
||||
error.contains("port") || error.contains("invalid WebSocket URL"),
|
||||
"{error}"
|
||||
);
|
||||
}
|
||||
}
|
||||
for suffix in ["", ":80", ":443", ":65535"] {
|
||||
let client = http::client(
|
||||
&format!("http://{host}{suffix}"),
|
||||
&tls::system(),
|
||||
&Limits::default(),
|
||||
&PoolLimits::default(),
|
||||
scope,
|
||||
)
|
||||
.unwrap();
|
||||
client.close().await.unwrap();
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_bounds_http2_concurrency_and_cancels_active_requests() {
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
runtime()
|
||||
.run(async |scope| {
|
||||
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
||||
let port = listener.port().unwrap();
|
||||
let limits = Limits {
|
||||
connections: 1,
|
||||
http2: Some(http::Http2Limits {
|
||||
streams: 2,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let active = Arc::new(AtomicUsize::new(0));
|
||||
let peak = Arc::new(AtomicUsize::new(0));
|
||||
let peak_read = peak.clone();
|
||||
let incoming = listener.clone();
|
||||
let server_limits = limits.clone();
|
||||
let server = scope
|
||||
.spawn(move |child| async move {
|
||||
http::serve::<_, _, false>(
|
||||
&incoming,
|
||||
&server_limits,
|
||||
&child,
|
||||
move |request, _| {
|
||||
let active = active.clone();
|
||||
let peak = peak.clone();
|
||||
async move {
|
||||
let count = active.fetch_add(1, Ordering::SeqCst) + 1;
|
||||
peak.fetch_max(count, Ordering::SeqCst);
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
active.fetch_sub(1, Ordering::SeqCst);
|
||||
Ok(Response {
|
||||
status: 200,
|
||||
headers: vec![],
|
||||
body: request.body,
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
})
|
||||
.unwrap();
|
||||
let client = http::client(
|
||||
&format!("http://127.0.0.1:{port}"),
|
||||
&tls::system(),
|
||||
&limits,
|
||||
&PoolLimits::default(),
|
||||
scope,
|
||||
)
|
||||
.unwrap();
|
||||
let replies =
|
||||
futures_util::future::join_all((0..8).map(|_| client.request(message()))).await;
|
||||
for response in replies {
|
||||
assert_eq!(response.unwrap().body.as_ref(), b"pooled");
|
||||
}
|
||||
assert_eq!(peak_read.load(Ordering::SeqCst), 2);
|
||||
let alias = client.clone();
|
||||
let pending = scope
|
||||
.spawn_leaf(move || async move { alias.request(message()).await })
|
||||
.unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(5)).await;
|
||||
client.close().await.unwrap();
|
||||
assert!(pending.wait().await.unwrap().is_err());
|
||||
listener.close().unwrap();
|
||||
server.wait().await.unwrap().unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_http1_http2_tls_reuse_close_and_scope_ownership() {
|
||||
for protocol in [false, true] {
|
||||
for secure in [false, true] {
|
||||
let escaped = runtime()
|
||||
.run(async |scope| {
|
||||
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
||||
let port = listener.port().unwrap();
|
||||
let limits = Limits {
|
||||
connections: 1,
|
||||
http2: protocol.then(Default::default),
|
||||
..Limits::default()
|
||||
};
|
||||
let incoming = listener.clone();
|
||||
let server_limits = limits.clone();
|
||||
let server = scope
|
||||
.spawn(move |child| async move {
|
||||
if secure {
|
||||
let config =
|
||||
tls::server(super::tls::CERTIFICATE, super::tls::KEY).unwrap();
|
||||
http::serve_tls::<_, _, false>(
|
||||
&incoming,
|
||||
&server_limits,
|
||||
&child,
|
||||
&config,
|
||||
route,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
http::serve::<_, _, false>(&incoming, &server_limits, &child, route)
|
||||
.await
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let origin = format!(
|
||||
"{}://localhost:{port}",
|
||||
if secure { "https" } else { "http" }
|
||||
);
|
||||
let config = tls::client(super::tls::ROOT).unwrap();
|
||||
let client =
|
||||
http::client(&origin, &config, &limits, &PoolLimits::default(), scope)
|
||||
.unwrap();
|
||||
for _ in 0..3 {
|
||||
assert_eq!(
|
||||
client.request(message()).await.unwrap().body.as_ref(),
|
||||
b"pooled"
|
||||
);
|
||||
}
|
||||
let mut changed = message();
|
||||
changed.headers.push(Header {
|
||||
name: "Host".into(),
|
||||
value: b"other.test".to_vec().into(),
|
||||
});
|
||||
assert!(client.request(changed).await.is_err());
|
||||
client.close().await.unwrap();
|
||||
client.close().await.unwrap();
|
||||
assert!(client.request(message()).await.is_err());
|
||||
listener.close().unwrap();
|
||||
server.wait().await.unwrap().unwrap();
|
||||
http::client(&origin, &config, &limits, &PoolLimits::default(), scope).unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
runtime()
|
||||
.run(async |_| {
|
||||
assert!(
|
||||
escaped
|
||||
.request(message())
|
||||
.await
|
||||
.unwrap_err()
|
||||
.contains("closed")
|
||||
);
|
||||
escaped.close().await.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_reuses_one_tcp_connection_and_closes_it_before_returning() {
|
||||
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let port = listener.local_addr().unwrap().port();
|
||||
let peer = std::thread::spawn(move || {
|
||||
let (stream, _) = listener.accept().unwrap();
|
||||
stream
|
||||
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
let mut stream = BufReader::new(stream);
|
||||
for _ in 0..3 {
|
||||
let mut size = 0;
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
assert!(stream.read_line(&mut line).unwrap() > 0);
|
||||
if line == "\r\n" {
|
||||
break;
|
||||
}
|
||||
if let Some(length) = line.to_ascii_lowercase().strip_prefix("content-length:") {
|
||||
size = length.trim().parse().unwrap();
|
||||
}
|
||||
}
|
||||
let mut body = vec![0; size];
|
||||
stream.read_exact(&mut body).unwrap();
|
||||
assert_eq!(body, b"pooled");
|
||||
stream
|
||||
.get_mut()
|
||||
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok")
|
||||
.unwrap();
|
||||
}
|
||||
assert_eq!(stream.read(&mut [0]).unwrap(), 0);
|
||||
});
|
||||
runtime()
|
||||
.run(async |scope| {
|
||||
let client = http::client(
|
||||
&format!("http://127.0.0.1:{port}"),
|
||||
&tls::system(),
|
||||
&Limits::default(),
|
||||
&PoolLimits::default(),
|
||||
scope,
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..3 {
|
||||
assert_eq!(
|
||||
client.request(message()).await.unwrap().body.as_ref(),
|
||||
b"ok"
|
||||
);
|
||||
}
|
||||
client.close().await.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
peer.join().unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user