imbolc / axum-client-ip

A client IP address extractor for Axum
MIT License
41 stars 13 forks source link

How do I add ConnectInfo #2

Closed quinn closed 1 year ago

quinn commented 1 year ago

in the docs I see:

// Don't forget to add `ConnetInfo` if you aren't behind a proxy

Since I am running locally I'm not behind a proxy, and I get a message "Can't determine the client IP, check forwarding configuration". How do I add ConnectInfo?

imbolc commented 1 year ago

The line following the comment does exactly this :)

quinn commented 1 year ago

@imbolc thanks. Not sure why it didn't work for me :(

imbolc commented 1 year ago

Hmm... I've just added an example which works for me locally, would you try to run it? What OS do you use?

quinn commented 1 year ago

Example works for me as well (i'm on ubuntu) I'm going to try to repro again

quinn commented 1 year ago

example you added works, however when I try to integrate it with hyper_reverse_proxy I get this error:

use axum::{
    body::Body, extract::ConnectInfo, http::StatusCode, response::Response, routing::any, Router,
};
use axum_client_ip::ClientIp;
use hyper::Request;
use std::net::IpAddr;
use std::{convert::Infallible, net::SocketAddr};

fn debug_request(req: Request<Body>) -> Result<Response<Body>, Infallible> {
    let body_str = format!("hi {:?}", req);
    Ok(Response::new(Body::from(body_str)))
}

async fn handle(client_ip: IpAddr, req: Request<Body>) -> Result<Response<Body>, Infallible> {
    if req.uri().path().starts_with("/target/first") {
        // will forward requests to port 13901
        match hyper_reverse_proxy::call(client_ip, "http://127.0.0.1:13901", req).await {
            Ok(response) => Ok(response),
            Err(_error) => Ok(Response::builder()
                .status(StatusCode::INTERNAL_SERVER_ERROR)
                .body(Body::empty())
                .unwrap()),
        }
    } else if req.uri().path().starts_with("/target/second") {
        // will forward requests to port 13902
        match hyper_reverse_proxy::call(client_ip, "http://127.0.0.1:13902", req).await {
            Ok(response) => Ok(response),
            Err(_error) => Ok(Response::builder()
                .status(StatusCode::INTERNAL_SERVER_ERROR)
                .body(Body::empty())
                .unwrap()),
        }
    } else {
        debug_request(req)
    }
}

#[tokio::main]
async fn main() {
    let bind_string = "127.0.0.1:8000";
    let bind_addr: SocketAddr = bind_string.parse().expect("Could not parse ip:port.");

    async fn hello(
        req: Request<Body>,
        ClientIp(ip): ClientIp,
    ) -> Result<Response<Body>, Infallible> {
        handle(ip, req).await
    }

    let app = Router::new().route("/", any(hello));

    let server = axum::Server::bind(&bind_addr)
        .serve(app.into_make_service_with_connect_info::<SocketAddr>());

    println!("Running server on {:?}", bind_addr);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

This is just the example code from their README added onto your example.

imbolc commented 1 year ago

Try swapping these lines :)

        req: Request<Body>,
        ClientIp(ip): ClientIp,

Here's what's probably happening: https://docs.rs/axum/0.5.17/axum/extract/index.html#be-careful-when-extracting-request

In Axum v0.6 (which is released yesterday) it should be a compilation error:

The request body is an asynchronous stream that can only be consumed once. Therefore you can only have one extractor that consumes the request body. axum enforces by that requiring such extractors to be the last argument your handler takes.

https://docs.rs/axum/latest/axum/extract/index.html#the-order-of-extractors