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
16pub 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 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 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 pub fn keep_alive<W: Into<KeepAlive>>(mut self, val: W) -> Self {
85 self.keep_alive = val.into();
86 self
87 }
88
89 pub fn secure(mut self) -> Self {
91 self.secure = true;
92 self
93 }
94
95 pub fn local_addr(mut self, addr: net::SocketAddr) -> Self {
97 self.local_addr = Some(addr);
98 self
99 }
100
101 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 pub fn client_disconnect_timeout(mut self, dur: Duration) -> Self {
130 self.client_disconnect_timeout = dur;
131 self
132 }
133
134 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 pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {
154 self.h1_allow_half_closed = allow;
155 self
156 }
157
158 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 pub fn h2_initial_window_size(mut self, size: u32) -> Self {
181 self.h2_stream_window_size = size;
182 self
183 }
184
185 pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self {
189 self.h2_conn_window_size = size;
190 self
191 }
192
193 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 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 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 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 #[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 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}