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

Handlers

rs-netty has five stage traits. They live in src/traits.rs, and trait-variant generates the public Send variants used on the main path.

Inbound

Inbound<I> is an inbound transformation stage after decoding and before the final handler:

#![allow(unused)]
fn main() {
struct Trim;

impl Inbound<String> for Trim {
    type Out = String;

    async fn read(
        &mut self,
        _ctx: &mut rs_netty::InboundContext,
        msg: String,
    ) -> rs_netty::Result<rs_netty::Flow<Self::Out>> {
        Ok(rs_netty::Flow::Next(msg.trim().to_string()))
    }
}
}

InboundContext exposes id(), peer_addr(), and local_addr(). It does not allow writes.

Business

Business<I> is an application-level transformation stage between inbound stages and the final handler. It has the same type shape as Inbound, but the method is handle and the context is BusinessContext. Once the builder enters the business phase, it can add more business stages or the final handler, but cannot go back to inbound stages.

Handler

Handler<I> is the end of the TCP inbound side:

#![allow(unused)]
fn main() {
impl Handler<Request> for Router {
    type Write = Response;

    async fn read(&mut self, ctx: &mut Context<Self::Write>, req: Request) -> Result<()> {
        ctx.write_and_flush(Response { body: req.body }).await
    }
}
}

type Write is the application type this handler can write to the outbound side. Outbound stages start from this type and eventually produce a value the codec can encode.

Context<W> provides:

  • identity: id, peer_addr, local_addr
  • channel(): a cloneable external channel
  • stats(): ConnectionStats when stats are enabled
  • write, flush, write_and_flush
  • close

DatagramHandler

DatagramHandler<I> is the end of the UDP inbound side. It uses DatagramContext<W>, which supports write, write_to, flush, write_and_flush, write_to_and_flush, and close.

#![allow(unused)]
fn main() {
impl DatagramHandler<String> for UdpEcho {
    type Write = String;

    async fn read(&mut self, ctx: &mut DatagramContext<Self::Write>, msg: String) -> Result<()> {
        ctx.write_and_flush(format!("echo: {msg}")).await
    }
}
}

Outbound

Outbound<I> converts the application type written by a handler into the next outbound type. The final outbound type must be encodable by the codec.

#![allow(unused)]
fn main() {
struct RenderResponse;

impl Outbound<Response> for RenderResponse {
    type Out = String;

    async fn write(
        &mut self,
        _ctx: &mut rs_netty::OutboundContext,
        msg: Response,
    ) -> rs_netty::Result<rs_netty::Flow<Self::Out>> {
        Ok(rs_netty::Flow::Next(msg.body))
    }
}
}

OutboundContext also exposes only identity information and does not allow direct writes.

Macro Or Manual Impl

#[handler] is good for simple one-in/one-out final handlers and consume-only handlers. Write a manual impl when you need direct Context / DatagramContext access, explicit flush timing, multiple writes, connection close, or channel().