146 lines
5.0 KiB
Rust
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();
|
|
}
|