praxis_core/server/
pingora.rs1use pingora_core::server::{RunArgs, Server, configuration::ServerConf};
7use tracing::info;
8
9use super::RuntimeOptions;
10
11pub struct PingoraServerRuntime {
18 server: Server,
20}
21
22impl std::fmt::Debug for PingoraServerRuntime {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("PingoraServerRuntime")
25 .field("threads", &self.server.configuration.threads)
26 .finish_non_exhaustive()
27 }
28}
29
30impl PingoraServerRuntime {
31 #[must_use]
33 pub fn new(config: &crate::config::Config) -> Self {
34 let opts = RuntimeOptions::from(&config.runtime);
35 let server = build_http_server(config.shutdown_timeout_secs, &opts);
36 Self { server }
37 }
38
39 pub fn server_mut(&mut self) -> &mut Server {
41 &mut self.server
42 }
43
44 pub fn run(self) -> ! {
46 self.server.run_forever()
47 }
48
49 pub fn run_with_args(self, args: RunArgs) {
58 self.server.run(args);
59 }
60}
61
62pub fn build_http_server(shutdown_timeout_secs: u64, runtime: &RuntimeOptions) -> Server {
76 let threads = resolve_thread_count(runtime.threads);
77 let conf = build_server_conf(shutdown_timeout_secs, threads, runtime);
78
79 let mut server = Server::new_with_opt_and_conf(None, conf);
80 server.bootstrap();
81
82 info!(
83 shutdown_timeout_secs, threads,
84 work_stealing = runtime.work_stealing,
85 upstream_ca_file = ?runtime.upstream_ca_file,
86 upstream_keepalive_pool_size = ?runtime.upstream_keepalive_pool_size,
87 "server configured"
88 );
89
90 server
91}
92
93fn build_server_conf(shutdown_timeout_secs: u64, threads: usize, runtime: &RuntimeOptions) -> ServerConf {
95 let mut conf = ServerConf {
96 grace_period_seconds: Some(shutdown_timeout_secs),
97 graceful_shutdown_timeout_seconds: Some(shutdown_timeout_secs),
98 threads,
99 work_stealing: runtime.work_stealing,
100 ..ServerConf::default()
101 };
102
103 if let Some(pool_size) = runtime.upstream_keepalive_pool_size {
104 conf.upstream_keepalive_pool_size = pool_size;
105 }
106
107 apply_upstream_ca(&mut conf, runtime);
108 warn_unsupported_global_queue_interval(runtime);
109
110 conf
111}
112
113fn apply_upstream_ca(conf: &mut ServerConf, runtime: &RuntimeOptions) {
115 if let Some(ca_file) = &runtime.upstream_ca_file {
116 info!(ca_file, "setting global upstream CA file (replaces system trust store)");
117 conf.ca_file = Some(ca_file.clone());
118 }
119}
120
121fn warn_unsupported_global_queue_interval(runtime: &RuntimeOptions) {
123 if runtime.global_queue_interval.is_some() {
124 tracing::warn!(
125 interval = ?runtime.global_queue_interval,
126 "global_queue_interval is configured but not yet supported by Pingora's ServerConf"
127 );
128 }
129}
130
131fn resolve_thread_count(configured: usize) -> usize {
137 if configured == 0 {
138 std::thread::available_parallelism().map_or(1, std::num::NonZero::get)
139 } else {
140 configured
141 }
142}
143
144#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn build_http_server_returns_bootstrapped_server() {
154 let server = build_http_server(30, &RuntimeOptions::default());
155 assert_eq!(
156 server.configuration.grace_period_seconds,
157 Some(30),
158 "grace period should match shutdown timeout"
159 );
160 }
161
162 #[test]
163 fn build_http_server_with_explicit_threads() {
164 let runtime = RuntimeOptions {
165 threads: 4,
166 work_stealing: false,
167 ..RuntimeOptions::default()
168 };
169
170 let server = build_http_server(10, &runtime);
171 assert_eq!(
172 server.configuration.threads, 4,
173 "thread count should match configured value"
174 );
175 assert!(!server.configuration.work_stealing, "work stealing should be disabled");
176 }
177}