Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Typed Pipeline

The typed pipeline is rs-netty’s central constraint mechanism. It puts stage order and message transitions into builder type parameters so many mistakes become compile-time errors.

Shape

TCP:

#![allow(unused)]
fn main() {
pipeline()
    .codec(...)
    .inbound(...)*
    .business(...)*
    .handler(...)
    .outbound(...)*
}

UDP:

#![allow(unused)]
fn main() {
datagram_pipeline()
    .codec(...)
    .inbound(...)*
    .business(...)*
    .handler(...)
    .outbound(...)*
}

The two builders have the same stage shape, but different codec traits and final handler traits.

Builder States

Shared state markers live in pipeline/core/state.rs:

  • Start: initial state; only a codec can be added.
  • InboundPhase: a codec exists; inbound, business, or final handler can be added.
  • BusinessPhase: business processing has started; more business stages or the final handler can be added.
  • Ready: the final handler exists; outbound stages can be added and the builder can become a runtime pipeline.

These states appear as the first type parameter of PipelineBuilder<State, C, InP, BizP, H, OutP, CurrentIn, Write, CurrentOut> and DatagramPipelineBuilder<...>. If a stage is illegal in the current state, the method is simply not implemented for that builder type.

Message Type Chain

For TCP, the constraints are:

C: Decoder<Item = A>
InboundPipe<A, Out = B>
BusinessPipe<B, Out = CIn>
H: Handler<CIn, Write = W>
OutboundPipe<W, Out = COut>
codec: Encoder<COut>

This means:

  • the first inbound stage must accept the type decoded by the codec.
  • each following inbound/business stage must accept the previous stage’s Out.
  • the final handler input must match the inbound/business chain output.
  • the first outbound stage must accept Handler::Write.
  • the final outbound output must be encodable by the stream codec.

UDP uses the same type chain, but swaps in DatagramDecoder / DatagramEncoder and DatagramHandler.

Compile Failures Are Intentional API

The trybuild compile-fail tests document these constraints. This pipeline does not compile because Parse converts String into Request, but the final handler expects String:

#![allow(unused)]
fn main() {
let _ = pipeline()
    .codec(LineCodec::new())
    .inbound(Parse)
    .handler(EchoString);
}

This one also fails because LineCodec can encode String, while the handler writes Response and no outbound stage converts it:

#![allow(unused)]
fn main() {
let _ = TcpServer::bind("127.0.0.1:0")
    .pipeline(|| pipeline().codec(LineCodec::new()).handler(Router));
}

The fix is to add an outbound stage:

#![allow(unused)]
fn main() {
let _ = pipeline()
    .codec(LineCodec::new())
    .inbound(Parse)
    .handler(Router)
    .outbound(RenderResponse);
}

Flow

Inbound, Business, and Outbound return Result<Flow<T>>:

#![allow(unused)]
fn main() {
pub enum Flow<T> {
    Next(T),
    Stop,
}
}

Flow::Next continues the pipeline. Flow::Stop consumes the current message and stops processing in that direction without error. Final Handler / DatagramHandler implementations do not return Flow; they return Result<()> because they are the end of the inbound side.