use log;
use std::net::TcpListener;
use std::sync::{Arc, Mutex};
use threadpool::ThreadPool;
use thrift::protocol::{
TInputProtocol, TInputProtocolFactory, TOutputProtocol, TOutputProtocolFactory,
};
use thrift::server::TProcessor;
use thrift::transport::{TIoChannel, TReadTransportFactory, TWriteTransportFactory};
use thrift::{ApplicationError, ApplicationErrorKind};
use rustls::ServerSession as RusTLSServerSession;
use rustls::StreamOwned as RusTLSStream;
use rustls::{RootCertStore, ServerConfig, ServerSession};
use super::{TLSStream, TLSTTcpChannel, X509Credentials};
type ConnectionHook = fn(TLSStream<ServerSession>);
pub struct TLSTServer<PRC, RTF, IPF, WTF, OPF>
where
PRC: TProcessor + Send + Sync + 'static,
RTF: TReadTransportFactory + 'static,
IPF: TInputProtocolFactory + 'static,
WTF: TWriteTransportFactory + 'static,
OPF: TOutputProtocolFactory + 'static,
{
r_trans_factory: RTF,
i_proto_factory: IPF,
w_trans_factory: WTF,
o_proto_factory: OPF,
processor: Arc<PRC>,
worker_pool: ThreadPool,
tls_config: Arc<ServerConfig>,
connection_hook: Option<ConnectionHook>,
}
impl<PRC, RTF, IPF, WTF, OPF> TLSTServer<PRC, RTF, IPF, WTF, OPF>
where
PRC: TProcessor + Send + Sync + 'static,
RTF: TReadTransportFactory + 'static,
IPF: TInputProtocolFactory + 'static,
WTF: TWriteTransportFactory + 'static,
OPF: TOutputProtocolFactory + 'static,
{
pub fn new(
read_transport_factory: RTF,
input_protocol_factory: IPF,
write_transport_factory: WTF,
output_protocol_factory: OPF,
processor: PRC,
num_workers: usize,
key_pair: X509Credentials,
root_cert_store: Option<RootCertStore>,
require_client_auth: bool,
connection_hook: Option<ConnectionHook>,
) -> TLSTServer<PRC, RTF, IPF, WTF, OPF> {
TLSTServer {
r_trans_factory: read_transport_factory,
i_proto_factory: input_protocol_factory,
w_trans_factory: write_transport_factory,
o_proto_factory: output_protocol_factory,
processor: Arc::new(processor),
worker_pool: ThreadPool::with_name("Thrift service processor".to_owned(), num_workers),
tls_config: super::make_tls_server_config(
key_pair,
root_cert_store,
require_client_auth,
),
connection_hook: connection_hook,
}
}
pub fn listen(&mut self, listen_address: &str) -> thrift::Result<()> {
let listener = TcpListener::bind(listen_address)?;
for stream in listener.incoming() {
match stream {
Ok(s) => {
let tls_session = RusTLSServerSession::new(&self.tls_config);
let so = RusTLSStream::new(tls_session, s);
let ts = Arc::new(Mutex::new(so));
let (i_prot, o_prot) = self.new_protocols_for_connection(ts.clone())?;
let processor = self.processor.clone();
let ch = self.connection_hook;
self.worker_pool.execute(move || {
if ch.is_some() {
ch.unwrap()(ts)
}
handle_incoming_connection(processor, i_prot, o_prot)
});
}
Err(e) => {
log::warn!("failed to accept remote connection with error {:?}", e);
}
}
}
Err(thrift::Error::Application(ApplicationError {
kind: ApplicationErrorKind::Unknown,
message: "aborted listen loop".into(),
}))
}
fn new_protocols_for_connection(
&mut self,
stream: TLSStream<RusTLSServerSession>,
) -> thrift::Result<(
Box<dyn TInputProtocol + Send>,
Box<dyn TOutputProtocol + Send>,
)> {
let channel = TLSTTcpChannel::with_stream(stream);
let (r_chan, w_chan) = channel.split()?;
let r_tran = self.r_trans_factory.create(Box::new(r_chan));
let i_prot = self.i_proto_factory.create(r_tran);
let w_tran = self.w_trans_factory.create(Box::new(w_chan));
let o_prot = self.o_proto_factory.create(w_tran);
Ok((i_prot, o_prot))
}
}
fn handle_incoming_connection<PRC>(
processor: Arc<PRC>,
i_prot: Box<dyn TInputProtocol>,
o_prot: Box<dyn TOutputProtocol>,
) where
PRC: TProcessor,
{
let mut i_prot = i_prot;
let mut o_prot = o_prot;
loop {
let r = processor.process(&mut *i_prot, &mut *o_prot);
if let Err(e) = r {
log::debug!("processor completed with error: {:?}", e);
break;
}
}
}