actix_http/
builder.rs

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