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: None,
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    pub fn tcp_nodelay(mut self, nodelay: bool) -> Self {
139        self.tcp_nodelay = Some(nodelay);
140        self
141    }
142
143    #[doc(hidden)]
144    #[deprecated(since = "3.0.0", note = "Renamed to `client_disconnect_timeout`.")]
145    pub fn client_disconnect(self, dur: Duration) -> Self {
146        self.client_disconnect_timeout(dur)
147    }
148
149    /// Sets whether HTTP/1 connections should support half-closures.
150    ///
151    /// Clients can choose to shutdown their writer-side of the connection after completing their
152    /// request and while waiting for the server response. Setting this to `false` will cause the
153    /// server to abort the connection handling as soon as it detects an EOF from the client.
154    ///
155    /// The default behavior is to allow, i.e. `true`
156    pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {
157        self.h1_allow_half_closed = allow;
158        self
159    }
160
161    /// Sets the maximum response write buffer size for HTTP/1 connections.
162    ///
163    /// Once the response buffer reaches this size, the dispatcher flushes it to the I/O stream.
164    ///
165    /// The default value is 32 KiB.
166    ///
167    /// # Panics
168    ///
169    /// Panics if `size` is 0.
170    pub fn h1_write_buffer_size(mut self, size: usize) -> Self {
171        assert!(
172            size > 0,
173            "HTTP/1 write buffer size must be greater than zero"
174        );
175
176        self.h1_write_buffer_size = size;
177        self
178    }
179
180    /// Sets a factory for graceful shutdown notifications.
181    #[doc(hidden)]
182    pub fn graceful_shutdown_signal<F, Fut>(mut self, signal: F) -> Self
183    where
184        F: Fn() -> Fut + 'static,
185        Fut: Future<Output = ()> + 'static,
186    {
187        self.graceful_shutdown_signal = Some(GracefulShutdownSignal::new(signal));
188        self
189    }
190
191    /// Sets initial stream-level flow control window size for HTTP/2 connections.
192    ///
193    /// See [`ServiceConfigBuilder::h2_initial_window_size`] for more details.
194    pub fn h2_initial_window_size(mut self, size: u32) -> Self {
195        self.h2_stream_window_size = size;
196        self
197    }
198
199    /// Sets initial connection-level flow control window size for HTTP/2 connections.
200    ///
201    /// See [`ServiceConfigBuilder::h2_initial_connection_window_size`] for more details.
202    pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self {
203        self.h2_conn_window_size = size;
204        self
205    }
206
207    /// Provide service for `EXPECT: 100-Continue` support.
208    ///
209    /// Service get called with request that contains `EXPECT` header.
210    /// Service must return request in case of success, in that case
211    /// request will be forwarded to main service.
212    pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U>
213    where
214        F: IntoServiceFactory<X1, Request>,
215        X1: ServiceFactory<Request, Config = (), Response = Request>,
216        X1::Error: Into<Response<BoxBody>>,
217        X1::InitError: fmt::Debug,
218    {
219        HttpServiceBuilder {
220            keep_alive: self.keep_alive,
221            client_request_timeout: self.client_request_timeout,
222            client_disconnect_timeout: self.client_disconnect_timeout,
223            tcp_nodelay: self.tcp_nodelay,
224            secure: self.secure,
225            local_addr: self.local_addr,
226            h1_allow_half_closed: self.h1_allow_half_closed,
227            h1_write_buffer_size: self.h1_write_buffer_size,
228            h2_conn_window_size: self.h2_conn_window_size,
229            h2_stream_window_size: self.h2_stream_window_size,
230            graceful_shutdown_signal: self.graceful_shutdown_signal,
231            expect: expect.into_factory(),
232            upgrade: self.upgrade,
233            on_connect_ext: self.on_connect_ext,
234            _phantom: PhantomData,
235        }
236    }
237
238    /// Provide service for custom `Connection: UPGRADE` support.
239    ///
240    /// If service is provided then normal requests handling get halted
241    /// and this service get called with original request and framed object.
242    pub fn upgrade<F, U1>(self, upgrade: F) -> HttpServiceBuilder<T, S, X, U1>
243    where
244        F: IntoServiceFactory<U1, (Request, Framed<T, h1::Codec>)>,
245        U1: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
246        U1::Error: fmt::Display,
247        U1::InitError: fmt::Debug,
248    {
249        HttpServiceBuilder {
250            keep_alive: self.keep_alive,
251            client_request_timeout: self.client_request_timeout,
252            client_disconnect_timeout: self.client_disconnect_timeout,
253            tcp_nodelay: self.tcp_nodelay,
254            secure: self.secure,
255            local_addr: self.local_addr,
256            h1_allow_half_closed: self.h1_allow_half_closed,
257            h1_write_buffer_size: self.h1_write_buffer_size,
258            h2_conn_window_size: self.h2_conn_window_size,
259            h2_stream_window_size: self.h2_stream_window_size,
260            graceful_shutdown_signal: self.graceful_shutdown_signal,
261            expect: self.expect,
262            upgrade: Some(upgrade.into_factory()),
263            on_connect_ext: self.on_connect_ext,
264            _phantom: PhantomData,
265        }
266    }
267
268    /// Sets the callback to be run on connection establishment.
269    ///
270    /// Has mutable access to a data container that will be merged into request extensions.
271    /// This enables transport layer data (like client certificates) to be accessed in middleware
272    /// and handlers.
273    pub fn on_connect_ext<F>(mut self, f: F) -> Self
274    where
275        F: Fn(&T, &mut Extensions) + 'static,
276    {
277        self.on_connect_ext = Some(Rc::new(f));
278        self
279    }
280
281    /// Finish service configuration and create a service for the HTTP/1 protocol.
282    pub fn h1<F, B>(self, service: F) -> H1Service<T, S, B, X, U>
283    where
284        B: MessageBody,
285        F: IntoServiceFactory<S, Request>,
286        S::Error: Into<Response<BoxBody>>,
287        S::InitError: fmt::Debug,
288        S::Response: Into<Response<B>>,
289    {
290        let cfg = ServiceConfigBuilder::new()
291            .keep_alive(self.keep_alive)
292            .client_request_timeout(self.client_request_timeout)
293            .client_disconnect_timeout(self.client_disconnect_timeout)
294            .tcp_nodelay(self.tcp_nodelay)
295            .secure(self.secure)
296            .local_addr(self.local_addr)
297            .h1_allow_half_closed(self.h1_allow_half_closed)
298            .h1_write_buffer_size(self.h1_write_buffer_size)
299            .h2_initial_window_size(self.h2_stream_window_size)
300            .h2_initial_connection_window_size(self.h2_conn_window_size)
301            .graceful_shutdown_signal(self.graceful_shutdown_signal)
302            .build();
303
304        H1Service::with_config(cfg, service.into_factory())
305            .expect(self.expect)
306            .upgrade(self.upgrade)
307            .on_connect_ext(self.on_connect_ext)
308    }
309
310    /// Finish service configuration and create a service for the HTTP/2 protocol.
311    #[cfg(feature = "http2")]
312    pub fn h2<F, B>(self, service: F) -> crate::h2::H2Service<T, S, B>
313    where
314        F: IntoServiceFactory<S, Request>,
315        S::Error: Into<Response<BoxBody>> + 'static,
316        S::InitError: fmt::Debug,
317        S::Response: Into<Response<B>> + 'static,
318
319        B: MessageBody + 'static,
320    {
321        let cfg = ServiceConfigBuilder::new()
322            .keep_alive(self.keep_alive)
323            .client_request_timeout(self.client_request_timeout)
324            .client_disconnect_timeout(self.client_disconnect_timeout)
325            .tcp_nodelay(self.tcp_nodelay)
326            .secure(self.secure)
327            .local_addr(self.local_addr)
328            .h1_allow_half_closed(self.h1_allow_half_closed)
329            .h1_write_buffer_size(self.h1_write_buffer_size)
330            .h2_initial_window_size(self.h2_stream_window_size)
331            .h2_initial_connection_window_size(self.h2_conn_window_size)
332            .graceful_shutdown_signal(self.graceful_shutdown_signal)
333            .build();
334
335        crate::h2::H2Service::with_config(cfg, service.into_factory())
336            .on_connect_ext(self.on_connect_ext)
337    }
338
339    /// Finish service configuration and create `HttpService` instance.
340    pub fn finish<F, B>(self, service: F) -> HttpService<T, S, B, X, U>
341    where
342        F: IntoServiceFactory<S, Request>,
343        S::Error: Into<Response<BoxBody>> + 'static,
344        S::InitError: fmt::Debug,
345        S::Response: Into<Response<B>> + 'static,
346
347        B: MessageBody + 'static,
348    {
349        let cfg = ServiceConfigBuilder::new()
350            .keep_alive(self.keep_alive)
351            .client_request_timeout(self.client_request_timeout)
352            .client_disconnect_timeout(self.client_disconnect_timeout)
353            .tcp_nodelay(self.tcp_nodelay)
354            .secure(self.secure)
355            .local_addr(self.local_addr)
356            .h1_allow_half_closed(self.h1_allow_half_closed)
357            .h1_write_buffer_size(self.h1_write_buffer_size)
358            .h2_initial_window_size(self.h2_stream_window_size)
359            .h2_initial_connection_window_size(self.h2_conn_window_size)
360            .graceful_shutdown_signal(self.graceful_shutdown_signal)
361            .build();
362
363        HttpService::with_config(cfg, service.into_factory())
364            .expect(self.expect)
365            .upgrade(self.upgrade)
366            .on_connect_ext(self.on_connect_ext)
367    }
368}