Skip to main content

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    config::{
9        DEFAULT_H1_WRITE_BUFFER_SIZE, DEFAULT_H2_CONN_WINDOW_SIZE, DEFAULT_H2_STREAM_WINDOW_SIZE,
10    },
11    h1::{self, ExpectHandler, H1Service, UpgradeHandler},
12    service::HttpService,
13    ConnectCallback, Extensions, KeepAlive, Request, Response, ServiceConfigBuilder,
14};
15
16/// An HTTP service builder.
17///
18/// This type can construct an instance of [`HttpService`] through a builder-like pattern.
19pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler> {
20    keep_alive: KeepAlive,
21    client_request_timeout: Duration,
22    client_disconnect_timeout: Duration,
23    tcp_nodelay: Option<bool>,
24    secure: bool,
25    local_addr: Option<net::SocketAddr>,
26    h1_allow_half_closed: bool,
27    h1_write_buffer_size: usize,
28    h2_conn_window_size: u32,
29    h2_stream_window_size: u32,
30    expect: X,
31    upgrade: Option<U>,
32    on_connect_ext: Option<Rc<ConnectCallback<T>>>,
33    _phantom: PhantomData<S>,
34}
35
36impl<T, S> Default for HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler>
37where
38    S: ServiceFactory<Request, Config = ()>,
39    S::Error: Into<Response<BoxBody>> + 'static,
40    S::InitError: fmt::Debug,
41    <S::Service as Service<Request>>::Future: 'static,
42{
43    fn default() -> Self {
44        HttpServiceBuilder {
45            // ServiceConfig parts (make sure defaults match)
46            keep_alive: KeepAlive::default(),
47            client_request_timeout: Duration::from_secs(5),
48            client_disconnect_timeout: Duration::ZERO,
49            tcp_nodelay: None,
50            secure: false,
51            local_addr: None,
52            h1_allow_half_closed: true,
53            h1_write_buffer_size: DEFAULT_H1_WRITE_BUFFER_SIZE,
54            h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE,
55            h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE,
56
57            // dispatcher parts
58            expect: ExpectHandler,
59            upgrade: None,
60            on_connect_ext: None,
61            _phantom: PhantomData,
62        }
63    }
64}
65
66impl<T, S, X, U> HttpServiceBuilder<T, S, X, U>
67where
68    S: ServiceFactory<Request, Config = ()>,
69    S::Error: Into<Response<BoxBody>> + 'static,
70    S::InitError: fmt::Debug,
71    <S::Service as Service<Request>>::Future: 'static,
72    X: ServiceFactory<Request, Config = (), Response = Request>,
73    X::Error: Into<Response<BoxBody>>,
74    X::InitError: fmt::Debug,
75    U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
76    U::Error: fmt::Display,
77    U::InitError: fmt::Debug,
78{
79    /// Set connection keep-alive setting.
80    ///
81    /// Applies to HTTP/1.1 keep-alive and HTTP/2 ping-pong.
82    ///
83    /// By default keep-alive is 5 seconds.
84    pub fn keep_alive<W: Into<KeepAlive>>(mut self, val: W) -> Self {
85        self.keep_alive = val.into();
86        self
87    }
88
89    /// Set connection secure state
90    pub fn secure(mut self) -> Self {
91        self.secure = true;
92        self
93    }
94
95    /// Set the local address that this service is bound to.
96    pub fn local_addr(mut self, addr: net::SocketAddr) -> Self {
97        self.local_addr = Some(addr);
98        self
99    }
100
101    /// Set client request timeout (for first request).
102    ///
103    /// Defines a timeout for reading client request header. If the client does not transmit the
104    /// request head within this duration, the connection is terminated with a `408 Request Timeout`
105    /// response error.
106    ///
107    /// A duration of zero disables the timeout.
108    ///
109    /// By default, the client timeout is 5 seconds.
110    pub fn client_request_timeout(mut self, dur: Duration) -> Self {
111        self.client_request_timeout = dur;
112        self
113    }
114
115    #[doc(hidden)]
116    #[deprecated(since = "3.0.0", note = "Renamed to `client_request_timeout`.")]
117    pub fn client_timeout(self, dur: Duration) -> Self {
118        self.client_request_timeout(dur)
119    }
120
121    /// Set client connection disconnect timeout.
122    ///
123    /// Defines a timeout for disconnect connection. If a disconnect procedure does not complete
124    /// within this time, the request get dropped. This timeout affects secure connections.
125    ///
126    /// A duration of zero disables the timeout.
127    ///
128    /// By default, the disconnect timeout is disabled.
129    pub fn client_disconnect_timeout(mut self, dur: Duration) -> Self {
130        self.client_disconnect_timeout = dur;
131        self
132    }
133
134    /// Sets `TCP_NODELAY` value on accepted TCP connections.
135    pub fn tcp_nodelay(mut self, nodelay: bool) -> Self {
136        self.tcp_nodelay = Some(nodelay);
137        self
138    }
139
140    #[doc(hidden)]
141    #[deprecated(since = "3.0.0", note = "Renamed to `client_disconnect_timeout`.")]
142    pub fn client_disconnect(self, dur: Duration) -> Self {
143        self.client_disconnect_timeout(dur)
144    }
145
146    /// Sets whether HTTP/1 connections should support half-closures.
147    ///
148    /// Clients can choose to shutdown their writer-side of the connection after completing their
149    /// request and while waiting for the server response. Setting this to `false` will cause the
150    /// server to abort the connection handling as soon as it detects an EOF from the client.
151    ///
152    /// The default behavior is to allow, i.e. `true`
153    pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {
154        self.h1_allow_half_closed = allow;
155        self
156    }
157
158    /// Sets the maximum response write buffer size for HTTP/1 connections.
159    ///
160    /// Once the response buffer reaches this size, the dispatcher flushes it to the I/O stream.
161    ///
162    /// The default value is 32 KiB.
163    ///
164    /// # Panics
165    ///
166    /// Panics if `size` is 0.
167    pub fn h1_write_buffer_size(mut self, size: usize) -> Self {
168        assert!(
169            size > 0,
170            "HTTP/1 write buffer size must be greater than zero"
171        );
172
173        self.h1_write_buffer_size = size;
174        self
175    }
176
177    /// Sets initial stream-level flow control window size for HTTP/2 connections.
178    ///
179    /// See [`ServiceConfigBuilder::h2_initial_window_size`] for more details.
180    pub fn h2_initial_window_size(mut self, size: u32) -> Self {
181        self.h2_stream_window_size = size;
182        self
183    }
184
185    /// Sets initial connection-level flow control window size for HTTP/2 connections.
186    ///
187    /// See [`ServiceConfigBuilder::h2_initial_connection_window_size`] for more details.
188    pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self {
189        self.h2_conn_window_size = size;
190        self
191    }
192
193    /// Provide service for `EXPECT: 100-Continue` support.
194    ///
195    /// Service get called with request that contains `EXPECT` header.
196    /// Service must return request in case of success, in that case
197    /// request will be forwarded to main service.
198    pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U>
199    where
200        F: IntoServiceFactory<X1, Request>,
201        X1: ServiceFactory<Request, Config = (), Response = Request>,
202        X1::Error: Into<Response<BoxBody>>,
203        X1::InitError: fmt::Debug,
204    {
205        HttpServiceBuilder {
206            keep_alive: self.keep_alive,
207            client_request_timeout: self.client_request_timeout,
208            client_disconnect_timeout: self.client_disconnect_timeout,
209            tcp_nodelay: self.tcp_nodelay,
210            secure: self.secure,
211            local_addr: self.local_addr,
212            h1_allow_half_closed: self.h1_allow_half_closed,
213            h1_write_buffer_size: self.h1_write_buffer_size,
214            h2_conn_window_size: self.h2_conn_window_size,
215            h2_stream_window_size: self.h2_stream_window_size,
216            expect: expect.into_factory(),
217            upgrade: self.upgrade,
218            on_connect_ext: self.on_connect_ext,
219            _phantom: PhantomData,
220        }
221    }
222
223    /// Provide service for custom `Connection: UPGRADE` support.
224    ///
225    /// If service is provided then normal requests handling get halted
226    /// and this service get called with original request and framed object.
227    pub fn upgrade<F, U1>(self, upgrade: F) -> HttpServiceBuilder<T, S, X, U1>
228    where
229        F: IntoServiceFactory<U1, (Request, Framed<T, h1::Codec>)>,
230        U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
231        U1::Error: fmt::Display,
232        U1::InitError: fmt::Debug,
233    {
234        HttpServiceBuilder {
235            keep_alive: self.keep_alive,
236            client_request_timeout: self.client_request_timeout,
237            client_disconnect_timeout: self.client_disconnect_timeout,
238            tcp_nodelay: self.tcp_nodelay,
239            secure: self.secure,
240            local_addr: self.local_addr,
241            h1_allow_half_closed: self.h1_allow_half_closed,
242            h1_write_buffer_size: self.h1_write_buffer_size,
243            h2_conn_window_size: self.h2_conn_window_size,
244            h2_stream_window_size: self.h2_stream_window_size,
245            expect: self.expect,
246            upgrade: Some(upgrade.into_factory()),
247            on_connect_ext: self.on_connect_ext,
248            _phantom: PhantomData,
249        }
250    }
251
252    /// Sets the callback to be run on connection establishment.
253    ///
254    /// Has mutable access to a data container that will be merged into request extensions.
255    /// This enables transport layer data (like client certificates) to be accessed in middleware
256    /// and handlers.
257    pub fn on_connect_ext<F>(mut self, f: F) -> Self
258    where
259        F: Fn(&T, &mut Extensions) + 'static,
260    {
261        self.on_connect_ext = Some(Rc::new(f));
262        self
263    }
264
265    /// Finish service configuration and create a service for the HTTP/1 protocol.
266    pub fn h1<F, B>(self, service: F) -> H1Service<T, S, B, X, U>
267    where
268        B: MessageBody,
269        F: IntoServiceFactory<S, Request>,
270        S::Error: Into<Response<BoxBody>>,
271        S::InitError: fmt::Debug,
272        S::Response: Into<Response<B>>,
273    {
274        let cfg = ServiceConfigBuilder::new()
275            .keep_alive(self.keep_alive)
276            .client_request_timeout(self.client_request_timeout)
277            .client_disconnect_timeout(self.client_disconnect_timeout)
278            .tcp_nodelay(self.tcp_nodelay)
279            .secure(self.secure)
280            .local_addr(self.local_addr)
281            .h1_allow_half_closed(self.h1_allow_half_closed)
282            .h1_write_buffer_size(self.h1_write_buffer_size)
283            .h2_initial_window_size(self.h2_stream_window_size)
284            .h2_initial_connection_window_size(self.h2_conn_window_size)
285            .build();
286
287        H1Service::with_config(cfg, service.into_factory())
288            .expect(self.expect)
289            .upgrade(self.upgrade)
290            .on_connect_ext(self.on_connect_ext)
291    }
292
293    /// Finish service configuration and create a service for the HTTP/2 protocol.
294    #[cfg(feature = "http2")]
295    pub fn h2<F, B>(self, service: F) -> crate::h2::H2Service<T, S, B>
296    where
297        F: IntoServiceFactory<S, Request>,
298        S::Error: Into<Response<BoxBody>> + 'static,
299        S::InitError: fmt::Debug,
300        S::Response: Into<Response<B>> + 'static,
301
302        B: MessageBody + 'static,
303    {
304        let cfg = ServiceConfigBuilder::new()
305            .keep_alive(self.keep_alive)
306            .client_request_timeout(self.client_request_timeout)
307            .client_disconnect_timeout(self.client_disconnect_timeout)
308            .tcp_nodelay(self.tcp_nodelay)
309            .secure(self.secure)
310            .local_addr(self.local_addr)
311            .h1_allow_half_closed(self.h1_allow_half_closed)
312            .h1_write_buffer_size(self.h1_write_buffer_size)
313            .h2_initial_window_size(self.h2_stream_window_size)
314            .h2_initial_connection_window_size(self.h2_conn_window_size)
315            .build();
316
317        crate::h2::H2Service::with_config(cfg, service.into_factory())
318            .on_connect_ext(self.on_connect_ext)
319    }
320
321    /// Finish service configuration and create `HttpService` instance.
322    pub fn finish<F, B>(self, service: F) -> HttpService<T, S, B, X, U>
323    where
324        F: IntoServiceFactory<S, Request>,
325        S::Error: Into<Response<BoxBody>> + 'static,
326        S::InitError: fmt::Debug,
327        S::Response: Into<Response<B>> + 'static,
328
329        B: MessageBody + 'static,
330    {
331        let cfg = ServiceConfigBuilder::new()
332            .keep_alive(self.keep_alive)
333            .client_request_timeout(self.client_request_timeout)
334            .client_disconnect_timeout(self.client_disconnect_timeout)
335            .tcp_nodelay(self.tcp_nodelay)
336            .secure(self.secure)
337            .local_addr(self.local_addr)
338            .h1_allow_half_closed(self.h1_allow_half_closed)
339            .h1_write_buffer_size(self.h1_write_buffer_size)
340            .h2_initial_window_size(self.h2_stream_window_size)
341            .h2_initial_connection_window_size(self.h2_conn_window_size)
342            .build();
343
344        HttpService::with_config(cfg, service.into_factory())
345            .expect(self.expect)
346            .upgrade(self.upgrade)
347            .on_connect_ext(self.on_connect_ext)
348    }
349}