4
4
5
5
use async_trait::async_trait;
6
6
use 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} ;
9
9
use log::info;
10
10
use std::future::Future;
11
11
use std::marker::PhantomData;
@@ -17,26 +17,36 @@ use swagger::auth::MakeAllowAllAuthenticator;
17
17
use swagger::EmptyContext;
18
18
use tokio::net::TcpListener;
19
19
20
+ use crate::tokio_io::TokioIo;
21
+
20
22
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
21
23
use openssl::ssl::{ Ssl, SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod} ;
22
24
23
25
use { {{externCrateName} }}::models;
24
26
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
+
25
47
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
26
48
pub 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" );
40
50
41
51
if https {
42
52
#[cfg(any(target_os = " macos" , target_os = " windows" , target_os = " ios" ))]
@@ -61,14 +71,14 @@ pub async fn create(addr: &str, https: bool) {
61
71
if let Ok((tcp, _)) = tcp_listener.accept().await {
62
72
let ssl = Ssl::new(tls_acceptor.context()).unwrap();
63
73
let addr = tcp.peer_addr().expect(" Unable to get remote address" );
64
- let service = service .call(addr);
74
+ let service = create_service ! () .call(addr);
65
75
66
76
tokio::spawn(async move {
67
77
let tls = tokio_openssl::SslStream::new(ssl, tcp).map_err(|_| ())?;
68
78
let service = service.await.map_err(|_| ())?;
69
79
70
- Http ::new()
71
- .serve_connection(tls , service)
80
+ http1::Builder ::new()
81
+ .serve_connection(TokioIo::new(tcp_stream) , service)
72
82
.await
73
83
.map_err(|_| ())
74
84
} );
@@ -78,11 +88,63 @@ pub async fn create(addr: &str, https: bool) {
78
88
} else {
79
89
info! (" Starting a server (over http, so no TLS)" );
80
90
// 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
+ }
82
144
}
83
145
}
84
146
85
- #[derive(Copy, Clone )]
147
+ #[derive(Copy)]
86
148
pub struct Server<C > {
87
149
marker: PhantomData< C> ,
88
150
}
@@ -92,3 +154,11 @@ impl<C> Server<C> {
92
154
Server{marker: PhantomData}
93
155
}
94
156
}
157
+
158
+ impl<C > Clone for Server<C > {
159
+ fn clone(&self) -> Self {
160
+ Self {
161
+ marker: PhantomData,
162
+ }
163
+ }
164
+ }
0 commit comments