warp
warp copied to clipboard
make FilteredService::call_with_addr() public
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)
}
}
Is there any update on this? Also, Why was the visibility of this function restricted to be crate-only in the first place?