Skip to content

Commit

Permalink
feat(client): port the hyper 0.14 as legacy::Client (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmonstar authored Feb 17, 2023
1 parent 9ecd28a commit 91d8b0a
Show file tree
Hide file tree
Showing 15 changed files with 1,803 additions and 38 deletions.
24 changes: 18 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ edition = "2018"
publish = false # no accidents while in dev

[dependencies]
hyper = "=1.0.0-rc.2"
hyper = { git = "https://github.com/hyperium/hyper" } #"=1.0.0-rc.2"
futures-channel = "0.3"
futures-util = { version = "0.3", default-features = false }
http = "0.2"
Expand All @@ -28,22 +28,34 @@ socket2 = "0.4"
tracing = { version = "0.1", default-features = false, features = ["std"] }
tokio = { version = "1", features = ["net", "rt", "time"] }
tower-service = "0.3"
tower = { version = "0.4", features = ["util"] }
tower = { version = "0.4", features = ["make", "util"] }

[dev-dependencies]
bytes = "1"
http-body-util = "0.1.0-rc.2"
tokio = { version = "1", features = ["macros", "test-util"] }

[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dev-dependencies]
pnet_datalink = "0.27.2"

[features]
default = []

# Shorthand to enable everything
full = ["http1", "http2", "tcp", "runtime"]
full = ["client", "server", "http1", "http2", "tcp", "runtime"]

client = ["hyper/client"]
server = ["hyper/server"]

http1 = ["hyper/http1"]
http2 = ["hyper/http2"]

runtime = []
tcp = []
http1 = []
http2 = []
runtime = []

# internal features used in CI
__internal_happy_eyeballs_tests = []

[[example]]
name = "client"
required-features = ["client", "http1", "tcp", "runtime"]
37 changes: 37 additions & 0 deletions examples/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::env;

use http_body_util::Empty;
use hyper::Request;
use hyper_util::client::{connect::HttpConnector, legacy::Client};

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let url = match env::args().nth(1) {
Some(url) => url,
None => {
eprintln!("Usage: client <url>");
return Ok(());
}
};

// HTTPS requires picking a TLS implementation, so give a better
// warning if the user tries to request an 'https' URL.
let url = url.parse::<hyper::Uri>()?;
if url.scheme_str() != Some("http") {
eprintln!("This example only works with 'http' URLs.");
return Ok(());
}

let client = Client::builder(hyper_util::rt::TokioExecutor::new()).build(HttpConnector::new());

let req = Request::builder()
.uri(url)
.body(Empty::<bytes::Bytes>::new())?;

let resp = client.request(req).await?;

eprintln!("{:?} {:?}", resp.version(), resp.status());
eprintln!("{:#?}", resp.headers());

Ok(())
}
125 changes: 125 additions & 0 deletions src/client/client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,132 @@
use hyper::{Request, Response};
use tower::{Service, MakeService};

use super::connect::Connect;
use super::pool;

pub struct Client<M> {
// Hi there. So, let's take a 0.14.x hyper::Client, and build up its layers
// here. We don't need to fully expose the layers to start with, but that
// is the end goal.
//
// Client = MakeSvcAsService<
// SetHost<
// Http1RequestTarget<
// DelayedRelease<
// ConnectingPool<C, P>
// >
// >
// >
// >
make_svc: M,
}

// We might change this... :shrug:
type PoolKey = hyper::Uri;

struct ConnectingPool<C, P> {
connector: C,
pool: P,
}

struct PoolableSvc<S>(S);

/// A marker to identify what version a pooled connection is.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[allow(dead_code)]
pub enum Ver {
Auto,
Http2,
}

// ===== impl Client =====

impl<M, /*ReqBody, ResBody,*/ E> Client<M>
where
M: MakeService<
hyper::Uri,
Request<()>,
Response = Response<()>,
Error = E,
MakeError = E,
>,
//M: Service<hyper::Uri, Error = E>,
//M::Response: Service<Request<ReqBody>, Response = Response<ResBody>>,
{
pub async fn request(&mut self, req: Request<()>) -> Result<Response<()>, E> {
let mut svc = self.make_svc.make_service(req.uri().clone()).await?;
svc.call(req).await
}
}

impl<M, /*ReqBody, ResBody,*/ E> Client<M>
where
M: MakeService<
hyper::Uri,
Request<()>,
Response = Response<()>,
Error = E,
MakeError = E,
>,
//M: Service<hyper::Uri, Error = E>,
//M::Response: Service<Request<ReqBody>, Response = Response<ResBody>>,
{

}

// ===== impl ConnectingPool =====

impl<C, P> ConnectingPool<C, P>
where
C: Connect,
C::_Svc: Unpin + Send + 'static,
{
async fn connection_for(&self, target: PoolKey) -> Result<pool::Pooled<PoolableSvc<C::_Svc>, PoolKey>, ()> {
todo!()
}
}

impl<S> pool::Poolable for PoolableSvc<S>
where
S: Unpin + Send + 'static,
{
fn is_open(&self) -> bool {
/*
match self.tx {
PoolTx::Http1(ref tx) => tx.is_ready(),
#[cfg(feature = "http2")]
PoolTx::Http2(ref tx) => tx.is_ready(),
}
*/
true
}

fn reserve(self) -> pool::Reservation<Self> {
/*
match self.tx {
PoolTx::Http1(tx) => Reservation::Unique(PoolClient {
conn_info: self.conn_info,
tx: PoolTx::Http1(tx),
}),
#[cfg(feature = "http2")]
PoolTx::Http2(tx) => {
let b = PoolClient {
conn_info: self.conn_info.clone(),
tx: PoolTx::Http2(tx.clone()),
};
let a = PoolClient {
conn_info: self.conn_info,
tx: PoolTx::Http2(tx),
};
Reservation::Shared(a, b)
}
}
*/
pool::Reservation::Unique(self)
}

fn can_share(&self) -> bool {
false
//self.is_http2()
}
}
Loading

0 comments on commit 91d8b0a

Please sign in to comment.