如何用Hyper将客户端IP传递给Tower Service层栈?
Hyper: 传递客户端地址到Layer栈并支持主动断开连接
实现方案
要解决你的两个核心问题——把客户端远程地址传递给整个Layer栈,以及支持主动断开连接不返回响应,可以通过自定义Layer封装请求和使用hyper的错误类型触发断开来实现,具体步骤如下:
1. 封装带地址的请求类型
先定义一个结构体,把原始请求和客户端地址绑定在一起,让后续的Layer和服务都能直接访问地址:
use hyper::{Request, Response, Body, Error}; use hyper::server::conn::AddrStream; use hyper::service::{Service, make_service_fn, service_fn}; use tower::{Layer, ServiceBuilder}; use std::net::SocketAddr; #[derive(Debug)] struct RequestWithAddr<B> { req: Request<B>, addr: SocketAddr, }
2. 自定义Layer提取地址
实现一个AddrLayer,它会从请求扩展中取出AddrStream,提取远程地址后将请求包装成RequestWithAddr,这样整个Layer栈都能拿到地址:
struct AddrLayer; impl<S> Layer<S> for AddrLayer { type Service = AddrService<S>; fn layer(&self, inner: S) -> Self::Service { AddrService { inner } } } struct AddrService<S> { inner: S, } impl<S, B> Service<Request<B>> for AddrService<S> where S: Service<RequestWithAddr<B>, Response = Response<Body>, Error = Error>, { type Response = S::Response; type Error = S::Error; type Future = S::Future; fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> { self.inner.poll_ready(cx) } fn call(&mut self, req: Request<B>) -> Self::Future { // 从请求扩展中获取客户端地址 let addr = req.extensions() .get::<AddrStream>() .map(|s| s.remote_addr()) .unwrap_or_else(|| "0.0.0.0:0".parse().unwrap()); self.inner.call(RequestWithAddr { req, addr }) } }
3. 构建服务栈并处理断开逻辑
在make_service_fn中把AddrStream存入请求扩展,然后用ServiceBuilder把AddrLayer加入栈。处理请求时,只要返回hyper::Error(比如Aborted类型),hyper就会直接断开连接,不会返回响应:
async fn handle_request(req: RequestWithAddr<Body>) -> Result<Response<Body>, Error> { println!("Request from: {}", req.addr); // 示例:拒绝本地地址,直接断开连接 if req.addr.ip().is_loopback() { return Err(Error::new(hyper::error::Kind::Aborted)); } // 正常处理请求 Ok(Response::new(Body::from("Hello from server!"))) } #[tokio::main] async fn main() -> Result<(), Error> { let listen_addr = ([127, 0, 0, 1], 3000).into(); let make_svc = make_service_fn(|conn: &AddrStream| { let conn_clone = conn.clone(); async move { Ok::<_, Error>(service_fn(move |mut req: Request<Body>| { // 将连接信息存入请求扩展,供Layer提取 req.extensions_mut().insert(conn_clone.clone()); // 构建包含AddrLayer的服务栈 let svc = ServiceBuilder::new() .layer(AddrLayer) .service_fn(handle_request); svc.call(req) })) } }); let server = hyper::Server::bind(&listen_addr).serve(make_svc); if let Err(e) = server.await { eprintln!("Server error: {}", e); } Ok(()) }
关键细节
- 地址传递逻辑:在
make_service_fn中能直接拿到每个连接的AddrStream,把它存入请求的extensions后,自定义Layer就能从中提取远程地址,确保整个栈都能访问到。 - 断开连接实现:返回
hyper::Error::Aborted会让hyper主动终止连接,不会向客户端发送任何响应,满足你的需求。 - 类型匹配:确保所有Layer和Service的请求、响应、错误类型统一(这里用
RequestWithAddr<Body>作为请求类型,Error作为错误类型),避免编译时的类型不匹配问题。
内容的提问来源于stack exchange,提问作者d2718
相关产品推荐
相关产品推荐

