Skip to main content

actix_http/
builder.rs

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