diff --git a/Cargo.toml b/Cargo.toml
index a1376f2a..d417747c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,6 +24,15 @@ reqwest = { version = "0.11", features = ["json", "stream"] }
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.6.1", features = ["macros", "rt", "rt-multi-thread"] }
tower = { version = "0.4", features = ["util", "make", "timeout"] }
-tower-http = { version = "0.1", features = ["trace", "compression", "add-extension"] }
tracing = "0.1"
tracing-subscriber = "0.2"
+
+[dev-dependencies.tower-http]
+version = "0.1"
+features = [
+ "trace",
+ "compression",
+ "compression-full",
+ "add-extension",
+ "fs",
+]
diff --git a/examples/hello_world.rs b/examples/hello_world.rs
index b6b57b19..848ae949 100644
--- a/examples/hello_world.rs
+++ b/examples/hello_world.rs
@@ -1,9 +1,8 @@
-use http::Request;
+use http::{Request, StatusCode};
use hyper::Server;
use std::net::SocketAddr;
-use tower::{make::Shared, ServiceBuilder};
-use tower_http::trace::TraceLayer;
-use tower_web::{body::Body, response::Html};
+use tower::make::Shared;
+use tower_web::{body::Body, extract, response::Html};
#[tokio::main]
async fn main() {
@@ -13,14 +12,11 @@ async fn main() {
let app = tower_web::app()
.at("/")
.get(handler)
+ .at("/greet/:name")
+ .get(greet)
// convert it into a `Service`
.into_service();
- // add some middleware
- let app = ServiceBuilder::new()
- .layer(TraceLayer::new_for_http())
- .service(app);
-
// run it with hyper
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
@@ -31,3 +27,12 @@ async fn main() {
async fn handler(_req: Request
) -> Html<&'static str> {
Html("Hello, World!
")
}
+
+async fn greet(_req: Request, params: extract::UrlParamsMap) -> Result {
+ if let Some(name) = params.get("name") {
+ Ok(format!("Hello {}!", name))
+ } else {
+ // if the route matches "name" will be present
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
+ }
+}
diff --git a/examples/key_value_store.rs b/examples/key_value_store.rs
index 9a2edd36..98c9c51d 100644
--- a/examples/key_value_store.rs
+++ b/examples/key_value_store.rs
@@ -11,7 +11,7 @@ use tower::{make::Shared, ServiceBuilder};
use tower_http::{
add_extension::AddExtensionLayer, compression::CompressionLayer, trace::TraceLayer,
};
-use tower_web::{body::Body, extract};
+use tower_web::{body::Body, extract, handler::Handler};
#[tokio::main]
async fn main() {
@@ -20,7 +20,7 @@ async fn main() {
// build our application with some routes
let app = tower_web::app()
.at("/:key")
- .get(get)
+ .get(get.layer(CompressionLayer::new()))
.post(set)
// convert it into a `Service`
.into_service();
@@ -29,7 +29,6 @@ async fn main() {
let app = ServiceBuilder::new()
.timeout(Duration::from_secs(10))
.layer(TraceLayer::new_for_http())
- .layer(CompressionLayer::new())
.layer(AddExtensionLayer::new(SharedState::default()))
.service(app);
@@ -51,10 +50,6 @@ async fn get(
_req: Request,
params: extract::UrlParams<(String,)>,
state: extract::Extension,
- // Anything that implements `IntoResponse` can be used a response
- //
- // Handlers cannot return errors. Everything will be converted
- // into a response. `BoxError` becomes `500 Internal server error`
) -> Result {
let state = state.into_inner();
let db = &state.lock().unwrap().db;
@@ -73,8 +68,6 @@ async fn set(
params: extract::UrlParams<(String,)>,
value: extract::BytesMaxLength<{ 1024 * 5_000 }>, // ~5mb
state: extract::Extension,
- // `()` also implements `IntoResponse` so we can use that to return
- // an empty response
) {
let state = state.into_inner();
let db = &mut state.lock().unwrap().db;
diff --git a/src/body.rs b/src/body.rs
index f1a30085..b03f7c1c 100644
--- a/src/body.rs
+++ b/src/body.rs
@@ -1,78 +1,64 @@
-use bytes::Buf;
-use futures_util::ready;
-use http_body::{Body as _, Empty};
+use bytes::Bytes;
+use http_body::{Empty, Full};
use std::{
fmt,
pin::Pin,
task::{Context, Poll},
};
+use tower::BoxError;
pub use hyper::body::Body;
use crate::BoxStdError;
/// A boxed [`Body`] trait object.
-pub struct BoxBody {
- inner: Pin + Send + Sync + 'static>>,
+pub struct BoxBody {
+ // when we've gotten rid of `BoxStdError` we should be able to change the error type to
+ // `BoxError`
+ inner: Pin + Send + Sync + 'static>>,
}
-impl BoxBody {
+impl BoxBody {
/// Create a new `BoxBody`.
pub fn new(body: B) -> Self
where
- B: http_body::Body + Send + Sync + 'static,
- D: Buf,
+ B: http_body::Body + Send + Sync + 'static,
+ B::Error: Into + Send + Sync + 'static,
{
Self {
- inner: Box::pin(body),
+ inner: Box::pin(body.map_err(|error| BoxStdError(error.into()))),
}
}
}
-// TODO: upstream this to http-body?
-impl Default for BoxBody
-where
- D: bytes::Buf + 'static,
-{
+impl Default for BoxBody {
fn default() -> Self {
- BoxBody::new(Empty::::new().map_err(|err| match err {}))
+ BoxBody::new(Empty::::new())
}
}
-impl fmt::Debug for BoxBody {
+impl fmt::Debug for BoxBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BoxBody").finish()
}
}
-// when we've gotten rid of `BoxStdError` then we can remove this
-impl http_body::Body for BoxBody
-where
- D: Buf,
- E: Into,
-{
- type Data = D;
+impl http_body::Body for BoxBody {
+ type Data = Bytes;
type Error = BoxStdError;
fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll