Skip to content

Commit c82390b

Browse files
committed
Implement tests to expose sequencing bugs
1 parent 4d8bb97 commit c82390b

File tree

4 files changed

+227
-6
lines changed

4 files changed

+227
-6
lines changed

src/server/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ where
162162
let bytes_written = io::copy(&mut encoder, &mut self.io).await?;
163163
log::trace!("wrote {} response bytes", bytes_written);
164164

165+
async_std::task::sleep(Duration::from_millis(1)).await;
166+
165167
let body_bytes_discarded = io::copy(&mut body, &mut io::sink()).await?;
166168
log::trace!(
167169
"discarded {} unread request body bytes",

tests/accept.rs

+39-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
mod test_utils;
22
mod accept {
3+
use std::time::Duration;
4+
35
use super::test_utils::TestServer;
46
use async_h1::{client::Encoder, server::ConnectionStatus};
7+
use async_std::future::timeout;
58
use async_std::io::{self, prelude::WriteExt, Cursor};
69
use http_types::{headers::CONNECTION, Body, Request, Response, Result};
710

@@ -17,7 +20,7 @@ mod accept {
1720
let content_length = 10;
1821

1922
let request_str = format!(
20-
"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {}\r\n\r\n{}\r\n\r\n",
23+
"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {}\r\n\r\n{}",
2124
content_length,
2225
std::str::from_utf8(&vec![b'|'; content_length]).unwrap()
2326
);
@@ -33,6 +36,39 @@ mod accept {
3336
Ok(())
3437
}
3538

39+
#[async_std::test]
40+
async fn pipelined() -> Result<()> {
41+
let mut server = TestServer::new(|req| async {
42+
let mut response = Response::new(200);
43+
let len = req.len();
44+
response.set_body(Body::from_reader(req, len));
45+
Ok(response)
46+
});
47+
48+
let content_length = 10;
49+
50+
let request_str = format!(
51+
"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {}\r\n\r\n{}",
52+
content_length,
53+
std::str::from_utf8(&vec![b'|'; content_length]).unwrap()
54+
);
55+
56+
server.write_all(request_str.as_bytes()).await?;
57+
server.write_all(request_str.as_bytes()).await?;
58+
assert_eq!(server.accept_one().await?, ConnectionStatus::KeepAlive);
59+
assert_eq!(
60+
timeout(Duration::from_secs(1), server.accept_one()).await??,
61+
ConnectionStatus::KeepAlive
62+
);
63+
64+
server.close();
65+
assert_eq!(server.accept_one().await?, ConnectionStatus::Close);
66+
67+
assert!(server.all_read());
68+
69+
Ok(())
70+
}
71+
3672
#[async_std::test]
3773
async fn request_close() -> Result<()> {
3874
let mut server = TestServer::new(|_| async { Ok(Response::new(200)) });
@@ -74,7 +110,7 @@ mod accept {
74110
let content_length = 10;
75111

76112
let request_str = format!(
77-
"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {}\r\n\r\n{}\r\n\r\n",
113+
"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {}\r\n\r\n{}",
78114
content_length,
79115
std::str::from_utf8(&vec![b'|'; content_length]).unwrap()
80116
);
@@ -130,7 +166,7 @@ mod accept {
130166
let content_length = 10000;
131167

132168
let request_str = format!(
133-
"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {}\r\n\r\n{}\r\n\r\n",
169+
"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {}\r\n\r\n{}",
134170
content_length,
135171
std::str::from_utf8(&vec![b'|'; content_length]).unwrap()
136172
);

tests/continue.rs

+185-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
mod test_utils;
22

3+
use async_h1::server::ConnectionStatus;
4+
use async_std::future::timeout;
5+
use async_std::io::BufReader;
36
use async_std::{io, prelude::*, task};
4-
use http_types::Result;
7+
use http_types::{Response, Result};
58
use std::time::Duration;
6-
use test_utils::TestIO;
9+
use test_utils::{TestIO, TestServer};
710

811
const REQUEST_WITH_EXPECT: &[u8] = b"POST / HTTP/1.1\r\n\
912
Host: example.com\r\n\
@@ -52,3 +55,183 @@ async fn test_without_expect_when_not_reading_body() -> Result<()> {
5255

5356
Ok(())
5457
}
58+
59+
#[async_std::test]
60+
async fn test_accept_unread_body() -> Result<()> {
61+
let mut server = TestServer::new(|_| async { Ok(Response::new(200)) });
62+
63+
server.write_all(REQUEST_WITH_EXPECT).await?;
64+
assert_eq!(
65+
timeout(Duration::from_secs(1), server.accept_one()).await??,
66+
ConnectionStatus::KeepAlive
67+
);
68+
69+
server.write_all(REQUEST_WITH_EXPECT).await?;
70+
assert_eq!(
71+
timeout(Duration::from_secs(1), server.accept_one()).await??,
72+
ConnectionStatus::KeepAlive
73+
);
74+
75+
server.close();
76+
assert_eq!(server.accept_one().await?, ConnectionStatus::Close);
77+
78+
assert!(server.all_read());
79+
80+
Ok(())
81+
}
82+
83+
#[async_std::test]
84+
async fn test_echo_server() -> Result<()> {
85+
let mut server = TestServer::new(|mut req| async move {
86+
let mut resp = Response::new(200);
87+
resp.set_body(req.take_body());
88+
Ok(resp)
89+
});
90+
91+
server.write_all(REQUEST_WITH_EXPECT).await?;
92+
server.write_all(b"0123456789").await?;
93+
assert_eq!(server.accept_one().await?, ConnectionStatus::KeepAlive);
94+
95+
task::sleep(SLEEP_DURATION).await; // wait for "continue" to be sent
96+
97+
server.close();
98+
99+
assert!(server
100+
.client
101+
.read
102+
.to_string()
103+
.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n"));
104+
105+
assert_eq!(server.accept_one().await?, ConnectionStatus::Close);
106+
107+
assert!(server.all_read());
108+
109+
Ok(())
110+
}
111+
112+
#[async_std::test]
113+
async fn test_delayed_read() -> Result<()> {
114+
let mut server = TestServer::new(|mut req| async move {
115+
let mut body = req.take_body();
116+
task::spawn(async move {
117+
let mut buf = Vec::new();
118+
body.read_to_end(&mut buf).await.unwrap();
119+
});
120+
Ok(Response::new(200))
121+
});
122+
123+
server.write_all(REQUEST_WITH_EXPECT).await?;
124+
assert_eq!(
125+
timeout(Duration::from_secs(1), server.accept_one()).await??,
126+
ConnectionStatus::KeepAlive
127+
);
128+
server.write_all(b"0123456789").await?;
129+
130+
server.write_all(REQUEST_WITH_EXPECT).await?;
131+
assert_eq!(
132+
timeout(Duration::from_secs(1), server.accept_one()).await??,
133+
ConnectionStatus::KeepAlive
134+
);
135+
server.write_all(b"0123456789").await?;
136+
137+
server.close();
138+
assert_eq!(server.accept_one().await?, ConnectionStatus::Close);
139+
140+
assert!(server.all_read());
141+
142+
Ok(())
143+
}
144+
145+
#[async_std::test]
146+
async fn test_accept_fast_unread_sequential_requests() -> Result<()> {
147+
let mut server = TestServer::new(|_| async move { Ok(Response::new(200)) });
148+
let mut client = server.client.clone();
149+
150+
task::spawn(async move {
151+
let mut reader = BufReader::new(client.clone());
152+
for _ in 0..10 {
153+
let mut buf = String::new();
154+
client.write_all(REQUEST_WITH_EXPECT).await.unwrap();
155+
156+
while !buf.ends_with("\r\n\r\n") {
157+
reader.read_line(&mut buf).await.unwrap();
158+
}
159+
160+
assert!(buf.starts_with("HTTP/1.1 200 OK\r\n"));
161+
}
162+
client.close();
163+
});
164+
165+
for _ in 0..10 {
166+
assert_eq!(
167+
timeout(Duration::from_secs(1), server.accept_one()).await??,
168+
ConnectionStatus::KeepAlive
169+
);
170+
}
171+
172+
assert_eq!(server.accept_one().await?, ConnectionStatus::Close);
173+
174+
assert!(server.all_read());
175+
176+
Ok(())
177+
}
178+
179+
#[async_std::test]
180+
async fn test_accept_partial_read_sequential_requests() -> Result<()> {
181+
const LARGE_REQUEST_WITH_EXPECT: &[u8] = b"POST / HTTP/1.1\r\n\
182+
Host: example.com\r\n\
183+
Content-Length: 1000\r\n\
184+
Expect: 100-continue\r\n\r\n";
185+
186+
let mut server = TestServer::new(|mut req| async move {
187+
let mut body = req.take_body();
188+
let mut buf = [0];
189+
body.read(&mut buf).await.unwrap();
190+
Ok(Response::new(200))
191+
});
192+
let mut client = server.client.clone();
193+
194+
task::spawn(async move {
195+
let mut reader = BufReader::new(client.clone());
196+
for _ in 0..10 {
197+
let mut buf = String::new();
198+
client.write_all(LARGE_REQUEST_WITH_EXPECT).await.unwrap();
199+
200+
// Wait for body to be requested
201+
while !buf.ends_with("\r\n\r\n") {
202+
reader.read_line(&mut buf).await.unwrap();
203+
}
204+
assert!(buf.starts_with("HTTP/1.1 100 Continue\r\n"));
205+
206+
// Write body
207+
for _ in 0..100 {
208+
client.write_all(b"0123456789").await.unwrap();
209+
}
210+
211+
// Wait for response
212+
buf.clear();
213+
while !buf.ends_with("\r\n\r\n") {
214+
reader.read_line(&mut buf).await.unwrap();
215+
}
216+
217+
assert!(buf.starts_with("HTTP/1.1 200 OK\r\n"));
218+
}
219+
client.close();
220+
});
221+
222+
for _ in 0..10 {
223+
assert_eq!(
224+
timeout(Duration::from_secs(1), server.accept_one()).await??,
225+
ConnectionStatus::KeepAlive
226+
);
227+
}
228+
229+
assert_eq!(
230+
timeout(Duration::from_secs(1), server.accept_one()).await??,
231+
ConnectionStatus::Close
232+
);
233+
234+
assert!(server.all_read());
235+
236+
Ok(())
237+
}

tests/test_utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use async_dup::Arc;
1919
pub struct TestServer<F, Fut> {
2020
server: Server<TestIO, F, Fut>,
2121
#[pin]
22-
client: TestIO,
22+
pub(crate) client: TestIO,
2323
}
2424

2525
impl<F, Fut> TestServer<F, Fut>

0 commit comments

Comments
 (0)