mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-05-07 09:28:00 +00:00
feat: Attempt ipinfo.io if ipapi.co returns no results (#204)
* feat: Attempt ipinfo.io if ipapi.co returns no results * fix: Use the GET method * feat: Added tests * chore: DRY
This commit is contained in:
+101
-12
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||
use actix::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use parking_lot::RwLock;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::chain::{Chain, LocateNode};
|
||||
use crate::types::{NodeId, NodeLocation};
|
||||
@@ -52,6 +53,34 @@ pub struct LocateRequest {
|
||||
pub chain: Addr<Chain>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IPApiLocate {
|
||||
city: Box<str>,
|
||||
loc: Box<str>,
|
||||
}
|
||||
|
||||
impl IPApiLocate {
|
||||
fn into_node_location(self) -> Option<NodeLocation> {
|
||||
let IPApiLocate { city, loc } = self;
|
||||
|
||||
let mut loc = loc.split(",").map(|n| n.parse());
|
||||
|
||||
let latitude = loc.next()?.ok()?;
|
||||
let longitude = loc.next()?.ok()?;
|
||||
|
||||
// Guarantee that the iterator has been exhausted
|
||||
if loc.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NodeLocation {
|
||||
latitude,
|
||||
longitude,
|
||||
city,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler<LocateRequest> for Locator {
|
||||
type Result = ();
|
||||
|
||||
@@ -66,18 +95,9 @@ impl Handler<LocateRequest> for Locator {
|
||||
return
|
||||
}
|
||||
|
||||
let ip_req = format!("https://ipapi.co/{}/json", ip);
|
||||
let mut response = match self.client.post(&ip_req).send() {
|
||||
Ok(response) => response,
|
||||
Err(err) => return debug!("POST error for ip location: {:?}", err),
|
||||
};
|
||||
|
||||
let location = match response.json::<NodeLocation>() {
|
||||
Ok(location) => Some(Arc::new(location)),
|
||||
Err(err) => {
|
||||
debug!("JSON error for ip location: {:?}", err);
|
||||
None
|
||||
}
|
||||
let location = match self.iplocate(ip) {
|
||||
Ok(location) => location,
|
||||
Err(err) => return debug!("GET error for ip location: {:?}", err),
|
||||
};
|
||||
|
||||
self.cache.write().insert(ip, location.clone());
|
||||
@@ -87,3 +107,72 @@ impl Handler<LocateRequest> for Locator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Locator {
|
||||
fn iplocate(&self, ip: Ipv4Addr) -> Result<Option<Arc<NodeLocation>>, reqwest::Error> {
|
||||
let location = self.iplocate_ipapi_co(ip)?;
|
||||
|
||||
match location {
|
||||
Some(location) => Ok(Some(location)),
|
||||
None => self.iplocate_ipinfo_io(ip),
|
||||
}
|
||||
}
|
||||
|
||||
fn iplocate_ipapi_co(&self, ip: Ipv4Addr) -> Result<Option<Arc<NodeLocation>>, reqwest::Error> {
|
||||
let location = self.query(&format!("https://ipapi.co/{}/json", ip))?.map(Arc::new);
|
||||
|
||||
Ok(location)
|
||||
}
|
||||
|
||||
fn iplocate_ipinfo_io(&self, ip: Ipv4Addr) -> Result<Option<Arc<NodeLocation>>, reqwest::Error> {
|
||||
let location = self.query(&format!("https://ipinfo.io/{}/json", ip))?.and_then(|loc: IPApiLocate| {
|
||||
loc.into_node_location().map(Arc::new)
|
||||
});
|
||||
|
||||
Ok(location)
|
||||
}
|
||||
|
||||
fn query<T>(&self, url: &str) -> Result<Option<T>, reqwest::Error>
|
||||
where
|
||||
for<'de> T: Deserialize<'de>,
|
||||
{
|
||||
match self.client.get(url).send()?.json::<T>() {
|
||||
Ok(result) => Ok(Some(result)),
|
||||
Err(err) => {
|
||||
debug!("JSON error for ip location: {:?}", err);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ipapi_locate_to_node_location() {
|
||||
let ipapi = IPApiLocate {
|
||||
loc: "12.5,56.25".into(),
|
||||
city: "Foobar".into(),
|
||||
};
|
||||
|
||||
let location = ipapi.into_node_location().unwrap();
|
||||
|
||||
assert_eq!(location.latitude, 12.5);
|
||||
assert_eq!(location.longitude, 56.25);
|
||||
assert_eq!(&*location.city, "Foobar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipapi_locate_to_node_location_too_many() {
|
||||
let ipapi = IPApiLocate {
|
||||
loc: "12.5,56.25,1.0".into(),
|
||||
city: "Foobar".into(),
|
||||
};
|
||||
|
||||
let location = ipapi.into_node_location();
|
||||
|
||||
assert!(location.is_none());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use num_traits::{Zero, NumOps, Bounded, ops::saturating::Saturating};
|
||||
use num_traits::{Zero, NumOps, Bounded};
|
||||
use std::iter::Sum;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
@@ -10,7 +10,7 @@ pub struct NumStats<T> {
|
||||
sum: T,
|
||||
}
|
||||
|
||||
impl<T: Saturating + NumOps + Zero + Bounded + Copy + Sum + TryFrom<usize>> NumStats<T> {
|
||||
impl<T: NumOps + Zero + Bounded + Copy + Sum + TryFrom<usize>> NumStats<T> {
|
||||
pub fn new(size: usize) -> Self {
|
||||
NumStats {
|
||||
stack: vec![T::zero(); size].into_boxed_slice(),
|
||||
|
||||
Reference in New Issue
Block a user