ntex_service/lib.rs
1//! See [`Service`] docs for information on this crate's foundational trait.
2#![allow(async_fn_in_trait)]
3#![deny(
4 rust_2018_idioms,
5 warnings,
6 unreachable_pub,
7 missing_debug_implementations
8)]
9use std::{rc::Rc, task::Context};
10
11mod and_then;
12mod apply;
13pub mod boxed;
14mod chain;
15mod ctx;
16mod fn_shutdown;
17mod macros;
18mod map;
19mod map_config;
20mod map_err;
21mod map_init_err;
22mod middleware;
23mod pipeline;
24mod then;
25mod util;
26
27#[cfg(not(feature = "async-fn"))]
28mod fn_service;
29
30#[cfg(feature = "async-fn")]
31#[path = "fn_service_async.rs"]
32mod fn_service;
33
34pub use self::apply::{apply_fn, apply_fn_factory};
35pub use self::chain::{chain, chain_factory};
36pub use self::ctx::ServiceCtx;
37pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service};
38pub use self::fn_shutdown::fn_shutdown;
39pub use self::map_config::{map_config, unit_config};
40pub use self::middleware::{apply, Identity, Middleware, Stack};
41pub use self::pipeline::{Pipeline, PipelineBinding, PipelineCall};
42
43#[allow(unused_variables)]
44/// An asynchronous function of `Request` to a `Response`.
45///
46/// The `Service` trait represents a request/response interaction, receiving requests and returning
47/// replies. You can think about service as a function with one argument that returns some result
48/// asynchronously. Conceptually, the operation looks like this:
49///
50/// ```rust,ignore
51/// async fn(Request) -> Result<Response, Error>
52/// ```
53///
54/// The `Service` trait just generalizes this form. Requests are defined as a generic type parameter
55/// and responses and other details are defined as associated types on the trait impl. Notice that
56/// this design means that services can receive many request types and converge them to a single
57/// response type.
58///
59/// Services can also have mutable state that influence computation by using a `Cell`, `RefCell`
60/// or `Mutex`. Services intentionally do not take `&mut self` to reduce overhead in the
61/// common cases.
62///
63/// `Service` provides a symmetric and uniform API; the same abstractions can be used to represent
64/// both clients and servers. Services describe only _transformation_ operations which encourage
65/// simple API surfaces. This leads to simpler design of each service, improves test-ability and
66/// makes composition easier.
67///
68/// ```rust
69/// # use std::convert::Infallible;
70/// #
71/// # use ntex_service::{Service, ServiceCtx};
72///
73/// struct MyService;
74///
75/// impl Service<u8> for MyService {
76/// type Response = u64;
77/// type Error = Infallible;
78///
79/// async fn call(&self, req: u8, ctx: ServiceCtx<'_, Self>) -> Result<Self::Response, Self::Error> {
80/// Ok(req as u64)
81/// }
82/// }
83/// ```
84///
85/// Sometimes it is not necessary to implement the Service trait. For example, the above service
86/// could be rewritten as a simple function and passed to [`fn_service`](fn_service()).
87///
88/// ```rust,ignore
89/// async fn my_service(req: u8) -> Result<u64, Infallible>;
90/// ```
91///
92/// Service cannot be called directly, it must be wrapped to an instance of [`Pipeline``] or
93/// by using `ctx` argument of the call method in case of chanined services.
94///
95pub trait Service<Req> {
96 /// Responses given by the service.
97 type Response;
98
99 /// Errors produced by the service when polling readiness or executing call.
100 type Error;
101
102 /// Process the request and return the response asynchronously.
103 ///
104 /// This function is expected to be callable off-task. As such, implementations of `call`
105 /// should take care to not call `poll_ready`. Caller of the service verifies readiness,
106 /// Only way to make a `call` is to use `ctx` argument, it enforces readiness before calling
107 /// service.
108 async fn call(
109 &self,
110 req: Req,
111 ctx: ServiceCtx<'_, Self>,
112 ) -> Result<Self::Response, Self::Error>;
113
114 #[inline]
115 /// Returns when the service is able to process requests.
116 ///
117 /// If the service is at capacity, then `ready` does not returns and the task is notified when
118 /// the service becomes ready again. This function is expected to be called while on a task.
119 ///
120 /// This is a **best effort** implementation. False positives are permitted. It is permitted for
121 /// the service to returns from a `ready` call and the next invocation of `call`
122 /// results in an error.
123 async fn ready(&self, ctx: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
124 Ok(())
125 }
126
127 #[inline]
128 /// Shutdown service.
129 ///
130 /// Returns when the service is properly shutdowns.
131 async fn shutdown(&self) {}
132
133 #[inline]
134 /// Polls service from the current task.
135 ///
136 /// Service may require to execute asynchronous computation or
137 /// maintain asynchronous state.
138 fn poll(&self, cx: &mut Context<'_>) -> Result<(), Self::Error> {
139 Ok(())
140 }
141
142 #[inline]
143 /// Map this service's output to a different type, returning a new service of the resulting type.
144 ///
145 /// This function is similar to the `Option::map` or `Iterator::map` where it will change
146 /// the type of the underlying service.
147 ///
148 /// Note that this function consumes the receiving service and returns a wrapped version of it,
149 /// similar to the existing `map` methods in the standard library.
150 fn map<F, Res>(self, f: F) -> dev::ServiceChain<dev::Map<Self, F, Req, Res>, Req>
151 where
152 Self: Sized,
153 F: Fn(Self::Response) -> Res,
154 {
155 chain(dev::Map::new(self, f))
156 }
157
158 #[inline]
159 /// Map this service's error to a different error, returning a new service.
160 ///
161 /// This function is similar to the `Result::map_err` where it will change the error type of
162 /// the underlying service. This is useful for example to ensure that services have the same
163 /// error type.
164 ///
165 /// Note that this function consumes the receiving service and returns a wrapped version of it.
166 fn map_err<F, E>(self, f: F) -> dev::ServiceChain<dev::MapErr<Self, F, E>, Req>
167 where
168 Self: Sized,
169 F: Fn(Self::Error) -> E,
170 {
171 chain(dev::MapErr::new(self, f))
172 }
173}
174
175/// Factory for creating `Service`s.
176///
177/// This is useful for cases where new `Service`s must be produced. One case is a TCP server
178/// listener: a listener accepts new connections, constructs a new `Service` for each using
179/// the `ServiceFactory` trait, and uses the new `Service` to process inbound requests on that
180/// new connection.
181///
182/// `Config` is a service factory configuration type.
183///
184/// Simple factories may be able to use [`fn_factory`] or [`fn_factory_with_config`] to
185/// reduce boilerplate.
186pub trait ServiceFactory<Req, Cfg = ()> {
187 /// Responses given by the created services.
188 type Response;
189
190 /// Errors produced by the created services.
191 type Error;
192
193 /// The kind of `Service` created by this factory.
194 type Service: Service<Req, Response = Self::Response, Error = Self::Error>;
195
196 /// Errors potentially raised while building a service.
197 type InitError;
198
199 /// Create and return a new service value asynchronously.
200 async fn create(&self, cfg: Cfg) -> Result<Self::Service, Self::InitError>;
201
202 #[inline]
203 /// Create and return a new service value asynchronously and wrap into a container
204 async fn pipeline(&self, cfg: Cfg) -> Result<Pipeline<Self::Service>, Self::InitError>
205 where
206 Self: Sized,
207 {
208 Ok(Pipeline::new(self.create(cfg).await?))
209 }
210
211 #[inline]
212 /// Map this service's output to a different type, returning a new service
213 /// of the resulting type.
214 fn map<F, Res>(
215 self,
216 f: F,
217 ) -> dev::ServiceChainFactory<dev::MapFactory<Self, F, Req, Res, Cfg>, Req, Cfg>
218 where
219 Self: Sized,
220 F: Fn(Self::Response) -> Res + Clone,
221 {
222 chain_factory(dev::MapFactory::new(self, f))
223 }
224
225 #[inline]
226 /// Map this service's error to a different error, returning a new service.
227 fn map_err<F, E>(
228 self,
229 f: F,
230 ) -> dev::ServiceChainFactory<dev::MapErrFactory<Self, Req, Cfg, F, E>, Req, Cfg>
231 where
232 Self: Sized,
233 F: Fn(Self::Error) -> E + Clone,
234 {
235 chain_factory(dev::MapErrFactory::new(self, f))
236 }
237
238 #[inline]
239 /// Map this factory's init error to a different error, returning a new service.
240 fn map_init_err<F, E>(
241 self,
242 f: F,
243 ) -> dev::ServiceChainFactory<dev::MapInitErr<Self, Req, Cfg, F, E>, Req, Cfg>
244 where
245 Self: Sized,
246 F: Fn(Self::InitError) -> E + Clone,
247 {
248 chain_factory(dev::MapInitErr::new(self, f))
249 }
250}
251
252impl<S, Req> Service<Req> for &S
253where
254 S: Service<Req>,
255{
256 type Response = S::Response;
257 type Error = S::Error;
258
259 #[inline]
260 async fn ready(&self, ctx: ServiceCtx<'_, Self>) -> Result<(), S::Error> {
261 ctx.ready(&**self).await
262 }
263
264 #[inline]
265 fn poll(&self, cx: &mut Context<'_>) -> Result<(), S::Error> {
266 (**self).poll(cx)
267 }
268
269 #[inline]
270 async fn shutdown(&self) {
271 (**self).shutdown().await
272 }
273
274 #[inline]
275 async fn call(
276 &self,
277 request: Req,
278 ctx: ServiceCtx<'_, Self>,
279 ) -> Result<Self::Response, Self::Error> {
280 ctx.call_nowait(&**self, request).await
281 }
282}
283
284impl<S, Req> Service<Req> for Box<S>
285where
286 S: Service<Req>,
287{
288 type Response = S::Response;
289 type Error = S::Error;
290
291 #[inline]
292 async fn ready(&self, ctx: ServiceCtx<'_, Self>) -> Result<(), S::Error> {
293 ctx.ready(&**self).await
294 }
295
296 #[inline]
297 async fn shutdown(&self) {
298 (**self).shutdown().await
299 }
300
301 #[inline]
302 async fn call(
303 &self,
304 request: Req,
305 ctx: ServiceCtx<'_, Self>,
306 ) -> Result<Self::Response, Self::Error> {
307 ctx.call_nowait(&**self, request).await
308 }
309
310 #[inline]
311 fn poll(&self, cx: &mut Context<'_>) -> Result<(), S::Error> {
312 (**self).poll(cx)
313 }
314}
315
316impl<S, Req, Cfg> ServiceFactory<Req, Cfg> for Rc<S>
317where
318 S: ServiceFactory<Req, Cfg>,
319{
320 type Response = S::Response;
321 type Error = S::Error;
322 type Service = S::Service;
323 type InitError = S::InitError;
324
325 async fn create(&self, cfg: Cfg) -> Result<Self::Service, Self::InitError> {
326 self.as_ref().create(cfg).await
327 }
328}
329
330/// Trait for types that can be converted to a `Service`
331pub trait IntoService<Svc, Req>
332where
333 Svc: Service<Req>,
334{
335 /// Convert to a `Service`
336 fn into_service(self) -> Svc;
337}
338
339/// Trait for types that can be converted to a `ServiceFactory`
340pub trait IntoServiceFactory<T, Req, Cfg = ()>
341where
342 T: ServiceFactory<Req, Cfg>,
343{
344 /// Convert `Self` to a `ServiceFactory`
345 fn into_factory(self) -> T;
346}
347
348impl<Svc, Req> IntoService<Svc, Req> for Svc
349where
350 Svc: Service<Req>,
351{
352 #[inline]
353 fn into_service(self) -> Svc {
354 self
355 }
356}
357
358impl<T, Req, Cfg> IntoServiceFactory<T, Req, Cfg> for T
359where
360 T: ServiceFactory<Req, Cfg>,
361{
362 #[inline]
363 fn into_factory(self) -> T {
364 self
365 }
366}
367
368pub mod dev {
369 pub use crate::and_then::{AndThen, AndThenFactory};
370 pub use crate::apply::{Apply, ApplyFactory};
371 pub use crate::chain::{ServiceChain, ServiceChainFactory};
372 pub use crate::fn_service::{
373 FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig,
374 };
375 pub use crate::fn_shutdown::FnShutdown;
376 pub use crate::map::{Map, MapFactory};
377 pub use crate::map_config::{MapConfig, UnitConfig};
378 pub use crate::map_err::{MapErr, MapErrFactory};
379 pub use crate::map_init_err::MapInitErr;
380 pub use crate::middleware::ApplyMiddleware;
381 pub use crate::then::{Then, ThenFactory};
382}