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