warp icon indicating copy to clipboard operation
warp copied to clipboard

make FilteredService::call_with_addr() public

Open wfraser opened this issue 2 years ago • 1 comments

This is an alternative to #713 which doesn't make breaking changes to any public interfaces.

This allows a caller to invoke the filter and manually specify a remote address, since this doesn't automatically get passed along when using one's own listener.

Invoking this properly is a bit involved, because it requires doing a bunch of Hyper service boilerplate, but if you're making your own listener, you probably don't mind doing this too.

My concrete use case is I want to listen on a socket opened by systemd, which it provides via a file descriptor. I can't call warp::Server::bind() with a file descriptor, and I can't pass the SocketAddr for that socket because Systemd is already listening on it on my behalf. I can make a TcpListener from a file descriptor just fine, but then Warp won't let me see the remote peers' addresses without this.

example of use:

    let filter = warp::filters::addr::remote()
        .map(|addr| format!("hello {:?}!", addr));

    let svc = warp::service(filter);
    let make_svc = make_service_fn(move |conn: &AddrStream| {
        let svc = svc.clone();
        let remote = conn.remote_addr();
        ready::<Result<_, Infallible>>(Ok(service_fn(move |req| {
            svc.call_with_addr(req, Some(remote))
        })))
    });

    hyper::Server::builder(MyCustomListener::new().await)
        .serve(make_svc)
        .await?;

Full compilable program:

Cargo.toml:

[package]
name = "warp-addr-inject"
version = "0.1.0"
edition = "2021"

[dependencies]
hyper = "0.14.18"
tokio = { version = "1.18.0", features = ["macros", "rt-multi-thread"] }
warp = "0.3.2"

src/main.rs:

use hyper::server::accept::Accept;
use hyper::server::conn::{AddrIncoming, AddrStream};
use hyper::service::{make_service_fn, service_fn};
use std::convert::Infallible;
use std::future::ready;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::net::TcpListener;
use warp::Filter;

#[tokio::main]
async fn main() -> Result<(), hyper::Error> {
    let filter = warp::filters::addr::remote()
        .map(|addr| format!("hello {:?}!", addr));

    let svc = warp::service(filter);
    let make_svc = make_service_fn(move |conn: &AddrStream| {
        let svc = svc.clone();
        let remote = conn.remote_addr();
        ready::<Result<_, Infallible>>(Ok(service_fn(move |req| {
            svc.call_with_addr(req, Some(remote))
        })))
    });

    hyper::Server::builder(MyCustomListener::new().await)
        .serve(make_svc)
        .await?;

    Ok(())
}

struct MyCustomListener {
    inner: hyper::server::conn::AddrIncoming,
}

impl MyCustomListener {
    pub async fn new() -> Self {
        // for example only, actual use case is more involved than this
        let listener = TcpListener::bind("127.0.0.1:42069").await.unwrap();
        let inner = AddrIncoming::from_listener(listener).unwrap();
        Self { inner }
    }
}

impl Accept for MyCustomListener {
    type Conn = AddrStream;
    type Error = io::Error;

    fn poll_accept(self: Pin<&mut Self>, cx: &mut Context<'_>)
        -> Poll<Option<Result<Self::Conn, Self::Error>>>
    {
        // this is where my custom cool listener logic lives, but for demo purposes do this
        // instead:
        Pin::new(&mut self.get_mut().inner)
            .poll_accept(cx)
    }
}

wfraser avatar Apr 29 '22 08:04 wfraser

Is there any update on this? Also, Why was the visibility of this function restricted to be crate-only in the first place?

NishantJoshi00 avatar Jan 12 '24 21:01 NishantJoshi00