1use std::{
2 future::Future,
3 marker::PhantomData,
4 mem, net,
5 pin::Pin,
6 rc::Rc,
7 task::{Context, Poll},
8};
9
10use actix_codec::{AsyncRead, AsyncWrite};
11use actix_service::{
12 fn_factory, fn_service, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _,
13};
14#[expect(deprecated, reason = "preserve the public ready future type")]
15use actix_utils::future::ready;
16use futures_core::{future::LocalBoxFuture, ready};
17use tokio::net::TcpStream;
18use tracing::{error, trace};
19
20use super::{dispatcher::Dispatcher, handshake_with_timeout, HandshakeWithTimeout};
21use crate::{
22 body::{BoxBody, MessageBody},
23 config::ServiceConfig,
24 error::DispatchError,
25 service::HttpFlow,
26 ConnectCallback, OnConnectData, Request, Response,
27};
28
29#[inline]
30fn desired_nodelay(tcp_nodelay: Option<bool>) -> Option<bool> {
31 tcp_nodelay
32}
33
34#[inline]
35fn set_nodelay(stream: &TcpStream, nodelay: bool) {
36 let _ = stream.set_nodelay(nodelay);
37}
38
39pub struct H2Service<T, S, B> {
41 srv: S,
42 cfg: ServiceConfig,
43 on_connect_ext: Option<Rc<ConnectCallback<T>>>,
44 _phantom: PhantomData<(T, B)>,
45}
46
47impl<T, S, B> H2Service<T, S, B>
48where
49 S: ServiceFactory<Request, Config = ()>,
50 S::Error: Into<Response<BoxBody>> + 'static,
51 S::Response: Into<Response<B>> + 'static,
52 <S::Service as Service<Request>>::Future: 'static,
53
54 B: MessageBody + 'static,
55{
56 pub(crate) fn with_config<F: IntoServiceFactory<S, Request>>(
58 cfg: ServiceConfig,
59 service: F,
60 ) -> Self {
61 H2Service {
62 cfg,
63 on_connect_ext: None,
64 srv: service.into_factory(),
65 _phantom: PhantomData,
66 }
67 }
68
69 pub(crate) fn on_connect_ext(mut self, f: Option<Rc<ConnectCallback<T>>>) -> Self {
71 self.on_connect_ext = f;
72 self
73 }
74}
75
76impl<S, B> H2Service<TcpStream, S, B>
77where
78 S: ServiceFactory<Request, Config = ()>,
79 S::Future: 'static,
80 S::Error: Into<Response<BoxBody>> + 'static,
81 S::Response: Into<Response<B>> + 'static,
82 <S::Service as Service<Request>>::Future: 'static,
83
84 B: MessageBody + 'static,
85{
86 #[expect(deprecated, reason = "preserve the public ready future type")]
88 pub fn tcp(
89 self,
90 ) -> impl ServiceFactory<
91 TcpStream,
92 Config = (),
93 Response = (),
94 Error = DispatchError,
95 InitError = S::InitError,
96 > {
97 let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());
98
99 fn_factory(move || {
100 ready(Ok::<_, S::InitError>(fn_service(move |io: TcpStream| {
101 if let Some(nodelay) = tcp_nodelay {
102 set_nodelay(&io, nodelay);
103 }
104 let peer_addr = io.peer_addr().ok();
105 ready(Ok::<_, DispatchError>((io, peer_addr)))
106 })))
107 })
108 .and_then(self)
109 }
110}
111
112#[cfg(feature = "openssl")]
113mod openssl {
114 use actix_tls::accept::{
115 openssl::{
116 reexports::{Error as SslError, SslAcceptor},
117 Acceptor, TlsStream,
118 },
119 TlsError,
120 };
121
122 use super::*;
123
124 impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
125 where
126 S: ServiceFactory<Request, Config = ()>,
127 S::Future: 'static,
128 S::Error: Into<Response<BoxBody>> + 'static,
129 S::Response: Into<Response<B>> + 'static,
130 <S::Service as Service<Request>>::Future: 'static,
131
132 B: MessageBody + 'static,
133 {
134 pub fn openssl(
136 self,
137 acceptor: SslAcceptor,
138 ) -> impl ServiceFactory<
139 TcpStream,
140 Config = (),
141 Response = (),
142 Error = TlsError<SslError, DispatchError>,
143 InitError = S::InitError,
144 > {
145 let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());
146
147 Acceptor::new(acceptor)
148 .map_init_err(|_| {
149 unreachable!("TLS acceptor service factory does not error on init")
150 })
151 .map_err(TlsError::into_service_error)
152 .map(move |io: TlsStream<TcpStream>| {
153 if let Some(nodelay) = tcp_nodelay {
154 set_nodelay(io.get_ref(), nodelay);
155 }
156 let peer_addr = io.get_ref().peer_addr().ok();
157 (io, peer_addr)
158 })
159 .and_then(self.map_err(TlsError::Service))
160 }
161 }
162}
163
164#[cfg(feature = "rustls-0_20")]
165mod rustls_0_20 {
166 use std::io;
167
168 use actix_tls::accept::{
169 rustls::{reexports::ServerConfig, Acceptor, TlsStream},
170 TlsError,
171 };
172
173 use super::*;
174
175 impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
176 where
177 S: ServiceFactory<Request, Config = ()>,
178 S::Future: 'static,
179 S::Error: Into<Response<BoxBody>> + 'static,
180 S::Response: Into<Response<B>> + 'static,
181 <S::Service as Service<Request>>::Future: 'static,
182
183 B: MessageBody + 'static,
184 {
185 pub fn rustls(
187 self,
188 mut config: ServerConfig,
189 ) -> impl ServiceFactory<
190 TcpStream,
191 Config = (),
192 Response = (),
193 Error = TlsError<io::Error, DispatchError>,
194 InitError = S::InitError,
195 > {
196 let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());
197 let mut protos = vec![b"h2".to_vec()];
198 protos.extend_from_slice(&config.alpn_protocols);
199 config.alpn_protocols = protos;
200
201 Acceptor::new(config)
202 .map_init_err(|_| {
203 unreachable!("TLS acceptor service factory does not error on init")
204 })
205 .map_err(TlsError::into_service_error)
206 .map(move |io: TlsStream<TcpStream>| {
207 if let Some(nodelay) = tcp_nodelay {
208 set_nodelay(io.get_ref().0, nodelay);
209 }
210 let peer_addr = io.get_ref().0.peer_addr().ok();
211 (io, peer_addr)
212 })
213 .and_then(self.map_err(TlsError::Service))
214 }
215 }
216}
217
218#[cfg(feature = "rustls-0_21")]
219mod rustls_0_21 {
220 use std::io;
221
222 use actix_tls::accept::{
223 rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},
224 TlsError,
225 };
226
227 use super::*;
228
229 impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
230 where
231 S: ServiceFactory<Request, Config = ()>,
232 S::Future: 'static,
233 S::Error: Into<Response<BoxBody>> + 'static,
234 S::Response: Into<Response<B>> + 'static,
235 <S::Service as Service<Request>>::Future: 'static,
236
237 B: MessageBody + 'static,
238 {
239 pub fn rustls_021(
241 self,
242 mut config: ServerConfig,
243 ) -> impl ServiceFactory<
244 TcpStream,
245 Config = (),
246 Response = (),
247 Error = TlsError<io::Error, DispatchError>,
248 InitError = S::InitError,
249 > {
250 let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());
251 let mut protos = vec![b"h2".to_vec()];
252 protos.extend_from_slice(&config.alpn_protocols);
253 config.alpn_protocols = protos;
254
255 Acceptor::new(config)
256 .map_init_err(|_| {
257 unreachable!("TLS acceptor service factory does not error on init")
258 })
259 .map_err(TlsError::into_service_error)
260 .map(move |io: TlsStream<TcpStream>| {
261 if let Some(nodelay) = tcp_nodelay {
262 set_nodelay(io.get_ref().0, nodelay);
263 }
264 let peer_addr = io.get_ref().0.peer_addr().ok();
265 (io, peer_addr)
266 })
267 .and_then(self.map_err(TlsError::Service))
268 }
269 }
270}
271
272#[cfg(feature = "rustls-0_22")]
273mod rustls_0_22 {
274 use std::io;
275
276 use actix_tls::accept::{
277 rustls_0_22::{reexports::ServerConfig, Acceptor, TlsStream},
278 TlsError,
279 };
280
281 use super::*;
282
283 impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
284 where
285 S: ServiceFactory<Request, Config = ()>,
286 S::Future: 'static,
287 S::Error: Into<Response<BoxBody>> + 'static,
288 S::Response: Into<Response<B>> + 'static,
289 <S::Service as Service<Request>>::Future: 'static,
290
291 B: MessageBody + 'static,
292 {
293 pub fn rustls_0_22(
295 self,
296 mut config: ServerConfig,
297 ) -> impl ServiceFactory<
298 TcpStream,
299 Config = (),
300 Response = (),
301 Error = TlsError<io::Error, DispatchError>,
302 InitError = S::InitError,
303 > {
304 let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());
305 let mut protos = vec![b"h2".to_vec()];
306 protos.extend_from_slice(&config.alpn_protocols);
307 config.alpn_protocols = protos;
308
309 Acceptor::new(config)
310 .map_init_err(|_| {
311 unreachable!("TLS acceptor service factory does not error on init")
312 })
313 .map_err(TlsError::into_service_error)
314 .map(move |io: TlsStream<TcpStream>| {
315 if let Some(nodelay) = tcp_nodelay {
316 set_nodelay(io.get_ref().0, nodelay);
317 }
318 let peer_addr = io.get_ref().0.peer_addr().ok();
319 (io, peer_addr)
320 })
321 .and_then(self.map_err(TlsError::Service))
322 }
323 }
324}
325
326#[cfg(feature = "rustls-0_23")]
327mod rustls_0_23 {
328 use std::io;
329
330 use actix_tls::accept::{
331 rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
332 TlsError,
333 };
334
335 use super::*;
336
337 impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
338 where
339 S: ServiceFactory<Request, Config = ()>,
340 S::Future: 'static,
341 S::Error: Into<Response<BoxBody>> + 'static,
342 S::Response: Into<Response<B>> + 'static,
343 <S::Service as Service<Request>>::Future: 'static,
344
345 B: MessageBody + 'static,
346 {
347 pub fn rustls_0_23(
349 self,
350 mut config: ServerConfig,
351 ) -> impl ServiceFactory<
352 TcpStream,
353 Config = (),
354 Response = (),
355 Error = TlsError<io::Error, DispatchError>,
356 InitError = S::InitError,
357 > {
358 let tcp_nodelay = desired_nodelay(self.cfg.tcp_nodelay());
359 let mut protos = vec![b"h2".to_vec()];
360 protos.extend_from_slice(&config.alpn_protocols);
361 config.alpn_protocols = protos;
362
363 Acceptor::new(config)
364 .map_init_err(|_| {
365 unreachable!("TLS acceptor service factory does not error on init")
366 })
367 .map_err(TlsError::into_service_error)
368 .map(move |io: TlsStream<TcpStream>| {
369 if let Some(nodelay) = tcp_nodelay {
370 set_nodelay(io.get_ref().0, nodelay);
371 }
372 let peer_addr = io.get_ref().0.peer_addr().ok();
373 (io, peer_addr)
374 })
375 .and_then(self.map_err(TlsError::Service))
376 }
377 }
378}
379
380impl<T, S, B> ServiceFactory<(T, Option<net::SocketAddr>)> for H2Service<T, S, B>
381where
382 T: AsyncRead + AsyncWrite + Unpin + 'static,
383
384 S: ServiceFactory<Request, Config = ()>,
385 S::Future: 'static,
386 S::Error: Into<Response<BoxBody>> + 'static,
387 S::Response: Into<Response<B>> + 'static,
388 <S::Service as Service<Request>>::Future: 'static,
389
390 B: MessageBody + 'static,
391{
392 type Response = ();
393 type Error = DispatchError;
394 type Config = ();
395 type Service = H2ServiceHandler<T, S::Service, B>;
396 type InitError = S::InitError;
397 type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
398
399 fn new_service(&self, _: ()) -> Self::Future {
400 let service = self.srv.new_service(());
401 let cfg = self.cfg.clone();
402 let on_connect_ext = self.on_connect_ext.clone();
403
404 Box::pin(async move {
405 let service = service.await?;
406 Ok(H2ServiceHandler::new(cfg, on_connect_ext, service))
407 })
408 }
409}
410
411pub struct H2ServiceHandler<T, S, B>
413where
414 S: Service<Request>,
415{
416 flow: Rc<HttpFlow<S, (), ()>>,
417 cfg: ServiceConfig,
418 on_connect_ext: Option<Rc<ConnectCallback<T>>>,
419 _phantom: PhantomData<B>,
420}
421
422impl<T, S, B> H2ServiceHandler<T, S, B>
423where
424 S: Service<Request>,
425 S::Error: Into<Response<BoxBody>> + 'static,
426 S::Future: 'static,
427 S::Response: Into<Response<B>> + 'static,
428 B: MessageBody + 'static,
429{
430 fn new(
431 cfg: ServiceConfig,
432 on_connect_ext: Option<Rc<ConnectCallback<T>>>,
433 service: S,
434 ) -> H2ServiceHandler<T, S, B> {
435 H2ServiceHandler {
436 flow: HttpFlow::new(service, (), None),
437 cfg,
438 on_connect_ext,
439 _phantom: PhantomData,
440 }
441 }
442}
443
444impl<T, S, B> Service<(T, Option<net::SocketAddr>)> for H2ServiceHandler<T, S, B>
445where
446 T: AsyncRead + AsyncWrite + Unpin,
447 S: Service<Request>,
448 S::Error: Into<Response<BoxBody>> + 'static,
449 S::Future: 'static,
450 S::Response: Into<Response<B>> + 'static,
451 B: MessageBody + 'static,
452{
453 type Response = ();
454 type Error = DispatchError;
455 type Future = H2ServiceHandlerResponse<T, S, B>;
456
457 fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
458 self.flow.service.poll_ready(cx).map_err(|err| {
459 let err = err.into();
460 error!("Service readiness error: {:?}", err);
461 DispatchError::Service(err)
462 })
463 }
464
465 fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
466 let on_connect_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
467
468 H2ServiceHandlerResponse {
469 state: State::Handshake(
470 Some(Rc::clone(&self.flow)),
471 Some(self.cfg.clone()),
472 addr,
473 on_connect_data,
474 handshake_with_timeout(io, &self.cfg),
475 ),
476 }
477 }
478}
479
480enum State<T, S: Service<Request>, B: MessageBody>
481where
482 T: AsyncRead + AsyncWrite + Unpin,
483 S::Future: 'static,
484{
485 Handshake(
486 Option<Rc<HttpFlow<S, (), ()>>>,
487 Option<ServiceConfig>,
488 Option<net::SocketAddr>,
489 OnConnectData,
490 HandshakeWithTimeout<T>,
491 ),
492 Established(Dispatcher<T, S, B, (), ()>),
493}
494
495pub struct H2ServiceHandlerResponse<T, S, B>
496where
497 T: AsyncRead + AsyncWrite + Unpin,
498 S: Service<Request>,
499 S::Error: Into<Response<BoxBody>> + 'static,
500 S::Future: 'static,
501 S::Response: Into<Response<B>> + 'static,
502 B: MessageBody + 'static,
503{
504 state: State<T, S, B>,
505}
506
507impl<T, S, B> Future for H2ServiceHandlerResponse<T, S, B>
508where
509 T: AsyncRead + AsyncWrite + Unpin,
510 S: Service<Request>,
511 S::Error: Into<Response<BoxBody>> + 'static,
512 S::Future: 'static,
513 S::Response: Into<Response<B>> + 'static,
514 B: MessageBody,
515{
516 type Output = Result<(), DispatchError>;
517
518 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
519 match self.state {
520 State::Handshake(
521 ref mut srv,
522 ref mut config,
523 ref peer_addr,
524 ref mut conn_data,
525 ref mut handshake,
526 ) => match ready!(Pin::new(handshake).poll(cx)) {
527 Ok((conn, timer)) => {
528 let on_connect_data = mem::take(conn_data);
529
530 self.state = State::Established(Dispatcher::new(
531 conn,
532 srv.take().unwrap(),
533 config.take().unwrap(),
534 *peer_addr,
535 on_connect_data,
536 timer,
537 ));
538
539 self.poll(cx)
540 }
541
542 Err(err) => {
543 trace!("H2 handshake error: {}", err);
544 Poll::Ready(Err(err))
545 }
546 },
547
548 State::Established(ref mut disp) => Pin::new(disp).poll(cx),
549 }
550 }
551}