44
55use async_trait::async_trait;
66use futures::{ future, Stream, StreamExt, TryFutureExt, TryStreamExt} ;
7- use hyper::server::conn::Http ;
8- use hyper::service::Service;
7+ use hyper::server::conn::http1 ;
8+ use hyper::service::{ service_fn, Service} ;
99use log::info;
1010use std::future::Future;
1111use std::marker::PhantomData;
@@ -17,26 +17,34 @@ use swagger::auth::MakeAllowAllAuthenticator;
1717use swagger::EmptyContext;
1818use tokio::net::TcpListener;
1919
20+ use crate::tokio_io::TokioIo;
21+
2022#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
2123use openssl::ssl::{ Ssl, SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod} ;
2224
2325use { {{externCrateName} }}::models;
2426
27+ /// Needed because `hyper`'s `service_fn` is sent to a `tokio::task::spawn`,
28+ /// which requires the future to be `'static`.
29+ ///
30+ /// Because `MakeAllowAllAuthenticator` is not `Clone`, this is a shorthand way
31+ /// of creating the `service`.
32+ ///
33+ /// This is not a `fn` because the generics are extremely deeply nested.
34+ macro_rules! create_service {
35+ () => {{
36+ let server = Server::new();
37+ let service = MakeService::new(server);
38+ let service = MakeAllowAllAuthenticator::new(service, " cosmo" );
39+ wayfinder_workflow_web::server::context::MakeAddContext::< _, EmptyContext> ::new(
40+ service
41+ )
42+ } };
43+ }
44+
2545/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
2646pub async fn create(addr: &str, https: bool) {
27- let addr = addr.parse().expect(" Failed to parse bind address" );
28-
29- let server = Server::new();
30-
31- let service = MakeService::new(server);
32-
33- let service = MakeAllowAllAuthenticator::new(service, " cosmo" );
34-
35- #[allow(unused_mut)]
36- let mut service =
37- {{{externCrateName} }}::server::context::MakeAddContext::<_, EmptyContext>::new(
38- service
39- );
47+ let addr: SocketAddr = addr.parse().expect(" Failed to parse bind address" );
4048
4149 if https {
4250 #[cfg(any(target_os = " macos" , target_os = " windows" , target_os = " ios" ))]
@@ -61,14 +69,14 @@ pub async fn create(addr: &str, https: bool) {
6169 if let Ok((tcp, _)) = tcp_listener.accept().await {
6270 let ssl = Ssl::new(tls_acceptor.context()).unwrap();
6371 let addr = tcp.peer_addr().expect(" Unable to get remote address" );
64- let service = service .call(addr);
72+ let service = create_service ! () .call(addr);
6573
6674 tokio::spawn(async move {
6775 let tls = tokio_openssl::SslStream::new(ssl, tcp).map_err(|_| ())?;
6876 let service = service.await.map_err(|_| ())?;
6977
70- Http ::new()
71- .serve_connection(tls , service)
78+ http1::Builder ::new()
79+ .serve_connection(TokioIo::new(tcp_stream) , service)
7280 .await
7381 .map_err(|_| ())
7482 } );
@@ -78,11 +86,63 @@ pub async fn create(addr: &str, https: bool) {
7886 } else {
7987 info! (" Starting a server (over http, so no TLS)" );
8088 // Using HTTP
81- hyper::server::Server::bind(&addr).serve(service).await.unwrap()
89+ let listener = TcpListener::bind(&addr).await.unwrap();
90+ println! (" Listening on http://{}" , addr);
91+
92+ loop {
93+ // When an incoming TCP connection is received grab a TCP stream for
94+ // client< -> server communication.
95+ //
96+ // Note, this is a .await point, this loop will loop forever but is not a busy loop. The
97+ // .await point allows the Tokio runtime to pull the task off of the thread until the task
98+ // has work to do. In this case , a connection arrives on the port we are listening on and
99+ // the task is woken up, at which point the task is then put back on a thread, and is
100+ // driven forward by the runtime, eventually yielding a TCP stream.
101+ let (tcp_stream, _addr) = listener.accept().await.expect(" Failed to accept connection" );
102+
103+ let service = create_service! ();
104+ let my_service_fn = service_fn(move |req| {
105+ let add_context = service.call(());
106+
107+ async move {
108+ let add_context = add_context.await?;
109+ add_context.call(req).await
110+ }
111+ });
112+
113+ // Spin up a new task in Tokio so we can continue to listen for new TCP connection on the
114+ // current task without waiting for the processing of the HTTP1 connection we just received
115+ // to finish
116+ tokio::task::spawn(async move {
117+ // Handle the connection from the client using HTTP1 and pass any
118+ // HTTP requests received on that connection to the `hello` function
119+ let result = hyper::server::conn::http1::Builder::new()
120+ .serve_connection(TokioIo::new(tcp_stream), my_service_fn)
121+ // `always_send` is here, because we run into:
122+ //
123+ // ```md
124+ // implementation of `From` is not general enough
125+ //
126+ // `Box< (dyn StdError + std::marker::Send + Sync + ' static)>` must implement `From<Box<(dyn StdError + std::marker::Send + Sync + ' 0)>> `, for any lifetime `' 0`...
127+ // ...but it actually implements `From<Box<(dyn StdError + std::marker::Send + Sync + ' static)>> `
128+ // ```
129+ //
130+ // This is caused by this rust bug:
131+ //
132+ // < https://users.rust-lang.org/t/implementation-of-from-is-not -general-enough-with-hyper/105799>
133+ // < https://github.com/rust-lang/rust/issues/102211>
134+ .always_send()
135+ .await;
136+ if let Err(err) = result
137+ {
138+ println! (" Error serving connection: {:?}" , err);
139+ }
140+ });
141+ }
82142 }
83143}
84144
85- #[derive(Copy, Clone )]
145+ #[derive(Copy)]
86146pub struct Server<C > {
87147 marker: PhantomData< C> ,
88148}
@@ -92,3 +152,11 @@ impl<C> Server<C> {
92152 Server{marker: PhantomData}
93153 }
94154}
155+
156+ impl<C > Clone for Server<C > {
157+ fn clone(&self) -> Self {
158+ Self {
159+ marker: PhantomData,
160+ }
161+ }
162+ }
0 commit comments