Пример
tower-layer 0.3.3: Stack::new takes inner before outer middleware layers, causing the second argument to execute first on incoming requests unlike tuple composition
Проверенный пример — cargo tower-layer 0.3.3: Stack::new takes inner before outer middleware layers, causing the second argument to execute first on incoming…
sha256:3ea8bc2e02e269464c4ff2edb5fac901d2c1c109ea6020628f8c94bf2ad780aa
Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла.
Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди.
MIT-0
Свидетельства выполнения
Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.
- Основа свидетельства
- Подписанный контракт пройден
- Квитанции проверки
- 3
- Ключи подписи, собравшие его
- 3
Заявленная среда
rust linux x64 rust rust cargo
Среды запусков проверки
| Окружение | Контракт | Этапы | Запуск |
|---|---|---|---|
| rust 1 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · cargo@1 |
2026-08-16 |
| rust 1 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · cargo@1 |
2026-08-18 |
| rust 1 · linux alpine/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · cargo@1rust:1-alpine@sha256:a10e64dd139b… |
2026-09-08 |
Кейс
HOW- Цель
- Stack::new takes inner before outer middleware layers, causing the second argument to execute first on incoming requests unlike tuple composition
- Пакеты
- Символы
-
- tower_layer::Stack
- tower_layer::Layer
- tower_layer::layer_fn
- tower_layer::Identity
- Окружение
- rust
- Создан
- 2026-08-16T11:21:45Z
Контракт
- Stack::new(layer_a, layer_b).layer(base) wraps base with layer_a first and layer_b second, executing layer_b before layer_a on incoming requests
- Stack's Debug implementation prints layers in outer-to-inner execution order [outer, inner], displaying the second constructor argument before the first
- Tuple layer composition (layer_a, layer_b).layer(base) executes layer_a before layer_b on incoming requests, the reverse of Stack::new positional ordering
- Stack::new(Identity::new(), layer_a) applies layer_a directly around the base service without altering request execution
Файлы
- Cargo.lock
- Cargo.toml
- NOTES.md
- csx.json
- src/lib.rs
Исходный код
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "sample-tower-layer"
version = "0.1.0"
dependencies = [
"tower-layer",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[package]
name = "sample-tower-layer"
version = "0.1.0"
edition = "2021"
[dependencies]
tower-layer = "0.3.3"
# tower-layer Middleware Composition Ordering Notes
## Prior Solutions from Search
`search_known_solution` returned three compatible entries targeting broader ecosystems:
- `sha256:70cb8cec09e074888556da7097e94d68ad979b471ca30473f3e2662c391dd7fd` (Tower middleware ordering and buffer/timeout interactions)
- `sha256:0add9e86ea54fb151285ed7fff0ac6a073835d84159f6bfc43b6e447f030a7be` (Axum stateful middleware and route_layer)
- `sha256:9b72838479c9a3e8a637a5b2c3ca91325f7991fe3f7b175244bff89210027aa5` (Tower ServiceBuilder outer-to-inner ordering)
This sample isolates `tower-layer` itself, verifying the discrepancy between `Stack::new(inner, outer)` parameter ordering and tuple `(L1, L2)` layer composition.
## Failure Mode
Fails silently with a green build: passing `Stack::new(auth, logging)` compiles cleanly but executes `logging` before `auth` on incoming requests, silently inverting security or tracing dependencies unless execution order is asserted.
{"case":{"believed":"Stack::new(a, b) establishes a middleware pipeline where layer a executes before layer b on incoming requests, matching left-to-right argument order and tuple composition","caseId":"case:sha256:a2e12dcff65709a68d88f32d2035a2c741fc068c1051c67d1b92797273c71c25","contract":["Stack::new(layer_a, layer_b).layer(base) wraps base with layer_a first and layer_b second, executing layer_b before layer_a on incoming requests","Stack's Debug implementation prints layers in outer-to-inner execution order [outer, inner], displaying the second constructor argument before the first","Tuple layer composition (layer_a, layer_b).layer(base) executes layer_a before layer_b on incoming requests, the reverse of Stack::new positional ordering","Stack::new(Identity::new(), layer_a) applies layer_a directly around the base service without altering request execution"],"goal":"Stack::new takes inner before outer middleware layers, causing the second argument to execute first on incoming requests unlike tuple composition","kind":"HOW","packages":["pkg:cargo/tower-layer@0.3.3"],"schemaVersion":1,"symbols":["tower_layer::Stack","tower_layer::Layer","tower_layer::layer_fn","tower_layer::Identity"]},"contractCommand":["cargo","test","--offline"],"environment":{"arch":"x64","ecosystem":"cargo","executionContext":"rust","language":"rust","os":"linux","packageManager":"cargo","runtime":"rust","schemaVersion":1},"license":"MIT-0","packages":["pkg:cargo/tower-layer@0.3.3"],"schemaVersion":1,"symbols":["tower_layer::Stack","tower_layer::Layer","tower_layer::layer_fn","tower_layer::Identity"],"verifierAdapter":"cargo@1"}
//! Contract tests verifying `tower-layer` composition ordering and behavior.
use std::sync::{Arc, Mutex};
pub use tower_layer::{layer_fn, Identity, Layer, Stack};
/// Thread-safe execution trace collector.
#[derive(Clone, Debug, Default)]
pub struct TraceLog(Arc<Mutex<Vec<&'static str>>>);
impl TraceLog {
pub fn new() -> Self {
Self::default()
}
pub fn record(&self, event: &'static str) {
self.0.lock().unwrap().push(event);
}
pub fn snapshot(&self) -> Vec<&'static str> {
self.0.lock().unwrap().clone()
}
}
/// A simple synchronous service trait for demonstration.
pub trait SimpleService {
fn call(&self, req: &'static str) -> String;
}
/// A base service that handles the request at the center of the middleware stack.
#[derive(Clone)]
pub struct BaseService {
trace: TraceLog,
}
impl BaseService {
pub fn new(trace: TraceLog) -> Self {
Self { trace }
}
}
impl SimpleService for BaseService {
fn call(&self, req: &'static str) -> String {
self.trace.record("base");
format!("base({})", req)
}
}
/// A middleware service that wraps an inner service, recording enter and exit events.
#[derive(Clone)]
pub struct TracedService<S> {
name: &'static str,
inner: S,
trace: TraceLog,
}
impl<S: SimpleService> SimpleService for TracedService<S> {
fn call(&self, req: &'static str) -> String {
let enter = match self.name {
"A" => "enter_a",
"B" => "enter_b",
_ => "enter_other",
};
let exit = match self.name {
"A" => "exit_a",
"B" => "exit_b",
_ => "exit_other",
};
self.trace.record(enter);
let res = self.inner.call(req);
self.trace.record(exit);
format!("{}({})", self.name, res)
}
}
/// A Layer implementation that decorates any `SimpleService` with a `TracedService`.
#[derive(Clone, Debug)]
pub struct TracedLayer {
name: &'static str,
trace: TraceLog,
}
impl TracedLayer {
pub fn new(name: &'static str, trace: TraceLog) -> Self {
Self { name, trace }
}
}
impl<S> Layer<S> for TracedLayer {
type Service = TracedService<S>;
fn layer(&self, inner: S) -> Self::Service {
TracedService {
name: self.name,
inner,
trace: self.trace.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stack_new_argument_order_is_inner_then_outer() {
let trace = TraceLog::new();
let layer_a = TracedLayer::new("A", trace.clone());
let layer_b = TracedLayer::new("B", trace.clone());
let base = BaseService::new(trace.clone());
// Stack::new(inner, outer) takes inner as 1st arg and outer as 2nd arg.
// Therefore Stack::new(layer_a, layer_b) wraps `base` with `layer_a` first,
// and then wraps that result with `layer_b`.
let stack = Stack::new(layer_a, layer_b);
let composed_service = stack.layer(base);
let output = composed_service.call("ping");
// Incoming requests enter outer (layer_b) FIRST, then inner (layer_a) SECOND.
assert_eq!(
trace.snapshot(),
vec!["enter_b", "enter_a", "base", "exit_a", "exit_b"]
);
assert_eq!(output, "B(A(base(ping)))");
}
#[test]
fn test_stack_debug_formats_outer_before_inner() {
// Stack::Debug prints outer first, then inner, reflecting request execution order.
let stack = Stack::new("layer_inner", "layer_outer");
assert_eq!(format!("{:?}", stack), "\"layer_outer\", \"layer_inner\"");
assert_eq!(
format!("{:#?}", stack),
"\"layer_outer\",\n\"layer_inner\""
);
}
#[test]
fn test_tuple_layer_order_is_outer_then_inner() {
let trace = TraceLog::new();
let layer_a = TracedLayer::new("A", trace.clone());
let layer_b = TracedLayer::new("B", trace.clone());
let base = BaseService::new(trace.clone());
// For tuples (L1, L2), L1 is outer and L2 is inner.
// This is the opposite positional argument ordering of Stack::new(L1, L2).
let tuple_layer = (layer_a, layer_b);
let composed_service = tuple_layer.layer(base);
let output = composed_service.call("ping");
// Incoming requests enter outer (layer_a) FIRST, then inner (layer_b) SECOND.
assert_eq!(
trace.snapshot(),
vec!["enter_a", "enter_b", "base", "exit_b", "exit_a"]
);
assert_eq!(output, "A(B(base(ping)))");
}
#[test]
fn test_identity_layer_is_transparent_in_stack() {
let trace = TraceLog::new();
let layer_a = TracedLayer::new("A", trace.clone());
let base = BaseService::new(trace.clone());
// Stack::new(Identity, layer_a) has Identity as inner and layer_a as outer.
let stack = Stack::new(Identity::new(), layer_a);
let composed_service = stack.layer(base);
let output = composed_service.call("ping");
assert_eq!(trace.snapshot(), vec!["enter_a", "base", "exit_a"]);
assert_eq!(output, "A(base(ping))");
}
#[test]
fn test_layer_fn_closure_wrapping() {
let trace = TraceLog::new();
let base = BaseService::new(trace.clone());
let t = trace.clone();
let custom_layer = layer_fn(move |inner: BaseService| TracedService {
name: "A",
inner,
trace: t.clone(),
});
let composed_service = custom_layer.layer(base);
let output = composed_service.call("ping");
assert_eq!(trace.snapshot(), vec!["enter_a", "base", "exit_a"]);
assert_eq!(output, "A(base(ping))");
}
}