Skip to content

Commit

Permalink
[propolis] address cancel-safety issues with InstanceSerialConsoleHel…
Browse files Browse the repository at this point in the history
…per::recv (oxidecomputer#435)

See oxidecomputer#434 and inline comments.
  • Loading branch information
sunshowers authored Jun 16, 2023
1 parent f85bc04 commit 85d901d
Show file tree
Hide file tree
Showing 4 changed files with 435 additions and 96 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 37 additions & 39 deletions bin/propolis-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use propolis_client::handmade::{
},
Client,
};
use propolis_client::support::InstanceSerialConsoleHelper;
use propolis_client::support::{InstanceSerialConsoleHelper, WSClientOffset};
use slog::{o, Drain, Level, Logger};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_tungstenite::tungstenite::{
Expand Down Expand Up @@ -385,38 +385,43 @@ async fn serial(
}
msg = ws_console.recv() => {
match msg {
Some(Ok(Message::Binary(input))) => {
stdout.write_all(&input).await?;
stdout.flush().await?;
}
Some(Ok(Message::Close(Some(CloseFrame {code, reason})))) => {
eprint!("\r\nConnection closed: {:?}\r\n", code);
match code {
CloseCode::Abnormal
| CloseCode::Error
| CloseCode::Extension
| CloseCode::Invalid
| CloseCode::Policy
| CloseCode::Protocol
| CloseCode::Size
| CloseCode::Unsupported => {
anyhow::bail!("{}", reason);
Some(Ok(msg)) => {
match msg.process().await {
Ok(Message::Binary(input)) => {
stdout.write_all(&input).await?;
stdout.flush().await?;
}
Ok(Message::Close(Some(CloseFrame {code, reason}))) => {
eprint!("\r\nConnection closed: {:?}\r\n", code);
match code {
CloseCode::Abnormal
| CloseCode::Error
| CloseCode::Extension
| CloseCode::Invalid
| CloseCode::Policy
| CloseCode::Protocol
| CloseCode::Size
| CloseCode::Unsupported => {
anyhow::bail!("{}", reason);
}
_ => break,
}
}
_ => break,
Ok(Message::Close(None)) => {
eprint!("\r\nConnection closed.\r\n");
break;
}
// note: migration events via Message::Text are
// already handled within ws_console.recv(), but
// would still be available to match here if we want
// to indicate that it happened to the user
_ => continue,
}
}
Some(Ok(Message::Close(None))) => {
eprint!("\r\nConnection closed.\r\n");
break;
}
None => {
eprint!("\r\nConnection lost.\r\n");
break;
}
// note: migration events via Message::Text are already
// handled within ws_console.recv(), but would still be
// available to match here if we want to indicate that it
// happened to the user
_ => continue,
}
}
Expand All @@ -431,20 +436,13 @@ async fn serial_connect(
byte_offset: Option<i64>,
log: Logger,
) -> anyhow::Result<InstanceSerialConsoleHelper> {
let client = propolis_client::Client::new(&format!("http://{}", addr));
let mut req = client.instance_serial();
let offset = match byte_offset {
Some(x) if x >= 0 => WSClientOffset::FromStart(x as u64),
Some(x) => WSClientOffset::MostRecent(-x as u64),
None => WSClientOffset::MostRecent(16384),
};

match byte_offset {
Some(x) if x >= 0 => req = req.from_start(x as u64),
Some(x) => req = req.most_recent(-x as u64),
None => req = req.most_recent(16384),
}
let upgraded = req
.send()
.await
.map_err(|e| anyhow!("Failed to upgrade connection: {}", e))?
.into_inner();
Ok(InstanceSerialConsoleHelper::new(upgraded, Some(log)).await)
Ok(InstanceSerialConsoleHelper::new(addr, offset, Some(log)).await?)
}

async fn migrate_instance(
Expand Down
4 changes: 4 additions & 0 deletions lib/propolis-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"

[dependencies]
propolis_types.workspace = true
async-trait.workspace = true
reqwest = { workspace = true, features = ["json", "rustls-tls"] }
base64.workspace = true
futures = { workspace = true, optional = true }
Expand All @@ -23,6 +24,9 @@ tokio = { workspace = true, features = [ "net" ], optional = true }
tokio-tungstenite = { workspace = true, optional = true }
crucible-client-types.workspace = true

[dev-dependencies]
tokio = { workspace = true, features = ["test-util"] }

[features]
default = []
generated = ["progenitor", "tokio", "tokio-tungstenite", "futures"]
Expand Down
Loading

0 comments on commit 85d901d

Please sign in to comment.