Skip to content

Commit b1ff6ab

Browse files
authored
Merge pull request #50 from yoshuawuyts/connect
Implement client::connect
2 parents 4450623 + 4e6eb59 commit b1ff6ab

File tree

6 files changed

+80
-45
lines changed

6 files changed

+80
-45
lines changed

examples/client.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ fn main() -> Result<(), Error> {
2020
println!("making request {}/2", i + 1);
2121
let url = Url::parse(&format!("http://{}/foo", peer_addr)).unwrap();
2222
let req = Request::new(Method::Get, dbg!(url));
23-
let mut req = client::encode(req).await?;
24-
io::copy(&mut req, &mut stream.clone()).await?;
25-
26-
// read the response
27-
let res = client::decode(stream.clone()).await?;
23+
let res = client::connect(stream.clone(), req).await?;
2824
println!("{:?}", res);
2925
}
3026
Ok(())

src/client.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Process HTTP connections on the client.
22
3-
use async_std::io::{self, BufReader, Read};
3+
use async_std::io::{self, BufReader, Read, Write};
44
use async_std::prelude::*;
55
use async_std::task::{Context, Poll};
66
use futures_core::ready;
@@ -48,6 +48,22 @@ impl Encoder {
4848
}
4949
}
5050

51+
/// Send an HTTP request over a stream.
52+
pub async fn connect<RW>(mut stream: RW, req: Request) -> Result<Response, Error>
53+
where
54+
RW: Read + Write + Send + Sync + Unpin + 'static,
55+
{
56+
let mut req = encode(req).await?;
57+
log::trace!("> {:?}", &req);
58+
59+
io::copy(&mut req, &mut stream).await?;
60+
61+
let res = decode(stream).await?;
62+
log::trace!("< {:?}", &res);
63+
64+
Ok(res)
65+
}
66+
5167
/// Encode an HTTP request on the client.
5268
pub async fn encode(req: Request) -> Result<Encoder, Error> {
5369
let mut buf: Vec<u8> = vec![];
@@ -75,8 +91,12 @@ pub async fn encode(req: Request) -> Result<Encoder, Error> {
7591
StatusCode::BadRequest,
7692
)
7793
})?;
94+
let val = if let Some(port) = req.url().port() {
95+
format!("host: {}:{}\r\n", host, port)
96+
} else {
97+
format!("host: {}\r\n", host)
98+
};
7899

79-
let val = format!("host: {}\r\n", host);
80100
log::trace!("> {}", &val);
81101
buf.write_all(val.as_bytes()).await?;
82102

tests/client.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
1-
use crate::common::{fixture_path, munge_date};
1+
use crate::common::fixture_path;
22
use async_h1::client;
33
use async_std::fs::File;
44
use async_std::io::SeekFrom;
55
use async_std::prelude::*;
6-
use http_types::{headers, Method, Request};
6+
use http_types::{headers, Method, Request, StatusCode};
77
use url::Url;
88

99
mod common;
1010

11+
use common::TestCase;
12+
1113
#[async_std::test]
1214
async fn test_encode_request_add_date() {
13-
let mut request_fixture = File::open(fixture_path("fixtures/client-request1.txt"))
14-
.await
15-
.unwrap();
16-
let mut expected = String::new();
17-
request_fixture.read_to_string(&mut expected).await.unwrap();
18-
19-
let url = Url::parse("http://example.com").unwrap();
20-
let req = Request::new(Method::Get, url);
21-
22-
let mut encoded_req = client::encode(req).await.unwrap();
15+
let case = TestCase::new_client("fixtures/request1.txt", "fixtures/response1.txt").await;
2316

24-
let mut actual = String::new();
25-
encoded_req.read_to_string(&mut actual).await.unwrap();
17+
let url = Url::parse("http://localhost:8080").unwrap();
18+
let mut req = Request::new(Method::Post, url);
19+
req.set_body("hello");
2620

27-
munge_date(&mut expected, &mut actual);
21+
let res = client::connect(case.clone(), req).await.unwrap();
22+
assert_eq!(res.status(), StatusCode::Ok);
2823

29-
pretty_assertions::assert_eq!(actual, expected);
24+
case.assert().await;
3025
}
3126

3227
#[async_std::test]

tests/common/mod.rs

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,41 @@ use async_std::task::{Context, Poll};
66
use std::pin::Pin;
77
use std::sync::Mutex;
88

9+
#[derive(Debug, Copy, Clone)]
10+
#[allow(dead_code)]
11+
enum Direction {
12+
Client,
13+
Server,
14+
}
15+
916
#[derive(Clone)]
1017
pub struct TestCase {
11-
request_fixture: Arc<File>,
12-
response_fixture: Arc<Mutex<File>>,
18+
direction: Direction,
19+
source_fixture: Arc<File>,
20+
expected_fixture: Arc<Mutex<File>>,
1321
result: Arc<Mutex<File>>,
1422
}
1523

1624
impl TestCase {
17-
pub async fn new(request_file_path: &str, response_file_path: &str) -> TestCase {
25+
pub async fn new_server(request_file_path: &str, response_file_path: &str) -> TestCase {
26+
Self::new(Direction::Server, request_file_path, response_file_path).await
27+
}
28+
29+
pub async fn new_client(request_file_path: &str, response_file_path: &str) -> TestCase {
30+
Self::new(Direction::Client, request_file_path, response_file_path).await
31+
}
32+
33+
async fn new(
34+
direction: Direction,
35+
request_file_path: &str,
36+
response_file_path: &str,
37+
) -> TestCase {
1838
let request_fixture = File::open(fixture_path(&request_file_path))
1939
.await
2040
.expect(&format!(
2141
"Could not open request fixture file: {:?}",
2242
&fixture_path(request_file_path)
2343
));
24-
let request_fixture = Arc::new(request_fixture);
2544

2645
let response_fixture =
2746
File::open(fixture_path(&response_file_path))
@@ -30,14 +49,19 @@ impl TestCase {
3049
"Could not open response fixture file: {:?}",
3150
&fixture_path(response_file_path)
3251
));
33-
let response_fixture = Arc::new(Mutex::new(response_fixture));
3452

3553
let temp = tempfile::tempfile().expect("Failed to create tempfile");
3654
let result = Arc::new(Mutex::new(temp.into()));
3755

56+
let (source_fixture, expected_fixture) = match direction {
57+
Direction::Client => (response_fixture, request_fixture),
58+
Direction::Server => (request_fixture, response_fixture),
59+
};
60+
3861
TestCase {
39-
request_fixture,
40-
response_fixture,
62+
direction,
63+
source_fixture: Arc::new(source_fixture),
64+
expected_fixture: Arc::new(Mutex::new(expected_fixture)),
4165
result,
4266
}
4367
}
@@ -54,7 +78,7 @@ impl TestCase {
5478
pub async fn read_expected(&self) -> String {
5579
use async_std::prelude::*;
5680
let mut expected = std::string::String::new();
57-
self.response_fixture
81+
self.expected_fixture
5882
.lock()
5983
.unwrap()
6084
.read_to_string(&mut expected)
@@ -83,13 +107,13 @@ pub(crate) fn fixture_path(relative_path: &str) -> PathBuf {
83107
pub(crate) fn munge_date(expected: &mut String, actual: &mut String) {
84108
match expected.find("{DATE}") {
85109
Some(i) => {
86-
expected.replace_range(i..i + 6, "");
87-
match expected.get(i..i + 1) {
88-
Some(byte) => {
89-
let j = actual[i..].find(byte).expect("Byte not found");
90-
actual.replace_range(i..i + j, "");
110+
println!("{}", expected);
111+
match actual.find("date: ") {
112+
Some(j) => {
113+
let eol = actual[j + 6..].find("\r\n").expect("missing eol");
114+
expected.replace_range(i..i + 6, &actual[j + 6..j + 6 + eol]);
91115
}
92-
None => expected.replace_range(i.., ""),
116+
None => expected.replace_range(i..i + 6, ""),
93117
}
94118
}
95119
None => {}
@@ -102,7 +126,7 @@ impl Read for TestCase {
102126
cx: &mut Context,
103127
buf: &mut [u8],
104128
) -> Poll<io::Result<usize>> {
105-
Pin::new(&mut &*self.request_fixture).poll_read(cx, buf)
129+
Pin::new(&mut &*self.source_fixture).poll_read(cx, buf)
106130
}
107131
}
108132

tests/fixtures/request1.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
POST / HTTP/1.1
22
host: localhost:8080
3-
user-agent: curl/7.54.0
4-
accept: */*
5-
content-length: 2
6-
content-type: application/x-www-form-urlencoded
3+
content-length: 5
4+
date: {DATE}
5+
content-type: text/plain; charset=utf-8
76

7+
hello

tests/server.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ mod common;
88

99
#[async_std::test]
1010
async fn test_basic_request() {
11-
let case = TestCase::new("fixtures/request1.txt", "fixtures/response1.txt").await;
11+
let case = TestCase::new_server("fixtures/request1.txt", "fixtures/response1.txt").await;
1212
let addr = "http://example.com";
1313

1414
server::accept(addr, case.clone(), |_req| async {
@@ -24,7 +24,7 @@ async fn test_basic_request() {
2424

2525
#[async_std::test]
2626
async fn test_chunked_basic() {
27-
let case = TestCase::new(
27+
let case = TestCase::new_server(
2828
"fixtures/request-chunked-basic.txt",
2929
"fixtures/response-chunked-basic.txt",
3030
)
@@ -50,7 +50,7 @@ async fn test_chunked_basic() {
5050

5151
#[async_std::test]
5252
async fn test_chunked_echo() {
53-
let case = TestCase::new(
53+
let case = TestCase::new_server(
5454
"fixtures/request-chunked-echo.txt",
5555
"fixtures/response-chunked-echo.txt",
5656
)

0 commit comments

Comments
 (0)