init structify

This commit is contained in:
Yoshua Wuyts 2020-04-18 18:27:58 +02:00
parent b8f3ba8a81
commit ed66b38b76
6 changed files with 112 additions and 68 deletions

View File

@ -15,7 +15,7 @@ edition = "2018"
url = "2.1.0"
httparse = "1.3.3"
async-std = { version = "1.5.0", features = ["unstable"] }
http-types = "1.2.0"
http-types = { version = "1.2.0", features = ["unstable"] }
pin-project-lite = "0.1.1"
byte-pool = "0.2.1"
lazy_static = "1.4.0"
@ -27,3 +27,6 @@ pretty_assertions = "0.6.1"
async-std = { version = "1.4.0", features = ["unstable", "attributes"] }
tempfile = "3.1.0"
async-test = "1.0.0"
[patch.crates-io]
http-types = { path = "../http-types", features = ["unstable"] }

View File

@ -1,4 +1,4 @@
use async_h1::client;
use async_h1::HttpClient;
use async_std::net::TcpStream;
use http_types::{Error, Method, Request, Url};
@ -12,7 +12,7 @@ async fn main() -> Result<(), Error> {
println!("making request {}/2", i + 1);
let url = Url::parse(&format!("http://{}/foo", peer_addr)).unwrap();
let req = Request::new(Method::Get, url);
let res = client::connect(stream.clone(), req).await?;
let res = HttpClient::connect(stream.clone(), req).await?;
println!("{:?}", res);
}
Ok(())

View File

@ -3,24 +3,76 @@
use async_std::io::{self, Read, Write};
use http_types::{Request, Response};
use std::future::Future;
use std::pin::Pin;
mod decode;
mod encode;
pub use decode::decode;
pub use encode::Encoder;
/// Opens an HTTP/1.1 connection to a remote host.
pub async fn connect<RW>(mut stream: RW, req: Request) -> http_types::Result<Response>
where
RW: Read + Write + Send + Sync + Unpin + 'static,
{
let mut req = Encoder::encode(req).await?;
log::trace!("> {:?}", &req);
/// HTTP/1.1 client.
#[derive(Debug)]
pub struct HttpClient;
io::copy(&mut req, &mut stream).await?;
impl HttpClient {
/// Creates a new instance of `HttpClientBuilder`.
pub fn builder() -> HttpClientBuilder {
HttpClientBuilder::new()
}
let res = decode(stream).await?;
log::trace!("< {:?}", &res);
// Public for testing purposes only.
#[doc(hidden)]
pub async fn decode<R>(reader: R) -> http_types::Result<Response>
where
R: Read + Unpin + Send + Sync + 'static,
{
decode(reader).await
}
Ok(res)
/// Opens an HTTP/1.1 connection to a remote host.
pub async fn connect<RW>(stream: RW, req: Request) -> http_types::Result<Response>
where
RW: Read + Write + Send + Sync + Unpin + 'static,
{
let builder = HttpClientBuilder::new();
builder.connect(stream, req).await
}
}
/// HTTP/1.1 client builder.
#[derive(Debug)]
pub struct HttpClientBuilder;
impl HttpClientBuilder {
/// Creates a new instance of `HttpClientBuilder`.
pub fn new() -> Self {
Self {}
}
/// Opens an HTTP/1.1 connection to a remote host.
pub async fn connect<RW>(self, mut stream: RW, req: Request) -> http_types::Result<Response>
where
RW: Read + Write + Send + Sync + Unpin + 'static,
{
let mut req = Encoder::encode(req).await?;
log::trace!("> {:?}", &req);
io::copy(&mut req, &mut stream).await?;
let res = decode(stream).await?;
log::trace!("< {:?}", &res);
Ok(res)
}
}
impl http_types::Client for HttpClient {
fn send_req(
&self,
req: Request,
) -> Pin<Box<dyn Future<Output = http_service::Result<Response>> + 'static + Send>> {
todo!();
}
}

View File

@ -27,7 +27,7 @@
//! __HTTP client__
//!
//! ```no_run
//! use async_h1::client;
//! use async_h1::HttpClient;
//! use async_std::net::{TcpStream};
//! use http_types::{Error, Method, Request, Url};
//!
@ -41,7 +41,7 @@
//! println!("making request {}/2", i + 1);
//! let url = Url::parse(&format!("http://{}/foo", peer_addr)).unwrap();
//! let req = Request::new(Method::Get, url);
//! let res = client::connect(stream.clone(), req).await?;
//! let res = HttpClient::connect(stream.clone(), req).await?;
//! println!("{:?}", res);
//! }
//! Ok(())
@ -115,10 +115,5 @@ mod server;
#[doc(hidden)]
pub mod client;
pub use client::connect;
pub use server::{HttpServer, HttpServerBuilder};
// pub struct HttpClient {}
// impl HttpClient {
// pub fn connect() {}
// }
pub use client::{HttpClient, HttpClientBuilder};
pub use server::HttpServer;

View File

@ -1,5 +1,6 @@
//! Process HTTP connections on the server.
use std::pin::Pin;
use std::time::Duration;
use async_std::future::{timeout, Future, TimeoutError};
@ -15,54 +16,35 @@ use encode::Encoder;
/// HTTP/1.1 server.
#[derive(Debug)]
pub struct HttpServer {
builder: HttpServerBuilder,
}
impl HttpServer {
/// Create a new `HttpServer` builder that takes various configuration.
pub fn builder() -> HttpServerBuilder {
HttpServerBuilder::new()
}
/// Accept a new incoming HTTP/1.1 connection.
///
/// Supports `KeepAlive` requests by default.
pub async fn accept<RW, F, Fut>(addr: &str, io: RW, endpoint: F) -> http_types::Result<()>
where
RW: Read + Write + Clone + Send + Sync + Unpin + 'static,
F: Fn(Request) -> Fut,
Fut: Future<Output = http_types::Result<Response>>,
{
let builder = HttpServerBuilder::new();
builder.accept(addr, io, endpoint).await
}
}
/// HTTP/1.1 server builder.
#[derive(Debug)]
pub struct HttpServerBuilder {
/// Timeout to handle headers. Defaults to 60s.
pub struct HttpServer<F, Fut> {
headers_timeout: Option<Duration>,
endpoint: F,
__fut: std::marker::PhantomData<Fut>,
}
impl HttpServerBuilder {
/// Create a new instance of `HttpServerBuilder`.
pub fn new() -> Self {
impl<F, Fut> HttpServer<F, Fut>
where
F: Fn(Request) -> Fut,
Fut: Future<Output = http_types::Result<Response>>,
{
/// Create a new `HttpServer` builder that takes various configuration.
// pub fn builder() -> HttpServerBuilder {
// HttpServerBuilder::new()
// }
/// Create a new instance of `HttpServer`.
pub async fn new(endpoint: F) -> Self {
Self {
headers_timeout: Some(Duration::from_secs(60)),
endpoint,
__fut: std::marker::PhantomData,
}
}
/// Accept a new incoming HTTP/1.1 connection.
///
/// Supports `KeepAlive` requests by default.
pub async fn accept<RW, F, Fut>(
self,
addr: &str,
mut io: RW,
endpoint: F,
) -> http_types::Result<()>
pub async fn accept<RW>(self, addr: &str, io: RW) -> http_types::Result<()>
where
RW: Read + Write + Clone + Send + Sync + Unpin + 'static,
F: Fn(Request) -> Fut,
@ -70,7 +52,7 @@ impl HttpServerBuilder {
{
loop {
// Decode a new request, timing out if this takes longer than the timeout duration.
let fut = decode(addr, io.clone());
let fut = decode(self.addr, io.clone());
let req = if let Some(timeout_duration) = self.headers_timeout {
match timeout(timeout_duration, fut).await {
@ -86,7 +68,7 @@ impl HttpServerBuilder {
};
// Pass the request to the endpoint and encode the response.
let res = endpoint(req).await?;
let res = self.endpoint(req).await?;
let mut encoder = Encoder::encode(res);
// Stream the response to the writer.
@ -96,3 +78,15 @@ impl HttpServerBuilder {
Ok(())
}
}
impl<F, Fut> http_types::Server for HttpServer<F, Fut> {
fn recv_req(
&self,
req: Request,
) -> Pin<Box<dyn Future<Output = http_types::Result<Response>> + 'static + Send>> {
Box::pin(async move {
let res = self.endpoint(req).await?;
Ok(res)
})
}
}

View File

@ -1,5 +1,5 @@
use crate::common::fixture_path;
use async_h1::client;
use async_h1::HttpClient;
use async_std::fs::File;
use http_types::{headers, Method, Request, StatusCode};
use url::Url;
@ -20,7 +20,7 @@ async fn test_encode_request_add_date() {
let mut req = Request::new(Method::Post, url);
req.set_body("hello");
let res = client::connect(case.clone(), req).await.unwrap();
let res = HttpClient::connect(case.clone(), req).await.unwrap();
assert_eq!(res.status(), StatusCode::Ok);
case.assert().await;
@ -32,7 +32,7 @@ async fn test_response_no_date() {
.await
.unwrap();
let res = client::decode(response_fixture).await.unwrap();
let res = HttpClient::decode(response_fixture).await.unwrap();
pretty_assertions::assert_eq!(res.header(&headers::DATE).is_some(), true);
}
@ -43,7 +43,7 @@ async fn test_multiple_header_values_for_same_header_name() {
.await
.unwrap();
let res = client::decode(response_fixture).await.unwrap();
let res = HttpClient::decode(response_fixture).await.unwrap();
pretty_assertions::assert_eq!(res.header(&headers::SET_COOKIE).unwrap().len(), 2);
}
@ -54,7 +54,7 @@ async fn test_response_newlines() {
.await
.unwrap();
let res = client::decode(response_fixture).await.unwrap();
let res = HttpClient::decode(response_fixture).await.unwrap();
pretty_assertions::assert_eq!(
res.header(&headers::CONTENT_LENGTH)
@ -79,7 +79,7 @@ async fn test_encode_request_with_connect() {
let url = Url::parse("https://example.com:443").unwrap();
let req = Request::new(Method::Connect, url);
let res = client::connect(case.clone(), req).await.unwrap();
let res = HttpClient::connect(case.clone(), req).await.unwrap();
assert_eq!(res.status(), StatusCode::Ok);
case.assert().await;