Adding a Protocol

Protocols translate between external network protocols and the internal filter pipeline.
On this page

Overview

Protocols translate between external network protocols and the internal filter pipeline. Each protocol registers one or more services on the network backend’s runtime that bind listeners and dispatch traffic through the filter pipeline. (A protocol is what a listener speaks; a backend is the library that owns the sockets. To add a backend instead, see Adding a Network Backend.)

Steps

1. Implement Protocol

Create a module under crates/protocol/src/ and implement the Protocol trait:

pub trait Protocol: Send {
    fn register(
        self: Box<Self>,
        server: &mut praxis_core::backend::Runtime,
        config: &Config,
        pipelines: &ListenerPipelines,
    ) -> Result<Vec<watch::Sender<bool>>, ProxyError>;
}

register filters listeners by protocol kind, resolves pipelines from ListenerPipelines, and registers services on the backend runtime (backend::Runtime is the selected backend’s runtime type). Return any TLS certificate watcher shutdown senders; the caller keeps them alive until server shutdown.

Reference implementations (exposed as praxis_protocol::backend::{Http, Tcp}):

  • HTTP: crates/protocol/src/http/pingora/mod.rs (PingoraHttp)
  • TCP: crates/protocol/src/tcp/service.rs (PingoraTcp)

2. Add ProtocolKind variant

Add a variant to ProtocolKind in crates/core/src/config/listener.rs:

#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ProtocolKind {
    #[default]
    Http,
    Tcp,
    // NewProtocol,
}

The #[serde(rename_all = "lowercase")] attribute maps YAML values (e.g. protocol: tcp) to enum variants.

3. Wire in server.rs

In crates/server/src/server.rs, add a branch to register_protocols that checks for listeners using the new protocol kind and registers the implementation:

if config.listeners.iter().any(|l| l.protocol == ProtocolKind::NewProtocol) {
    let shutdowns = Box::new(NewProtocolImpl)
        .register(server, config, pipelines)
        .unwrap_or_else(|e| fatal(&e));
    all_shutdowns.extend(shutdowns);
}

4. Add filter compatibility

If the protocol uses a different filter trait than HttpFilter or TcpFilter, define the new trait in crates/filter/src/ and ensure the pipeline engine can dispatch to it.

5. Test and document

  • Add unit tests for the protocol implementation
  • Add integration tests under tests/integration/
  • Add example configs under examples/configs/
  • Update examples/README.md
  • Update docs/features.md with the new protocol