Store bridge data with key 'bridges', guess and report blockages
This commit is contained in:
parent
12e699f979
commit
1ccd676e5c
|
@ -0,0 +1,22 @@
|
|||
use crate::BridgeInfo;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Provides a function for predicting which countries block this bridge
|
||||
pub trait Analyzer {
|
||||
fn blocked_in(&self, bridge_info: &BridgeInfo) -> HashSet<String>;
|
||||
}
|
||||
|
||||
pub struct ExampleAnalyzer {}
|
||||
|
||||
/// Dummy example which just tells us about blockages we already know about
|
||||
impl Analyzer for ExampleAnalyzer {
|
||||
fn blocked_in(&self, bridge_info: &BridgeInfo) -> HashSet<String> {
|
||||
let mut blocked_in = HashSet::<String>::new();
|
||||
for (country, info) in &bridge_info.info_by_country {
|
||||
if info.blocked {
|
||||
blocked_in.insert(country.to_string());
|
||||
}
|
||||
}
|
||||
blocked_in
|
||||
}
|
||||
}
|
|
@ -61,6 +61,8 @@ async fn update_daily_info(db: &Db, distributors: &BTreeMap<BridgeDistributor, S
|
|||
update_extra_infos(&db).await;
|
||||
update_negative_reports(&db, &distributors).await;
|
||||
update_positive_reports(&db, &distributors).await;
|
||||
let new_blockages = guess_blockages(&db, &analyzer::ExampleAnalyzer {});
|
||||
report_blockages(&distributors, new_blockages).await;
|
||||
}
|
||||
|
||||
async fn create_context_manager(
|
||||
|
|
140
src/lib.rs
140
src/lib.rs
|
@ -7,12 +7,14 @@ use std::{
|
|||
fmt,
|
||||
};
|
||||
|
||||
pub mod analyzer;
|
||||
pub mod bridge_verification_info;
|
||||
pub mod extra_info;
|
||||
pub mod negative_report;
|
||||
pub mod positive_report;
|
||||
pub mod request_handler;
|
||||
|
||||
use analyzer::Analyzer;
|
||||
use extra_info::*;
|
||||
use negative_report::*;
|
||||
use positive_report::*;
|
||||
|
@ -170,11 +172,18 @@ impl fmt::Display for BridgeCountryInfo {
|
|||
/// but this extra-info contains different data for some reason, use the
|
||||
/// greater count of connections from each country.
|
||||
pub fn add_extra_info_to_db(db: &Db, extra_info: ExtraInfo) {
|
||||
let fingerprint = extra_info.fingerprint;
|
||||
let mut bridge_info = match db.get(&fingerprint).unwrap() {
|
||||
let mut bridges = match db.get("bridges").unwrap() {
|
||||
Some(v) => bincode::deserialize(&v).unwrap(),
|
||||
None => BridgeInfo::new(fingerprint, &extra_info.nickname),
|
||||
None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
||||
};
|
||||
let fingerprint = extra_info.fingerprint;
|
||||
if !bridges.contains_key(&fingerprint) {
|
||||
bridges.insert(
|
||||
fingerprint,
|
||||
BridgeInfo::new(fingerprint, &extra_info.nickname),
|
||||
);
|
||||
}
|
||||
let bridge_info = bridges.get_mut(&fingerprint).unwrap();
|
||||
for country in extra_info.bridge_ips.keys() {
|
||||
if bridge_info.info_by_country.contains_key::<String>(country) {
|
||||
bridge_info
|
||||
|
@ -200,7 +209,7 @@ pub fn add_extra_info_to_db(db: &Db, extra_info: ExtraInfo) {
|
|||
}
|
||||
}
|
||||
// Commit changes to database
|
||||
db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
||||
db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
@ -322,19 +331,23 @@ pub async fn update_negative_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|||
let country = first_report.country;
|
||||
let count_valid = verify_negative_reports(&distributors, reports).await;
|
||||
|
||||
let mut bridge_info = match db.get(&fingerprint).unwrap() {
|
||||
let mut bridges = match db.get("bridges").unwrap() {
|
||||
Some(v) => bincode::deserialize(&v).unwrap(),
|
||||
// It should already exist, unless the bridge hasn't published
|
||||
// any bridge stats.
|
||||
None => BridgeInfo::new(fingerprint, &"".to_string()),
|
||||
None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
||||
};
|
||||
|
||||
// Get bridge info or make new one
|
||||
if !bridges.contains_key(&fingerprint) {
|
||||
// This case shouldn't happen unless the bridge hasn't published
|
||||
// any bridge stats.
|
||||
bridges.insert(fingerprint, BridgeInfo::new(fingerprint, &"".to_string()));
|
||||
}
|
||||
let bridge_info = bridges.get_mut(&fingerprint).unwrap();
|
||||
|
||||
// Add the new report count to it
|
||||
if bridge_info.info_by_country.contains_key(&country) {
|
||||
let bridge_country_info = bridge_info.info_by_country.get_mut(&country).unwrap();
|
||||
bridge_country_info.add_info(BridgeInfoType::NegativeReports, date, count_valid);
|
||||
// Commit changes to database
|
||||
db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
||||
.unwrap();
|
||||
} else {
|
||||
// No existing entry; make a new one.
|
||||
let mut bridge_country_info = BridgeCountryInfo::new();
|
||||
|
@ -342,10 +355,11 @@ pub async fn update_negative_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|||
bridge_info
|
||||
.info_by_country
|
||||
.insert(country, bridge_country_info);
|
||||
// Commit changes to database
|
||||
db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Commit changes to database
|
||||
db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
// TODO: Would it be cheaper to just recreate it?
|
||||
|
@ -434,19 +448,25 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|||
let date = first_report.date;
|
||||
let country = first_report.country.clone();
|
||||
let count_valid = verify_positive_reports(&distributors, reports).await;
|
||||
let mut bridge_info = match db.get(&fingerprint).unwrap() {
|
||||
|
||||
// Get bridge data from database
|
||||
let mut bridges = match db.get("bridges").unwrap() {
|
||||
Some(v) => bincode::deserialize(&v).unwrap(),
|
||||
// It should already exist, unless the bridge hasn't published
|
||||
// any bridge stats.
|
||||
None => BridgeInfo::new(fingerprint, &"".to_string()),
|
||||
None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
||||
};
|
||||
|
||||
// Get bridge info or make new one
|
||||
if !bridges.contains_key(&fingerprint) {
|
||||
// This case shouldn't happen unless the bridge hasn't published
|
||||
// any bridge stats.
|
||||
bridges.insert(fingerprint, BridgeInfo::new(fingerprint, &"".to_string()));
|
||||
}
|
||||
let bridge_info = bridges.get_mut(&fingerprint).unwrap();
|
||||
|
||||
// Add the new report count to it
|
||||
if bridge_info.info_by_country.contains_key(&country) {
|
||||
let bridge_country_info = bridge_info.info_by_country.get_mut(&country).unwrap();
|
||||
bridge_country_info.add_info(BridgeInfoType::PositiveReports, date, count_valid);
|
||||
// Commit changes to database
|
||||
db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
||||
.unwrap();
|
||||
} else {
|
||||
// No existing entry; make a new one.
|
||||
let mut bridge_country_info = BridgeCountryInfo::new();
|
||||
|
@ -454,10 +474,10 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|||
bridge_info
|
||||
.info_by_country
|
||||
.insert(country, bridge_country_info);
|
||||
// Commit changes to database
|
||||
db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
// Commit changes to database
|
||||
db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
// TODO: Would it be cheaper to just recreate it?
|
||||
|
@ -470,4 +490,74 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
// TODO: function to mark a bridge as blocked
|
||||
// Verdict on bridge reachability
|
||||
|
||||
/// Guess which countries block a bridge. This function returns a map of new
|
||||
/// blockages (fingerprint : set of countries which block the bridge)
|
||||
pub fn guess_blockages(db: &Db, analyzer: &dyn Analyzer) -> HashMap<[u8; 20], HashSet<String>> {
|
||||
// Map of bridge fingerprint to set of countries which newly block it
|
||||
let mut blockages = HashMap::<[u8; 20], HashSet<String>>::new();
|
||||
|
||||
// Get bridge data from database
|
||||
let mut bridges = match db.get("bridges").unwrap() {
|
||||
Some(v) => bincode::deserialize(&v).unwrap(),
|
||||
None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
||||
};
|
||||
|
||||
// Guess for each bridge
|
||||
for (fingerprint, bridge_info) in &mut bridges {
|
||||
let mut new_blockages = HashSet::<String>::new();
|
||||
let blocked_in = analyzer.blocked_in(&bridge_info);
|
||||
for country in blocked_in {
|
||||
let bridge_country_info = bridge_info.info_by_country.get_mut(&country).unwrap();
|
||||
if !bridge_country_info.blocked {
|
||||
new_blockages.insert(country.to_string());
|
||||
// Mark bridge as blocked when db gets updated
|
||||
bridge_country_info.blocked = true;
|
||||
}
|
||||
}
|
||||
blockages.insert(*fingerprint, new_blockages);
|
||||
}
|
||||
|
||||
// Commit changes to database
|
||||
db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
||||
.unwrap();
|
||||
|
||||
// Return map of new blockages
|
||||
blockages
|
||||
}
|
||||
|
||||
/// Report blocked bridges to bridge distributor
|
||||
pub async fn report_blockages(
|
||||
distributors: &BTreeMap<BridgeDistributor, String>,
|
||||
blockages: HashMap<[u8; 20], HashSet<String>>,
|
||||
) {
|
||||
// For now, only report to Lox
|
||||
// TODO: Support more distributors
|
||||
let uri: String = (distributors
|
||||
.get(&BridgeDistributor::Lox)
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
+ "/reportblocked")
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
// Convert map keys from [u8; 20] to 40-character hex strings
|
||||
let mut blockages_str = HashMap::<String, HashSet<String>>::new();
|
||||
for (fingerprint, countries) in blockages {
|
||||
let fpr_string = array_bytes::bytes2hex("", fingerprint);
|
||||
blockages_str.insert(fpr_string, countries);
|
||||
}
|
||||
|
||||
// Report blocked bridges to bridge distributor
|
||||
let client = Client::new();
|
||||
let req = Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri)
|
||||
.body(Body::from(serde_json::to_string(&blockages_str).unwrap()))
|
||||
.unwrap();
|
||||
let resp = client.request(req).await.unwrap();
|
||||
let buf = hyper::body::to_bytes(resp).await.unwrap();
|
||||
let resp_str: String = serde_json::from_slice(&buf).unwrap();
|
||||
assert_eq!("OK", resp_str);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue