mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-06-18 13:11:02 +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 actix::prelude::*;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::chain::{Chain, LocateNode};
|
use crate::chain::{Chain, LocateNode};
|
||||||
use crate::types::{NodeId, NodeLocation};
|
use crate::types::{NodeId, NodeLocation};
|
||||||
@@ -52,6 +53,34 @@ pub struct LocateRequest {
|
|||||||
pub chain: Addr<Chain>,
|
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 {
|
impl Handler<LocateRequest> for Locator {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
@@ -66,18 +95,9 @@ impl Handler<LocateRequest> for Locator {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let ip_req = format!("https://ipapi.co/{}/json", ip);
|
let location = match self.iplocate(ip) {
|
||||||
let mut response = match self.client.post(&ip_req).send() {
|
Ok(location) => location,
|
||||||
Ok(response) => response,
|
Err(err) => return debug!("GET error for ip location: {:?}", err),
|
||||||
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
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cache.write().insert(ip, location.clone());
|
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::iter::Sum;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ pub struct NumStats<T> {
|
|||||||
sum: 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 {
|
pub fn new(size: usize) -> Self {
|
||||||
NumStats {
|
NumStats {
|
||||||
stack: vec![T::zero(); size].into_boxed_slice(),
|
stack: vec![T::zero(); size].into_boxed_slice(),
|
||||||
|
|||||||
Reference in New Issue
Block a user