use crate::{connector::Connection, executor::Executor}; use remote_control_rs::tasks::NETWORK_T; use reqwest::{ Error, StatusCode, Url, blocking::{Client, RequestBuilder}, }; use std::{net::IpAddr, thread::sleep, time::Duration}; #[derive(Debug)] pub enum HTTPConnectorError { Url(url::ParseError), Reqwest(reqwest::Error), } impl From for HTTPConnectorError { fn from(e: url::ParseError) -> Self { HTTPConnectorError::Url(e) } } impl From for HTTPConnectorError { fn from(e: reqwest::Error) -> Self { HTTPConnectorError::Reqwest(e) } } pub struct HTTPConnector { network_enforcer: Executor<'static, 'static>, pull_url: Url, ntfy_url: Url, ip_getter_url: Url, client: Client, prev: ::Ok, } impl HTTPConnector { pub fn new(pull_url: &str, ntfy_url: &str, ip_getter_url: &str) -> std::result::Result { Ok(Self { network_enforcer: Executor::new(&NETWORK_T, ["true"]).unwrap(), // .unwrap() should never panic prev: String::new(), pull_url: Url::parse(pull_url)?, ntfy_url: Url::parse(ntfy_url)?, ip_getter_url: match Url::parse(ip_getter_url) { Ok(url) => url, Err(_) => Url::parse((pull_url.to_string() + ip_getter_url).as_str())?, }, client: match Client::builder().build() { Ok(c) => c, Err(_) => match Client::builder().danger_accept_invalid_certs(true).build() { Ok(c) => c, Err(_) => Client::builder().danger_accept_invalid_certs(true).danger_accept_invalid_hostnames(true).build()?, }, }, }) } #[allow(clippy::only_used_in_recursion)] fn handle_response RequestBuilder>(&self, req: F) -> Result<::Ok, ::Err> { match req().send() { Ok(resp) => match resp.status() { StatusCode::TOO_MANY_REQUESTS => { sleep(Duration::from_secs(5)); self.handle_response(req) } status => { if !status.is_success() { return Result::Err(Err::StatusCode(status)); } match resp.text() { Ok(text) => Result::Ok(text), Err(e) => Result::Err(Err::Reqwest(e)), } } }, Err(e) => Result::Err(Err::Reqwest(e)), } } } #[derive(Debug)] pub enum Err { Reqwest(Error), StatusCode(StatusCode), } impl std::fmt::Display for Err { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Err::Reqwest(e) => e.fmt(f), Err::StatusCode(e) => e.fmt(f), } } } impl Connection for HTTPConnector { type Err = Err; // consider StatusCode::NOT_MODIFIED fn update(&self) -> Result { self.handle_response(|| self.client.get(self.pull_url.as_str())) } fn ntfy(&self, data: String) -> std::result::Result<(), Self::Err> { self.handle_response(|| self.client.post(self.ntfy_url.as_str()).body(data.to_owned()))?; Ok(()) } fn is_prev(&mut self, str: &Self::Ok) -> bool { if str == &self.prev { return true; } else { self.prev = str.to_owned() } false } fn force_network(&mut self) { _ = self.network_enforcer.run(); } fn get_ip(&self) -> Option { match self.handle_response(|| self.client.get(self.ip_getter_url.as_str())) { Result::Ok(ip) => Some(ip.parse().ok()?), Result::Err(_) => match self.handle_response(|| self.client.get("ifconfig.me")) { Ok(ip) => Some(ip.parse().ok()?), Err(_) => None, }, } } }