requiem_http/
builder.rs

1use std::marker::PhantomData;
2use std::rc::Rc;
3use std::{fmt, net};
4
5use requiem_codec::Framed;
6use requiem_service::{IntoServiceFactory, Service, ServiceFactory};
7
8use crate::body::MessageBody;
9use crate::config::{KeepAlive, ServiceConfig};
10use crate::error::Error;
11use crate::h1::{Codec, ExpectHandler, H1Service, UpgradeHandler};
12use crate::h2::H2Service;
13use crate::helpers::{Data, DataFactory};
14use crate::request::Request;
15use crate::response::Response;
16use crate::service::HttpService;
17
18/// A http service builder
19///
20/// This type can be used to construct an instance of `http service` through a
21/// builder-like pattern.
22pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler<T>> {
23    keep_alive: KeepAlive,
24    client_timeout: u64,
25    client_disconnect: u64,
26    secure: bool,
27    local_addr: Option<net::SocketAddr>,
28    expect: X,
29    upgrade: Option<U>,
30    on_connect: Option<Rc<dyn Fn(&T) -> Box<dyn DataFactory>>>,
31    _t: PhantomData<(T, S)>,
32}
33
34impl<T, S> HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler<T>>
35where
36    S: ServiceFactory<Config = (), Request = Request>,
37    S::Error: Into<Error> + 'static,
38    S::InitError: fmt::Debug,
39    <S::Service as Service>::Future: 'static,
40{
41    /// Create instance of `ServiceConfigBuilder`
42    pub fn new() -> Self {
43        HttpServiceBuilder {
44            keep_alive: KeepAlive::Timeout(5),
45            client_timeout: 5000,
46            client_disconnect: 0,
47            secure: false,
48            local_addr: None,
49            expect: ExpectHandler,
50            upgrade: None,
51            on_connect: None,
52            _t: PhantomData,
53        }
54    }
55}
56
57impl<T, S, X, U> HttpServiceBuilder<T, S, X, U>
58where
59    S: ServiceFactory<Config = (), Request = Request>,
60    S::Error: Into<Error> + 'static,
61    S::InitError: fmt::Debug,
62    <S::Service as Service>::Future: 'static,
63    X: ServiceFactory<Config = (), Request = Request, Response = Request>,
64    X::Error: Into<Error>,
65    X::InitError: fmt::Debug,
66    <X::Service as Service>::Future: 'static,
67    U: ServiceFactory<Config = (), Request = (Request, Framed<T, Codec>), Response = ()>,
68    U::Error: fmt::Display,
69    U::InitError: fmt::Debug,
70    <U::Service as Service>::Future: 'static,
71{
72    /// Set server keep-alive setting.
73    ///
74    /// By default keep alive is set to a 5 seconds.
75    pub fn keep_alive<W: Into<KeepAlive>>(mut self, val: W) -> Self {
76        self.keep_alive = val.into();
77        self
78    }
79
80    /// Set connection secure state
81    pub fn secure(mut self) -> Self {
82        self.secure = true;
83        self
84    }
85
86    /// Set the local address that this service is bound to.
87    pub fn local_addr(mut self, addr: net::SocketAddr) -> Self {
88        self.local_addr = Some(addr);
89        self
90    }
91
92    /// Set server client timeout in milliseconds for first request.
93    ///
94    /// Defines a timeout for reading client request header. If a client does not transmit
95    /// the entire set headers within this time, the request is terminated with
96    /// the 408 (Request Time-out) error.
97    ///
98    /// To disable timeout set value to 0.
99    ///
100    /// By default client timeout is set to 5000 milliseconds.
101    pub fn client_timeout(mut self, val: u64) -> Self {
102        self.client_timeout = val;
103        self
104    }
105
106    /// Set server connection disconnect timeout in milliseconds.
107    ///
108    /// Defines a timeout for disconnect connection. If a disconnect procedure does not complete
109    /// within this time, the request get dropped. This timeout affects secure connections.
110    ///
111    /// To disable timeout set value to 0.
112    ///
113    /// By default disconnect timeout is set to 0.
114    pub fn client_disconnect(mut self, val: u64) -> Self {
115        self.client_disconnect = val;
116        self
117    }
118
119    /// Provide service for `EXPECT: 100-Continue` support.
120    ///
121    /// Service get called with request that contains `EXPECT` header.
122    /// Service must return request in case of success, in that case
123    /// request will be forwarded to main service.
124    pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U>
125    where
126        F: IntoServiceFactory<X1>,
127        X1: ServiceFactory<Config = (), Request = Request, Response = Request>,
128        X1::Error: Into<Error>,
129        X1::InitError: fmt::Debug,
130        <X1::Service as Service>::Future: 'static,
131    {
132        HttpServiceBuilder {
133            keep_alive: self.keep_alive,
134            client_timeout: self.client_timeout,
135            client_disconnect: self.client_disconnect,
136            secure: self.secure,
137            local_addr: self.local_addr,
138            expect: expect.into_factory(),
139            upgrade: self.upgrade,
140            on_connect: self.on_connect,
141            _t: PhantomData,
142        }
143    }
144
145    /// Provide service for custom `Connection: UPGRADE` support.
146    ///
147    /// If service is provided then normal requests handling get halted
148    /// and this service get called with original request and framed object.
149    pub fn upgrade<F, U1>(self, upgrade: F) -> HttpServiceBuilder<T, S, X, U1>
150    where
151        F: IntoServiceFactory<U1>,
152        U1: ServiceFactory<
153            Config = (),
154            Request = (Request, Framed<T, Codec>),
155            Response = (),
156        >,
157        U1::Error: fmt::Display,
158        U1::InitError: fmt::Debug,
159        <U1::Service as Service>::Future: 'static,
160    {
161        HttpServiceBuilder {
162            keep_alive: self.keep_alive,
163            client_timeout: self.client_timeout,
164            client_disconnect: self.client_disconnect,
165            secure: self.secure,
166            local_addr: self.local_addr,
167            expect: self.expect,
168            upgrade: Some(upgrade.into_factory()),
169            on_connect: self.on_connect,
170            _t: PhantomData,
171        }
172    }
173
174    /// Set on-connect callback.
175    ///
176    /// It get called once per connection and result of the call
177    /// get stored to the request's extensions.
178    pub fn on_connect<F, I>(mut self, f: F) -> Self
179    where
180        F: Fn(&T) -> I + 'static,
181        I: Clone + 'static,
182    {
183        self.on_connect = Some(Rc::new(move |io| Box::new(Data(f(io)))));
184        self
185    }
186
187    /// Finish service configuration and create *http service* for HTTP/1 protocol.
188    pub fn h1<F, B>(self, service: F) -> H1Service<T, S, B, X, U>
189    where
190        B: MessageBody,
191        F: IntoServiceFactory<S>,
192        S::Error: Into<Error>,
193        S::InitError: fmt::Debug,
194        S::Response: Into<Response<B>>,
195    {
196        let cfg = ServiceConfig::new(
197            self.keep_alive,
198            self.client_timeout,
199            self.client_disconnect,
200            self.secure,
201            self.local_addr,
202        );
203        H1Service::with_config(cfg, service.into_factory())
204            .expect(self.expect)
205            .upgrade(self.upgrade)
206            .on_connect(self.on_connect)
207    }
208
209    /// Finish service configuration and create *http service* for HTTP/2 protocol.
210    pub fn h2<F, B>(self, service: F) -> H2Service<T, S, B>
211    where
212        B: MessageBody + 'static,
213        F: IntoServiceFactory<S>,
214        S::Error: Into<Error> + 'static,
215        S::InitError: fmt::Debug,
216        S::Response: Into<Response<B>> + 'static,
217        <S::Service as Service>::Future: 'static,
218    {
219        let cfg = ServiceConfig::new(
220            self.keep_alive,
221            self.client_timeout,
222            self.client_disconnect,
223            self.secure,
224            self.local_addr,
225        );
226        H2Service::with_config(cfg, service.into_factory()).on_connect(self.on_connect)
227    }
228
229    /// Finish service configuration and create `HttpService` instance.
230    pub fn finish<F, B>(self, service: F) -> HttpService<T, S, B, X, U>
231    where
232        B: MessageBody + 'static,
233        F: IntoServiceFactory<S>,
234        S::Error: Into<Error> + 'static,
235        S::InitError: fmt::Debug,
236        S::Response: Into<Response<B>> + 'static,
237        <S::Service as Service>::Future: 'static,
238    {
239        let cfg = ServiceConfig::new(
240            self.keep_alive,
241            self.client_timeout,
242            self.client_disconnect,
243            self.secure,
244            self.local_addr,
245        );
246        HttpService::with_config(cfg, service.into_factory())
247            .expect(self.expect)
248            .upgrade(self.upgrade)
249            .on_connect(self.on_connect)
250    }
251}