Update to latest versions of hyper and http-body (#1882)

Co-authored-by: Michael Scofield <[email protected]>
Co-authored-by: Jonas Platte <[email protected]>
This commit is contained in:
David Pedersen
2023-11-23 11:03:03 +00:00
committed by GitHub
co-authored by Michael Scofield Jonas Platte
parent 2f4720907a
commit 43b14a5f02
93 changed files with 1463 additions and 1448 deletions
@@ -6,9 +6,10 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = "0.14"
http-body-util = "0.1.0"
hyper = "1.0.0"
tokio = { version = "1.0", features = ["full"] }
tower = "0.4"
tower-http = { version = "0.4.0", features = ["map-request-body", "util"] }
tower-http = { version = "0.5.0", features = ["map-request-body", "util"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
@@ -14,6 +14,7 @@ use axum::{
routing::post,
Router,
};
use http_body_util::BodyExt;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
@@ -50,9 +51,11 @@ async fn buffer_request_body(request: Request) -> Result<Request, Response> {
let (parts, body) = request.into_parts();
// this wont work if the body is an long running stream
let bytes = hyper::body::to_bytes(body)
let bytes = body
.collect()
.await
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response())?;
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response())?
.to_bytes();
do_thing_with_request_body(bytes.clone());
+1 -1
View File
@@ -7,4 +7,4 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
tokio = { version = "1.0", features = ["full"] }
tower-http = { version = "0.4.0", features = ["cors"] }
tower-http = { version = "0.5.0", features = ["cors"] }
+1 -1
View File
@@ -6,5 +6,5 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
tokio = { version = "1.0", features = ["full"] }
+46 -41
View File
@@ -5,51 +5,56 @@
//! kill or ctrl-c
//! ```
use axum::{response::Html, routing::get, Router};
use std::net::SocketAddr;
use tokio::signal;
#[tokio::main]
async fn main() {
// build our application with a route
let app = Router::new().route("/", get(handler));
// run it
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("listening on {addr}");
hyper::Server::bind(&addr)
.serve(app.into_make_service())
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
// TODO
fn main() {
eprint!("this example has not yet been updated to hyper 1.0");
}
async fn handler() -> Html<&'static str> {
Html("<h1>Hello, World!</h1>")
}
// use axum::{response::Html, routing::get, Router};
// use std::net::SocketAddr;
// use tokio::signal;
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
// #[tokio::main]
// async fn main() {
// // build our application with a route
// let app = Router::new().route("/", get(handler));
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
// // run it
// let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
// println!("listening on {}", addr);
// hyper::Server::bind(&addr)
// .serve(app.into_make_service())
// .with_graceful_shutdown(shutdown_signal())
// .await
// .unwrap();
// }
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
// async fn handler() -> Html<&'static str> {
// Html("<h1>Hello, World!</h1>")
// }
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
// async fn shutdown_signal() {
// let ctrl_c = async {
// signal::ctrl_c()
// .await
// .expect("failed to install Ctrl+C handler");
// };
println!("signal received, starting graceful shutdown");
}
// #[cfg(unix)]
// let terminate = async {
// signal::unix::signal(signal::unix::SignalKind::terminate())
// .expect("failed to install signal handler")
// .recv()
// .await;
// };
// #[cfg(not(unix))]
// let terminate = std::future::pending::<()>();
// tokio::select! {
// _ = ctrl_c => {},
// _ = terminate => {},
// }
// println!("signal received, starting graceful shutdown");
// }
+2 -1
View File
@@ -9,5 +9,6 @@ axum = { path = "../../axum" }
tokio = { version = "1.0", features = ["full"] }
[dev-dependencies]
hyper = { version = "0.14", features = ["full"] }
http-body-util = "0.1.0"
hyper = { version = "1.0.0", features = ["full"] }
tower = { version = "0.4", features = ["util"] }
+3 -2
View File
@@ -44,6 +44,7 @@ mod tests {
use super::*;
use axum::body::Body;
use axum::http::{Request, StatusCode};
use http_body_util::BodyExt;
use tower::ServiceExt;
#[tokio::test]
@@ -58,7 +59,7 @@ mod tests {
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.headers()["x-some-header"], "header from GET");
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body = response.collect().await.unwrap().to_bytes();
assert_eq!(&body[..], b"body from GET");
}
@@ -74,7 +75,7 @@ mod tests {
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.headers()["x-some-header"], "header from HEAD");
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body = response.collect().await.unwrap().to_bytes();
assert!(body.is_empty());
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["make"] }
tracing = "0.1"
+85 -76
View File
@@ -12,87 +12,96 @@
//!
//! Example is based on <https://github.com/hyperium/hyper/blob/master/examples/http_proxy.rs>
use axum::{
body::Body,
extract::Request,
http::{Method, StatusCode},
response::{IntoResponse, Response},
routing::get,
Router,
};
use hyper::upgrade::Upgraded;
use std::net::SocketAddr;
use tokio::net::TcpStream;
use tower::{make::Shared, ServiceExt};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_http_proxy=trace,tower_http=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let router_svc = Router::new().route("/", get(|| async { "Hello, World!" }));
let service = tower::service_fn(move |req: Request<_>| {
let router_svc = router_svc.clone();
let req = req.map(Body::new);
async move {
if req.method() == Method::CONNECT {
proxy(req).await
} else {
router_svc.oneshot(req).await.map_err(|err| match err {})
}
}
});
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {addr}");
hyper::Server::bind(&addr)
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.serve(Shared::new(service))
.await
.unwrap();
// TODO
fn main() {
eprint!("this example has not yet been updated to hyper 1.0");
}
async fn proxy(req: Request) -> Result<Response, hyper::Error> {
tracing::trace!(?req);
// use axum::{
// body::Body,
// extract::Request,
// http::{Method, StatusCode},
// response::{IntoResponse, Response},
// routing::get,
// Router,
// };
// use hyper::upgrade::Upgraded;
// use std::net::SocketAddr;
// use tokio::net::TcpStream;
// use tower::{make::Shared, ServiceExt};
// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
if let Some(host_addr) = req.uri().authority().map(|auth| auth.to_string()) {
tokio::task::spawn(async move {
match hyper::upgrade::on(req).await {
Ok(upgraded) => {
if let Err(e) = tunnel(upgraded, host_addr).await {
tracing::warn!("server io error: {e}");
};
}
Err(e) => tracing::warn!("upgrade error: {e}"),
}
});
// #[tokio::main]
// async fn main() {
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_http_proxy=trace,tower_http=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
Ok(Response::new(Body::empty()))
} else {
tracing::warn!("CONNECT host is not socket addr: {:?}", req.uri());
Ok((
StatusCode::BAD_REQUEST,
"CONNECT must be to a socket address",
)
.into_response())
}
}
// let router_svc = Router::new().route("/", get(|| async { "Hello, World!" }));
async fn tunnel(mut upgraded: Upgraded, addr: String) -> std::io::Result<()> {
let mut server = TcpStream::connect(addr).await?;
// let service = tower::service_fn(move |req: Request<_>| {
// let router_svc = router_svc.clone();
// let req = req.map(Body::new);
// async move {
// if req.method() == Method::CONNECT {
// proxy(req).await
// } else {
// router_svc.oneshot(req).await.map_err(|err| match err {})
// }
// }
// });
let (from_client, from_server) =
tokio::io::copy_bidirectional(&mut upgraded, &mut server).await?;
// let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
// tracing::debug!("listening on {}", addr);
// hyper::Server::bind(&addr)
// .http1_preserve_header_case(true)
// .http1_title_case_headers(true)
// .serve(Shared::new(service))
// .await
// .unwrap();
// }
tracing::debug!("client wrote {from_client} bytes and received {from_server} bytes");
// async fn proxy(req: Request) -> Result<Response, hyper::Error> {
// tracing::trace!(?req);
Ok(())
}
// if let Some(host_addr) = req.uri().authority().map(|auth| auth.to_string()) {
// tokio::task::spawn(async move {
// match hyper::upgrade::on(req).await {
// Ok(upgraded) => {
// if let Err(e) = tunnel(upgraded, host_addr).await {
// tracing::warn!("server io error: {}", e);
// };
// }
// Err(e) => tracing::warn!("upgrade error: {}", e),
// }
// });
// Ok(Response::new(Body::empty()))
// } else {
// tracing::warn!("CONNECT host is not socket addr: {:?}", req.uri());
// Ok((
// StatusCode::BAD_REQUEST,
// "CONNECT must be to a socket address",
// )
// .into_response())
// }
// }
// async fn tunnel(mut upgraded: Upgraded, addr: String) -> std::io::Result<()> {
// let mut server = TcpStream::connect(addr).await?;
// let (from_client, from_server) =
// tokio::io::copy_bidirectional(&mut upgraded, &mut server).await?;
// tracing::debug!(
// "client wrote {} bytes and received {} bytes",
// from_client,
// from_server
// );
// Ok(())
// }
-15
View File
@@ -1,15 +0,0 @@
[package]
name = "example-hyper-1-0"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = { version = "=1.0.0-rc.4", features = ["full"] }
hyper-util = { git = "https://github.com/hyperium/hyper-util", rev = "f898015", features = ["full"] }
tokio = { version = "1.0", features = ["full"] }
tower-http = { version = "0.4", features = ["trace"] }
tower-hyper-http-body-compat = { version = "0.2", features = ["http1", "server"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
-53
View File
@@ -1,53 +0,0 @@
//! Run with
//!
//! ```not_rust
//! cargo run -p example-hyper-1-0
//! ```
use axum::{routing::get, Router};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tower_http::trace::TraceLayer;
use tower_hyper_http_body_compat::TowerService03HttpServiceAsHyper1HttpService;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// this is hyper 1.0
use hyper::server::conn::http1;
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_hyper_1_0=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
// we can still add regular tower middleware
.layer(TraceLayer::new_for_http());
// `Router` implements tower-service 0.3's `Service` trait. Convert that to something
// that implements hyper 1.0's `Service` trait.
let service = TowerService03HttpServiceAsHyper1HttpService::new(app);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let tcp_listener = TcpListener::bind(addr).await.unwrap();
tracing::debug!("listening on {addr}");
loop {
let (tcp_stream, _) = tcp_listener.accept().await.unwrap();
let tcp_stream = hyper_util::rt::TokioIo::new(tcp_stream);
let service = service.clone();
tokio::task::spawn(async move {
if let Err(http_err) = http1::Builder::new()
.keep_alive(true)
.serve_connection(tcp_stream, service)
.await
{
eprintln!("Error while serving HTTP connection: {http_err}");
}
});
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ publish = false
axum = { path = "../../axum" }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] }
tower-http = { version = "0.4.0", features = [
tower-http = { version = "0.5.0", features = [
"add-extension",
"auth",
"compression-full",
+1 -1
View File
@@ -6,5 +6,5 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
tokio = { version = "1", features = ["full"] }
+58 -46
View File
@@ -5,56 +5,68 @@
//! listen on both IPv4 and IPv6 when the IPv6 catch-all listener is used (`::`),
//! [like older versions of Windows.](https://docs.microsoft.com/en-us/windows/win32/winsock/dual-stack-sockets)
use axum::{routing::get, Router};
use hyper::server::{accept::Accept, conn::AddrIncoming};
use std::{
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
pin::Pin,
task::{Context, Poll},
};
//! Showcases how listening on multiple addrs is possible by
//! implementing Accept for a custom struct.
//!
//! This may be useful in cases where the platform does not
//! listen on both IPv4 and IPv6 when the IPv6 catch-all listener is used (`::`),
//! [like older versions of Windows.](https://docs.microsoft.com/en-us/windows/win32/winsock/dual-stack-sockets)
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
let localhost_v4 = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8080);
let incoming_v4 = AddrIncoming::bind(&localhost_v4).unwrap();
let localhost_v6 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 8080);
let incoming_v6 = AddrIncoming::bind(&localhost_v6).unwrap();
let combined = CombinedIncoming {
a: incoming_v4,
b: incoming_v6,
};
hyper::Server::builder(combined)
.serve(app.into_make_service())
.await
.unwrap();
// TODO
fn main() {
eprint!("this example has not yet been updated to hyper 1.0");
}
struct CombinedIncoming {
a: AddrIncoming,
b: AddrIncoming,
}
// use axum::{routing::get, Router};
// use hyper::server::{accept::Accept, conn::AddrIncoming};
// use std::{
// net::{Ipv4Addr, Ipv6Addr, SocketAddr},
// pin::Pin,
// task::{Context, Poll},
// };
impl Accept for CombinedIncoming {
type Conn = <AddrIncoming as Accept>::Conn;
type Error = <AddrIncoming as Accept>::Error;
// #[tokio::main]
// async fn main() {
// let app = Router::new().route("/", get(|| async { "Hello, World!" }));
fn poll_accept(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
if let Poll::Ready(Some(value)) = Pin::new(&mut self.a).poll_accept(cx) {
return Poll::Ready(Some(value));
}
// let localhost_v4 = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8080);
// let incoming_v4 = AddrIncoming::bind(&localhost_v4).unwrap();
if let Poll::Ready(Some(value)) = Pin::new(&mut self.b).poll_accept(cx) {
return Poll::Ready(Some(value));
}
// let localhost_v6 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 8080);
// let incoming_v6 = AddrIncoming::bind(&localhost_v6).unwrap();
Poll::Pending
}
}
// let combined = CombinedIncoming {
// a: incoming_v4,
// b: incoming_v6,
// };
// hyper::Server::builder(combined)
// .serve(app.into_make_service())
// .await
// .unwrap();
// }
// struct CombinedIncoming {
// a: AddrIncoming,
// b: AddrIncoming,
// }
// impl Accept for CombinedIncoming {
// type Conn = <AddrIncoming as Accept>::Conn;
// type Error = <AddrIncoming as Accept>::Error;
// fn poll_accept(
// mut self: Pin<&mut Self>,
// cx: &mut Context<'_>,
// ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
// if let Poll::Ready(Some(value)) = Pin::new(&mut self.a).poll_accept(cx) {
// return Poll::Ready(Some(value));
// }
// if let Poll::Ready(Some(value)) = Pin::new(&mut self.b).poll_accept(cx) {
// return Poll::Ready(Some(value));
// }
// Poll::Pending
// }
// }
+1 -1
View File
@@ -7,7 +7,7 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
openssl = "0.10"
tokio = { version = "1", features = ["full"] }
tokio-openssl = "0.6"
+89 -84
View File
@@ -1,86 +1,91 @@
use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod};
use tokio_openssl::SslStream;
use axum::{body::Body, http::Request, routing::get, Router};
use futures_util::future::poll_fn;
use hyper::server::{
accept::Accept,
conn::{AddrIncoming, Http},
};
use std::{path::PathBuf, pin::Pin, sync::Arc};
use tokio::net::TcpListener;
use tower::MakeService;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_low_level_openssl=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let mut tls_builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).unwrap();
tls_builder
.set_certificate_file(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("cert.pem"),
SslFiletype::PEM,
)
.unwrap();
tls_builder
.set_private_key_file(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("key.pem"),
SslFiletype::PEM,
)
.unwrap();
tls_builder.check_private_key().unwrap();
let acceptor = tls_builder.build();
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
let mut listener = AddrIncoming::from_listener(listener).unwrap();
let protocol = Arc::new(Http::new());
let mut app = Router::new().route("/", get(handler)).into_make_service();
tracing::info!("listening on https://localhost:3000");
loop {
let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx))
.await
.unwrap()
.unwrap();
let acceptor = acceptor.clone();
let protocol = protocol.clone();
let svc = MakeService::<_, Request<Body>>::make_service(&mut app, &stream);
tokio::spawn(async move {
let ssl = Ssl::new(acceptor.context()).unwrap();
let mut tls_stream = SslStream::new(ssl, stream).unwrap();
SslStream::accept(Pin::new(&mut tls_stream)).await.unwrap();
let _ = protocol
.serve_connection(tls_stream, svc.await.unwrap())
.await;
});
}
// TODO
fn main() {
eprint!("this example has not yet been updated to hyper 1.0");
}
async fn handler() -> &'static str {
"Hello, World!"
}
// use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod};
// use tokio_openssl::SslStream;
// use axum::{body::Body, http::Request, routing::get, Router};
// use futures_util::future::poll_fn;
// use hyper::server::{
// accept::Accept,
// conn::{AddrIncoming, Http},
// };
// use std::{path::PathBuf, pin::Pin, sync::Arc};
// use tokio::net::TcpListener;
// use tower::MakeService;
// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// #[tokio::main]
// async fn main() {
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_low_level_openssl=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
// let mut tls_builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).unwrap();
// tls_builder
// .set_certificate_file(
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("cert.pem"),
// SslFiletype::PEM,
// )
// .unwrap();
// tls_builder
// .set_private_key_file(
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("key.pem"),
// SslFiletype::PEM,
// )
// .unwrap();
// tls_builder.check_private_key().unwrap();
// let acceptor = tls_builder.build();
// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
// let mut listener = AddrIncoming::from_listener(listener).unwrap();
// let protocol = Arc::new(Http::new());
// let mut app = Router::new().route("/", get(handler)).into_make_service();
// tracing::info!("listening on https://localhost:3000");
// loop {
// let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx))
// .await
// .unwrap()
// .unwrap();
// let acceptor = acceptor.clone();
// let protocol = protocol.clone();
// let svc = MakeService::<_, Request<Body>>::make_service(&mut app, &stream);
// tokio::spawn(async move {
// let ssl = Ssl::new(acceptor.context()).unwrap();
// let mut tls_stream = SslStream::new(ssl, stream).unwrap();
// SslStream::accept(Pin::new(&mut tls_stream)).await.unwrap();
// let _ = protocol
// .serve_connection(tls_stream, svc.await.unwrap())
// .await;
// });
// }
// }
// async fn handler() -> &'static str {
// "Hello, World!"
// }
+1 -1
View File
@@ -7,7 +7,7 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
rustls-pemfile = "0.3"
tokio = { version = "1", features = ["full"] }
tokio-rustls = "0.23"
+95 -90
View File
@@ -4,100 +4,105 @@
//! cargo run -p example-low-level-rustls
//! ```
use axum::{extract::Request, routing::get, Router};
use futures_util::future::poll_fn;
use hyper::server::{
accept::Accept,
conn::{AddrIncoming, Http},
};
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::{
fs::File,
io::BufReader,
path::{Path, PathBuf},
pin::Pin,
sync::Arc,
};
use tokio::net::TcpListener;
use tokio_rustls::{
rustls::{Certificate, PrivateKey, ServerConfig},
TlsAcceptor,
};
use tower::make::MakeService;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_tls_rustls=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let rustls_config = rustls_server_config(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("key.pem"),
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("cert.pem"),
);
let acceptor = TlsAcceptor::from(rustls_config);
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
let mut listener = AddrIncoming::from_listener(listener).unwrap();
let protocol = Arc::new(Http::new());
let mut app = Router::<()>::new()
.route("/", get(handler))
.into_make_service();
loop {
let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx))
.await
.unwrap()
.unwrap();
let acceptor = acceptor.clone();
let protocol = protocol.clone();
let svc = MakeService::<_, Request<hyper::Body>>::make_service(&mut app, &stream);
tokio::spawn(async move {
if let Ok(stream) = acceptor.accept(stream).await {
let _ = protocol.serve_connection(stream, svc.await.unwrap()).await;
}
});
}
// TODO
fn main() {
eprint!("this example has not yet been updated to hyper 1.0");
}
async fn handler() -> &'static str {
"Hello, World!"
}
// use axum::{extract::Request, routing::get, Router};
// use futures_util::future::poll_fn;
// use hyper::server::{
// accept::Accept,
// conn::{AddrIncoming, Http},
// };
// use rustls_pemfile::{certs, pkcs8_private_keys};
// use std::{
// fs::File,
// io::BufReader,
// path::{Path, PathBuf},
// pin::Pin,
// sync::Arc,
// };
// use tokio::net::TcpListener;
// use tokio_rustls::{
// rustls::{Certificate, PrivateKey, ServerConfig},
// TlsAcceptor,
// };
// use tower::make::MakeService;
// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
fn rustls_server_config(key: impl AsRef<Path>, cert: impl AsRef<Path>) -> Arc<ServerConfig> {
let mut key_reader = BufReader::new(File::open(key).unwrap());
let mut cert_reader = BufReader::new(File::open(cert).unwrap());
// #[tokio::main]
// async fn main() {
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_tls_rustls=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
let key = PrivateKey(pkcs8_private_keys(&mut key_reader).unwrap().remove(0));
let certs = certs(&mut cert_reader)
.unwrap()
.into_iter()
.map(Certificate)
.collect();
// let rustls_config = rustls_server_config(
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("key.pem"),
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("cert.pem"),
// );
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(certs, key)
.expect("bad certificate/key");
// let acceptor = TlsAcceptor::from(rustls_config);
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
// let mut listener = AddrIncoming::from_listener(listener).unwrap();
Arc::new(config)
}
// let protocol = Arc::new(Http::new());
// let mut app = Router::<()>::new()
// .route("/", get(handler))
// .into_make_service();
// loop {
// let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx))
// .await
// .unwrap()
// .unwrap();
// let acceptor = acceptor.clone();
// let protocol = protocol.clone();
// let svc = MakeService::<_, Request<hyper::Body>>::make_service(&mut app, &stream);
// tokio::spawn(async move {
// if let Ok(stream) = acceptor.accept(stream).await {
// let _ = protocol.serve_connection(stream, svc.await.unwrap()).await;
// }
// });
// }
// }
// async fn handler() -> &'static str {
// "Hello, World!"
// }
// fn rustls_server_config(key: impl AsRef<Path>, cert: impl AsRef<Path>) -> Arc<ServerConfig> {
// let mut key_reader = BufReader::new(File::open(key).unwrap());
// let mut cert_reader = BufReader::new(File::open(cert).unwrap());
// let key = PrivateKey(pkcs8_private_keys(&mut key_reader).unwrap().remove(0));
// let certs = certs(&mut cert_reader)
// .unwrap()
// .into_iter()
// .map(Certificate)
// .collect();
// let mut config = ServerConfig::builder()
// .with_safe_defaults()
// .with_no_client_auth()
// .with_single_cert(certs, key)
// .expect("bad certificate/key");
// config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
// Arc::new(config)
// }
+1 -1
View File
@@ -7,6 +7,6 @@ publish = false
[dependencies]
axum = { path = "../../axum", features = ["multipart"] }
tokio = { version = "1.0", features = ["full"] }
tower-http = { version = "0.4.0", features = ["limit", "trace"] }
tower-http = { version = "0.5.0", features = ["limit", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+1 -1
View File
@@ -9,7 +9,7 @@ anyhow = "1"
async-session = "3.0.0"
axum = { path = "../../axum" }
axum-extra = { path = "../../axum-extra", features = ["typed-header"] }
http = "0.2"
http = "1.0.0"
oauth2 = "4.1"
# Use Rustls because it makes it easier to cross-compile on CI
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "json"] }
+1 -4
View File
@@ -69,10 +69,7 @@ async fn main() {
.unwrap()
);
axum::serve(listener, app)
.await
.context("failed to serve service")
.unwrap();
axum::serve(listener, app).await.unwrap();
}
#[derive(Clone)]
+2 -1
View File
@@ -6,7 +6,8 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = { version = "0.14", features = ["full"] }
http-body-util = "0.1.0"
hyper = { version = "1.0.0", features = ["full"] }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["util", "filter"] }
tracing = "0.1"
+3 -2
View File
@@ -13,6 +13,7 @@ use axum::{
routing::post,
Router,
};
use http_body_util::BodyExt;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
@@ -58,8 +59,8 @@ where
B: axum::body::HttpBody<Data = Bytes>,
B::Error: std::fmt::Display,
{
let bytes = match hyper::body::to_bytes(body).await {
Ok(bytes) => bytes,
let bytes = match body.collect().await {
Ok(collected) => collected.to_bytes(),
Err(err) => {
return Err((
StatusCode::BAD_REQUEST,
@@ -6,7 +6,8 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = "0.14"
http-body-util = "0.1.0"
hyper = "1.0.0"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["util"] }
@@ -58,6 +58,7 @@ where
mod tests {
use super::*;
use axum::{body::Body, http::Request};
use http_body_util::BodyExt;
use tower::ServiceExt;
#[tokio::test]
@@ -114,7 +115,7 @@ mod tests {
.await
.unwrap()
.into_body();
let bytes = hyper::body::to_bytes(body).await.unwrap();
let bytes = body.collect().await.unwrap().to_bytes();
String::from_utf8(bytes.to_vec()).unwrap()
}
}
+1 -1
View File
@@ -9,6 +9,6 @@ axum = { path = "../../axum" }
reqwest = { version = "0.11", features = ["stream"] }
tokio = { version = "1.0", features = ["full"] }
tokio-stream = "0.1"
tower-http = { version = "0.4", features = ["trace"] }
tower-http = { version = "0.5.0", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+71 -67
View File
@@ -4,76 +4,80 @@
//! cargo run -p example-reqwest-response
//! ```
use std::{convert::Infallible, time::Duration};
use axum::{
body::{Body, Bytes},
extract::State,
response::{IntoResponse, Response},
routing::get,
Router,
};
use reqwest::{Client, StatusCode};
use tokio_stream::StreamExt;
use tower_http::trace::TraceLayer;
use tracing::Span;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_reqwest_response=debug,tower_http=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let client = Client::new();
let app = Router::new()
.route("/", get(proxy_via_reqwest))
.route("/stream", get(stream_some_data))
// Add some logging so we can see the streams going through
.layer(TraceLayer::new_for_http().on_body_chunk(
|chunk: &Bytes, _latency: Duration, _span: &Span| {
tracing::debug!("streaming {} bytes", chunk.len());
},
))
.with_state(client);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
tracing::debug!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
fn main() {
// this examples requires reqwest to be updated to hyper and http 1.0
}
async fn proxy_via_reqwest(State(client): State<Client>) -> Response {
let reqwest_response = match client.get("http://127.0.0.1:3000/stream").send().await {
Ok(res) => res,
Err(err) => {
tracing::error!(%err, "request failed");
return StatusCode::BAD_GATEWAY.into_response();
}
};
// use std::{convert::Infallible, time::Duration};
let mut response_builder = Response::builder().status(reqwest_response.status());
// use axum::{
// body::{Body, Bytes},
// extract::State,
// response::{IntoResponse, Response},
// routing::get,
// Router,
// };
// use reqwest::{Client, StatusCode};
// use tokio_stream::StreamExt;
// use tower_http::trace::TraceLayer;
// use tracing::Span;
// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// This unwrap is fine because we haven't insert any headers yet so there can't be any invalid
// headers
*response_builder.headers_mut().unwrap() = reqwest_response.headers().clone();
// #[tokio::main]
// async fn main() {
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_reqwest_response=debug,tower_http=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
response_builder
.body(Body::from_stream(reqwest_response.bytes_stream()))
// Same goes for this unwrap
.unwrap()
}
// let client = Client::new();
async fn stream_some_data() -> Body {
let stream = tokio_stream::iter(0..5)
.throttle(Duration::from_secs(1))
.map(|n| n.to_string())
.map(Ok::<_, Infallible>);
Body::from_stream(stream)
}
// let app = Router::new()
// .route("/", get(proxy_via_reqwest))
// .route("/stream", get(stream_some_data))
// // Add some logging so we can see the streams going through
// .layer(TraceLayer::new_for_http().on_body_chunk(
// |chunk: &Bytes, _latency: Duration, _span: &Span| {
// tracing::debug!("streaming {} bytes", chunk.len());
// },
// ))
// .with_state(client);
// let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
// .await
// .unwrap();
// tracing::debug!("listening on {}", listener.local_addr().unwrap());
// axum::serve(listener, app).await.unwrap();
// }
// async fn proxy_via_reqwest(State(client): State<Client>) -> Response {
// let reqwest_response = match client.get("http://127.0.0.1:3000/stream").send().await {
// Ok(res) => res,
// Err(err) => {
// tracing::error!(%err, "request failed");
// return StatusCode::BAD_GATEWAY.into_response();
// }
// };
// let mut response_builder = Response::builder().status(reqwest_response.status());
// // This unwrap is fine because we haven't insert any headers yet so there can't be any invalid
// // headers
// *response_builder.headers_mut().unwrap() = reqwest_response.headers().clone();
// response_builder
// .body(Body::from_stream(reqwest_response.bytes_stream()))
// // Same goes for this unwrap
// .unwrap()
// }
// async fn stream_some_data() -> Body {
// let stream = tokio_stream::iter(0..5)
// .throttle(Duration::from_secs(1))
// .map(|n| n.to_string())
// .map(Ok::<_, Infallible>);
// Body::from_stream(stream)
// }
+1 -1
View File
@@ -7,7 +7,7 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
futures = "0.3"
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
prost = "0.11"
tokio = { version = "1", features = ["full"] }
tonic = { version = "0.9" }
+70 -65
View File
@@ -4,79 +4,84 @@
//! cargo run -p example-rest-grpc-multiplex
//! ```
use self::multiplex_service::MultiplexService;
use axum::{routing::get, Router};
use proto::{
greeter_server::{Greeter, GreeterServer},
HelloReply, HelloRequest,
};
use std::net::SocketAddr;
use tonic::{Response as TonicResponse, Status};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
mod multiplex_service;
mod proto {
tonic::include_proto!("helloworld");
pub(crate) const FILE_DESCRIPTOR_SET: &[u8] =
tonic::include_file_descriptor_set!("helloworld_descriptor");
// TODO
fn main() {
eprint!("this example has not yet been updated to hyper 1.0");
}
#[derive(Default)]
struct GrpcServiceImpl {}
// use self::multiplex_service::MultiplexService;
// use axum::{routing::get, Router};
// use proto::{
// greeter_server::{Greeter, GreeterServer},
// HelloReply, HelloRequest,
// };
// use std::net::SocketAddr;
// use tonic::{Response as TonicResponse, Status};
// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tonic::async_trait]
impl Greeter for GrpcServiceImpl {
async fn say_hello(
&self,
request: tonic::Request<HelloRequest>,
) -> Result<TonicResponse<HelloReply>, Status> {
tracing::info!("Got a request from {:?}", request.remote_addr());
// mod multiplex_service;
let reply = HelloReply {
message: format!("Hello {}!", request.into_inner().name),
};
// mod proto {
// tonic::include_proto!("helloworld");
Ok(TonicResponse::new(reply))
}
}
// pub(crate) const FILE_DESCRIPTOR_SET: &[u8] =
// tonic::include_file_descriptor_set!("helloworld_descriptor");
// }
async fn web_root() -> &'static str {
"Hello, World!"
}
// #[derive(Default)]
// struct GrpcServiceImpl {}
#[tokio::main]
async fn main() {
// initialize tracing
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_rest_grpc_multiplex=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
// #[tonic::async_trait]
// impl Greeter for GrpcServiceImpl {
// async fn say_hello(
// &self,
// request: tonic::Request<HelloRequest>,
// ) -> Result<TonicResponse<HelloReply>, Status> {
// tracing::info!("Got a request from {:?}", request.remote_addr());
// build the rest service
let rest = Router::new().route("/", get(web_root));
// let reply = HelloReply {
// message: format!("Hello {}!", request.into_inner().name),
// };
// build the grpc service
let reflection_service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(proto::FILE_DESCRIPTOR_SET)
.build()
.unwrap();
let grpc = tonic::transport::Server::builder()
.add_service(reflection_service)
.add_service(GreeterServer::new(GrpcServiceImpl::default()))
.into_service();
// Ok(TonicResponse::new(reply))
// }
// }
// combine them into one service
let service = MultiplexService::new(rest, grpc);
// async fn web_root() -> &'static str {
// "Hello, World!"
// }
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {addr}");
hyper::Server::bind(&addr)
.serve(tower::make::Shared::new(service))
.await
.unwrap();
}
// #[tokio::main]
// async fn main() {
// // initialize tracing
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_rest_grpc_multiplex=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
// // build the rest service
// let rest = Router::new().route("/", get(web_root));
// // build the grpc service
// let reflection_service = tonic_reflection::server::Builder::configure()
// .register_encoded_file_descriptor_set(proto::FILE_DESCRIPTOR_SET)
// .build()
// .unwrap();
// let grpc = tonic::transport::Server::builder()
// .add_service(reflection_service)
// .add_service(GreeterServer::new(GrpcServiceImpl::default()))
// .into_service();
// // combine them into one service
// let service = MultiplexService::new(rest, grpc);
// let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
// tracing::debug!("listening on {}", addr);
// hyper::Server::bind(&addr)
// .serve(tower::make::Shared::new(service))
// .await
// .unwrap();
// }
@@ -1,4 +1,5 @@
use axum::{
body::Body,
extract::Request,
http::header::CONTENT_TYPE,
response::{IntoResponse, Response},
+1 -1
View File
@@ -5,5 +5,5 @@ edition = "2021"
[dependencies]
axum = { path = "../../axum" }
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
tokio = { version = "1", features = ["full"] }
+53 -48
View File
@@ -7,58 +7,63 @@
//! cargo run -p example-reverse-proxy
//! ```
use axum::{
body::Body,
extract::{Request, State},
http::uri::Uri,
response::{IntoResponse, Response},
routing::get,
Router,
};
use hyper::{client::HttpConnector, StatusCode};
type Client = hyper::client::Client<HttpConnector, Body>;
#[tokio::main]
async fn main() {
tokio::spawn(server());
let client: Client = hyper::Client::builder().build(HttpConnector::new());
let app = Router::new().route("/", get(handler)).with_state(client);
let listener = tokio::net::TcpListener::bind("127.0.0.1:4000")
.await
.unwrap();
println!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
// TODO
fn main() {
eprint!("this example has not yet been updated to hyper 1.0");
}
async fn handler(State(client): State<Client>, mut req: Request) -> Result<Response, StatusCode> {
let path = req.uri().path();
let path_query = req
.uri()
.path_and_query()
.map(|v| v.as_str())
.unwrap_or(path);
// use axum::{
// body::Body,
// extract::{Request, State},
// http::uri::Uri,
// response::{IntoResponse, Response},
// routing::get,
// Router,
// };
// use hyper::{client::HttpConnector, StatusCode};
let uri = format!("http://127.0.0.1:3000{path_query}");
// type Client = hyper::client::Client<HttpConnector, Body>;
*req.uri_mut() = Uri::try_from(uri).unwrap();
// #[tokio::main]
// async fn main() {
// tokio::spawn(server());
Ok(client
.request(req)
.await
.map_err(|_| StatusCode::BAD_REQUEST)?
.into_response())
}
// let client: Client = hyper::Client::builder().build(HttpConnector::new());
async fn server() {
let app = Router::new().route("/", get(|| async { "Hello, world!" }));
// let app = Router::new().route("/", get(handler)).with_state(client);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
println!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}
// let listener = tokio::net::TcpListener::bind("127.0.0.1:4000")
// .await
// .unwrap();
// println!("listening on {}", listener.local_addr().unwrap());
// axum::serve(listener, app).await.unwrap();
// }
// async fn handler(State(client): State<Client>, mut req: Request) -> Result<Response, StatusCode> {
// let path = req.uri().path();
// let path_query = req
// .uri()
// .path_and_query()
// .map(|v| v.as_str())
// .unwrap_or(path);
// let uri = format!("http://127.0.0.1:3000{}", path_query);
// *req.uri_mut() = Uri::try_from(uri).unwrap();
// Ok(client
// .request(req)
// .await
// .map_err(|_| StatusCode::BAD_REQUEST)?
// .into_response())
// }
// async fn server() {
// let app = Router::new().route("/", get(|| async { "Hello, world!" }));
// let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
// .await
// .unwrap();
// println!("listening on {}", listener.local_addr().unwrap());
// axum::serve(listener, app).await.unwrap();
// }
+1 -1
View File
@@ -12,5 +12,5 @@ axum = { path = "../../axum", default-features = false }
# works in wasm as well
axum-extra = { path = "../../axum-extra", default-features = false }
futures-executor = "0.3.21"
http = "0.2.7"
http = "1.0.0"
tower-service = "0.3.1"
+1 -1
View File
@@ -11,6 +11,6 @@ futures = "0.3"
headers = "0.3"
tokio = { version = "1.0", features = ["full"] }
tokio-stream = "0.1"
tower-http = { version = "0.4.0", features = ["fs", "trace"] }
tower-http = { version = "0.5.0", features = ["fs", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+1 -1
View File
@@ -9,6 +9,6 @@ axum = { path = "../../axum" }
axum-extra = { path = "../../axum-extra" }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["util"] }
tower-http = { version = "0.4.0", features = ["fs", "trace"] }
tower-http = { version = "0.5.0", features = ["fs", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+1 -1
View File
@@ -53,7 +53,7 @@ async fn save_request_body(
Path(file_name): Path<String>,
request: Request,
) -> Result<(), (StatusCode, String)> {
stream_to_file(&file_name, request.into_body()).await
stream_to_file(&file_name, request.into_body().into_data_stream()).await
}
// Handler that returns HTML for a multipart form.
+1 -1
View File
@@ -7,6 +7,6 @@ publish = false
[dependencies]
axum = { path = "../../axum", features = ["ws"] }
futures = "0.3"
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
tokio = { version = "1.0", features = ["full"] }
tokio-tungstenite = "0.20"
+3 -2
View File
@@ -6,11 +6,12 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
hyper = { version = "0.14", features = ["full"] }
http-body-util = "0.1.0"
hyper = { version = "1.0.0", features = ["full"] }
mime = "0.3"
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
tower-http = { version = "0.4.0", features = ["trace"] }
tower-http = { version = "0.5.0", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+181 -167
View File
@@ -4,194 +4,208 @@
//! cargo test -p example-testing
//! ```
use std::net::SocketAddr;
use axum::{
extract::ConnectInfo,
routing::{get, post},
Json, Router,
};
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_testing=debug,tower_http=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
tracing::debug!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app()).await.unwrap();
fn main() {
// This example has not yet been updated to Hyper 1.0
}
/// Having a function that produces our app makes it easy to call it from tests
/// without having to create an HTTP server.
fn app() -> Router {
Router::new()
.route("/", get(|| async { "Hello, World!" }))
.route(
"/json",
post(|payload: Json<serde_json::Value>| async move {
Json(serde_json::json!({ "data": payload.0 }))
}),
)
.route(
"/requires-connect-into",
get(|ConnectInfo(addr): ConnectInfo<SocketAddr>| async move { format!("Hi {addr}") }),
)
// We can still add middleware
.layer(TraceLayer::new_for_http())
}
//use std::net::SocketAddr;
#[cfg(test)]
mod tests {
use super::*;
use axum::{
body::Body,
extract::connect_info::MockConnectInfo,
http::{self, Request, StatusCode},
};
use serde_json::{json, Value};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tower::Service; // for `call`
use tower::ServiceExt; // for `oneshot` and `ready`
//use axum::{
// extract::ConnectInfo,
// routing::{get, post},
// Json, Router,
//};
//use tower_http::trace::TraceLayer;
//use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::test]
async fn hello_world() {
let app = app();
//#[tokio::main]
//async fn main() {
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_testing=debug,tower_http=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
// `Router` implements `tower::Service<Request<Body>>` so we can
// call it like any tower service, no need to run an HTTP server.
let response = app
.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
.await
.unwrap();
// let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
// .await
// .unwrap();
// tracing::debug!("listening on {}", listener.local_addr().unwrap());
// axum::serve(listener, app()).await.unwrap();
//}
assert_eq!(response.status(), StatusCode::OK);
///// Having a function that produces our app makes it easy to call it from tests
///// without having to create an HTTP server.
//fn app() -> Router {
// Router::new()
// .route("/", get(|| async { "Hello, World!" }))
// .route(
// "/json",
// post(|payload: Json<serde_json::Value>| async move {
// Json(serde_json::json!({ "data": payload.0 }))
// }),
// )
// .route(
// "/requires-connect-into",
// get(|ConnectInfo(addr): ConnectInfo<SocketAddr>| async move { format!("Hi {addr}") }),
// )
// // We can still add middleware
// .layer(TraceLayer::new_for_http())
//}
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
assert_eq!(&body[..], b"Hello, World!");
}
//#[cfg(test)]
//mod tests {
// use super::*;
// use axum::{
// body::Body,
// extract::connect_info::MockConnectInfo,
// http::{self, Request, StatusCode},
// };
// use http_body_util::BodyExt;
// use serde_json::{json, Value};
// use std::net::SocketAddr;
// use tokio::net::{TcpListener, TcpStream};
// use tower::Service; // for `call`
// use tower::ServiceExt; // for `oneshot` and `ready` // for `collect`
#[tokio::test]
async fn json() {
let app = app();
// #[tokio::test]
// async fn hello_world() {
// let app = app();
let response = app
.oneshot(
Request::builder()
.method(http::Method::POST)
.uri("/json")
.header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
.body(Body::from(
serde_json::to_vec(&json!([1, 2, 3, 4])).unwrap(),
))
.unwrap(),
)
.await
.unwrap();
// // `Router` implements `tower::Service<Request<Body>>` so we can
// // call it like any tower service, no need to run an HTTP server.
// let response = app
// .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
// .await
// .unwrap();
assert_eq!(response.status(), StatusCode::OK);
// assert_eq!(response.status(), StatusCode::OK);
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body: Value = serde_json::from_slice(&body).unwrap();
assert_eq!(body, json!({ "data": [1, 2, 3, 4] }));
}
// let body = response.into_body().collect().await.unwrap().to_bytes();
// assert_eq!(&body[..], b"Hello, World!");
// }
#[tokio::test]
async fn not_found() {
let app = app();
// #[tokio::test]
// async fn json() {
// let app = app();
let response = app
.oneshot(
Request::builder()
.uri("/does-not-exist")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
// let response = app
// .oneshot(
// Request::builder()
// .method(http::Method::POST)
// .uri("/json")
// .header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
// .body(Body::from(
// serde_json::to_vec(&json!([1, 2, 3, 4])).unwrap(),
// ))
// .unwrap(),
// )
// .await
// .unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
assert!(body.is_empty());
}
// assert_eq!(response.status(), StatusCode::OK);
// You can also spawn a server and talk to it like any other HTTP server:
#[tokio::test]
async fn the_real_deal() {
let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
let addr = listener.local_addr().unwrap();
// let body = response.into_body().collect().await.unwrap().to_bytes();
// let body: Value = serde_json::from_slice(&body).unwrap();
// assert_eq!(body, json!({ "data": [1, 2, 3, 4] }));
// }
tokio::spawn(async move {
axum::serve(listener, app()).await.unwrap();
});
// #[tokio::test]
// async fn not_found() {
// let app = app();
let client = hyper::Client::new();
// let response = app
// .oneshot(
// Request::builder()
// .uri("/does-not-exist")
// .body(Body::empty())
// .unwrap(),
// )
// .await
// .unwrap();
let response = client
.request(
Request::builder()
.uri(format!("http://{addr}"))
.body(hyper::Body::empty())
.unwrap(),
)
.await
.unwrap();
// assert_eq!(response.status(), StatusCode::NOT_FOUND);
// let body = response.into_body().collect().await.unwrap().to_bytes();
// assert!(body.is_empty());
// }
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
assert_eq!(&body[..], b"Hello, World!");
}
// // You can also spawn a server and talk to it like any other HTTP server:
// #[tokio::test]
// async fn the_real_deal() {
// // TODO(david): convert this to hyper-util when thats published
// You can use `ready()` and `call()` to avoid using `clone()`
// in multiple request
#[tokio::test]
async fn multiple_request() {
let mut app = app().into_service();
// use hyper::client::conn;
let request = Request::builder().uri("/").body(Body::empty()).unwrap();
let response = ServiceExt::<Request<Body>>::ready(&mut app)
.await
.unwrap()
.call(request)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
// let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
// let addr = listener.local_addr().unwrap();
let request = Request::builder().uri("/").body(Body::empty()).unwrap();
let response = ServiceExt::<Request<Body>>::ready(&mut app)
.await
.unwrap()
.call(request)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
// tokio::spawn(async move {
// axum::serve(listener, app()).await.unwrap();
// });
// Here we're calling `/requires-connect-into` which requires `ConnectInfo`
//
// That is normally set with `Router::into_make_service_with_connect_info` but we can't easily
// use that during tests. The solution is instead to set the `MockConnectInfo` layer during
// tests.
#[tokio::test]
async fn with_into_make_service_with_connect_info() {
let mut app = app()
.layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 3000))))
.into_service();
// let target_stream = TcpStream::connect(addr).await.unwrap();
let request = Request::builder()
.uri("/requires-connect-into")
.body(Body::empty())
.unwrap();
let response = app.ready().await.unwrap().call(request).await.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
}
// let (mut request_sender, connection) = conn::http1::handshake(target_stream).await.unwrap();
// tokio::spawn(async move { connection.await.unwrap() });
// let response = request_sender
// .send_request(
// Request::builder()
// .uri(format!("http://{addr}"))
// .header("Host", "localhost")
// .body(Body::empty())
// .unwrap(),
// )
// .await
// .unwrap();
// let body = response.into_body().collect().await.unwrap().to_bytes();
// assert_eq!(&body[..], b"Hello, World!");
// }
// // You can use `ready()` and `call()` to avoid using `clone()`
// // in multiple request
// #[tokio::test]
// async fn multiple_request() {
// let mut app = app().into_service();
// let request = Request::builder().uri("/").body(Body::empty()).unwrap();
// let response = ServiceExt::<Request<Body>>::ready(&mut app)
// .await
// .unwrap()
// .call(request)
// .await
// .unwrap();
// assert_eq!(response.status(), StatusCode::OK);
// let request = Request::builder().uri("/").body(Body::empty()).unwrap();
// let response = ServiceExt::<Request<Body>>::ready(&mut app)
// .await
// .unwrap()
// .call(request)
// .await
// .unwrap();
// assert_eq!(response.status(), StatusCode::OK);
// }
// // Here we're calling `/requires-connect-into` which requires `ConnectInfo`
// //
// // That is normally set with `Router::into_make_service_with_connect_info` but we can't easily
// // use that during tests. The solution is instead to set the `MockConnectInfo` layer during
// // tests.
// #[tokio::test]
// async fn with_into_make_service_with_connect_info() {
// let mut app = app()
// .layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 3000))))
// .into_service();
// let request = Request::builder()
// .uri("/requires-connect-into")
// .body(Body::empty())
// .unwrap();
// let response = app.ready().await.unwrap().call(request).await.unwrap();
// assert_eq!(response.status(), StatusCode::OK);
// }
//}
+116 -112
View File
@@ -4,136 +4,140 @@
//! cargo run -p example-tls-graceful-shutdown
//! ```
use axum::{
extract::Host,
handler::HandlerWithoutStateExt,
http::{StatusCode, Uri},
response::Redirect,
routing::get,
BoxError, Router,
};
use axum_server::tls_rustls::RustlsConfig;
use std::{future::Future, net::SocketAddr, path::PathBuf, time::Duration};
use tokio::signal;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[derive(Clone, Copy)]
struct Ports {
http: u16,
https: u16,
fn main() {
// This example has not yet been updated to Hyper 1.0
}
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_tls_graceful_shutdown=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
//use axum::{
// extract::Host,
// handler::HandlerWithoutStateExt,
// http::{StatusCode, Uri},
// response::Redirect,
// routing::get,
// BoxError, Router,
//};
//use axum_server::tls_rustls::RustlsConfig;
//use std::{future::Future, net::SocketAddr, path::PathBuf, time::Duration};
//use tokio::signal;
//use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
let ports = Ports {
http: 7878,
https: 3000,
};
//#[derive(Clone, Copy)]
//struct Ports {
// http: u16,
// https: u16,
//}
//Create a handle for our TLS server so the shutdown signal can all shutdown
let handle = axum_server::Handle::new();
//save the future for easy shutting down of redirect server
let shutdown_future = shutdown_signal(handle.clone());
//#[tokio::main]
//async fn main() {
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_tls_graceful_shutdown=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
// optional: spawn a second server to redirect http requests to this server
tokio::spawn(redirect_http_to_https(ports, shutdown_future));
// let ports = Ports {
// http: 7878,
// https: 3000,
// };
// configure certificate and private key used by https
let config = RustlsConfig::from_pem_file(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("cert.pem"),
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("key.pem"),
)
.await
.unwrap();
// //Create a handle for our TLS server so the shutdown signal can all shutdown
// let handle = axum_server::Handle::new();
// //save the future for easy shutting down of redirect server
// let shutdown_future = shutdown_signal(handle.clone());
let app = Router::new().route("/", get(handler));
// // optional: spawn a second server to redirect http requests to this server
// tokio::spawn(redirect_http_to_https(ports, shutdown_future));
// run https server
let addr = SocketAddr::from(([127, 0, 0, 1], ports.https));
tracing::debug!("listening on {addr}");
axum_server::bind_rustls(addr, config)
.handle(handle)
.serve(app.into_make_service())
.await
.unwrap();
}
// // configure certificate and private key used by https
// let config = RustlsConfig::from_pem_file(
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("cert.pem"),
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("key.pem"),
// )
// .await
// .unwrap();
async fn shutdown_signal(handle: axum_server::Handle) {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
// let app = Router::new().route("/", get(handler));
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
// // run https server
// let addr = SocketAddr::from(([127, 0, 0, 1], ports.https));
// tracing::debug!("listening on {addr}");
// axum_server::bind_rustls(addr, config)
// .handle(handle)
// .serve(app.into_make_service())
// .await
// .unwrap();
//}
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
//async fn shutdown_signal(handle: axum_server::Handle) {
// let ctrl_c = async {
// signal::ctrl_c()
// .await
// .expect("failed to install Ctrl+C handler");
// };
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
// #[cfg(unix)]
// let terminate = async {
// signal::unix::signal(signal::unix::SignalKind::terminate())
// .expect("failed to install signal handler")
// .recv()
// .await;
// };
tracing::info!("Received termination signal shutting down");
handle.graceful_shutdown(Some(Duration::from_secs(10))); // 10 secs is how long docker will wait
// to force shutdown
}
// #[cfg(not(unix))]
// let terminate = std::future::pending::<()>();
async fn handler() -> &'static str {
"Hello, World!"
}
// tokio::select! {
// _ = ctrl_c => {},
// _ = terminate => {},
// }
async fn redirect_http_to_https(ports: Ports, signal: impl Future<Output = ()>) {
fn make_https(host: String, uri: Uri, ports: Ports) -> Result<Uri, BoxError> {
let mut parts = uri.into_parts();
// tracing::info!("Received termination signal shutting down");
// handle.graceful_shutdown(Some(Duration::from_secs(10))); // 10 secs is how long docker will wait
// // to force shutdown
//}
parts.scheme = Some(axum::http::uri::Scheme::HTTPS);
//async fn handler() -> &'static str {
// "Hello, World!"
//}
if parts.path_and_query.is_none() {
parts.path_and_query = Some("/".parse().unwrap());
}
//async fn redirect_http_to_https(ports: Ports, signal: impl Future<Output = ()>) {
// fn make_https(host: String, uri: Uri, ports: Ports) -> Result<Uri, BoxError> {
// let mut parts = uri.into_parts();
let https_host = host.replace(&ports.http.to_string(), &ports.https.to_string());
parts.authority = Some(https_host.parse()?);
// parts.scheme = Some(axum::http::uri::Scheme::HTTPS);
Ok(Uri::from_parts(parts)?)
}
// if parts.path_and_query.is_none() {
// parts.path_and_query = Some("/".parse().unwrap());
// }
let redirect = move |Host(host): Host, uri: Uri| async move {
match make_https(host, uri, ports) {
Ok(uri) => Ok(Redirect::permanent(&uri.to_string())),
Err(error) => {
tracing::warn!(%error, "failed to convert URI to HTTPS");
Err(StatusCode::BAD_REQUEST)
}
}
};
// let https_host = host.replace(&ports.http.to_string(), &ports.https.to_string());
// parts.authority = Some(https_host.parse()?);
let addr = SocketAddr::from(([127, 0, 0, 1], ports.http));
//let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
tracing::debug!("listening on {addr}");
hyper::Server::bind(&addr)
.serve(redirect.into_make_service())
.with_graceful_shutdown(signal)
.await
.unwrap();
}
// Ok(Uri::from_parts(parts)?)
// }
// let redirect = move |Host(host): Host, uri: Uri| async move {
// match make_https(host, uri, ports) {
// Ok(uri) => Ok(Redirect::permanent(&uri.to_string())),
// Err(error) => {
// tracing::warn!(%error, "failed to convert URI to HTTPS");
// Err(StatusCode::BAD_REQUEST)
// }
// }
// };
// let addr = SocketAddr::from(([127, 0, 0, 1], ports.http));
// //let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
// tracing::debug!("listening on {addr}");
// hyper::Server::bind(&addr)
// .serve(redirect.into_make_service())
// .with_graceful_shutdown(signal)
// .await
// .unwrap();
//}
+39 -32
View File
@@ -4,6 +4,8 @@
//! cargo run -p example-tls-rustls
//! ```
#![allow(unused_imports)]
use axum::{
extract::Host,
handler::HandlerWithoutStateExt,
@@ -16,6 +18,7 @@ use axum_server::tls_rustls::RustlsConfig;
use std::{net::SocketAddr, path::PathBuf};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[allow(dead_code)]
#[derive(Clone, Copy)]
struct Ports {
http: u16,
@@ -24,48 +27,52 @@ struct Ports {
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_tls_rustls=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
// Updating this example to hyper 1.0 requires axum_server to update first
let ports = Ports {
http: 7878,
https: 3000,
};
// optional: spawn a second server to redirect http requests to this server
tokio::spawn(redirect_http_to_https(ports));
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "example_tls_rustls=debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
// configure certificate and private key used by https
let config = RustlsConfig::from_pem_file(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("cert.pem"),
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("self_signed_certs")
.join("key.pem"),
)
.await
.unwrap();
// let ports = Ports {
// http: 7878,
// https: 3000,
// };
// // optional: spawn a second server to redirect http requests to this server
// tokio::spawn(redirect_http_to_https(ports));
let app = Router::new().route("/", get(handler));
// // configure certificate and private key used by https
// let config = RustlsConfig::from_pem_file(
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("cert.pem"),
// PathBuf::from(env!("CARGO_MANIFEST_DIR"))
// .join("self_signed_certs")
// .join("key.pem"),
// )
// .await
// .unwrap();
// run https server
let addr = SocketAddr::from(([127, 0, 0, 1], ports.https));
tracing::debug!("listening on {addr}");
axum_server::bind_rustls(addr, config)
.serve(app.into_make_service())
.await
.unwrap();
// let app = Router::new().route("/", get(handler));
// // run https server
// let addr = SocketAddr::from(([127, 0, 0, 1], ports.https));
// tracing::debug!("listening on {}", addr);
// axum_server::bind_rustls(addr, config)
// .await
// .unwrap();
}
#[allow(dead_code)]
async fn handler() -> &'static str {
"Hello, World!"
}
#[allow(dead_code)]
async fn redirect_http_to_https(ports: Ports) {
fn make_https(host: String, uri: Uri, ports: Ports) -> Result<Uri, BoxError> {
let mut parts = uri.into_parts();
+1 -1
View File
@@ -9,7 +9,7 @@ axum = { path = "../../axum" }
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["util", "timeout"] }
tower-http = { version = "0.4.0", features = ["add-extension", "trace"] }
tower-http = { version = "0.5.0", features = ["add-extension", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1.0", features = ["serde", "v4"] }
+1 -1
View File
@@ -7,6 +7,6 @@ publish = false
[dependencies]
axum = { path = "../../axum", features = ["tracing"] }
tokio = { version = "1.0", features = ["full"] }
tower-http = { version = "0.4.0", features = ["trace"] }
tower-http = { version = "0.5.0", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+1 -1
View File
@@ -7,7 +7,7 @@ publish = false
[dependencies]
axum = { path = "../../axum" }
futures = "0.3"
hyper = { version = "0.14", features = ["full"] }
hyper = { version = "1.0.0", features = ["full"] }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["util"] }
tracing = "0.1"
+153 -148
View File
@@ -4,178 +4,183 @@
//! cargo run -p example-unix-domain-socket
//! ```
#[cfg(unix)]
#[tokio::main]
async fn main() {
unix::server().await;
}
#[cfg(not(unix))]
// TODO
fn main() {
println!("This example requires unix")
eprint!("this example has not yet been updated to hyper 1.0");
}
#[cfg(unix)]
mod unix {
use axum::{
body::Body,
extract::connect_info::{self, ConnectInfo},
http::{Method, Request, StatusCode, Uri},
routing::get,
Router,
};
use futures::ready;
use hyper::{
client::connect::{Connected, Connection},
server::accept::Accept,
};
use std::{
io,
path::PathBuf,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use tokio::{
io::{AsyncRead, AsyncWrite},
net::{unix::UCred, UnixListener, UnixStream},
};
use tower::BoxError;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// #[cfg(unix)]
// #[tokio::main]
// async fn main() {
// unix::server().await;
// }
pub async fn server() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
// #[cfg(not(unix))]
// fn main() {
// println!("This example requires unix")
// }
let path = PathBuf::from("/tmp/axum/helloworld");
// #[cfg(unix)]
// mod unix {
// use axum::{
// body::Body,
// extract::connect_info::{self, ConnectInfo},
// http::{Method, Request, StatusCode, Uri},
// routing::get,
// Router,
// };
// use futures::ready;
// use hyper::{
// client::connect::{Connected, Connection},
// server::accept::Accept,
// };
// use std::{
// io,
// path::PathBuf,
// pin::Pin,
// sync::Arc,
// task::{Context, Poll},
// };
// use tokio::{
// io::{AsyncRead, AsyncWrite},
// net::{unix::UCred, UnixListener, UnixStream},
// };
// use tower::BoxError;
// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
let _ = tokio::fs::remove_file(&path).await;
tokio::fs::create_dir_all(path.parent().unwrap())
.await
.unwrap();
// pub async fn server() {
// tracing_subscriber::registry()
// .with(
// tracing_subscriber::EnvFilter::try_from_default_env()
// .unwrap_or_else(|_| "debug".into()),
// )
// .with(tracing_subscriber::fmt::layer())
// .init();
let uds = UnixListener::bind(path.clone()).unwrap();
tokio::spawn(async {
let app = Router::new().route("/", get(handler));
// let path = PathBuf::from("/tmp/axum/helloworld");
hyper::Server::builder(ServerAccept { uds })
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo>())
.await
.unwrap();
});
// let _ = tokio::fs::remove_file(&path).await;
// tokio::fs::create_dir_all(path.parent().unwrap())
// .await
// .unwrap();
let connector = tower::service_fn(move |_: Uri| {
let path = path.clone();
Box::pin(async move {
let stream = UnixStream::connect(path).await?;
Ok::<_, io::Error>(ClientConnection { stream })
})
});
let client = hyper::Client::builder().build(connector);
// let uds = UnixListener::bind(path.clone()).unwrap();
// tokio::spawn(async {
// let app = Router::new().route("/", get(handler));
let request = Request::builder()
.method(Method::GET)
.uri("http://uri-doesnt-matter.com")
.body(Body::empty())
.unwrap();
// hyper::Server::builder(ServerAccept { uds })
// .serve(app.into_make_service_with_connect_info::<UdsConnectInfo>())
// .await
// .unwrap();
// });
let response = client.request(request).await.unwrap();
// let connector = tower::service_fn(move |_: Uri| {
// let path = path.clone();
// Box::pin(async move {
// let stream = UnixStream::connect(path).await?;
// Ok::<_, io::Error>(ClientConnection { stream })
// })
// });
// let client = hyper::Client::builder().build(connector);
assert_eq!(response.status(), StatusCode::OK);
// let request = Request::builder()
// .method(Method::GET)
// .uri("http://uri-doesnt-matter.com")
// .body(Body::empty())
// .unwrap();
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body = String::from_utf8(body.to_vec()).unwrap();
assert_eq!(body, "Hello, World!");
}
// let response = client.request(request).await.unwrap();
async fn handler(ConnectInfo(info): ConnectInfo<UdsConnectInfo>) -> &'static str {
println!("new connection from `{info:?}`");
// assert_eq!(response.status(), StatusCode::OK);
"Hello, World!"
}
// let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
// let body = String::from_utf8(body.to_vec()).unwrap();
// assert_eq!(body, "Hello, World!");
// }
struct ServerAccept {
uds: UnixListener,
}
// async fn handler(ConnectInfo(info): ConnectInfo<UdsConnectInfo>) -> &'static str {
// println!("new connection from `{:?}`", info);
impl Accept for ServerAccept {
type Conn = UnixStream;
type Error = BoxError;
// "Hello, World!"
// }
fn poll_accept(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
let (stream, _addr) = ready!(self.uds.poll_accept(cx))?;
Poll::Ready(Some(Ok(stream)))
}
}
// struct ServerAccept {
// uds: UnixListener,
// }
struct ClientConnection {
stream: UnixStream,
}
// impl Accept for ServerAccept {
// type Conn = UnixStream;
// type Error = BoxError;
impl AsyncWrite for ClientConnection {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
Pin::new(&mut self.stream).poll_write(cx, buf)
}
// fn poll_accept(
// self: Pin<&mut Self>,
// cx: &mut Context<'_>,
// ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
// let (stream, _addr) = ready!(self.uds.poll_accept(cx))?;
// Poll::Ready(Some(Ok(stream)))
// }
// }
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), io::Error>> {
Pin::new(&mut self.stream).poll_flush(cx)
}
// struct ClientConnection {
// stream: UnixStream,
// }
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), io::Error>> {
Pin::new(&mut self.stream).poll_shutdown(cx)
}
}
// impl AsyncWrite for ClientConnection {
// fn poll_write(
// mut self: Pin<&mut Self>,
// cx: &mut Context<'_>,
// buf: &[u8],
// ) -> Poll<Result<usize, io::Error>> {
// Pin::new(&mut self.stream).poll_write(cx, buf)
// }
impl AsyncRead for ClientConnection {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.stream).poll_read(cx, buf)
}
}
// fn poll_flush(
// mut self: Pin<&mut Self>,
// cx: &mut Context<'_>,
// ) -> Poll<Result<(), io::Error>> {
// Pin::new(&mut self.stream).poll_flush(cx)
// }
impl Connection for ClientConnection {
fn connected(&self) -> Connected {
Connected::new()
}
}
// fn poll_shutdown(
// mut self: Pin<&mut Self>,
// cx: &mut Context<'_>,
// ) -> Poll<Result<(), io::Error>> {
// Pin::new(&mut self.stream).poll_shutdown(cx)
// }
// }
#[derive(Clone, Debug)]
#[allow(dead_code)]
struct UdsConnectInfo {
peer_addr: Arc<tokio::net::unix::SocketAddr>,
peer_cred: UCred,
}
// impl AsyncRead for ClientConnection {
// fn poll_read(
// mut self: Pin<&mut Self>,
// cx: &mut Context<'_>,
// buf: &mut tokio::io::ReadBuf<'_>,
// ) -> Poll<io::Result<()>> {
// Pin::new(&mut self.stream).poll_read(cx, buf)
// }
// }
impl connect_info::Connected<&UnixStream> for UdsConnectInfo {
fn connect_info(target: &UnixStream) -> Self {
let peer_addr = target.peer_addr().unwrap();
let peer_cred = target.peer_cred().unwrap();
// impl Connection for ClientConnection {
// fn connected(&self) -> Connected {
// Connected::new()
// }
// }
Self {
peer_addr: Arc::new(peer_addr),
peer_cred,
}
}
}
}
// #[derive(Clone, Debug)]
// #[allow(dead_code)]
// struct UdsConnectInfo {
// peer_addr: Arc<tokio::net::unix::SocketAddr>,
// peer_cred: UCred,
// }
// impl connect_info::Connected<&UnixStream> for UdsConnectInfo {
// fn connect_info(target: &UnixStream) -> Self {
// let peer_addr = target.peer_addr().unwrap();
// let peer_cred = target.peer_cred().unwrap();
// Self {
// peer_addr: Arc::new(peer_addr),
// peer_cred,
// }
// }
// }
// }
+1 -1
View File
@@ -7,7 +7,7 @@ version = "0.1.0"
[dependencies]
async-trait = "0.1.67"
axum = { path = "../../axum" }
http-body = "0.4.3"
http-body = "1.0.0"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0.29"
tokio = { version = "1.0", features = ["full"] }
+1 -1
View File
@@ -13,7 +13,7 @@ headers = "0.3"
tokio = { version = "1.0", features = ["full"] }
tokio-tungstenite = "0.20"
tower = { version = "0.4", features = ["util"] }
tower-http = { version = "0.4.0", features = ["fs", "trace"] }
tower-http = { version = "0.5.0", features = ["fs", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }