Skip to content

Commit

Permalink
feat: upgrade to hyper-v1, use hyper-utils for now (#104)
Browse files Browse the repository at this point in the history
* feat: upgrade to hyper-v1, use hyper-utils for now

* chore: bump hyper-utils

* chore: fix example inside lib
  • Loading branch information
kristof-mattei authored Nov 3, 2023
1 parent 5e4cb93 commit 9adf50b
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 37 deletions.
11 changes: 9 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ vendored = ["native-tls/vendored"]
[dependencies]
bytes = "1"
native-tls = "0.2.1"
hyper = { version = "0.14.2", default-features = false, features = ["tcp", "client"] }
hyper = { version = "1.0.0-rc.4", default-features = false }
hyper-util = { git = "https://github.com/hyperium/hyper-util", default-features = false, features = [
"client",
], rev = "11776bd742196691d03203caa0f4acd29df696bd" }
tokio = "1"
tokio-native-tls = "0.3"
tower-service = "0.3"
http-body-util = "0.1.0-rc.3"

[dev-dependencies]
tokio = { version = "1.0.0", features = ["io-std", "macros", "io-util"] }
hyper = { version = "0.14.2", default-features = false, features = ["http1"] }
hyper-util = { git = "https://github.com/hyperium/hyper-util", default-features = false, features = [
"http1",
], rev = "11776bd742196691d03203caa0f4acd29df696bd" }
19 changes: 14 additions & 5 deletions examples/client.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
use hyper::{body::HttpBody as _, Client};
use bytes::Bytes;
use http_body_util::BodyExt;

use http_body_util::Empty;
use hyper_tls::HttpsConnector;
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
use tokio::io::{self, AsyncWriteExt as _};

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let https = HttpsConnector::new();
let client = Client::builder().build::<_, hyper::Body>(https);

let client = Client::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(https);

let mut res = client.get("https://hyper.rs".parse()?).await?;

println!("Status: {}", res.status());
println!("Headers:\n{:#?}", res.headers());

while let Some(chunk) = res.body_mut().data().await {
let chunk = chunk?;
io::stdout().write_all(&chunk).await?
while let Some(frame) = res.body_mut().frame().await {
let frame = frame?;

if let Some(d) = frame.data_ref() {
io::stdout().write_all(d).await?;
}
}

Ok(())
}
53 changes: 35 additions & 18 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use hyper::{
rt::{Read, Write},
Uri,
};
use hyper_util::{client::connect::HttpConnector, rt::TokioIo};
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

use hyper::{client::connect::HttpConnector, service::Service, Uri};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_native_tls::TlsConnector;
use tower_service::Service;

use crate::stream::MaybeHttpsStream;

Expand All @@ -20,17 +23,17 @@ pub struct HttpsConnector<T> {
}

impl HttpsConnector<HttpConnector> {
/// Construct a new HttpsConnector.
/// Construct a new `HttpsConnector`.
///
/// This uses hyper's default `HttpConnector`, and default `TlsConnector`.
/// If you wish to use something besides the defaults, use `From::from`.
///
/// # Note
///
/// By default this connector will use plain HTTP if the URL provided uses
/// the HTTP scheme (eg: http://example.com/).
/// the HTTP scheme (eg: <http://example.com/>).
///
/// If you would like to force the use of HTTPS then call https_only(true)
/// If you would like to force the use of HTTPS then call `https_only(true)`
/// on the returned connector.
///
/// # Panics
Expand All @@ -39,10 +42,12 @@ impl HttpsConnector<HttpConnector> {
///
/// To handle that error yourself, you can use the `HttpsConnector::from`
/// constructor after trying to make a `TlsConnector`.
#[must_use]
pub fn new() -> Self {
native_tls::TlsConnector::new()
.map(|tls| HttpsConnector::new_(tls.into()))
.unwrap_or_else(|e| panic!("HttpsConnector::new() failure: {}", e))
native_tls::TlsConnector::new().map_or_else(
|e| panic!("HttpsConnector::new() failure: {}", e),
|tls| HttpsConnector::new_(tls.into()),
)
}

fn new_(tls: TlsConnector) -> Self {
Expand All @@ -68,15 +73,22 @@ impl<T> HttpsConnector<T> {

/// With connector constructor
///
/// # Panics
///
/// This will panic if the underlying TLS context could not be created.
///
/// To handle that error yourself, you can use the `HttpsConnector::from`
/// constructor after trying to make a `TlsConnector`.
pub fn new_with_connector(http: T) -> Self {
native_tls::TlsConnector::new()
.map(|tls| HttpsConnector::from((http, tls.into())))
.unwrap_or_else(|e| {
native_tls::TlsConnector::new().map_or_else(
|e| {
panic!(
"HttpsConnector::new_with_connector(<connector>) failure: {}",
e
)
})
},
|tls| HttpsConnector::from((http, tls.into())),
)
}
}

Expand All @@ -95,14 +107,14 @@ impl<T: fmt::Debug> fmt::Debug for HttpsConnector<T> {
f.debug_struct("HttpsConnector")
.field("force_https", &self.force_https)
.field("http", &self.http)
.finish()
.finish_non_exhaustive()
}
}

impl<T> Service<Uri> for HttpsConnector<T>
where
T: Service<Uri>,
T::Response: AsyncRead + AsyncWrite + Send + Unpin,
T::Response: Read + Write + Send + Unpin,
T::Future: Send + 'static,
T::Error: Into<BoxError>,
{
Expand Down Expand Up @@ -131,11 +143,16 @@ where
.trim_matches(|c| c == '[' || c == ']')
.to_owned();
let connecting = self.http.call(dst);
let tls = self.tls.clone();

let tls_connector = self.tls.clone();

let fut = async move {
let tcp = connecting.await.map_err(Into::into)?;

let maybe = if is_https {
let tls = tls.connect(&host, tcp).await?;
let stream = TokioIo::new(tcp);

let tls = TokioIo::new(tls_connector.connect(&host, stream).await?);
MaybeHttpsStream::Https(tls)
} else {
MaybeHttpsStream::Http(tcp)
Expand All @@ -155,7 +172,7 @@ type BoxedFut<T> = Pin<Box<dyn Future<Output = Result<MaybeHttpsStream<T>, BoxEr
/// A Future representing work to connect to a URL, and a TLS handshake.
pub struct HttpsConnecting<T>(BoxedFut<T>);

impl<T: AsyncRead + AsyncWrite + Unpin> Future for HttpsConnecting<T> {
impl<T: Read + Write + Unpin> Future for HttpsConnecting<T> {
type Output = Result<MaybeHttpsStream<T>, BoxError>;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
//! ## Example
//!
//! ```no_run
//! use bytes::Bytes;
//! use http_body_util::Empty;
//! use hyper_tls::HttpsConnector;
//! use hyper::Client;
//! use hyper_util::{client::legacy::Client, rt::TokioExecutor};
//!
//! #[tokio::main(flavor = "current_thread")]
//! async fn main() -> Result<(), Box<dyn std::error::Error>>{
//! let https = HttpsConnector::new();
//! let client = Client::builder().build::<_, hyper::Body>(https);
//! let client = Client::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(https);
//!
//! let res = client.get("https://hyper.rs".parse()?).await?;
//! assert_eq!(res.status(), 200);
Expand Down
32 changes: 22 additions & 10 deletions src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ use std::io::IoSlice;
use std::pin::Pin;
use std::task::{Context, Poll};

use hyper::client::connect::{Connected, Connection};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use hyper::rt::{Read, ReadBufCursor, Write};

use hyper_util::{
client::connect::{Connected, Connection},
rt::TokioIo,
};
pub use tokio_native_tls::TlsStream;

/// A stream that might be protected with TLS.
pub enum MaybeHttpsStream<T> {
/// A stream over plain text.
Http(T),
/// A stream protected with TLS.
Https(TlsStream<T>),
Https(TokioIo<TlsStream<TokioIo<T>>>),
}

// ===== impl MaybeHttpsStream =====
Expand All @@ -33,18 +37,24 @@ impl<T> From<T> for MaybeHttpsStream<T> {
}
}

impl<T> From<TlsStream<T>> for MaybeHttpsStream<T> {
fn from(inner: TlsStream<T>) -> Self {
impl<T> From<TlsStream<TokioIo<T>>> for MaybeHttpsStream<T> {
fn from(inner: TlsStream<TokioIo<T>>) -> Self {
MaybeHttpsStream::Https(TokioIo::new(inner))
}
}

impl<T> From<TokioIo<TlsStream<TokioIo<T>>>> for MaybeHttpsStream<T> {
fn from(inner: TokioIo<TlsStream<TokioIo<T>>>) -> Self {
MaybeHttpsStream::Https(inner)
}
}

impl<T: AsyncRead + AsyncWrite + Unpin> AsyncRead for MaybeHttpsStream<T> {
impl<T: Read + Write + Unpin> Read for MaybeHttpsStream<T> {
#[inline]
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut ReadBuf,
buf: ReadBufCursor<'_>,
) -> Poll<Result<(), io::Error>> {
match Pin::get_mut(self) {
MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(cx, buf),
Expand All @@ -53,7 +63,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> AsyncRead for MaybeHttpsStream<T> {
}
}

impl<T: AsyncWrite + AsyncRead + Unpin> AsyncWrite for MaybeHttpsStream<T> {
impl<T: Write + Read + Unpin> Write for MaybeHttpsStream<T> {
#[inline]
fn poll_write(
self: Pin<&mut Self>,
Expand Down Expand Up @@ -101,11 +111,13 @@ impl<T: AsyncWrite + AsyncRead + Unpin> AsyncWrite for MaybeHttpsStream<T> {
}
}

impl<T: AsyncRead + AsyncWrite + Connection + Unpin> Connection for MaybeHttpsStream<T> {
impl<T: Connection + Unpin> Connection for MaybeHttpsStream<T> {
fn connected(&self) -> Connected {
match self {
MaybeHttpsStream::Http(s) => s.connected(),
MaybeHttpsStream::Https(s) => s.get_ref().get_ref().get_ref().connected(),
MaybeHttpsStream::Https(s) => {
s.inner().get_ref().get_ref().get_ref().inner().connected()
}
}
}
}

0 comments on commit 9adf50b

Please sign in to comment.