summaryrefslogtreecommitdiff
path: root/src/client/connector/httpconnector.rs
blob: 8f06074263ce5e855cd46c951a18f63dc1e988c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::{connector::Connection, executor::Executor};
use remote_control_rs::tasks::TASKS;
use reqwest::{
    Error, StatusCode, Url,
    blocking::{Client, RequestBuilder},
};
use std::{net::IpAddr, thread::sleep, time::Duration};

pub struct HTTPConnector {
    network_enforcer: Executor,
    pull_url: Url,
    ntfy_url: Url,
    ip_getter_url: Url,
    client: Client,
    prev: <HTTPConnector as Connection>::Ok,
}

impl HTTPConnector {
    pub fn new(
        pull_url: &str,
        ntfy_url: &str,
        ip_getter_url: &str,
    ) -> std::result::Result<Self, url::ParseError> {
        // TODO: proper type
        assert_eq!(TASKS[2].name, "network");
        Ok(Self {
            network_enforcer: Executor::new(&TASKS[2], ["on".to_string()]).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: Client::new(),
        })
    }

    fn handle_response<F: Fn() -> RequestBuilder>(
        &self,
        resp: F,
    ) -> Result<<HTTPConnector as Connection>::Ok, <HTTPConnector as Connection>::Err> {
        match resp().send() {
            Ok(resp) => match resp.status() {
                StatusCode::TOO_MANY_REQUESTS => {
                    sleep(Duration::from_secs(5));
                    self.update()
                }
                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::Ok, Self::Err> {
        self.handle_response(|| self.client.get(self.pull_url.as_str()))
    }

    fn ntfy(&self, data: String) -> Result<Self::Ok, Self::Err> {
        self.handle_response(|| {
            self.client
                .post(self.ntfy_url.as_str())
                .body(data.to_owned())
        })
    }

    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<IpAddr> {
        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,
            },
        }
    }
}