#![warn(clippy::pedantic, clippy::nursery)] use axum::extract::ConnectInfo; use axum::handler::get; use axum::http::{HeaderMap, HeaderValue}; use axum::{Router, Server}; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; #[tokio::main] async fn main() { let app = Router::new().route("/", get(root)); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); Server::bind(&addr) .serve(app.into_make_service_with_connect_info::()) .await .unwrap(); } #[allow(clippy::unused_async)] async fn root(header: HeaderMap, ConnectInfo(socket_info): ConnectInfo) -> String { if let Some(Ok(value)) = header.get("Forwarded").map(HeaderValue::to_str) { let match_str = "or="; // the `for` key is case-insensitive let maybe_index = value.find(match_str); if let Some(index) = maybe_index { let ip_start = value.split_at(index + match_str.len()).1; let mut chars = ip_start.chars().peekable(); if let Some('"') = chars.peek() { // We're dealing with an ipv6 address // skip first character, since we know it's an `"` chars.next(); let res: String = chars.take_while(|c| *c != '"').collect(); if res.parse::().is_ok() { return res; } } else { let res: String = chars.take_while(|c| *c != 'c').collect(); if res.parse::().is_ok() { return res; } } } } if let Some(Ok(value)) = header.get("X-Forwarded-For").map(HeaderValue::to_str) { return match value.split_once(',').map(|v| v.0) { Some(v) => v, None => value, } .to_string(); } socket_info.ip().to_string() }