use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; use std::{net, rc}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::net::TcpStream; use actix_service::{ fn_factory, fn_service, pipeline_factory, IntoServiceFactory, Service, ServiceFactory, }; use bytes::Bytes; use futures::future::ok; use futures::ready; use h2::server::{self, Handshake}; use log::error; use crate::body::MessageBody; use crate::cloneable::CloneableService; use crate::config::ServiceConfig; use crate::error::{DispatchError, Error}; use crate::helpers::DataFactory; use crate::request::Request; use crate::response::Response; use super::dispatcher::Dispatcher; /// `ServiceFactory` implementation for HTTP2 transport pub struct H2Service { srv: S, cfg: ServiceConfig, on_connect: Option Box>>, _t: PhantomData<(T, B)>, } impl H2Service where S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { /// Create new `HttpService` instance with config. pub(crate) fn with_config>( cfg: ServiceConfig, service: F, ) -> Self { H2Service { cfg, on_connect: None, srv: service.into_factory(), _t: PhantomData, } } /// Set on connect callback. pub(crate) fn on_connect( mut self, f: Option Box>>, ) -> Self { self.on_connect = f; self } } impl H2Service where S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { /// Create simple tcp based service pub fn tcp( self, ) -> impl ServiceFactory< Config = (), Request = TcpStream, Response = (), Error = DispatchError, InitError = S::InitError, > { pipeline_factory(fn_factory(|| { async { Ok::<_, S::InitError>(fn_service(|io: TcpStream| { let peer_addr = io.peer_addr().ok(); ok::<_, DispatchError>((io, peer_addr)) })) } })) .and_then(self) } } #[cfg(feature = "openssl")] mod openssl { use actix_service::{fn_factory, fn_service}; use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream}; use actix_tls::{openssl::HandshakeError, SslError}; use super::*; impl H2Service, S, B> where S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { /// Create ssl based service pub fn openssl( self, acceptor: SslAcceptor, ) -> impl ServiceFactory< Config = (), Request = TcpStream, Response = (), Error = SslError, DispatchError>, InitError = S::InitError, > { pipeline_factory( Acceptor::new(acceptor) .map_err(SslError::Ssl) .map_init_err(|_| panic!()), ) .and_then(fn_factory(|| { ok::<_, S::InitError>(fn_service(|io: SslStream| { let peer_addr = io.get_ref().peer_addr().ok(); ok((io, peer_addr)) })) })) .and_then(self.map_err(SslError::Service)) } } } #[cfg(feature = "rustls")] mod rustls { use super::*; use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream}; use actix_tls::SslError; use std::{fmt, io}; impl H2Service, S, B> where S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { /// Create openssl based service pub fn rustls( self, mut config: ServerConfig, ) -> impl ServiceFactory< Config = (), Request = TcpStream, Response = (), Error = SslError, InitError = S::InitError, > { let protos = vec!["h2".to_string().into()]; config.set_protocols(&protos); pipeline_factory( Acceptor::new(config) .map_err(SslError::Ssl) .map_init_err(|_| panic!()), ) .and_then(fn_factory(|| { ok::<_, S::InitError>(fn_service(|io: TlsStream| { let peer_addr = io.get_ref().0.peer_addr().ok(); ok((io, peer_addr)) })) })) .and_then(self.map_err(SslError::Service)) } } } impl ServiceFactory for H2Service where T: AsyncRead + AsyncWrite + Unpin, S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { type Config = (); type Request = (T, Option); type Response = (); type Error = DispatchError; type InitError = S::InitError; type Service = H2ServiceHandler; type Future = H2ServiceResponse; fn new_service(&self, _: ()) -> Self::Future { H2ServiceResponse { fut: self.srv.new_service(()), cfg: Some(self.cfg.clone()), on_connect: self.on_connect.clone(), _t: PhantomData, } } } #[doc(hidden)] #[pin_project::pin_project] pub struct H2ServiceResponse { #[pin] fut: S::Future, cfg: Option, on_connect: Option Box>>, _t: PhantomData<(T, B)>, } impl Future for H2ServiceResponse where T: AsyncRead + AsyncWrite + Unpin, S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { type Output = Result, S::InitError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.as_mut().project(); Poll::Ready(ready!(this.fut.poll(cx)).map(|service| { let this = self.as_mut().project(); H2ServiceHandler::new( this.cfg.take().unwrap(), this.on_connect.clone(), service, ) })) } } /// `Service` implementation for http/2 transport pub struct H2ServiceHandler { srv: CloneableService, cfg: ServiceConfig, on_connect: Option Box>>, _t: PhantomData<(T, B)>, } 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: Option Box>>, srv: S, ) -> H2ServiceHandler { H2ServiceHandler { cfg, on_connect, srv: CloneableService::new(srv), _t: PhantomData, } } } impl Service for H2ServiceHandler where T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, S::Response: Into> + 'static, B: MessageBody + 'static, { type Request = (T, Option); type Response = (); type Error = DispatchError; type Future = H2ServiceHandlerResponse; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.srv.poll_ready(cx).map_err(|e| { let e = e.into(); error!("Service readiness error: {:?}", e); DispatchError::Service(e) }) } fn call(&mut self, (io, addr): Self::Request) -> Self::Future { let on_connect = if let Some(ref on_connect) = self.on_connect { Some(on_connect(&io)) } else { None }; H2ServiceHandlerResponse { state: State::Handshake( Some(self.srv.clone()), Some(self.cfg.clone()), addr, on_connect, server::handshake(io), ), } } } enum State, B: MessageBody> where T: AsyncRead + AsyncWrite + Unpin, S::Future: 'static, { Incoming(Dispatcher), Handshake( Option>, Option, Option, Option>, Handshake, ), } 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, { 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, ref mut handshake, ) => match Pin::new(handshake).poll(cx) { Poll::Ready(Ok(conn)) => { self.state = State::Incoming(Dispatcher::new( srv.take().unwrap(), conn, on_connect.take(), config.take().unwrap(), None, *peer_addr, )); self.poll(cx) } Poll::Ready(Err(err)) => { trace!("H2 handshake error: {}", err); Poll::Ready(Err(err.into())) } Poll::Pending => Poll::Pending, }, } } }