Skip to content

Commit c769a27

Browse files
traskCopilot
andcommitted
Preserve Tower server task errors during shutdown
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 8821091 commit c769a27

1 file changed

Lines changed: 116 additions & 3 deletions

File tree

  • scenarios/http/rust/tower/opentelemetry-instrumentation-tower/server/src

‎scenarios/http/rust/tower/opentelemetry-instrumentation-tower/server/src/main.rs‎

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,127 @@ async fn run() -> Result<(), BoxError> {
3434
});
3535

3636
let wait = tokio::task::spawn_blocking(otel_conformance_scenario::wait_for_eof).await?;
37-
shutdown_sender.send(()).map_err(|()| {
38-
io::Error::new(
37+
shutdown_server(shutdown_sender, server, wait).await
38+
}
39+
40+
async fn shutdown_server(
41+
shutdown_sender: oneshot::Sender<()>,
42+
server: tokio::task::JoinHandle<io::Result<()>>,
43+
wait: io::Result<()>,
44+
) -> Result<(), BoxError> {
45+
if shutdown_sender.send(()).is_err() {
46+
server.await??;
47+
return Err(io::Error::new(
3948
io::ErrorKind::BrokenPipe,
4049
"Tower server stopped before the shutdown signal",
4150
)
42-
})?;
51+
.into());
52+
}
4353
let server_result = server.await?;
4454
wait?;
4555
server_result?;
4656
Ok(())
4757
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use super::shutdown_server;
62+
use std::io;
63+
use tokio::sync::oneshot;
64+
use tokio::task::JoinError;
65+
66+
#[tokio::test]
67+
async fn closed_shutdown_channel_preserves_server_error() {
68+
let (sender, receiver) = oneshot::channel();
69+
drop(receiver);
70+
let server = tokio::spawn(async { Err(io::Error::other("server failed")) });
71+
72+
let error = shutdown_server(sender, server, Ok(())).await.unwrap_err();
73+
74+
assert_eq!(
75+
error.downcast_ref::<io::Error>().unwrap().kind(),
76+
io::ErrorKind::Other
77+
);
78+
assert_eq!(error.to_string(), "server failed");
79+
}
80+
81+
#[tokio::test]
82+
async fn closed_shutdown_channel_preserves_server_panic() {
83+
let (sender, receiver) = oneshot::channel();
84+
drop(receiver);
85+
let server = tokio::spawn(async { panic!("server task panicked") });
86+
87+
let error = shutdown_server(sender, server, Ok(())).await.unwrap_err();
88+
89+
assert!(error.downcast_ref::<JoinError>().unwrap().is_panic());
90+
}
91+
92+
#[tokio::test]
93+
async fn closed_shutdown_channel_preserves_server_cancellation() {
94+
let (sender, receiver) = oneshot::channel();
95+
drop(receiver);
96+
let server = tokio::spawn(std::future::pending());
97+
server.abort();
98+
99+
let error = shutdown_server(sender, server, Ok(())).await.unwrap_err();
100+
101+
assert!(error.downcast_ref::<JoinError>().unwrap().is_cancelled());
102+
}
103+
104+
#[tokio::test]
105+
async fn unexpected_successful_exit_reports_broken_pipe() {
106+
let (sender, receiver) = oneshot::channel();
107+
drop(receiver);
108+
let server = tokio::spawn(async { Ok(()) });
109+
110+
let error = shutdown_server(sender, server, Ok(())).await.unwrap_err();
111+
112+
assert_eq!(
113+
error.downcast_ref::<io::Error>().unwrap().kind(),
114+
io::ErrorKind::BrokenPipe
115+
);
116+
assert_eq!(
117+
error.to_string(),
118+
"Tower server stopped before the shutdown signal"
119+
);
120+
}
121+
122+
#[tokio::test]
123+
async fn graceful_shutdown_succeeds() {
124+
let (sender, receiver) = oneshot::channel();
125+
let server = tokio::spawn(async {
126+
receiver.await.expect("shutdown signal");
127+
Ok(())
128+
});
129+
130+
shutdown_server(sender, server, Ok(())).await.unwrap();
131+
}
132+
133+
#[tokio::test]
134+
async fn graceful_shutdown_preserves_server_error() {
135+
let (sender, receiver) = oneshot::channel();
136+
let server = tokio::spawn(async {
137+
receiver.await.expect("shutdown signal");
138+
Err(io::Error::other("server failed"))
139+
});
140+
141+
let error = shutdown_server(sender, server, Ok(())).await.unwrap_err();
142+
143+
assert_eq!(error.to_string(), "server failed");
144+
}
145+
146+
#[tokio::test]
147+
async fn graceful_shutdown_preserves_eof_error_priority() {
148+
let (sender, receiver) = oneshot::channel();
149+
let server = tokio::spawn(async {
150+
receiver.await.expect("shutdown signal");
151+
Err(io::Error::other("server failed"))
152+
});
153+
154+
let error = shutdown_server(sender, server, Err(io::Error::other("stdin failed")))
155+
.await
156+
.unwrap_err();
157+
158+
assert_eq!(error.to_string(), "stdin failed");
159+
}
160+
}

0 commit comments

Comments
 (0)