praxis_core/server/
pingora.rs1use pingora_core::server::{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
50pub fn build_http_server(shutdown_timeout_secs: u64, runtime: &RuntimeOptions) -> Server {
64 let threads = resolve_thread_count(runtime.threads);
65 let conf = build_server_conf(shutdown_timeout_secs, threads, runtime);
66
67 let mut server = Server::new_with_opt_and_conf(None, conf);
68 server.bootstrap();
69
70 info!(
71 shutdown_timeout_secs, threads,
72 work_stealing = runtime.work_stealing,
73 upstream_ca_file = ?runtime.upstream_ca_file,
74 upstream_keepalive_pool_size = ?runtime.upstream_keepalive_pool_size,
75 "server configured"
76 );
77
78 server
79}
80
81fn build_server_conf(shutdown_timeout_secs: u64, threads: usize, runtime: &RuntimeOptions) -> ServerConf {
83 let mut conf = ServerConf {
84 grace_period_seconds: Some(shutdown_timeout_secs),
85 graceful_shutdown_timeout_seconds: Some(shutdown_timeout_secs),
86 threads,
87 work_stealing: runtime.work_stealing,
88 ..ServerConf::default()
89 };
90
91 if let Some(pool_size) = runtime.upstream_keepalive_pool_size {
92 conf.upstream_keepalive_pool_size = pool_size;
93 }
94
95 if let Some(ref ca_file) = runtime.upstream_ca_file {
96 info!(ca_file, "setting global upstream CA file (replaces system trust store)");
97 conf.ca_file = Some(ca_file.clone());
98 }
99
100 if runtime.global_queue_interval.is_some() {
101 tracing::warn!(
102 interval = ?runtime.global_queue_interval,
103 "global_queue_interval is configured but not yet supported by Pingora's ServerConf"
104 );
105 }
106
107 conf
108}
109
110fn resolve_thread_count(configured: usize) -> usize {
116 if configured == 0 {
117 std::thread::available_parallelism()
118 .map(std::num::NonZero::get)
119 .unwrap_or(1)
120 } else {
121 configured
122 }
123}
124
125#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn build_http_server_returns_bootstrapped_server() {
135 let server = build_http_server(30, &RuntimeOptions::default());
136 assert_eq!(
137 server.configuration.grace_period_seconds,
138 Some(30),
139 "grace period should match shutdown timeout"
140 );
141 }
142
143 #[test]
144 fn build_http_server_with_explicit_threads() {
145 let runtime = RuntimeOptions {
146 threads: 4,
147 work_stealing: false,
148 ..RuntimeOptions::default()
149 };
150
151 let server = build_http_server(10, &runtime);
152 assert_eq!(
153 server.configuration.threads, 4,
154 "thread count should match configured value"
155 );
156 assert!(!server.configuration.work_stealing, "work stealing should be disabled");
157 }
158}