280 lines
11 KiB
Rust
280 lines
11 KiB
Rust
use super::*;
|
|
use bytes::Bytes;
|
|
use http_body_util::{BodyExt, Empty};
|
|
use hyper_util::rt::TokioIo;
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
use tokio::sync::Semaphore;
|
|
|
|
mod lifecycle;
|
|
mod performance;
|
|
mod transport;
|
|
|
|
async fn client(
|
|
port: i64,
|
|
scope: &Scope<'_>,
|
|
) -> (
|
|
hyper::client::conn::http1::SendRequest<Empty<Bytes>>,
|
|
dever_runtime::task::Task<()>,
|
|
) {
|
|
let socket = tokio::net::TcpStream::connect(("127.0.0.1", port as u16))
|
|
.await
|
|
.unwrap();
|
|
let (sender, connection) = hyper::client::conn::http1::handshake(TokioIo::new(socket))
|
|
.await
|
|
.unwrap();
|
|
let driver = scope
|
|
.spawn_leaf(move || async move {
|
|
let _ = connection.await;
|
|
})
|
|
.unwrap();
|
|
(sender, driver)
|
|
}
|
|
|
|
fn message(method: &str) -> hyper::Request<Empty<Bytes>> {
|
|
hyper::Request::builder()
|
|
.method(method)
|
|
.uri("/")
|
|
.header("Host", "fixture.test")
|
|
.body(Empty::new())
|
|
.unwrap()
|
|
}
|
|
|
|
struct Running(Arc<AtomicUsize>);
|
|
impl Drop for Running {
|
|
fn drop(&mut self) {
|
|
self.0.fetch_sub(1, Ordering::SeqCst);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn sse_encoding_preserves_lines_ids_and_exact_limit() {
|
|
let event = http::Event {
|
|
event: "change".into(),
|
|
data: "一\r\n二\r\n\r".into(),
|
|
id: Some("".into()),
|
|
retry_ms: Some(0),
|
|
};
|
|
let expected = "event: change\nid: \nretry: 0\ndata: 一\ndata: 二\ndata: \ndata: \n\n";
|
|
assert_eq!(event.encode(expected.len()).unwrap(), expected);
|
|
assert!(event.encode(expected.len() - 1).is_err());
|
|
for event in [
|
|
http::Event {
|
|
event: "bad\n".into(),
|
|
..event.clone()
|
|
},
|
|
http::Event {
|
|
id: Some("bad\0".into()),
|
|
..event.clone()
|
|
},
|
|
http::Event {
|
|
retry_ms: Some(-1),
|
|
..event.clone()
|
|
},
|
|
] {
|
|
assert!(event.encode(1024).is_err());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn first_chunk_arrives_before_handler_finishes_and_outlives_head_deadline() {
|
|
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 gate = Arc::new(Semaphore::new(0));
|
|
let waiting = gate.clone();
|
|
let server = scope
|
|
.spawn(move |child| async move {
|
|
let limits = Limits {
|
|
timeout_ms: 100,
|
|
..Limits::default()
|
|
};
|
|
http::serve_live(
|
|
&incoming,
|
|
&limits,
|
|
&http::LiveLimits::default(),
|
|
&child,
|
|
move |_, reply, _| {
|
|
let gate = waiting.clone();
|
|
async move {
|
|
reply
|
|
.start(200, vec![])
|
|
.await
|
|
.map_err(|error| error.to_string())?;
|
|
reply
|
|
.write(b"first")
|
|
.await
|
|
.map_err(|error| error.to_string())?;
|
|
let _permit = gate.acquire().await.unwrap();
|
|
reply
|
|
.write(b"last")
|
|
.await
|
|
.map_err(|error| error.to_string())
|
|
}
|
|
},
|
|
)
|
|
.await
|
|
})
|
|
.unwrap();
|
|
let (mut sender, driver) = client(port, scope).await;
|
|
let response = sender.send_request(message("GET")).await.unwrap();
|
|
assert_eq!(response.status(), 200);
|
|
let mut body = response.into_body();
|
|
assert_eq!(
|
|
body.frame().await.unwrap().unwrap().into_data().unwrap(),
|
|
b"first"[..]
|
|
);
|
|
tokio::time::sleep(Duration::from_millis(180)).await;
|
|
gate.add_permits(1);
|
|
assert_eq!(body.collect().await.unwrap().to_bytes(), b"last"[..]);
|
|
drop(sender);
|
|
driver.wait().await.unwrap();
|
|
listener.close().unwrap();
|
|
server.wait().await.unwrap().unwrap();
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn sse_heartbeats_keep_session_alive_and_stop_drains_handler() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let incoming = listener.clone();
|
|
let running = Arc::new(AtomicUsize::new(0));
|
|
let active = running.clone();
|
|
let server = scope
|
|
.spawn(move |child| async move {
|
|
http::serve_live(
|
|
&incoming,
|
|
&Limits {
|
|
timeout_ms: 80,
|
|
..Limits::default()
|
|
},
|
|
&http::LiveLimits {
|
|
chunk_bytes: 64,
|
|
idle_ms: 100,
|
|
heartbeat_ms: 20,
|
|
},
|
|
&child,
|
|
move |_, reply, _| {
|
|
let active = active.clone();
|
|
async move {
|
|
active.fetch_add(1, Ordering::SeqCst);
|
|
let _running = Running(active);
|
|
reply
|
|
.sse_start(vec![])
|
|
.await
|
|
.map_err(|error| error.to_string())?;
|
|
std::future::pending::<Result<(), String>>().await
|
|
}
|
|
},
|
|
)
|
|
.await
|
|
})
|
|
.unwrap();
|
|
let (mut sender, driver) = client(listener.port().unwrap(), scope).await;
|
|
let response = sender.send_request(message("GET")).await.unwrap();
|
|
assert_eq!(response.headers()["content-type"], "text/event-stream");
|
|
assert_eq!(response.headers()["cache-control"], "no-cache");
|
|
let mut body = response.into_body();
|
|
for _ in 0..8 {
|
|
assert_eq!(
|
|
body.frame().await.unwrap().unwrap().into_data().unwrap(),
|
|
b":\n\n"[..]
|
|
);
|
|
}
|
|
assert_eq!(running.load(Ordering::SeqCst), 1);
|
|
server.stop().await.unwrap();
|
|
assert_eq!(running.load(Ordering::SeqCst), 0);
|
|
drop(body);
|
|
drop(sender);
|
|
driver.wait().await.unwrap();
|
|
listener.close().unwrap();
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_reply_operations_preserve_started_response_and_close_aliases() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let incoming = listener.clone();
|
|
let (sent, mut saved) = tokio::sync::mpsc::channel(1);
|
|
let server = scope
|
|
.spawn(move |child| async move {
|
|
http::serve_live(
|
|
&incoming,
|
|
&Limits::default(),
|
|
&http::LiveLimits {
|
|
chunk_bytes: 32,
|
|
..http::LiveLimits::default()
|
|
},
|
|
&child,
|
|
move |_, reply, _| {
|
|
let sent = sent.clone();
|
|
async move {
|
|
assert!(reply.write(b"before").await.is_err());
|
|
assert!(reply.start(204, vec![]).await.is_err());
|
|
reply.sse_start(vec![]).await.unwrap();
|
|
assert!(reply.start(200, vec![]).await.is_err());
|
|
assert!(reply.write(b"ordinary").await.is_err());
|
|
assert!(
|
|
reply
|
|
.sse_send(&http::Event {
|
|
event: "".into(),
|
|
data: "x".repeat(33),
|
|
id: None,
|
|
retry_ms: None
|
|
})
|
|
.await
|
|
.is_err()
|
|
);
|
|
reply
|
|
.sse_send(&http::Event {
|
|
event: "".into(),
|
|
data: "ok".into(),
|
|
id: None,
|
|
retry_ms: None,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
reply.finish().await.unwrap();
|
|
assert!(reply.finish().await.is_err());
|
|
assert!(
|
|
reply
|
|
.sse_send(&http::Event {
|
|
event: "".into(),
|
|
data: "late".into(),
|
|
id: None,
|
|
retry_ms: None,
|
|
})
|
|
.await
|
|
.is_err()
|
|
);
|
|
sent.send(reply).await.unwrap();
|
|
Ok(())
|
|
}
|
|
},
|
|
)
|
|
.await
|
|
})
|
|
.unwrap();
|
|
let (mut sender, driver) = client(listener.port().unwrap(), scope).await;
|
|
let response = sender.send_request(message("GET")).await.unwrap();
|
|
assert_eq!(
|
|
response.into_body().collect().await.unwrap().to_bytes(),
|
|
b"data: ok\n\n"[..]
|
|
);
|
|
let alias = saved.recv().await.unwrap();
|
|
assert!(alias.start(200, vec![]).await.is_err());
|
|
drop(sender);
|
|
driver.wait().await.unwrap();
|
|
listener.close().unwrap();
|
|
server.wait().await.unwrap().unwrap();
|
|
})
|
|
.unwrap();
|
|
}
|