Files
core/test/rust/runtime/http/mod.rs
T
2026-11-03 03:32:07 +00:00

146 lines
5.0 KiB
Rust

use dever_runtime::{
http::{self, Header, Limits, Request, Response},
net,
task::{Runtime, Scope},
};
use std::{
io::{BufRead, BufReader, Read, Write},
net::TcpStream,
sync::Arc,
time::Duration,
};
mod http2;
mod lifecycle;
mod limits;
mod live;
mod pool;
mod streaming;
mod tls;
mod websocket;
fn runtime() -> Runtime {
Runtime::new(dever_runtime::task::Limits {
threads: 2,
workers: 2,
tasks: 32,
})
.unwrap()
}
fn request(target: &str, body: &[u8]) -> Request {
Request {
method: "POST".into(),
target: target.into(),
headers: vec![Header {
name: "Host".into(),
value: b"fixture.test".to_vec().into(),
}],
body: body.to_vec().into(),
}
}
async fn route(request: Request, _: Arc<Scope<'static>>) -> Result<Response, String> {
if request.target == "/fail" {
return Err("owned fixture failure".into());
}
if request.target == "/wait" {
tokio::time::sleep(Duration::from_secs(60)).await;
}
Ok(Response {
status: 200,
headers: vec![
Header {
name: "x-data".into(),
value: vec![0x80].into(),
},
Header {
name: "x-data".into(),
value: b"second".to_vec().into(),
},
],
body: request.body,
})
}
fn connect(port: i64) -> BufReader<TcpStream> {
let stream = TcpStream::connect(("127.0.0.1", port as u16)).unwrap();
stream
.set_read_timeout(Some(Duration::from_secs(3)))
.unwrap();
stream
.set_write_timeout(Some(Duration::from_secs(3)))
.unwrap();
BufReader::new(stream)
}
fn read_response(stream: &mut BufReader<TcpStream>, head: bool) -> (u16, Vec<u8>) {
let mut line = Vec::new();
stream.read_until(b'\n', &mut line).unwrap();
let status = std::str::from_utf8(&line)
.unwrap()
.split_whitespace()
.nth(1)
.unwrap()
.parse()
.unwrap();
let mut size = 0;
loop {
line.clear();
assert!(stream.read_until(b'\n', &mut line).unwrap() > 0);
if line == b"\r\n" {
break;
}
if line.to_ascii_lowercase().starts_with(b"content-length:") {
size = std::str::from_utf8(&line[15..])
.unwrap()
.trim()
.parse()
.unwrap();
}
}
let mut body = vec![0; if head { 0 } else { size }];
stream.read_exact(&mut body).unwrap();
(status, body)
}
#[test]
fn client_and_independent_peer_cover_keepalive_chunking_and_request_isolation() {
runtime().run(async |scope| {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let incoming = listener.clone();
let server = scope.spawn(move |child| async move { http::serve::<_, _, false>(&incoming, &Limits::default(), &child, route).await }).unwrap();
let response = http::send("127.0.0.1", port, request("/echo", b"owned body"), &Limits::default()).await.unwrap();
assert_eq!(response.body.as_ref(), b"owned body");
let headers = response.headers.iter().filter(|value| value.name == "x-data").map(|value| value.value.clone()).collect::<Vec<_>>();
assert_eq!(headers, vec![vec![0x80], b"second".to_vec()]);
scope.blocking(move || {
let mut stream = connect(port);
for message in [
&b"GET /echo HTTP/1.1\r\nHost: fixture.test\r\n\r\n"[..],
&b"POST /echo HTTP/1.1\r\nHost: fixture.test\r\nContent-Length: 0\r\n\r\n"[..],
&b"POST /echo HTTP/1.1\r\nHost: fixture.test\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n"[..],
] {
stream.get_mut().write_all(message).unwrap();
assert_eq!(read_response(&mut stream, false), (200, vec![]));
}
for (path, status) in [("/echo", 200), ("/fail", 500), ("/echo", 200)] {
write!(stream.get_mut(), "POST {path} HTTP/1.1\r\nHost: fixture.test\r\nContent-Length: 3\r\n\r\nabc").unwrap();
let (actual, body) = read_response(&mut stream, false);
assert_eq!(actual, status);
if status == 200 { assert_eq!(body, b"abc"); }
}
stream.get_mut().write_all(b"HEAD /echo HTTP/1.1\r\nHost: fixture.test\r\nContent-Length: 3\r\n\r\nabc").unwrap();
assert_eq!(read_response(&mut stream, true), (200, vec![]));
stream.get_mut().write_all(b"POST /echo HTTP/1.1\r\nHost: fixture.test\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n3\r\none\r\n3\r\ntwo\r\n0\r\n\r\n").unwrap();
assert_eq!(read_response(&mut stream, false), (200, b"onetwo".to_vec()));
let mut tail = Vec::new();
stream.read_to_end(&mut tail).unwrap();
assert!(tail.is_empty());
}).await.unwrap();
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
}).unwrap();
}