Skip to content

Commit 3ff3efb

Browse files
committed
Merge remote-tracking branch 'grin/staging' into grim
2 parents 7ae52bc + 0a36e55 commit 3ff3efb

13 files changed

Lines changed: 215 additions & 139 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/src/rest.rs

Lines changed: 87 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -143,23 +143,37 @@ impl ApiServer {
143143
conf: Option<TLSConfig>,
144144
api_chan: (mpsc::Sender<()>, mpsc::Receiver<()>),
145145
) -> Result<thread::JoinHandle<()>, Error> {
146-
let _ = rustls::crypto::ring::default_provider().install_default();
147-
148146
if self.shutdown_sender.is_some() {
149147
return Err(Error::Internal(
150148
"Can't start API server, it's running already".to_string(),
151149
));
152150
}
153-
self.shutdown_sender = Some(api_chan.0);
151+
152+
let _ = rustls::crypto::ring::default_provider().install_default();
153+
154+
// Check if provided address is free.
155+
let listener = match std::net::TcpListener::bind(addr) {
156+
Ok(l) => {
157+
l.set_nonblocking(true)
158+
.map_err(|e| Error::Internal(format!("API listener binding error: {}", e)))?;
159+
l
160+
}
161+
Err(e) => {
162+
error!("API listener binding error: {}", e);
163+
return Err(Error::Internal(e.to_string()));
164+
}
165+
};
154166

155167
let tls = match conf {
156168
Some(conf) => Some(TlsAcceptor::from(conf.build_server_config()?)),
157169
None => None,
158170
};
159-
thread::Builder::new()
171+
let res = thread::Builder::new()
160172
.name("apis".to_string())
161-
.spawn(move || start_server(addr, router, api_chan.1, tls))
162-
.map_err(|_| Error::Internal("failed to spawn API thread".to_string()))
173+
.spawn(move || start_server(listener, router, api_chan.1, tls))
174+
.map_err(|_| Error::Internal("failed to spawn API thread".to_string()))?;
175+
self.shutdown_sender = Some(api_chan.0);
176+
Ok(res)
163177
}
164178

165179
/// Stops the API server.
@@ -184,7 +198,7 @@ impl ApiServer {
184198

185199
/// Start API server with optional TLS support.
186200
fn start_server(
187-
addr: SocketAddr,
201+
l: std::net::TcpListener,
188202
router: Router,
189203
rx: mpsc::Receiver<()>,
190204
tls: Option<TlsAcceptor>,
@@ -194,77 +208,77 @@ fn start_server(
194208
// When this signal completes, start shutdown.
195209
let mut signal = std::pin::pin!(shutdown_signal(rx));
196210

197-
// Start server loop.
198-
match TcpListener::bind(addr).await {
199-
Ok(l) => {
200-
loop {
201-
tokio::select! {
202-
Ok(s) = async {
203-
match l.accept().await {
204-
Ok((s, _)) => Ok::<Option<tokio::net::TcpStream>, Error>(Some(s)),
205-
Err(e) => {
206-
error!("Failed to accept connection: {e:#}");
207-
Ok(None)
208-
}
209-
}
210-
} => {
211-
if let Some(s) = s {
212-
if let Some(tls) = tls.clone() {
213-
let router = router.clone();
214-
let watcher = graceful.watcher();
215-
tokio::spawn(async move {
216-
let handshake = timeout(TLS_HANDSHAKE_TIMEOUT, tls.accept(s));
217-
let tls_stream = match handshake.await {
218-
Ok(Ok(tls_stream)) => tls_stream,
219-
Ok(Err(err)) => {
220-
error!("failed to perform TLS handshake: {err:#}");
221-
return;
222-
}
223-
Err(_) => {
224-
error!("TLS handshake timed out");
225-
return;
226-
}
227-
};
228-
let io = TokioIo::new(tls_stream);
229-
let conn = http1::Builder::new().serve_connection(io, router);
230-
if let Err(e) = watcher.watch(conn).await {
231-
error!("API TLS server error: {:?}", e);
232-
}
233-
});
234-
} else {
235-
let io = TokioIo::new(s);
236-
let conn = http1::Builder::new().serve_connection(io, router.clone());
237-
let fut = graceful.watch(conn);
238-
tokio::spawn(async move {
239-
if let Err(e) = fut.await {
240-
error!("API HTTP server error: {:?}", e);
241-
}
242-
});
243-
};
244-
} else {
245-
continue;
246-
}
247-
}
248-
_ = &mut signal => {
249-
drop(l);
250-
break;
211+
let l = match TcpListener::from_std(l) {
212+
Ok(l) => l,
213+
Err(e) => {
214+
error!("HTTP API server error: {}", e);
215+
return;
216+
}
217+
};
218+
219+
loop {
220+
tokio::select! {
221+
Ok(s) = async {
222+
match l.accept().await {
223+
Ok((s, _)) => Ok::<Option<tokio::net::TcpStream>, Error>(Some(s)),
224+
Err(e) => {
225+
error!("Failed to accept connection: {e:#}");
226+
Ok(None)
251227
}
252228
}
253-
}
254-
255-
// Now start the shutdown and wait for them to complete
256-
// Also start a timeout to limit how long to wait.
257-
tokio::select! {
258-
_ = graceful.shutdown() => {
259-
warn!("API server gracefully stopped");
260-
},
261-
_ = sleep(GRACEFUL_SHUTDOWN_TIMEOUT) => {
262-
warn!("API server timed out wait for all connections to close");
229+
} => {
230+
if let Some(s) = s {
231+
if let Some(tls) = tls.clone() {
232+
let router = router.clone();
233+
let watcher = graceful.watcher();
234+
tokio::spawn(async move {
235+
let handshake = timeout(TLS_HANDSHAKE_TIMEOUT, tls.accept(s));
236+
let tls_stream = match handshake.await {
237+
Ok(Ok(tls_stream)) => tls_stream,
238+
Ok(Err(err)) => {
239+
error!("failed to perform TLS handshake: {err:#}");
240+
return;
241+
}
242+
Err(_) => {
243+
error!("TLS handshake timed out");
244+
return;
245+
}
246+
};
247+
let io = TokioIo::new(tls_stream);
248+
let conn = http1::Builder::new().serve_connection(io, router);
249+
if let Err(e) = watcher.watch(conn).await {
250+
error!("API TLS server error: {:?}", e);
251+
}
252+
});
253+
} else {
254+
let io = TokioIo::new(s);
255+
let conn = http1::Builder::new().serve_connection(io, router.clone());
256+
let fut = graceful.watch(conn);
257+
tokio::spawn(async move {
258+
if let Err(e) = fut.await {
259+
error!("API HTTP server error: {:?}", e);
260+
}
261+
});
262+
};
263+
} else {
264+
continue;
263265
}
264266
}
267+
_ = &mut signal => {
268+
drop(l);
269+
break;
270+
}
265271
}
266-
Err(e) => {
267-
error!("API listener binding error: {}", e);
272+
}
273+
274+
// Now start the shutdown and wait for them to complete
275+
// Also start a timeout to limit how long to wait.
276+
tokio::select! {
277+
_ = graceful.shutdown() => {
278+
warn!("API server gracefully stopped");
279+
},
280+
_ = sleep(GRACEFUL_SHUTDOWN_TIMEOUT) => {
281+
warn!("API server timed out wait for all connections to close");
268282
}
269283
}
270284
};

api/tests/rest.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,24 @@ fn test_start_api() {
107107
thread::sleep(time::Duration::from_millis(1_000));
108108
}
109109

110+
#[test]
111+
fn test_start_api_address_in_use() {
112+
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
113+
let addr = listener.local_addr().unwrap();
114+
let mut server = ApiServer::new();
115+
116+
assert!(server
117+
.start(addr, build_router(), None, mpsc::channel::<()>(1))
118+
.is_err());
119+
120+
drop(listener);
121+
let handle = server
122+
.start(addr, build_router(), None, mpsc::channel::<()>(1))
123+
.unwrap();
124+
assert!(server.stop());
125+
handle.join().unwrap();
126+
}
127+
110128
// To enable this test you need a trusted PKCS12 (p12) certificate bundle
111129
// Hyper-tls client doesn't accept self-signed certificates. The easiest way is to use mkcert
112130
// https://github.com/FiloSottile/mkcert to install CA and generate a certificate on your local machine.

p2p/src/peer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,8 +434,8 @@ impl Peer {
434434
}
435435

436436
/// Stops the peer
437-
pub fn stop(&self) {
438-
debug!("Stopping peer {:?}", self.info.addr);
437+
pub fn stop(&self, reason: &str) {
438+
debug!("Stopping peer {:?}, reason: {}", self.info.addr, reason);
439439
match self.stop_handle.try_lock() {
440440
Some(handle) => handle.stop(),
441441
None => error!("can't get stop lock for peer"),

0 commit comments

Comments
 (0)