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
//! **A library-first, lightweight, high-performance, cloud-native supported API gateway🪐**
//!
//! ## 🚀 Installation
//!
//! see [installation.md](https://github.com/ideal-world/spacegate/blob/master/docs/k8s/installation.md)
//!
//! ## Special instructions for configuration
//! ### Setting HTTP Route Priority
//! You can specify the priority of an httproute by adding a priority field in the annotations section of the route.
//! A higher value for the priority field indicates a higher priority. The httproute library stores the priority
//! value using the i64 data type, so the maximum and minimum values for the priority are [i64::MAX]
//! (https://doc.rust-lang.org/std/primitive.i64.html#associatedconstant.MAX) and
//! [i64::MIN](https://doc.rust-lang.org/std/primitive.i64.html#associatedconstant.MIN) respectively.
//!
//! If the priority field is not present in an httproute, its priority will be default to 0, and the default priority
//! will be determined based on the creation order (earlier routes will have higher priority).
//!
//! Note: Trace-level logs will print the contents of both the request and response bodies,
//! potentially causing significant performance overhead. It is recommended to use debug level
//! logs at most.

#![warn(clippy::unwrap_used)]
use config::{gateway_dto::SgGateway, http_route_dto::SgHttpRoute};
use functions::{http_route, server};
pub use http;
pub use hyper;
use plugins::filters::{self, SgPluginFilterDef};
use tardis::{basic::result::TardisResult, log, tokio::signal};

pub mod config;
pub mod constants;
pub mod functions;
pub mod helpers;
pub mod instance;
pub mod plugins;

pub async fn startup_k8s(namespace: Option<String>) -> TardisResult<()> {
    startup(true, namespace, None).await
}

pub async fn startup_native(conf_uri: String, check_interval_sec: u64) -> TardisResult<()> {
    startup(false, Some(conf_uri), Some(check_interval_sec)).await
}

pub async fn startup_simplify(conf_path: String, check_interval_sec: u64) -> TardisResult<()> {
    startup(false, Some(conf_path), Some(check_interval_sec)).await
}

pub async fn startup(k8s_mode: bool, namespace_or_conf_uri: Option<String>, check_interval_sec: Option<u64>) -> TardisResult<()> {
    // Initialize configuration according to different modes
    let configs = config::init(k8s_mode, namespace_or_conf_uri, check_interval_sec).await?;
    for (gateway, http_routes) in configs {
        do_startup(gateway, http_routes).await?;
    }
    Ok(())
}

pub async fn do_startup(gateway: SgGateway, http_routes: Vec<SgHttpRoute>) -> TardisResult<()> {
    // Initialize service instances
    let server_insts = server::init(&gateway).await?;
    let gateway_name = &gateway.name.clone();
    #[cfg(feature = "cache")]
    {
        // Initialize cache instances
        if let Some(url) = &gateway.parameters.redis_url {
            log::trace!("Initialize cache client...url:{url}");
            functions::cache_client::init(gateway_name, url).await?;
        }
    }
    // Initialize route instances
    http_route::init(gateway, http_routes).await?;
    // Start service instances
    server::startup(gateway_name, server_insts).await
}

pub async fn shutdown(gateway_name: &str) -> TardisResult<()> {
    // Remove route instances
    http_route::remove(gateway_name).await?;
    #[cfg(feature = "cache")]
    {
        // Remove cache instances
        functions::cache_client::remove(gateway_name).await?;
    }
    // Shutdown service instances
    server::shutdown(gateway_name).await
}

pub async fn wait_graceful_shutdown() -> TardisResult<()> {
    match signal::ctrl_c().await {
        Ok(_) => {
            log::info!("Received ctrl+c signal, shutting down...");
        }
        Err(error) => {
            log::error!("Received the ctrl+c signal, but with an error: {error}");
        }
    }
    Ok(())
}

#[inline]
pub fn register_filter_def(filter_def: impl SgPluginFilterDef + 'static) {
    register_filter_def_boxed(Box::new(filter_def))
}

#[inline]
pub fn register_filter_def_boxed(filter_def: Box<dyn SgPluginFilterDef>) {
    filters::register_filter_def(filter_def.get_code().to_string(), filter_def)
}