use std::{ error::Error as StdError, future::Future, marker::PhantomData, net, pin::Pin, rc::Rc, task::{Context, Poll}, }; use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::net::TcpStream; use actix_service::{ fn_factory, fn_service, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _, }; use actix_utils::future::ready; use bytes::Bytes; use futures_core::{future::LocalBoxFuture, ready}; use h2::server::{handshake as h2_handshake, Handshake as H2Handshake}; use log::error; use crate::{ body::{AnyBody, MessageBody}, config::ServiceConfig, error::DispatchError, service::HttpFlow, ConnectCallback, OnConnectData, Request, Response, }; use super::dispatcher::Dispatcher; /// `ServiceFactory` implementation for HTTP/2 transport pub struct H2Service { srv: S, cfg: ServiceConfig, on_connect_ext: Option>>, _phantom: PhantomData<(T, B)>, } impl H2Service where S: ServiceFactory, S::Error: Into> + 'static, S::Response: Into> + 'static, >::Future: 'static, B: MessageBody + 'static, B::Error: Into>, { /// Create new `H2Service` instance with config. pub(crate) fn with_config>( cfg: ServiceConfig, service: F, ) -> Self { H2Service { cfg, on_connect_ext: None, srv: service.into_factory(), _phantom: PhantomData, } } /// Set on connect callback. pub(crate) fn on_connect_ext(mut self, f: Option>>) -> Self { self.on_connect_ext = f; self } } impl H2Service where S: ServiceFactory, S::Future: 'static, S::Error: Into> + 'static, S::Response: Into> + 'static, >::Future: 'static, B: MessageBody + 'static, B::Error: Into>, { /// Create plain TCP based service pub fn tcp( self, ) -> impl ServiceFactory< TcpStream, Config = (), Response = (), Error = DispatchError, InitError = S::InitError, > { fn_factory(|| { ready(Ok::<_, S::InitError>(fn_service(|io: TcpStream| { let peer_addr = io.peer_addr().ok(); ready(Ok::<_, DispatchError>((io, peer_addr))) }))) }) .and_then(self) } } #[cfg(feature = "openssl")] mod openssl { use actix_service::{fn_factory, fn_service, ServiceFactoryExt}; use actix_tls::accept::openssl::{Acceptor, SslAcceptor, SslError, TlsStream}; use actix_tls::accept::TlsError; use super::*; impl H2Service, S, B> where S: ServiceFactory, S::Future: 'static, S::Error: Into> + 'static, S::Response: Into> + 'static, >::Future: 'static, B: MessageBody + 'static, B::Error: Into>, { /// Create OpenSSL based service pub fn openssl( self, acceptor: SslAcceptor, ) -> impl ServiceFactory< TcpStream, Config = (), Response = (), Error = TlsError, InitError = S::InitError, > { Acceptor::new(acceptor) .map_err(TlsError::Tls) .map_init_err(|_| panic!()) .and_then(fn_factory(|| { ready(Ok::<_, S::InitError>(fn_service( |io: TlsStream| { let peer_addr = io.get_ref().peer_addr().ok(); ready(Ok((io, peer_addr))) }, ))) })) .and_then(self.map_err(TlsError::Service)) } } } #[cfg(feature = "rustls")] mod rustls { use super::*; use actix_service::ServiceFactoryExt; use actix_tls::accept::rustls::{Acceptor, ServerConfig, TlsStream}; use actix_tls::accept::TlsError; use std::io; impl H2Service, S, B> where S: ServiceFactory, S::Future: 'static, S::Error: Into> + 'static, S::Response: Into> + 'static, >::Future: 'static, B: MessageBody + 'static, B::Error: Into>, { /// Create Rustls based service pub fn rustls( self, mut config: ServerConfig, ) -> impl ServiceFactory< TcpStream, Config = (), Response = (), Error = TlsError, InitError = S::InitError, > { let mut protos = vec![b"h2".to_vec()]; protos.extend_from_slice(&config.alpn_protocols); config.set_protocols(&protos); Acceptor::new(config) .map_err(TlsError::Tls) .map_init_err(|_| panic!()) .and_then(fn_factory(|| { ready(Ok::<_, S::InitError>(fn_service( |io: TlsStream| { let peer_addr = io.get_ref().0.peer_addr().ok(); ready(Ok((io, peer_addr))) }, ))) })) .and_then(self.map_err(TlsError::Service)) } } } impl ServiceFactory<(T, Option)> for H2Service where T: AsyncRead + AsyncWrite + Unpin + 'static, S: ServiceFactory, S::Future: 'static, S::Error: Into> + 'static, S::Response: Into> + 'static, >::Future: 'static, B: MessageBody + 'static, B::Error: Into>, { type Response = (); type Error = DispatchError; type Config = (); type Service = H2ServiceHandler; type InitError = S::InitError; type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { let service = self.srv.new_service(()); let cfg = self.cfg.clone(); let on_connect_ext = self.on_connect_ext.clone(); Box::pin(async move { let service = service.await?; Ok(H2ServiceHandler::new(cfg, on_connect_ext, service)) }) } } /// `Service` implementation for HTTP/2 transport pub struct H2ServiceHandler where S: Service, { flow: Rc>, cfg: ServiceConfig, on_connect_ext: Option>>, _phantom: PhantomData, } impl H2ServiceHandler where S: Service, S::Error: Into> + 'static, S::Future: 'static, S::Response: Into> + 'static, B: MessageBody + 'static, { fn new( cfg: ServiceConfig, on_connect_ext: Option>>, service: S, ) -> H2ServiceHandler { H2ServiceHandler { flow: HttpFlow::new(service, (), None), cfg, on_connect_ext, _phantom: PhantomData, } } } impl Service<(T, Option)> for H2ServiceHandler where T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into> + 'static, S::Future: 'static, S::Response: Into> + 'static, B: MessageBody + 'static, B::Error: Into>, { type Response = (); type Error = DispatchError; type Future = H2ServiceHandlerResponse; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { self.flow.service.poll_ready(cx).map_err(|e| { let e = e.into(); error!("Service readiness error: {:?}", e); DispatchError::Service(e) }) } fn call(&self, (io, addr): (T, Option)) -> Self::Future { let on_connect_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref()); H2ServiceHandlerResponse { state: State::Handshake( Some(self.flow.clone()), Some(self.cfg.clone()), addr, on_connect_data, h2_handshake(io), ), } } } enum State, B: MessageBody> where T: AsyncRead + AsyncWrite + Unpin, S::Future: 'static, { Incoming(Dispatcher), Handshake( Option>>, Option, Option, OnConnectData, H2Handshake, ), } pub struct H2ServiceHandlerResponse where T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into> + 'static, S::Future: 'static, S::Response: Into> + 'static, B: MessageBody + 'static, { state: State, } impl Future for H2ServiceHandlerResponse where T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into> + 'static, S::Future: 'static, S::Response: Into> + 'static, B: MessageBody, B::Error: Into>, { type Output = Result<(), DispatchError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.state { State::Incoming(ref mut disp) => Pin::new(disp).poll(cx), State::Handshake( ref mut srv, ref mut config, ref peer_addr, ref mut on_connect_data, ref mut handshake, ) => match ready!(Pin::new(handshake).poll(cx)) { Ok(conn) => { let on_connect_data = std::mem::take(on_connect_data); self.state = State::Incoming(Dispatcher::new( srv.take().unwrap(), conn, on_connect_data, config.take().unwrap(), *peer_addr, )); self.poll(cx) } Err(err) => { trace!("H2 handshake error: {}", err); Poll::Ready(Err(err.into())) } }, } } }