You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:15:40