Skip to content

Commit

Permalink
Upgrade hyper to 1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
iamjpotts committed Jan 7, 2024
1 parent 2ee4d14 commit 0ee050f
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 246 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.9.0

* upgrade to hyper 1.0

# 0.8.0

* upgrade to tokio 1.0 and hyper 0.14 [#44](https://github.com/softprops/hyperlocal/pull/44)
Expand Down
30 changes: 20 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,43 +1,53 @@
[package]
name = "hyperlocal"
version = "0.8.0"
version = "0.9.0-alpha"
authors = ["softprops <d.tangren@gmail.com>"]
description = "Hyper bindings for Unix domain sockets"
homepage = "https://github.com/softprops/hyperlocal"
repository = "https://github.com/softprops/hyperlocal"
keywords = ["hyper", "unix", "sockets", "http"]
license = "MIT"
readme = "README.md"
edition = "2018"
edition = "2021"

[dependencies]
hex = "0.4"
hyper = "0.14"
tokio = { version = "1.0", features = ["net"] }
http-body-util = { version = "0.1", optional = true }
hyper = "1.1"
hyper-util = { version = "0.1.2", optional = true }
tokio = { version = "1.35", default-features = false, features = ["net"] }
tower-service = { version = "0.3", optional = true }
pin-project-lite = "0.2"

[dev-dependencies]
tokio = { version = "1.0", features = ["io-std", "io-util", "macros", "rt-multi-thread"] }
thiserror = "1.0"
tokio = { version = "1.35", features = ["io-std", "io-util", "macros", "rt-multi-thread"] }

[features]
default = []
default = ["client"]
client = [
"http-body-util",
"hyper/client",
"hyper/http1",
"hyper-util/client-legacy",
"hyper-util/http1",
"hyper-util/tokio",
"tower-service"
]
server = [
"hyper/http1",
"hyper/server"
"hyper/server",
"hyper-util/tokio",
]

[[example]]
name = "client"
required-features = ["client", "hyper/runtime"]
required-features = ["client"]

[[example]]
name = "server"
required-features = ["server", "hyper/runtime"]
required-features = ["server"]

[[test]]
name = "server_client"
required-features = ["client", "server", "hyper/runtime"]
required-features = ["client", "server"]
67 changes: 10 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,47 +47,20 @@ Add the following to your `Cargo.toml` file

```toml
[dependencies]
hyperlocal = "0.8"
hyperlocal = "0.9"
```

## Usage

### Servers

A typical server can be built with `hyperlocal::server::UnixServerExt`.
A typical server can be built by creating a `tokio::net::UnixListener` and accepting connections in a loop using
`hyper::service::service_fn` to create a request/response processing function, and connecting the `UnixStream` to it
using `hyper::server::conn::http1::Builder::new().serve_connection()`.

```rust
use std::{error::Error, fs, path::Path};
use hyper::{
service::{make_service_fn, service_fn},
Body, Response, Server,
};
use hyperlocal::UnixServerExt;
An example is at [examples/server.rs](./examples/server.rs).

const PHRASE: &str = "It's a Unix system. I know this.";

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let path = Path::new("/tmp/hyperlocal.sock");

if path.exists() {
fs::remove_file(path)?;
}

let make_service = make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(|_req| async {
Ok::<_, hyper::Error>(Response::new(Body::from(PHRASE)))
}))
});

Server::bind_unix(path)?.serve(make_service).await?;

Ok(())
}

```

To test that your server is working you can use an out of the box tool like `curl`
To test that your server is working you can use an out-of-the-box tool like `curl`


```sh
Expand All @@ -98,9 +71,10 @@ It's a Unix system. I know this.
### Clients
`hyperlocal` also provides bindings for writing unix domain socket based HTTP clients using `Hyper`'s native `Client` interface.
`hyperlocal` also provides bindings for writing unix domain socket based HTTP clients the `Client` interface from the
`hyper-utils` crate.
Configure your `Hyper` client using `hyper::Client::builder()`.
An example is at [examples/client.rs](./examples/client.rs).
Hyper's client interface makes it easy to send typical HTTP methods like `GET`, `POST`, `DELETE` with factory
methods, `get`, `post`, `delete`, etc. These require an argument that can be tranformed into a `hyper::Uri`.
Expand All @@ -109,27 +83,6 @@ Since Unix domain sockets aren't represented with hostnames that resolve to ip a
your standard over the counter URL string won't do. Instead, use a `hyperlocal::Uri`, which represents both file path to the domain
socket and the resource URI path and query string.

```rust
use std::error::Error;
use hyper::{body::HttpBody, Client};
use hyperlocal::{UnixClientExt, Uri};
use tokio::io::{self, AsyncWriteExt as _};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let url = Uri::new("/tmp/hyperlocal.sock", "/").into();
let client = Client::unix();
let mut response = client.get(url).await?;
while let Some(next) = response.data().await {
let chunk = next?;
io::stdout().write_all(&chunk).await?;
}
Ok(())
}
```
---

Doug Tangren (softprops) 2015-2020
17 changes: 11 additions & 6 deletions examples/client.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
use hyper::{body::HttpBody, Client};
use hyperlocal::{UnixClientExt, Uri};
use http_body_util::{BodyExt, Full};
use hyper::body::Bytes;
use hyper_util::client::legacy::Client;
use hyperlocal::{UnixClientExt, UnixConnector, Uri};
use std::error::Error;
use tokio::io::{self, AsyncWriteExt as _};

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let url = Uri::new("/tmp/hyperlocal.sock", "/").into();

let client = Client::unix();
let client: Client<UnixConnector, Full<Bytes>> = Client::unix();

let mut response = client.get(url).await?;

while let Some(next) = response.data().await {
let chunk = next?;
io::stdout().write_all(&chunk).await?;
while let Some(frame_result) = response.frame().await {
let frame = frame_result?;

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

Ok(())
Expand Down
39 changes: 27 additions & 12 deletions examples/server.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use hyper::{
service::{make_service_fn, service_fn},
Body, Response, Server,
};
use hyperlocal::UnixServerExt;
use hyper::{service::service_fn, Response};
use hyper_util::rt::TokioIo;
use std::{error::Error, fs, path::Path};
use tokio::net::UnixListener;

const PHRASE: &str = "It's a Unix system. I know this.";

// Adapted from https://hyper.rs/guides/1/server/hello-world/
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let path = Path::new("/tmp/hyperlocal.sock");
Expand All @@ -15,13 +14,29 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
fs::remove_file(path)?;
}

let make_service = make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(|_req| async {
Ok::<_, hyper::Error>(Response::new(Body::from(PHRASE)))
}))
});
let listener = UnixListener::bind(path)?;

Server::bind_unix(path)?.serve(make_service).await?;
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);

Ok(())
tokio::task::spawn(async move {
let svc_fn = service_fn(|_req| async {
let body = PHRASE.to_string();
Ok::<_, hyper::Error>(Response::new(body))
});

match hyper::server::conn::http1::Builder::new()
.serve_connection(io, svc_fn)
.await
{
Ok(()) => {
println!("Accepted connection.");
}
Err(err) => {
eprintln!("Failed to accept connection: {err:?}");
}
};
});
}
}
76 changes: 58 additions & 18 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
use hex::FromHex;

Check failure on line 1 in src/client.rs

View workflow job for this annotation

GitHub Actions / codestyle

Diff in /home/runner/work/hyperlocal/hyperlocal/src/client.rs
use hyper::{
client::connect::{Connected, Connection},
service::Service,
Body, Client, Uri,
};
use hyper::body::Body;
use hyper::rt::ReadBufCursor;
use hyper::Uri;
use hyper_util::client::legacy::connect::{Connected, Connection};
use hyper_util::client::legacy::Client;
use hyper_util::rt::{TokioExecutor, TokioIo};
use pin_project_lite::pin_project;
use std::io::Error;
use std::{
future::Future,
io,
path::{Path, PathBuf},

Check failure on line 13 in src/client.rs

View workflow job for this annotation

GitHub Actions / codestyle

Diff in /home/runner/work/hyperlocal/hyperlocal/src/client.rs
pin::Pin,
task::{Context, Poll},
};
use tokio::io::ReadBuf;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tower_service::Service;

pin_project! {
#[derive(Debug)]
Expand All @@ -29,7 +32,7 @@ impl UnixStream {
}
}

impl tokio::io::AsyncWrite for UnixStream {
impl AsyncWrite for UnixStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
Expand All @@ -47,7 +50,25 @@ impl tokio::io::AsyncWrite for UnixStream {
}
}

impl tokio::io::AsyncRead for UnixStream {
impl hyper::rt::Write for UnixStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, Error>> {
self.project().unix_stream.poll_write(cx, buf)
}

fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
self.project().unix_stream.poll_flush(cx)
}

fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
self.project().unix_stream.poll_shutdown(cx)
}
}

impl AsyncRead for UnixStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
Expand All @@ -57,16 +78,30 @@ impl tokio::io::AsyncRead for UnixStream {
}
}

impl hyper::rt::Read for UnixStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: ReadBufCursor<'_>,
) -> Poll<Result<(), Error>> {
let mut t = TokioIo::new(self.project().unix_stream);
Pin::new(&mut t).poll_read(cx, buf)
}
}

/// the `[UnixConnector]` can be used to construct a `[hyper::Client]` which can
/// speak to a unix domain socket.
///
/// # Example
/// ```
/// use hyper::{Client, Body};
/// use http_body_util::Full;
/// use hyper::body::Bytes;
/// use hyper_util::client::legacy::Client;
/// use hyper_util::rt::TokioExecutor;
/// use hyperlocal::UnixConnector;
///
/// let connector = UnixConnector;
/// let client: Client<UnixConnector, Body> = Client::builder().build(connector);
/// let client: Client<UnixConnector, Full<Bytes>> = Client::builder(TokioExecutor::new()).build(connector);
/// ```
///
/// # Note
Expand Down Expand Up @@ -129,22 +164,27 @@ fn parse_socket_path(uri: &Uri) -> Result<PathBuf, io::Error> {
}
}

/// Extention trait for constructing a hyper HTTP client over a Unix domain
/// Extension trait for constructing a hyper HTTP client over a Unix domain
/// socket.
pub trait UnixClientExt {
pub trait UnixClientExt<B: Body + Send> {
/// Construct a client which speaks HTTP over a Unix domain socket
///
/// # Example
/// ```
/// use hyper::Client;
/// use hyperlocal::UnixClientExt;
/// use http_body_util::Full;
/// use hyper::body::Bytes;
/// use hyper_util::client::legacy::Client;
/// use hyperlocal::{UnixClientExt, UnixConnector};
///
/// let client = Client::unix();
/// let client: Client<UnixConnector, Full<Bytes>> = Client::unix();
/// ```
#[must_use]
fn unix() -> Client<UnixConnector, Body> {
Client::builder().build(UnixConnector)
fn unix() -> Client<UnixConnector, B>
where
B::Data: Send,
{
Client::builder(TokioExecutor::new()).build(UnixConnector)
}
}

impl UnixClientExt for Client<UnixConnector> {}
impl<B: Body + Send> UnixClientExt<B> for Client<UnixConnector, B> {}
Loading

0 comments on commit 0ee050f

Please sign in to comment.