CodeSampleX

Пример

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

Контракт

  1. 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
  2. Stack's Debug implementation prints layers in outer-to-inner execution order [outer, inner], displaying the second constructor argument before the first
  3. 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
  4. 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

Скачать артефакт с исходным кодом (tar.gz)

Исходный код

Cargo.lock
# 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"
Cargo.toml
[package]
name = "sample-tower-layer"
version = "0.1.0"
edition = "2021"

[dependencies]
tower-layer = "0.3.3"
NOTES.md
# 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.
csx.json
{"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"}
src/lib.rs
//! 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))");
    }
}

Исходный сидер

csx-seed