Skip to content

feat(client): remove multi-version client::conn types #2987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async fn fetch_url(url: hyper::Uri) -> Result<()> {
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await?;

let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
let (mut sender, conn) = hyper::client::conn::http1::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
Expand Down
2 changes: 1 addition & 1 deletion examples/client_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async fn fetch_json(url: hyper::Uri) -> Result<Vec<User>> {

let stream = TcpStream::connect(addr).await?;

let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
let (mut sender, conn) = hyper::client::conn::http1::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
Expand Down
3 changes: 2 additions & 1 deletion examples/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
async move {
let client_stream = TcpStream::connect(addr).await.unwrap();

let (mut sender, conn) = hyper::client::conn::handshake(client_stream).await?;
let (mut sender, conn) =
hyper::client::conn::http1::handshake(client_stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
Expand Down
2 changes: 1 addition & 1 deletion examples/http_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::net::SocketAddr;

use bytes::Bytes;
use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
use hyper::client::conn::Builder;
use hyper::client::conn::http1::Builder;
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::upgrade::Upgraded;
Expand Down
2 changes: 1 addition & 1 deletion examples/upgrades.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ async fn client_upgrade_request(addr: SocketAddr) -> Result<()> {
.unwrap();

let stream = TcpStream::connect(addr).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
let (mut sender, conn) = hyper::client::conn::http1::handshake(stream).await?;

tokio::task::spawn(async move {
if let Err(err) = conn.await {
Expand Down
2 changes: 1 addition & 1 deletion examples/web_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async fn client_request_response() -> Result<Response<BoxBody>> {
let port = req.uri().port_u16().expect("uri has no port");
let stream = TcpStream::connect(format!("{}:{}", host, port)).await?;

let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
let (mut sender, conn) = hyper::client::conn::http1::handshake(stream).await?;

tokio::task::spawn(async move {
if let Err(err) = conn.await {
Expand Down
84 changes: 82 additions & 2 deletions src/client/conn/http1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::error::Error as StdError;
use std::fmt;
use std::sync::Arc;

use bytes::Bytes;
use http::{Request, Response};
use httparse::ParserConfig;
use tokio::io::{AsyncRead, AsyncWrite};
Expand All @@ -27,6 +28,27 @@ pub struct SendRequest<B> {
dispatch: dispatch::Sender<Request<B>, Response<Recv>>,
}

/// Deconstructed parts of a `Connection`.
///
/// This allows taking apart a `Connection` at a later time, in order to
/// reclaim the IO object, and additional related pieces.
#[derive(Debug)]
pub struct Parts<T> {
/// The original IO object used in the handshake.
pub io: T,
/// A buffer of bytes that have been read but not processed as HTTP.
///
/// For instance, if the `Connection` is used for an HTTP upgrade request,
/// it is possible the server sent back the first bytes of the new protocol
/// along with the response upgrade.
///
/// You will want to check for any existing bytes if you plan to continue
/// communicating on the IO object.
pub read_buf: Bytes,
_inner: (),
}


/// A future that processes all HTTP state for the IO object.
///
/// In most cases, this should just be spawned into an executor, so that it
Expand All @@ -40,6 +62,40 @@ where
inner: Option<Dispatcher<T, B>>,
}

impl<T, B> Connection<T, B>
where
T: AsyncRead + AsyncWrite + Send + Unpin + 'static,
B: Body + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
{
/// Return the inner IO object, and additional information.
///
/// Only works for HTTP/1 connections. HTTP/2 connections will panic.
pub fn into_parts(self) -> Parts<T> {
let (io, read_buf, _) = self.inner.expect("already upgraded").into_inner();
Parts {
io,
read_buf,
_inner: (),
}
}

/// Poll the connection for completion, but without calling `shutdown`
/// on the underlying IO.
///
/// This is useful to allow running a connection while doing an HTTP
/// upgrade. Once the upgrade is completed, the connection would be "done",
/// but it is not desired to actually shutdown the IO object. Instead you
/// would take it back using `into_parts`.
///
/// Use [`poll_fn`](https://docs.rs/futures/0.1.25/futures/future/fn.poll_fn.html)
/// and [`try_ready!`](https://docs.rs/futures/0.1.25/futures/macro.try_ready.html)
/// to work with this function; or use the `without_shutdown` wrapper.
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
self.inner.as_mut().expect("algready upgraded").poll_without_shutdown(cx)
}
}

/// A builder to configure an HTTP connection.
///
/// After setting options, the builder is used to create a handshake future.
Expand All @@ -52,6 +108,8 @@ pub struct Builder {
h1_title_case_headers: bool,
h1_preserve_header_case: bool,
#[cfg(feature = "ffi")]
h1_headers_raw: bool,
#[cfg(feature = "ffi")]
h1_preserve_header_order: bool,
h1_read_buf_exact_size: Option<usize>,
h1_max_buf_size: Option<usize>,
Expand All @@ -61,11 +119,14 @@ pub struct Builder {
///
/// This is a shortcut for `Builder::new().handshake(io)`.
/// See [`client::conn`](crate::client::conn) for more.
pub async fn handshake<T>(
pub async fn handshake<T, B>(
io: T,
) -> crate::Result<(SendRequest<crate::Recv>, Connection<T, crate::Recv>)>
) -> crate::Result<(SendRequest<B>, Connection<T, B>)>
where
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
B: Body + 'static,
B::Data: Send,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
{
Builder::new().handshake(io).await
}
Expand All @@ -80,6 +141,13 @@ impl<B> SendRequest<B> {
self.dispatch.poll_ready(cx)
}

/// Waits until the dispatcher is ready
///
/// If the associated connection is closed, this returns an Error.
pub async fn ready(&mut self) -> crate::Result<()> {
futures_util::future::poll_fn(|cx| self.poll_ready(cx)).await
}

/*
pub(super) async fn when_ready(self) -> crate::Result<Self> {
let mut me = Some(self);
Expand Down Expand Up @@ -232,6 +300,8 @@ impl Builder {
h1_title_case_headers: false,
h1_preserve_header_case: false,
#[cfg(feature = "ffi")]
h1_headers_raw: false,
#[cfg(feature = "ffi")]
h1_preserve_header_order: false,
h1_max_buf_size: None,
}
Expand Down Expand Up @@ -386,6 +456,12 @@ impl Builder {
self
}

#[cfg(feature = "ffi")]
pub(crate) fn http1_headers_raw(&mut self, enabled: bool) -> &mut Builder {
self.h1_headers_raw = enabled;
self
}

/// Sets the exact size of the read buffer to *always* use.
///
/// Note that setting this option unsets the `http1_max_buf_size` option.
Expand Down Expand Up @@ -459,6 +535,10 @@ impl Builder {
if opts.h1_preserve_header_order {
conn.set_preserve_header_order();
}
#[cfg(feature = "ffi")]
if opts.h1_headers_raw {
conn.set_raw_headers(true);
}
if opts.h09_responses {
conn.set_h09_responses();
}
Expand Down
35 changes: 33 additions & 2 deletions src/client/conn/http2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ pub struct Builder {
///
/// This is a shortcut for `Builder::new().handshake(io)`.
/// See [`client::conn`](crate::client::conn) for more.
pub async fn handshake<T>(
pub async fn handshake<T, B>(
io: T,
) -> crate::Result<(SendRequest<crate::Recv>, Connection<T, crate::Recv>)>
) -> crate::Result<(SendRequest<B>, Connection<T, B>)>
where
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
B: Body + 'static,
B::Data: Send,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
{
Builder::new().handshake(io).await
}
Expand All @@ -75,6 +78,13 @@ impl<B> SendRequest<B> {
}
}

/// Waits until the dispatcher is ready
///
/// If the associated connection is closed, this returns an Error.
pub async fn ready(&mut self) -> crate::Result<()> {
futures_util::future::poll_fn(|cx| self.poll_ready(cx)).await
}

/*
pub(super) async fn when_ready(self) -> crate::Result<Self> {
let mut me = Some(self);
Expand Down Expand Up @@ -175,6 +185,27 @@ impl<B> fmt::Debug for SendRequest<B> {

// ===== impl Connection

impl<T, B> Connection<T, B>
where
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
B: Body + Unpin + Send + 'static,
B::Data: Send,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
{
/// Returns whether the [extended CONNECT protocol][1] is enabled or not.
///
/// This setting is configured by the server peer by sending the
/// [`SETTINGS_ENABLE_CONNECT_PROTOCOL` parameter][2] in a `SETTINGS` frame.
/// This method returns the currently acknowledged value received from the
/// remote.
///
/// [1]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
/// [2]: https://datatracker.ietf.org/doc/html/rfc8441#section-3
pub fn is_extended_connect_protocol_enabled(&self) -> bool {
self.inner.1.is_extended_connect_protocol_enabled()
}
}

impl<T, B> fmt::Debug for Connection<T, B>
where
T: AsyncRead + AsyncWrite + fmt::Debug + Send + 'static,
Expand Down
Loading