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
// This file 'service.rs' is part of the 'hisho' project.
//
// Copyright 2023 Thomas Obernosterer (https://atjon.tv).
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::net::{Shutdown, TcpStream};

use crate::config_models::{Service, ServiceProtocol, Services};
use crate::log;

/// Check that all services are running
pub async fn are_running(services: &Services) -> bool {
    if !services.is_empty() {
        log::print("Checking Services ...".to_string());
        for service in services {
            if !is_running(service).await {
                log::error(format!("\tService '{}' is not running.", service.name));
                return false;
            } else {
                log::print(format!("\tService '{}' is running.", service.name));
            }
        }
    }
    true
}

async fn is_running(service: &Service) -> bool {
    return match service.protocol {
        ServiceProtocol::HTTP => match reqwest::get(service.uri.as_str()).await {
            Ok(response) => response.status().is_success(),
            Err(e) => {
                log::error(format!(
                    "\tService '{}' is not reachable: {}",
                    service.name, e
                ));
                false
            }
        },
        ServiceProtocol::TCP => match TcpStream::connect(service.uri.as_str()) {
            Ok(stream) => {
                let _ = stream.shutdown(Shutdown::Both);
                true
            }
            Err(e) => {
                log::error(format!(
                    "\tService '{}' is not reachable: {}",
                    service.name, e
                ));
                false
            }
        },
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config_models::{Service, ServiceProtocol};

    #[tokio::test]
    async fn cloudflare_is_running() {
        let test_service = Service {
            name: "cloudflare".to_string(),
            protocol: ServiceProtocol::HTTP,
            uri: "https://cloudflare.com".to_string(),
        };
        assert!(is_running(&test_service).await);
    }

    #[tokio::test]
    async fn cloudflare_tcp_ping() {
        let test_service = Service {
            name: "cloudflare tcp".to_string(),
            protocol: ServiceProtocol::TCP,
            uri: "cloudflare.com:80".to_string(),
        };
        assert!(is_running(&test_service).await);
    }

    #[tokio::test]
    async fn ip_172_32_137_254_port_31330_is_offline() {
        let test_service = Service {
            name: "172.32.137.254:31330".to_string(),
            protocol: ServiceProtocol::HTTP,
            uri: "http://172.32.137.254:31330/status".to_string(),
        };
        assert_eq!(is_running(&test_service).await, false);
    }

    #[tokio::test]
    async fn ip_172_32_137_254_port_31330_wont_tcp_ping() {
        let test_service = Service {
            name: "172.32.137.254:31330".to_string(),
            protocol: ServiceProtocol::TCP,
            uri: "172.32.137.254:31330".to_string(),
        };
        assert_eq!(is_running(&test_service).await, false);
    }
}