Skip to content

Commit 9fc81a1

Browse files
committed
add middleware for server
1 parent b8ffeb7 commit 9fc81a1

File tree

2 files changed

+170
-1
lines changed

2 files changed

+170
-1
lines changed

_stable/server/middleware.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
title: Getting Started with a Server Middleware
3+
layout: guide
4+
---
5+
6+
As [Upgrade](https://hyper.rs/guides/1/upgrading/) mentioned, hyper v1 is not depended on tower for the Service trait. When we want to add tower-like middleware, there are 2 kind of approach to make it.
7+
8+
Let's create a Logger middleware in [hello-world server](https://hyper.rs/guides/1/server/hello-world/) for instance:
9+
10+
Add tower dependency first
11+
12+
```rust
13+
[dependencies]
14+
hyper = { version = "1", features = ["full"] }
15+
tokio = { version = "1", features = ["full"] }
16+
http-body-util = "0.1"
17+
hyper-util = { version = "0.1", features = ["full"] }
18+
tower = { version = "0.4.13" } // here
19+
```
20+
21+
## Option 1: Use hyper Service trait
22+
23+
Implement hyper Logger middleware
24+
25+
```rust
26+
use hyper::{body::Incoming, service::Service}; // using hyper Service trait
27+
use tower::{Layer, Service};
28+
29+
#[derive(Debug, Clone)]
30+
pub struct Logger<S> {
31+
inner: S,
32+
}
33+
34+
type Req = hyper::Request<Incoming>;
35+
36+
impl<S> Service<Req> for Logger<S>
37+
where
38+
S: Service<Req>,
39+
{
40+
type Response = S::Response;
41+
type Error = S::Error;
42+
type Future = S::Future;
43+
fn call(&self, req: Req) -> Self::Future {
44+
println!("processing request: {} {}", req.method(), req.uri().path());
45+
self.inner.call(req)
46+
}
47+
}
48+
```
49+
50+
Then this can be used in server:
51+
52+
```rust
53+
use std::{convert::Infallible, net::SocketAddr};
54+
55+
use hyper::{
56+
body::{Bytes, Incoming},
57+
server::conn::http1,
58+
Request, Response,
59+
};
60+
61+
use http_body_util::Full;
62+
use hyper_util::rt::TokioIo;
63+
use tokio::net::TcpListener;
64+
use tower::ServiceBuilder;
65+
async fn hello(_: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
66+
Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
67+
}
68+
#[tokio::main]
69+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
70+
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
71+
let listener = TcpListener::bind(addr).await?;
72+
loop {
73+
let (stream, _) = listener.accept().await?;
74+
let io = TokioIo::new(stream);
75+
tokio::spawn(async move {
76+
// N.B. should use hyper service_fn here, since it's required to be implemented hyper Service trait!
77+
let svc = hyper::service::service_fn(hello);
78+
let svc = ServiceBuilder::new().layer_fn(Logger::new).service(svc);
79+
if let Err(err) = http1::Builder::new().serve_connection(io, svc).await {
80+
eprintln!("server error: {}", err);
81+
}
82+
});
83+
}
84+
}
85+
```
86+
87+
## Option 2: use hyper TowerToHyperService trait
88+
89+
[hyper_util::service::TowerToHyperService](https://docs.rs/hyper-util/latest/hyper_util/service/struct.TowerToHyperService.html) trait is an adapter to convert tower Service to hyper Service.
90+
91+
Now implement a tower Logger middleware
92+
93+
```rust
94+
use hyper::body::Incoming;
95+
use tower::Service;
96+
97+
#[derive(Debug, Clone)]
98+
pub struct Logger<S> {
99+
inner: S,
100+
}
101+
impl<S> Logger<S> {
102+
pub fn new(inner: S) -> Self {
103+
Logger { inner }
104+
}
105+
}
106+
type Req = hyper::Request<Incoming>;
107+
impl<S> Service<Req> for Logger<S>
108+
where
109+
S: Service<Req> + Clone,
110+
{
111+
type Response = S::Response;
112+
113+
type Error = S::Error;
114+
115+
type Future = S::Future;
116+
117+
fn poll_ready(
118+
&mut self,
119+
cx: &mut std::task::Context<'_>,
120+
) -> std::task::Poll<Result<(), Self::Error>> {
121+
self.inner.poll_ready(cx)
122+
}
123+
124+
fn call(&mut self, req: Req) -> Self::Future {
125+
println!("processing request: {} {}", req.method(), req.uri().path());
126+
self.inner.call(req)
127+
}
128+
}
129+
```
130+
131+
Then use it in the server:
132+
133+
```rust
134+
use std::{convert::Infallible, net::SocketAddr};
135+
136+
use hyper::{
137+
body::{Bytes, Incoming},
138+
server::conn::http1,
139+
Request, Response,
140+
};
141+
142+
use http_body_util::Full;
143+
use hyper_util::{rt::TokioIo, service::TowerToHyperService};
144+
use tokio::net::TcpListener;
145+
use tower::ServiceBuilder;
146+
use crate::logger::Logger as Logger;
147+
async fn hello(_: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
148+
Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
149+
}
150+
#[tokio::main]
151+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
152+
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
153+
let listener = TcpListener::bind(addr).await?;
154+
loop {
155+
let (stream, _) = listener.accept().await?;
156+
let io = TokioIo::new(stream);
157+
tokio::spawn(async move {
158+
// N.B. should use tower service_fn here, since it's reuqired to be implemented tower Service trait before convert to hyper Service!
159+
let svc = tower::service_fn(hello);
160+
let svc = ServiceBuilder::new().layer_fn(Logger::new).service(svc);
161+
// Convert it to hyper service
162+
let svc = TowerToHyperService::new(svc);
163+
if let Err(err) = http1::Builder::new().serve_connection(io, svc).await {
164+
eprintln!("server error: {}", err);
165+
}
166+
});
167+
}
168+
}
169+
```

_stable/upgrading.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ The listening server acceptor can be replaced with a simple loop.
6868
Previously, hyper depended on `tower` for the `Service` trait. Because
6969
`tower` is not yet 1.0, hyper could not publicly depend on it. So, it's
7070
`Service` trait and the helper function `service_fn` are defined in
71-
`hyper::service`.
71+
`hyper::service`. Check [middleware](https://hyper.rs/guides/1/server/middleware/) for more details.
7272

7373
[changelog]: https://github.com/hyperium/hyper/blob/master/CHANGELOG.md#v100-2023-11-15
7474
[`hyper-util`]: https://crates.io/crates/hyper-util

0 commit comments

Comments
 (0)