58 lines
1.8 KiB
Rust
58 lines
1.8 KiB
Rust
#![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::<SocketAddr, _>())
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
#[allow(clippy::unused_async)]
|
|
async fn root(header: HeaderMap, ConnectInfo(socket_info): ConnectInfo<SocketAddr>) -> 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::<Ipv6Addr>().is_ok() {
|
|
return res;
|
|
}
|
|
} else {
|
|
let res: String = chars.take_while(|c| *c != ',').collect();
|
|
if res.parse::<Ipv4Addr>().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()
|
|
}
|