Skip to content

Conversation

@wfraser
Copy link

@wfraser wfraser commented Apr 29, 2022

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)
    }
}

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.
@NishantJoshi00
Copy link

NishantJoshi00 commented Jan 12, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants