spacegate_kernel/
lib.rs

1//! # Spacegate kernel crate.
2//!
3//! This crate provides the core functionality of spacegate.
4
5#![deny(clippy::unwrap_used, clippy::dbg_macro, clippy::unimplemented, clippy::todo, clippy::missing_safety_doc)]
6#![warn(
7    clippy::missing_errors_doc,
8    clippy::indexing_slicing,
9    clippy::inline_always,
10    clippy::fn_params_excessive_bools,
11    missing_debug_implementations
12)]
13/// https services, ws services, and static file services.
14pub mod backend_service;
15/// a boxed body
16pub mod body;
17/// extensions for request and response
18pub mod extension;
19/// extractors for request
20pub mod extractor;
21
22/// helper layers
23pub mod helper_layers;
24/// injectors for reqeust
25pub mod injector;
26/// tcp listener
27pub mod listener;
28/// gateway service
29pub mod service;
30/// util functions and structs
31pub mod utils;
32
33pub use backend_service::ArcHyperService;
34pub use body::SgBody;
35use extension::Reflect;
36pub use extractor::Extract;
37use extractor::OptionalExtract;
38use hyper::{body::Bytes, Request, Response, StatusCode};
39use injector::Inject;
40use std::{convert::Infallible, fmt, ops::Deref};
41pub use tokio_util::sync::CancellationToken;
42pub use tower_layer::Layer;
43use utils::{PathIter, QueryKvIter};
44
45use tower_layer::layer_fn;
46
47pub type BoxResult<T> = Result<T, BoxError>;
48/// A boxed error.
49pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
50
51/// Alias for a request with a boxed body.
52pub type SgRequest = Request<SgBody>;
53/// Alias for a response with a boxed body.
54pub type SgResponse = Response<SgBody>;
55
56/// Provides extension methods for [`Request`](hyper::Request).
57pub trait SgRequestExt {
58    fn with_reflect(&mut self);
59    fn reflect_mut(&mut self) -> &mut Reflect;
60    fn reflect(&self) -> &Reflect;
61    #[cfg(feature = "ext-redis")]
62    fn get_redis_client_by_gateway_name(&self) -> Option<spacegate_ext_redis::RedisClient>;
63    fn extract<M: Extract>(&self) -> M;
64    fn try_extract<M: OptionalExtract>(&self) -> Option<M>;
65    /// # Errors
66    /// If the injection fails.
67    fn inject<I: Inject>(&mut self, i: &I) -> BoxResult<()>;
68    fn defer_call<F>(&mut self, f: F)
69    where
70        F: FnOnce(SgRequest) -> SgRequest + Send + 'static;
71    fn path_iter(&self) -> PathIter;
72    fn query_kv_iter(&self) -> Option<QueryKvIter>;
73}
74
75impl SgRequestExt for SgRequest {
76    /// Get a mutable reference to the reflect extension.
77    ///
78    /// # Panics
79    /// Panics if the reflect extension is not found.
80    /// If you are using a request created by spacegate, this should never happen.
81    fn reflect_mut(&mut self) -> &mut Reflect {
82        self.extensions_mut().get_mut::<Reflect>().expect("reflect extension not found")
83    }
84    /// Get a reference to the reflect extension.
85    ///
86    /// # Panics
87    /// Panics if the reflect extension is not found.
88    /// If you are using a request created by spacegate, this should never happen.
89    fn reflect(&self) -> &Reflect {
90        self.extensions().get::<Reflect>().expect("reflect extension not found")
91    }
92    /// Add a reflect extension to the request if it does not exist.
93    fn with_reflect(&mut self) {
94        if self.extensions().get::<Reflect>().is_none() {
95            self.extensions_mut().insert(Reflect::new());
96        }
97    }
98
99    #[cfg(feature = "ext-redis")]
100    /// Get a redis client by the [`extension::GatewayName`], which would exist once the request had entered some gateway.
101    fn get_redis_client_by_gateway_name(&self) -> Option<spacegate_ext_redis::RedisClient> {
102        self.extensions().get::<extension::GatewayName>().and_then(|gateway_name| spacegate_ext_redis::RedisClientRepo::global().get(gateway_name))
103    }
104
105    /// Extract a value from the request.
106    fn extract<M: Extract>(&self) -> M {
107        M::extract(self)
108    }
109
110    /// Try to extract a value from the request.
111    fn try_extract<M: OptionalExtract>(&self) -> Option<M> {
112        OptionalExtract::extract(self)
113    }
114
115    /// Inject some data into the request.
116    fn inject<I: Inject>(&mut self, i: &I) -> BoxResult<()> {
117        i.inject(self)
118    }
119
120    /// Defer a call to the request. The call will be executed before the request has been sent to the backend.
121    fn defer_call<F>(&mut self, f: F)
122    where
123        F: FnOnce(SgRequest) -> SgRequest + Send + 'static,
124    {
125        let defer = self.extensions_mut().get_or_insert_default::<extension::Defer>();
126        defer.push_back(f);
127    }
128
129    fn path_iter(&self) -> PathIter {
130        PathIter::new(self.uri().path())
131    }
132
133    fn query_kv_iter(&self) -> Option<QueryKvIter> {
134        self.uri().query().map(QueryKvIter::new)
135    }
136}
137
138/// Provides extension methods for [`Response`](hyper::Response).
139pub trait SgResponseExt {
140    fn with_code_message(code: StatusCode, message: impl Into<Bytes>) -> Self;
141    fn with_code_empty(code: StatusCode) -> Self;
142    fn bad_gateway<E: std::error::Error>(e: E) -> Self
143    where
144        Self: Sized,
145    {
146        let message = e.to_string();
147        let src = e.source();
148        let message = if let Some(src) = src { format!("{}:\n {}", message, src) } else { message };
149        Self::with_code_message(StatusCode::BAD_GATEWAY, message)
150    }
151    fn inherit_reflect(&mut self, req: &SgRequest);
152}
153
154impl SgResponseExt for Response<SgBody> {
155    fn with_code_message(code: StatusCode, message: impl Into<Bytes>) -> Self {
156        let body = SgBody::full(message);
157        let mut resp = Response::builder().status(code).body(body).expect("response builder error");
158        resp.extensions_mut().insert(Reflect::new());
159        resp
160    }
161    fn with_code_empty(code: StatusCode) -> Self {
162        let body = SgBody::empty();
163        let mut resp = Response::builder().status(code).body(body).expect("response builder error");
164        resp.extensions_mut().insert(Reflect::new());
165        resp
166    }
167    fn inherit_reflect(&mut self, req: &SgRequest) {
168        if let Some(reflect) = req.extensions().get::<Reflect>() {
169            self.extensions_mut().extend(reflect.deref().clone());
170        }
171    }
172}
173
174/// A boxed [`Layer`] that can be used as a plugin layer in gateway.
175pub struct BoxLayer {
176    boxed: Box<dyn Layer<ArcHyperService, Service = ArcHyperService> + Send + Sync + 'static>,
177}
178
179impl BoxLayer {
180    /// Create a new [`BoxLayer`].
181    pub fn new<L>(inner_layer: L) -> Self
182    where
183        L: Layer<ArcHyperService> + Send + Sync + 'static,
184        L::Service: Clone + hyper::service::Service<Request<SgBody>, Response = Response<SgBody>, Error = Infallible> + Send + Sync + 'static,
185        <L::Service as hyper::service::Service<Request<SgBody>>>::Future: Send + 'static,
186    {
187        let layer = layer_fn(move |inner: ArcHyperService| {
188            let out = inner_layer.layer(inner);
189            ArcHyperService::new(out)
190        });
191
192        Self { boxed: Box::new(layer) }
193    }
194
195    /// Create a new [`BoxLayer`] with an arc wrapped layer.
196    #[must_use]
197    pub fn layer_shared(&self, inner: ArcHyperService) -> ArcHyperService {
198        self.boxed.layer(inner)
199    }
200}
201
202impl<S> Layer<S> for BoxLayer
203where
204    S: hyper::service::Service<Request<SgBody>, Response = Response<SgBody>, Error = Infallible> + Send + Sync + 'static,
205    <S as hyper::service::Service<hyper::Request<SgBody>>>::Future: std::marker::Send,
206{
207    type Service = ArcHyperService;
208
209    fn layer(&self, inner: S) -> Self::Service {
210        self.boxed.layer(ArcHyperService::new(inner))
211    }
212}
213
214impl fmt::Debug for BoxLayer {
215    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
216        fmt.debug_struct("BoxLayer").finish()
217    }
218}