Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions pkgs/id/src/world_session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1584,8 +1584,9 @@ async fn run_world_session<I: SessionIo>(service: &WorldService, io: &mut I, fra
}
}

/// Serve query frames (`info`, `records`, `records_ticket`,
/// `download_module`) until the peer closes, denies, or sends a frame that
/// Serve query frames (`info`, `caps`, `execution_profile`, `metrics`,
/// `records`, `records_ticket`, `download_module`) until the peer closes,
/// denies, or sends a frame that
/// ends the session. A `join` frame switches to the joined session.
async fn query_session<I: SessionIo>(service: &WorldService, io: &mut I, first: ClientFrame) {
let mut next = Some(first);
Expand Down Expand Up @@ -1744,6 +1745,85 @@ async fn query_session<I: SessionIo>(service: &WorldService, io: &mut I, first:
return;
}
}
ClientFrame::Caps { capability } => {
// Read-only like `info`/`metrics` (#6802): the stats
// verb chains info -> caps -> profile -> metrics over
// one connection, so the query session serves caps
// with the first-frame authorization (a participant
// capability) instead of refusing the frame.
let token = JoinCapability::from_wire(capability);
if service.world().participant_id(token).await.is_err() {
let _ = send_error(service.frame_meter(), io, "join denied").await;
return;
}
io.mark_authorized();
match service.world().caps_report().await {
Ok(report) => {
if !send_json(
service.frame_meter(),
io,
&ServerFrame::Caps { report },
)
.await
{
return;
}
}
Err(_) => {
let _ = send_error(
service.frame_meter(),
io,
"capability report unavailable",
)
.await;
return;
}
}
}
ClientFrame::ExecutionProfileInfo {
admin_token,
capability,
} => {
// Same authorization as the first-frame handler: an
// admin token, or a participant capability (the stats
// verb sends the latter). (#6802)
let auth_ok = if let Some(admin) = admin_token.as_deref() {
service.authorize_admin(Some(admin)).is_ok()
} else if let Some(cap) = capability.as_deref() {
let token = JoinCapability::from_wire(cap.to_string());
service.world().participant_id(token).await.is_ok()
} else {
false
};
if !auth_ok {
let _ = send_error(service.frame_meter(), io, "access denied").await;
return;
}
match service.world().execution_profile().await {
Ok(profile) => {
if !send_json(
service.frame_meter(),
io,
&ServerFrame::ExecutionProfile {
update: crate::world::ExecutionProfileUpdate {
requested: profile,
applies_on_next_program_install_or_process_restart: false,
},
message: "current persisted execution profile",
},
)
.await
{
return;
}
}
Err(error) => {
let _ =
send_error(service.frame_meter(), io, &format!("{error:#}")).await;
return;
}
}
}
_ => {
let _ = send_error(service.frame_meter(), io, "expected a query frame").await;
return;
Expand Down
13 changes: 6 additions & 7 deletions pkgs/id/tests/cli_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1972,7 +1972,7 @@ mod serve_tests {

/// #5238 (9-8): `id world logs`, `caps --ceiling`, and `profile`
/// execute against a live world host — not just parse. (`stats`
/// has its own test below; it is ignored until #6802 is fixed.)
/// has its own test below.)
/// Each verb's output is asserted on the state the host reports,
/// and a change made through one verb is read back through the
/// same verb's read path, so a verb that silently no-ops cannot
Expand Down Expand Up @@ -2128,13 +2128,12 @@ mod serve_tests {

/// #5238 (9-8): `id world stats` executes against a live world host.
///
/// Ignored until #6802 is fixed: stats issues its `info`, `caps`,
/// and `profile` queries over one connection, but the host only
/// serves `caps` / `profile` as first frames — the second query
/// is refused with "expected a query frame", so the verb cannot
/// succeed against a live host today. Un-ignore in that fix.
/// Was ignored until #6802: stats issues its `info`, `caps`, and
/// `profile` queries over one connection, but the host only served
/// `caps` / `profile` as first frames — the second query was
/// refused with "expected a query frame". #6802 taught the query
/// session both read-only frames, so this runs in the suite now.
#[test]
#[ignore]
fn test_world_stats_executes_against_a_live_world() {
let server_dir = TempDir::new().unwrap();
let client_dir = TempDir::new().unwrap();
Expand Down
Loading