diff --git a/Cargo.lock b/Cargo.lock index 46ba63c..f64963e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1272,9 +1272,9 @@ checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "maxminddb" -version = "0.26.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a197e44322788858682406c74b0b59bf8d9b4954fe1f224d9a25147f1880bba" +checksum = "99681a80368084e68fff1a4ec657b09ae6a04f1107762ec6346a82b8cc19d8eb" dependencies = [ "ipnetwork", "log", diff --git a/Cargo.toml b/Cargo.toml index 54cea60..76f756b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ cadence = "^1.6.0" envy = "^0.4.2" futures = "^0.3" lazy_static = "^1.5.0" -maxminddb = "^0.26.0" +maxminddb = "0.27.1" regex = "^1.12.2" sentry = "^0.45.0" sentry-actix = "^0.45.0" diff --git a/src/endpoints/classify.rs b/src/endpoints/classify.rs index 52942cf..f1b0351 100644 --- a/src/endpoints/classify.rs +++ b/src/endpoints/classify.rs @@ -17,13 +17,11 @@ fn country_iso_code( country_info: &Option, serializer: S, ) -> Result { - let iso_code: Option<&str> = country_info - .clone() - .and_then(|country_info| country_info.country) - .and_then(|country| country.iso_code); - - match iso_code { - Some(code) => serializer.serialize_str(code), + match country_info { + Some(x) => match x.country.iso_code { + Some(code) => serializer.serialize_str(code), + _ => serializer.serialize_none(), + }, None => serializer.serialize_none(), } } @@ -78,17 +76,54 @@ mod tests { let value = serde_json::to_value(&classification).unwrap(); assert_eq!(*value.get("country").unwrap(), Value::Null); + let none_names = geoip2::Names { + german: None, + english: None, + spanish: None, + french: None, + japanese: None, + brazilian_portuguese: None, + russian: None, + simplified_chinese: None, + }; + classification.country = Some(geoip2::Country { - country: Some(geoip2::country::Country { + country: geoip2::country::Country { geoname_id: None, iso_code: Some("US"), - names: None, + names: geoip2::Names { + german: None, + english: Some("United States of America"), + spanish: None, + french: None, + japanese: None, + brazilian_portuguese: None, + russian: None, + simplified_chinese: None, + }, + is_in_european_union: None, + }, + continent: geoip2::country::Continent { + code: None, + geoname_id: None, + names: none_names.clone(), + }, + registered_country: geoip2::city::Country { + geoname_id: None, + is_in_european_union: None, + iso_code: None, + names: none_names.clone(), + }, + represented_country: geoip2::city::RepresentedCountry { + geoname_id: None, is_in_european_union: None, - }), - continent: None, - registered_country: None, - represented_country: None, - traits: None, + iso_code: None, + names: none_names.clone(), + representation_type: None, + }, + traits: geoip2::city::Traits { + is_anycast: Some(false), + }, }); let value = serde_json::to_value(&classification).unwrap(); diff --git a/src/endpoints/country.rs b/src/endpoints/country.rs index ebab953..f92c764 100644 --- a/src/endpoints/country.rs +++ b/src/endpoints/country.rs @@ -75,21 +75,9 @@ pub async fn get_country( } // return country if we can identify it based on IP address - state - .geoip - .locate(req.client_ip()?) - .map(move |location| { - let country_opt = match location { - Some(x) => x.country, - None => None, - }; - - if country_opt.is_none() { - let mut response = HttpResponse::NotFound(); - metrics.incr_with_tags("country_miss").send(); - return response.json(&COUNTRY_NOT_FOUND_RESPONSE); - } - + let country_info = state.geoip.locate(req.client_ip()?); + match country_info { + Ok(Some(country_info)) => { let mut response = HttpResponse::Ok(); response.append_header(( http::header::CACHE_CONTROL, @@ -98,16 +86,17 @@ pub async fn get_country( metrics.incr_with_tags("country_hit").send(); - let country = country_opt.unwrap(); - response.json(CountryResponse { - country_code: country.iso_code.unwrap_or_default(), - country_name: match country.names { - Some(x) => x["en"], - None => "", - }, - }) - }) - .map_err(|err| ClassifyError::from_source("Future failure", err)) + Ok(response.json(CountryResponse { + country_code: country_info.country.iso_code.unwrap_or_default(), + country_name: country_info.country.names.english.unwrap_or_default(), + })) + } + _ => { + let mut response = HttpResponse::NotFound(); + metrics.incr_with_tags("country_miss").send(); + Ok(response.json(&COUNTRY_NOT_FOUND_RESPONSE)) + } + } } #[cfg(test)] diff --git a/src/endpoints/dockerflow.rs b/src/endpoints/dockerflow.rs index e7376ab..d1dbaef 100644 --- a/src/endpoints/dockerflow.rs +++ b/src/endpoints/dockerflow.rs @@ -25,7 +25,7 @@ pub async fn heartbeat(app_data: Data) -> Result country_info .country - .and_then(|country| country.iso_code) + .iso_code .map(|iso_code| Ok(!iso_code.is_empty())) .unwrap_or(Ok(false)), None => Ok(false), diff --git a/src/geoip.rs b/src/geoip.rs index 96759f0..9321088 100644 --- a/src/geoip.rs +++ b/src/geoip.rs @@ -1,6 +1,6 @@ use crate::errors::ClassifyError; use cadence::{StatsdClient, prelude::*}; -use maxminddb::{self, MaxMindDbError, geoip2}; +use maxminddb::{self, geoip2}; use std::{fmt, net::IpAddr, path::PathBuf, sync::Arc}; pub struct GeoIp { @@ -14,32 +14,25 @@ impl GeoIp { } pub fn locate(&'_ self, ip: IpAddr) -> Result>, ClassifyError> { - self.reader + let lookup_result = self + .reader .as_ref() .ok_or_else(|| ClassifyError::new("No geoip database available"))? - .lookup(ip) - .inspect(|country_info: &Option| { - // Send a metrics ping about the geolocation result - let iso_code = country_info - .clone() - .and_then(|country_info| country_info.country) - .and_then(|country| country.iso_code); - self.metrics - .incr_with_tags("location") - .with_tag("country", iso_code.unwrap_or("unknown")) - .send(); - }) - .or_else(|err| match err { - MaxMindDbError::InvalidNetwork(_) => { - self.metrics - .incr_with_tags("location") - .with_tag("country", "unknown") - .send(); - Ok(None) - } - _ => Err(err), - }) - .map_err(|err| err.into()) + .lookup(ip); + if let Some(country_info) = lookup_result?.decode::()? { + // Send a metrics ping about the geolocation result + let iso_code = country_info.country.iso_code.unwrap_or("unknown"); + self.metrics + .incr_with_tags("location") + .with_tag("country", iso_code) + .send(); + return Ok(Some(country_info)); + } + self.metrics + .incr_with_tags("location") + .with_tag("country", "unknown") + .send(); + Ok(None) } } @@ -115,7 +108,7 @@ mod tests { let ip = "7.7.7.7".parse()?; let rv = geoip.locate(ip).unwrap().unwrap(); - assert_eq!(rv.country.unwrap().iso_code.unwrap(), "US"); + assert_eq!(rv.country.iso_code.unwrap(), "US"); Ok(()) }