From 0ff5d01c9450e60d61c43794e3fd893151a53aa4 Mon Sep 17 00:00:00 2001 From: drobson03 <17976794+drobson03@users.noreply.github.com> Date: Sun, 3 Oct 2021 21:52:23 +1000 Subject: [PATCH] Add distance calculation --- .env.example | 4 +++- Cargo.lock | 7 +++++++ Cargo.toml | 1 + README.md | 6 +++++- src/get_ip.rs | 20 ++++++++++++++++--- src/util.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index dbc93d6..4ae302d 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,5 @@ IPINFO_TOKEN= +SERVER_LATITUDE=1 +SERVER_LONGITUDE=1 ROCKET_PORT=8000 -ROCKET_ADDRESS=0.0.0.0 \ No newline at end of file +ROCKET_ADDRESS=0.0.0.0 diff --git a/Cargo.lock b/Cargo.lock index 5a252d4..9494e7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,6 +711,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "haversine" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9482608b217d89a3caeb5fe2336a0491d1dc0be5d89c67125ea514b1f6a660" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1943,6 +1949,7 @@ name = "speedtest-rs" version = "0.1.0" dependencies = [ "dotenv", + "haversine", "ipinfo", "rand 0.8.4", "regex", diff --git a/Cargo.toml b/Cargo.toml index 0052d93..b793662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ ipinfo = "0.2.0" serde_with = { version = "1.10.0", features = ["json"] } rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "a062933" } dotenv = "0.15.0" +haversine = "0.2.1" diff --git a/README.md b/README.md index 090a531..6eb7938 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Supported by all Librespeed frontends, though some features are missing (see bel - [x] IP Address, ISP - [x] Multiple Points of Test (optional) - [x] Compatible with PHP frontend predefined endpoints (with `.php` suffixes) -- [ ] Distance from server (optional) +- [x] Distance from server (optional) - [ ] Telemetry (optional) - [ ] Results sharing (optional) - [ ] [Proxy Protocol](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt)? @@ -55,6 +55,10 @@ cargo build --release ```sh # your ipinfo.io API token IPINFO_TOKEN= +# your server's latitude +SERVER_LATITUDE=1 +# your server's longitude +SERVER_LONGITUDE=1 # the port to bind to ROCKET_PORT=8000 # the bind address (0.0.0.0 is all interfaces) diff --git a/src/get_ip.rs b/src/get_ip.rs index ee35f4b..56e9276 100644 --- a/src/get_ip.rs +++ b/src/get_ip.rs @@ -2,11 +2,11 @@ use ipinfo::{IpDetails, IpInfo, IpInfoConfig}; use regex::Regex; use rocket::serde::{json::Json, Serialize}; use rocket_client_addr::ClientRealAddr; - use std::env::var; +use std::fmt; use crate::serialized_ip_info::IpDetailsDef; -use crate::util::get_ip_type; +use crate::util::{get_client_server_distance_string, get_ip_type}; #[derive(FromFormField, PartialEq)] pub enum Distance { @@ -15,7 +15,16 @@ pub enum Distance { Nm, } -#[allow(dead_code)] +impl fmt::Display for Distance { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Distance::Km => write!(f, "km"), + Distance::Mi => write!(f, "mi"), + Distance::Nm => write!(f, "NM"), + } + } +} + #[derive(FromForm)] pub struct GetIPOptions { #[field(default = true)] @@ -69,6 +78,11 @@ pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json (String, bool) { let private_regex: Regex = Regex::new(r"^172\.(1[6-9]|2\d|3[01])\.").unwrap(); @@ -25,3 +30,51 @@ pub fn get_ip_type(ip: &str) -> (String, bool) { (processed_string, is_special_ip) } + +pub fn parse_location_string(location: String) -> Result { + let location_parts: Vec<&str> = location.split(",").collect::>(); + + if location_parts.len() != 2 { + return Err(format!("Unknown location format: {}", &location)); + } + + let latitude = location_parts[0].parse::().unwrap_or_default(); + let longitude = location_parts[1].parse::().unwrap_or_default(); + + Ok(Location { + latitude, + longitude, + }) +} + +pub fn get_client_server_distance_string(client_location: String, units: Distance) -> String { + let client_location = parse_location_string(client_location).unwrap_or(Location { + latitude: 0.0, + longitude: 0.0, + }); + + let server_location = Location { + latitude: var("SERVER_LATITUDE") + .unwrap_or_default() + .parse::() + .unwrap_or_default(), + longitude: var("SERVER_LONGITUDE") + .unwrap_or_default() + .parse::() + .unwrap_or_default(), + }; + + let haversine_units = match units { + Distance::Km => Units::Kilometers, + Distance::Mi => Units::Miles, + Distance::Nm => Units::Kilometers, + }; + + let distance = distance(client_location, server_location, haversine_units); + let distance = match units { + Distance::Km => distance, + Distance::Mi => distance, + Distance::Nm => distance / 1.852, + }; + format!("{:.2} {}", &distance, units) +}