praxis_protocol/lib.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2024 Praxis Contributors
3
4#![deny(unreachable_pub)]
5#![expect(
6 clippy::arithmetic_side_effects,
7 clippy::as_conversions,
8 clippy::iter_over_hash_type,
9 clippy::min_ident_chars,
10 clippy::mod_module_files,
11 clippy::partial_pub_fields,
12 clippy::pub_underscore_fields,
13 clippy::shadow_unrelated,
14 clippy::single_char_lifetime_names,
15 clippy::wildcard_enum_match_arm,
16 reason = "TODO(conventions-sync): fix violations and remove"
17)]
18
19//! Protocol adapters for Praxis.
20//!
21//! `praxis-protocol` sits below `server` and above `filter` in the
22//! crate dependency flow `server -> protocol -> filter -> core -> tls`.
23//! It binds the [`praxis_filter`] pipeline engine to Pingora's HTTP and
24//! TCP proxy services, so that inbound connections are served, filters
25//! run at the right lifecycle points, and requests are forwarded to
26//! upstream clusters.
27//!
28//! Responsibilities:
29//! - HTTP protocol implementations and Pingora adapters ([`http`]).
30//! - Raw TCP/L4 forwarding ([`tcp`]).
31//! - Active health-check probes and admin/observability endpoints.
32//! - TLS listener setup (the `tls_setup` module) and keeping certificate hot-reload watchers alive for the process
33//! lifetime ([`CertWatcherShutdowns`]).
34//!
35//! Boundary with Pingora: Pingora owns request-smuggling prevention,
36//! HTTP/2 backpressure, connection-pool safety, and HTTP/1.1 upgrade
37//! detection with bidirectional forwarding (WebSocket and similar).
38//! Praxis code in this crate and in [`praxis_filter`] owns hop-by-hop
39//! header stripping (with conditional preservation for upgrade
40//! requests), Host validation, `X-Forwarded-*` injection, and retry
41//! logic.
42
43use praxis_core::{PingoraServerRuntime, ProxyError, config::Config};
44use tokio::sync::watch;
45
46mod pipelines;
47pub use pipelines::ListenerPipelines;
48
49/// Process-wide connection limit.
50pub mod connections;
51/// HTTP protocol implementations.
52pub mod http;
53/// Raw TCP/L4 forwarding protocol.
54pub mod tcp;
55
56/// Shared TLS settings builder for HTTP and TCP listeners.
57pub(crate) mod tls_setup;
58
59// -----------------------------------------------------------------------------
60// CertWatcherShutdowns
61// -----------------------------------------------------------------------------
62
63/// Collected TLS certificate watcher shutdown senders.
64///
65/// Background [`CertWatcher`] tasks run for the process lifetime. These
66/// [`watch::Sender`]s are held so a watcher can be asked to stop early via
67/// `send(true)`; dropping them does not stop the watchers (they end at process
68/// exit).
69///
70/// [`watch::Sender`]: tokio::sync::watch::Sender
71/// [`CertWatcher`]: praxis_tls::watcher::CertWatcher
72pub struct CertWatcherShutdowns {
73 /// Shutdown senders kept alive for the server lifetime.
74 _senders: Vec<watch::Sender<bool>>,
75}
76
77impl CertWatcherShutdowns {
78 /// Wrap collected shutdown senders.
79 pub fn new(senders: Vec<watch::Sender<bool>>) -> Self {
80 Self { _senders: senders }
81 }
82}
83
84// -----------------------------------------------------------------------------
85// Protocol
86// -----------------------------------------------------------------------------
87
88/// A protocol implementation that registers services onto a shared server runtime.
89pub trait Protocol: Send {
90 /// Register this protocol's services. Does not block.
91 ///
92 /// Returns any TLS certificate watcher shutdown senders. The caller keeps
93 /// these alive to retain the ability to stop a watcher early via
94 /// `send(true)`; the watcher tasks otherwise run for the process lifetime
95 /// (dropping the senders does not stop them).
96 ///
97 /// # Errors
98 ///
99 /// Returns [`ProxyError`] if listener binding or setup fails.
100 ///
101 /// [`ProxyError`]: praxis_core::ProxyError
102 fn register(
103 self: Box<Self>,
104 server: &mut PingoraServerRuntime,
105 config: &Config,
106 pipelines: &ListenerPipelines,
107 ) -> Result<Vec<watch::Sender<bool>>, ProxyError>;
108}