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>) -> Result { 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 { 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, head: bool) -> (u16, Vec) { 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::>(); 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(); }