refactor(gateway): delete dead accessors and FromRef impls (review 002 COV-13)

Coverage-confirmed dead code (every binary, zero hits):

- server/state.rs: drop FromRef<RouterState> impls for
  Arc<OperationRegistry> and Arc<dyn IdentityProvider> — no route
  extracts these types; the auth middleware receives the provider
  directly via from_fn_with_state
- gateway/dispatch.rs: drop identity_provider() and resolve_bearer()
  accessors; resolve_bearer's doc promised an auth hook the middleware
  never calls (spec/code drift). Wire-or-delete resolved to delete:
  bearer resolution lives in the middleware (SRV-11 single-resolve
  ordering), the dispatch spine only needs the per-call
  Option<Identity>. GatewayDispatch::new consequently takes the
  registry alone (GatewayState loses its unused identity_provider
  passthrough; dispatch.rs/to_mcp.rs tests simplified)
- websocket/upgrade.rs: drop FromRef<SessionState> for
  Arc<OperationRegistry> — no router carries SessionState as its state
  type; the inverse FromRef<Arc<OperationRegistry>> for SessionState
  (custom upgrade routes, integration tests) remains

Verification: ./scripts/verify.sh (352 passed), ./scripts/verify.sh
--all-features (466 passed), clippy -D warnings, fmt --check.
This commit is contained in:
2026-08-30 22:50:15 +00:00
parent 8261fefd8f
commit 08584b229d
6 changed files with 41 additions and 167 deletions
+20 -71
View File
@@ -502,7 +502,6 @@ pub fn to_mcp_service(dispatch: Arc<GatewayDispatch>) -> ToMcpService {
#[cfg(test)]
mod tests {
use super::*;
use alkcall::core::auth::{AuthToken, IdentityProvider};
use alkcall::core::types::Capabilities;
use alkcall::registry::context::{OperationContext, ScopedPeerEnv};
use alkcall::registry::discovery::{
@@ -516,41 +515,6 @@ mod tests {
use futures::StreamExt;
use rmcp::model::Extensions;
use std::collections::HashMap;
use std::sync::Mutex as StdMutex;
struct StaticIdentityProvider {
tokens: StdMutex<HashMap<String, Identity>>,
}
impl StaticIdentityProvider {
fn new() -> Self {
Self {
tokens: StdMutex::new(HashMap::new()),
}
}
fn with_token(self, token: &str, identity: Identity) -> Self {
self.tokens
.lock()
.unwrap_or_else(|e| e.into_inner())
.insert(token.to_string(), identity);
self
}
}
impl IdentityProvider for StaticIdentityProvider {
fn resolve_from_fingerprint(&self, _fp: &str) -> Option<Identity> {
None
}
fn resolve_from_token(&self, token: &AuthToken) -> Option<Identity> {
let token_str = String::from_utf8_lossy(&token.raw);
self.tokens
.lock()
.unwrap_or_else(|e| e.into_inner())
.get(token_str.as_ref())
.cloned()
}
}
fn identity_with_scopes(id: &str, scopes: &[&str]) -> Identity {
Identity {
@@ -675,15 +639,8 @@ mod tests {
Arc::new(dispatch_registry)
}
fn dispatch(
registry: Arc<OperationRegistry>,
provider: Arc<dyn alkcall::core::auth::IdentityProvider>,
) -> Arc<GatewayDispatch> {
Arc::new(GatewayDispatch::new(registry, provider))
}
fn provider() -> Arc<dyn alkcall::core::auth::IdentityProvider> {
Arc::new(StaticIdentityProvider::new())
fn dispatch(registry: Arc<OperationRegistry>) -> Arc<GatewayDispatch> {
Arc::new(GatewayDispatch::new(registry))
}
fn extensions_with_identity(identity: Option<Identity>) -> Extensions {
@@ -728,7 +685,7 @@ mod tests {
#[tokio::test]
async fn list_tools_returns_exactly_four_gateway_tools() {
let _gateway = ToMcpGateway::new(dispatch(full_registry_with_ops(vec![]), provider()));
let _gateway = ToMcpGateway::new(dispatch(full_registry_with_ops(vec![])));
let tools = gateway_tools();
let names: Vec<String> = tools.iter().map(|t| t.name.to_string()).collect();
assert_eq!(names.len(), 4);
@@ -745,7 +702,7 @@ mod tests {
OperationType::Query,
AccessControl::default(),
)]);
let _gateway = ToMcpGateway::new(dispatch(registry, provider()));
let _gateway = ToMcpGateway::new(dispatch(registry));
let tools = gateway_tools();
for tool in &tools {
assert_ne!(tool.name, "fs/readFile");
@@ -777,9 +734,7 @@ mod tests {
AccessControl::default(),
),
]);
let idp: Arc<dyn alkcall::core::auth::IdentityProvider> =
Arc::new(StaticIdentityProvider::new());
let gateway = ToMcpGateway::new(dispatch(registry, idp));
let gateway = ToMcpGateway::new(dispatch(registry));
let result = invoke_tool(
&gateway,
@@ -833,7 +788,7 @@ mod tests {
AccessControl::default(),
),
]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let result = invoke_tool(&gateway, "search", None, None).await;
assert_eq!(result.is_error, Some(false));
@@ -871,7 +826,7 @@ mod tests {
AccessControl::default(),
),
]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert("query".to_string(), Value::String("fs".to_string()));
@@ -900,7 +855,7 @@ mod tests {
OperationType::Query,
AccessControl::default(),
)]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert("name".to_string(), Value::String("fs/readFile".to_string()));
@@ -926,7 +881,7 @@ mod tests {
..Default::default()
},
)]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert("name".to_string(), Value::String("fs/readFile".to_string()));
@@ -956,7 +911,7 @@ mod tests {
..Default::default()
},
)]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert(
@@ -1027,7 +982,7 @@ mod tests {
Capabilities::new(),
))
.unwrap();
let gateway = ToMcpGateway::new(dispatch(Arc::new(dispatch_registry), provider()));
let gateway = ToMcpGateway::new(dispatch(Arc::new(dispatch_registry)));
let mut args = Map::new();
args.insert("name".to_string(), Value::String("secret/op".to_string()));
@@ -1066,7 +1021,7 @@ mod tests {
OperationType::Query,
AccessControl::default(),
)]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert(
@@ -1095,7 +1050,7 @@ mod tests {
OperationType::Query,
AccessControl::default(),
)]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert(
@@ -1116,7 +1071,7 @@ mod tests {
#[tokio::test]
async fn call_argument_errors_carry_retryable_and_truthy_messages() {
let registry = full_registry_with_ops(vec![]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let missing = invoke_tool(&gateway, "call", None, None).await;
assert_eq!(missing.is_error, Some(true));
@@ -1154,7 +1109,7 @@ mod tests {
#[tokio::test]
async fn call_returns_structured_error_for_call_error() {
let registry = full_registry_with_ops(vec![]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert(
@@ -1178,7 +1133,7 @@ mod tests {
OperationType::Query,
AccessControl::default(),
)]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert(
@@ -1235,9 +1190,7 @@ mod tests {
..Default::default()
},
)]);
let idp: Arc<dyn alkcall::core::auth::IdentityProvider> =
Arc::new(StaticIdentityProvider::new());
let gateway = ToMcpGateway::new(dispatch(registry, idp));
let gateway = ToMcpGateway::new(dispatch(registry));
let mut args = Map::new();
args.insert(
@@ -1256,7 +1209,7 @@ mod tests {
#[tokio::test]
async fn unknown_tool_name_returns_not_found_structured_error() {
let gateway = ToMcpGateway::new(dispatch(Arc::new(OperationRegistry::new()), provider()));
let gateway = ToMcpGateway::new(dispatch(Arc::new(OperationRegistry::new())));
let result = invoke_tool(&gateway, "bogus", None, None).await;
assert_eq!(result.is_error, Some(true));
let structured = result.structured_content.expect("structured error present");
@@ -1276,11 +1229,7 @@ mod tests {
..Default::default()
},
)]);
let idp: Arc<dyn alkcall::core::auth::IdentityProvider> = Arc::new(
StaticIdentityProvider::new()
.with_token("alk_admin", identity_with_scopes("admin-peer", &["admin"])),
);
let gateway = ToMcpGateway::new(dispatch(registry, idp));
let gateway = ToMcpGateway::new(dispatch(registry));
let admin_identity = identity_with_scopes("admin-peer", &["admin"]);
let extensions = extensions_with_identity(Some(admin_identity.clone()));
@@ -1354,7 +1303,7 @@ mod tests {
OperationType::Query,
AccessControl::default(),
)]);
let gateway = ToMcpGateway::new(dispatch(registry, provider()));
let gateway = ToMcpGateway::new(dispatch(registry));
let search_result = invoke_tool(&gateway, "search", None, None).await;
let ops = search_result
+17 -61
View File
@@ -3,8 +3,9 @@
//!
//! Thin concrete struct (not a trait — the alknet research ruled out a
//! trait with an associated output type). Holds `Arc<OperationRegistry>`
//! and `Arc<dyn IdentityProvider>` and exposes a `resolve_bearer()` and
//! `invoke()` method pair returning the neutral `ResponseEnvelope`. Each
//! and exposes an `invoke()` family returning the neutral
//! `ResponseEnvelope` — identity is supplied per-call, resolved upstream
//! in the auth middleware. Each
//! gateway maps the envelope to its own wire shape (`to_openapi` → HTTP
//! `Response`, `to_mcp` → `CallToolResult`).
//!
@@ -33,7 +34,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use alkcall::core::auth::{AuthToken, Identity, IdentityProvider};
use alkcall::core::auth::Identity;
use alkcall::core::types::Capabilities;
use alkcall::protocol::wire::{CallError, ResponseEnvelope};
use alkcall::registry::context::{AbortPolicy, OperationContext, ScopedPeerEnv};
@@ -44,25 +45,19 @@ use serde_json::Value;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
/// The shared dispatch spine: registry + identity provider, wired for
/// The shared dispatch spine over the registry, invoking operations for
/// the neutral `ResponseEnvelope` result shape both gateway projections
/// map to their wire formats.
/// map to their wire formats. Identity arrives per-call as
/// `Option<Identity>` — bearer resolution happens upstream in the auth
/// middleware, not here.
pub struct GatewayDispatch {
registry: Arc<OperationRegistry>,
identity_provider: Arc<dyn IdentityProvider>,
}
impl GatewayDispatch {
/// Assemble a dispatch spine over a registry and an identity
/// provider.
pub fn new(
registry: Arc<OperationRegistry>,
identity_provider: Arc<dyn IdentityProvider>,
) -> Self {
Self {
registry,
identity_provider,
}
/// Assemble a dispatch spine over a registry.
pub fn new(registry: Arc<OperationRegistry>) -> Self {
Self { registry }
}
/// The registry operations resolve against.
@@ -70,16 +65,6 @@ impl GatewayDispatch {
&self.registry
}
/// The identity provider bearer tokens resolve against.
pub fn identity_provider(&self) -> &Arc<dyn IdentityProvider> {
&self.identity_provider
}
/// Resolve a bearer token to an identity (the auth-middleware hook).
pub fn resolve_bearer(&self, token: &AuthToken) -> Option<Identity> {
self.identity_provider.resolve_from_token(token)
}
/// Invoke a Query/Mutation op under the 30 s gateway deadline; a
/// hung handler surfaces as a `TIMEOUT` error envelope (504).
pub async fn invoke(
@@ -221,33 +206,10 @@ mod tests {
};
use alkcall::registry::spec::{AccessControl, OperationSpec, OperationType, Visibility};
use futures::StreamExt;
use std::sync::Mutex as StdMutex;
// The tests below run against the module's real 30 s deadline; only
// the elapsed-time bound (< 60 s, anti-flake) is asserted by hand.
struct StaticIdentityProvider {
tokens: StdMutex<HashMap<String, Identity>>,
}
impl StaticIdentityProvider {
fn new() -> Self {
Self {
tokens: StdMutex::new(HashMap::new()),
}
}
}
impl IdentityProvider for StaticIdentityProvider {
fn resolve_from_fingerprint(&self, _fp: &str) -> Option<Identity> {
None
}
fn resolve_from_token(&self, token: &AuthToken) -> Option<Identity> {
let token_str = String::from_utf8_lossy(&token.raw);
self.tokens.lock().unwrap().get(token_str.as_ref()).cloned()
}
}
fn spec(name: &str, visibility: Visibility, op_type: OperationType) -> OperationSpec {
OperationSpec::new(
name,
@@ -280,8 +242,7 @@ mod tests {
#[tokio::test]
async fn invoke_external_op_round_trips() {
let dispatch =
GatewayDispatch::new(echo_registry(), Arc::new(StaticIdentityProvider::new()));
let dispatch = GatewayDispatch::new(echo_registry());
let envelope = dispatch
.invoke(None, "/echo/run", serde_json::json!({ "x": 1 }))
.await;
@@ -290,8 +251,7 @@ mod tests {
#[tokio::test]
async fn invoke_unknown_op_returns_not_found() {
let dispatch =
GatewayDispatch::new(echo_registry(), Arc::new(StaticIdentityProvider::new()));
let dispatch = GatewayDispatch::new(echo_registry());
let envelope = dispatch
.invoke(None, "/missing/op", serde_json::json!({}))
.await;
@@ -316,8 +276,7 @@ mod tests {
Capabilities::new(),
))
.unwrap();
let dispatch =
GatewayDispatch::new(Arc::new(registry), Arc::new(StaticIdentityProvider::new()));
let dispatch = GatewayDispatch::new(Arc::new(registry));
let envelope = dispatch
.invoke(None, "/internal/op", serde_json::json!({}))
.await;
@@ -345,8 +304,7 @@ mod tests {
Capabilities::new(),
))
.unwrap();
let dispatch =
GatewayDispatch::new(Arc::new(registry), Arc::new(StaticIdentityProvider::new()));
let dispatch = GatewayDispatch::new(Arc::new(registry));
let started = std::time::Instant::now();
let envelope = dispatch
.invoke(None, "/hung/op", serde_json::json!({}))
@@ -367,8 +325,7 @@ mod tests {
#[tokio::test]
async fn invoke_completes_within_the_deadline_for_a_fast_handler() {
let dispatch =
GatewayDispatch::new(echo_registry(), Arc::new(StaticIdentityProvider::new()));
let dispatch = GatewayDispatch::new(echo_registry());
let envelope = dispatch
.invoke(None, "/echo/run", serde_json::json!({}))
.await;
@@ -394,8 +351,7 @@ mod tests {
Capabilities::new(),
))
.unwrap();
let dispatch =
GatewayDispatch::new(Arc::new(registry), Arc::new(StaticIdentityProvider::new()));
let dispatch = GatewayDispatch::new(Arc::new(registry));
let mut stream =
dispatch.invoke_streaming(None, "/tick/stream", serde_json::json!({ "count": 3 }));
let mut ticks = Vec::new();
+3 -13
View File
@@ -24,7 +24,7 @@ use std::convert::Infallible;
use std::sync::Arc;
use std::time::Duration;
use alkcall::core::auth::{Identity, IdentityProvider};
use alkcall::core::auth::Identity;
use alkcall::protocol::wire::{CallError, ResponseEnvelope};
use alkcall::registry::registration::OperationRegistry;
use alkcall::registry::spec::{AccessResult, Visibility};
@@ -83,38 +83,28 @@ type ByteStream = futures::stream::BoxStream<'static, Result<Bytes, axum::Error>
#[derive(Clone)]
pub(crate) struct GatewayState {
registry: Arc<OperationRegistry>,
identity_provider: Arc<dyn IdentityProvider>,
publish_schemas: crate::gateway::schema_cache::PublishSchemaCache,
}
impl GatewayState {
pub(crate) fn new(
registry: Arc<OperationRegistry>,
identity_provider: Arc<dyn IdentityProvider>,
publish_schemas: crate::gateway::schema_cache::PublishSchemaCache,
) -> Self {
Self {
registry,
identity_provider,
publish_schemas,
}
}
fn dispatch(&self) -> GatewayDispatch {
GatewayDispatch::new(
Arc::clone(&self.registry),
Arc::clone(&self.identity_provider),
)
GatewayDispatch::new(Arc::clone(&self.registry))
}
}
impl FromRef<RouterState> for GatewayState {
fn from_ref(state: &RouterState) -> Self {
GatewayState::new(
Arc::clone(&state.registry),
Arc::clone(&state.identity_provider),
state.publish_schemas.clone(),
)
GatewayState::new(Arc::clone(&state.registry), state.publish_schemas.clone())
}
}
+1 -4
View File
@@ -285,10 +285,7 @@ fn build_router(state: RouterState, extra_routes: Option<Router>) -> Router {
#[cfg(feature = "mcp")]
let mcp_router: Router<RouterState> = {
let dispatch = crate::gateway::GatewayDispatch::new(
Arc::clone(&state.registry),
Arc::clone(&state.identity_provider),
);
let dispatch = crate::gateway::GatewayDispatch::new(Arc::clone(&state.registry));
Router::new()
.nest_service(
"/mcp",
-12
View File
@@ -71,18 +71,6 @@ impl axum::extract::FromRef<RouterState> for DecoyConfig {
}
}
impl axum::extract::FromRef<RouterState> for Arc<OperationRegistry> {
fn from_ref(state: &RouterState) -> Self {
Arc::clone(&state.registry)
}
}
impl axum::extract::FromRef<RouterState> for Arc<dyn IdentityProvider> {
fn from_ref(state: &RouterState) -> Self {
Arc::clone(&state.identity_provider)
}
}
impl axum::extract::FromRef<RouterState> for crate::server::adapter::CachedOpenAPIDoc {
fn from_ref(state: &RouterState) -> Self {
state.openapi_doc.clone()
-6
View File
@@ -141,12 +141,6 @@ impl SessionState {
}
}
impl axum::extract::FromRef<SessionState> for Arc<OperationRegistry> {
fn from_ref(state: &SessionState) -> Self {
Arc::clone(&state.registry)
}
}
/// `FromRef` chain: a bare `Arc<OperationRegistry>` router state lifts
/// into the handler's [`SessionState`]; a full `RouterState` carries
/// the shared [`WsSessions`] instance and lifts through its own impl.