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,36 @@ 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+ {
37+ let server = Server::new();
38+ let service = MakeService::new(server);
39+ let service = MakeAllowAllAuthenticator::new(service, " cosmo" );
40+ {{{externCrateName} }}::server::context::MakeAddContext::<_, EmptyContext>::new(
41+ service
42+ )
43+ }
44+ };
45+ }
46+
2547/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
2648pub 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- );
49+ let addr: SocketAddr = addr.parse().expect(" Failed to parse bind address" );
4050
4151 if https {
4252 #[cfg(any(target_os = " macos" , target_os = " windows" , target_os = " ios" ))]
@@ -61,14 +71,14 @@ pub async fn create(addr: &str, https: bool) {
6171 if let Ok((tcp, _)) = tcp_listener.accept().await {
6272 let ssl = Ssl::new(tls_acceptor.context()).unwrap();
6373 let addr = tcp.peer_addr().expect(" Unable to get remote address" );
64- let service = service .call(addr);
74+ let service = create_service ! () .call(addr);
6575
6676 tokio::spawn(async move {
6777 let tls = tokio_openssl::SslStream::new(ssl, tcp).map_err(|_| ())?;
6878 let service = service.await.map_err(|_| ())?;
6979
70- Http ::new()
71- .serve_connection(tls , service)
80+ http1::Builder ::new()
81+ .serve_connection(TokioIo::new(tcp_stream) , service)
7282 .await
7383 .map_err(|_| ())
7484 } );
@@ -78,11 +88,63 @@ pub async fn create(addr: &str, https: bool) {
7888 } else {
7989 info! (" Starting a server (over http, so no TLS)" );
8090 // Using HTTP
81- hyper::server::Server::bind(&addr).serve(service).await.unwrap()
91+ let listener = TcpListener::bind(&addr).await.unwrap();
92+ println! (" Listening on http://{}" , addr);
93+
94+ loop {
95+ // When an incoming TCP connection is received grab a TCP stream for
96+ // client< -> server communication.
97+ //
98+ // Note, this is a .await point, this loop will loop forever but is not a busy loop. The
99+ // .await point allows the Tokio runtime to pull the task off of the thread until the task
100+ // has work to do. In this case , a connection arrives on the port we are listening on and
101+ // the task is woken up, at which point the task is then put back on a thread, and is
102+ // driven forward by the runtime, eventually yielding a TCP stream.
103+ let (tcp_stream, _addr) = listener.accept().await.expect(" Failed to accept connection" );
104+
105+ let service = create_service! ();
106+ let my_service_fn = service_fn(move |req| {
107+ let add_context = service.call(());
108+
109+ async move {
110+ let add_context = add_context.await?;
111+ add_context.call(req).await
112+ }
113+ });
114+
115+ // Spin up a new task in Tokio so we can continue to listen for new TCP connection on the
116+ // current task without waiting for the processing of the HTTP1 connection we just received
117+ // to finish
118+ tokio::task::spawn(async move {
119+ // Handle the connection from the client using HTTP1 and pass any
120+ // HTTP requests received on that connection to the `hello` function
121+ let result = hyper::server::conn::http1::Builder::new()
122+ .serve_connection(TokioIo::new(tcp_stream), my_service_fn)
123+ // `always_send` is here, because we run into:
124+ //
125+ // ```md
126+ // implementation of `From` is not general enough
127+ //
128+ // `Box< (dyn StdError + std::marker::Send + Sync + ' static)>` must implement `From<Box<(dyn StdError + std::marker::Send + Sync + ' 0)>> `, for any lifetime `' 0`...
129+ // ...but it actually implements `From<Box<(dyn StdError + std::marker::Send + Sync + ' static)>> `
130+ // ```
131+ //
132+ // This is caused by this rust bug:
133+ //
134+ // < https://users.rust-lang.org/t/implementation-of-from-is-not -general-enough-with-hyper/105799>
135+ // < https://github.com/rust-lang/rust/issues/102211>
136+ .always_send()
137+ .await;
138+ if let Err(err) = result
139+ {
140+ println! (" Error serving connection: {:?}" , err);
141+ }
142+ });
143+ }
82144 }
83145}
84146
85- #[derive(Copy, Clone )]
147+ #[derive(Copy)]
86148pub struct Server<C > {
87149 marker: PhantomData< C> ,
88150}
@@ -92,3 +154,11 @@ impl<C> Server<C> {
92154 Server{marker: PhantomData}
93155 }
94156}
157+
158+ impl<C > Clone for Server<C > {
159+ fn clone(&self) -> Self {
160+ Self {
161+ marker: PhantomData,
162+ }
163+ }
164+ }
0 commit comments