itsi-scheduler 0.2.22-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +8 -0
  3. data/Cargo.lock +997 -0
  4. data/Cargo.toml +7 -0
  5. data/Rakefile +39 -0
  6. data/ext/itsi_acme/Cargo.toml +86 -0
  7. data/ext/itsi_acme/examples/high_level.rs +63 -0
  8. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  9. data/ext/itsi_acme/examples/low_level.rs +87 -0
  10. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  11. data/ext/itsi_acme/src/acceptor.rs +81 -0
  12. data/ext/itsi_acme/src/acme.rs +354 -0
  13. data/ext/itsi_acme/src/axum.rs +86 -0
  14. data/ext/itsi_acme/src/cache.rs +39 -0
  15. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  16. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  17. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  18. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  19. data/ext/itsi_acme/src/caches/no.rs +78 -0
  20. data/ext/itsi_acme/src/caches/test.rs +136 -0
  21. data/ext/itsi_acme/src/config.rs +172 -0
  22. data/ext/itsi_acme/src/https_helper.rs +69 -0
  23. data/ext/itsi_acme/src/incoming.rs +142 -0
  24. data/ext/itsi_acme/src/jose.rs +161 -0
  25. data/ext/itsi_acme/src/lib.rs +142 -0
  26. data/ext/itsi_acme/src/resolver.rs +59 -0
  27. data/ext/itsi_acme/src/state.rs +424 -0
  28. data/ext/itsi_error/Cargo.lock +368 -0
  29. data/ext/itsi_error/Cargo.toml +12 -0
  30. data/ext/itsi_error/src/lib.rs +140 -0
  31. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  32. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  33. data/ext/itsi_rb_helpers/Cargo.lock +355 -0
  34. data/ext/itsi_rb_helpers/Cargo.toml +11 -0
  35. data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
  36. data/ext/itsi_rb_helpers/src/lib.rs +232 -0
  37. data/ext/itsi_scheduler/Cargo.toml +24 -0
  38. data/ext/itsi_scheduler/extconf.rb +11 -0
  39. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  40. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  41. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  42. data/ext/itsi_scheduler/src/itsi_scheduler.rs +320 -0
  43. data/ext/itsi_scheduler/src/lib.rs +39 -0
  44. data/ext/itsi_server/Cargo.lock +2956 -0
  45. data/ext/itsi_server/Cargo.toml +94 -0
  46. data/ext/itsi_server/src/default_responses/mod.rs +14 -0
  47. data/ext/itsi_server/src/env.rs +43 -0
  48. data/ext/itsi_server/src/lib.rs +154 -0
  49. data/ext/itsi_server/src/prelude.rs +2 -0
  50. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +116 -0
  51. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +149 -0
  52. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +346 -0
  53. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +265 -0
  54. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +399 -0
  55. data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +447 -0
  56. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +545 -0
  57. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +650 -0
  58. data/ext/itsi_server/src/ruby_types/itsi_server.rs +102 -0
  59. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  60. data/ext/itsi_server/src/server/binds/bind.rs +204 -0
  61. data/ext/itsi_server/src/server/binds/bind_protocol.rs +37 -0
  62. data/ext/itsi_server/src/server/binds/listener.rs +485 -0
  63. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  64. data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +132 -0
  65. data/ext/itsi_server/src/server/binds/tls.rs +278 -0
  66. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  67. data/ext/itsi_server/src/server/frame_stream.rs +143 -0
  68. data/ext/itsi_server/src/server/http_message_types.rs +230 -0
  69. data/ext/itsi_server/src/server/io_stream.rs +128 -0
  70. data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +93 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +329 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +300 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
  80. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
  81. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +188 -0
  82. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +168 -0
  83. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +183 -0
  84. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  85. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
  86. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +133 -0
  87. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  88. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +122 -0
  89. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +407 -0
  90. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +155 -0
  91. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +54 -0
  92. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +138 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +269 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +62 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +218 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
  99. data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
  100. data/ext/itsi_server/src/server/mod.rs +14 -0
  101. data/ext/itsi_server/src/server/process_worker.rs +247 -0
  102. data/ext/itsi_server/src/server/redirect_type.rs +26 -0
  103. data/ext/itsi_server/src/server/request_job.rs +11 -0
  104. data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +100 -0
  105. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +411 -0
  106. data/ext/itsi_server/src/server/serve_strategy/mod.rs +31 -0
  107. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +449 -0
  108. data/ext/itsi_server/src/server/signal.rs +129 -0
  109. data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
  110. data/ext/itsi_server/src/server/thread_worker.rs +504 -0
  111. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  112. data/ext/itsi_server/src/services/itsi_http_service.rs +270 -0
  113. data/ext/itsi_server/src/services/mime_types.rs +2896 -0
  114. data/ext/itsi_server/src/services/mod.rs +6 -0
  115. data/ext/itsi_server/src/services/password_hasher.rs +89 -0
  116. data/ext/itsi_server/src/services/rate_limiter.rs +609 -0
  117. data/ext/itsi_server/src/services/static_file_server.rs +1400 -0
  118. data/ext/itsi_tracing/Cargo.lock +274 -0
  119. data/ext/itsi_tracing/Cargo.toml +17 -0
  120. data/ext/itsi_tracing/src/lib.rs +370 -0
  121. data/itsi-scheduler-100.png +0 -0
  122. data/lib/itsi/schedule_refinement.rb +96 -0
  123. data/lib/itsi/scheduler/3.1/itsi_scheduler.so +0 -0
  124. data/lib/itsi/scheduler/3.2/itsi_scheduler.so +0 -0
  125. data/lib/itsi/scheduler/3.3/itsi_scheduler.so +0 -0
  126. data/lib/itsi/scheduler/3.4/itsi_scheduler.so +0 -0
  127. data/lib/itsi/scheduler/4.0/itsi_scheduler.so +0 -0
  128. data/lib/itsi/scheduler/native_extension.rb +34 -0
  129. data/lib/itsi/scheduler/version.rb +7 -0
  130. data/lib/itsi/scheduler.rb +153 -0
  131. data/vendor/rb-sys-build/.cargo-ok +1 -0
  132. data/vendor/rb-sys-build/.cargo_vcs_info.json +6 -0
  133. data/vendor/rb-sys-build/Cargo.lock +294 -0
  134. data/vendor/rb-sys-build/Cargo.toml +71 -0
  135. data/vendor/rb-sys-build/Cargo.toml.orig +32 -0
  136. data/vendor/rb-sys-build/LICENSE-APACHE +190 -0
  137. data/vendor/rb-sys-build/LICENSE-MIT +21 -0
  138. data/vendor/rb-sys-build/src/bindings/sanitizer.rs +185 -0
  139. data/vendor/rb-sys-build/src/bindings/stable_api.rs +247 -0
  140. data/vendor/rb-sys-build/src/bindings/wrapper.h +71 -0
  141. data/vendor/rb-sys-build/src/bindings.rs +280 -0
  142. data/vendor/rb-sys-build/src/cc.rs +421 -0
  143. data/vendor/rb-sys-build/src/lib.rs +12 -0
  144. data/vendor/rb-sys-build/src/rb_config/flags.rs +101 -0
  145. data/vendor/rb-sys-build/src/rb_config/library.rs +132 -0
  146. data/vendor/rb-sys-build/src/rb_config/search_path.rs +57 -0
  147. data/vendor/rb-sys-build/src/rb_config.rs +906 -0
  148. data/vendor/rb-sys-build/src/utils.rs +53 -0
  149. metadata +210 -0
@@ -0,0 +1,278 @@
1
+ use base64::{engine::general_purpose, Engine as _};
2
+ use itsi_acme::{AcmeAcceptor, AcmeConfig, AcmeState};
3
+ use itsi_error::Result;
4
+ use itsi_tracing::info;
5
+ use locked_dir_cache::LockedDirCache;
6
+ use rcgen::ExtendedKeyUsagePurpose;
7
+ use rcgen::{
8
+ BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, KeyPair, KeyUsagePurpose,
9
+ SanType,
10
+ };
11
+ use rustls::{
12
+ pki_types::{CertificateDer, PrivateKeyDer},
13
+ ClientConfig, RootCertStore,
14
+ };
15
+ use rustls_pemfile::{certs, pkcs8_private_keys};
16
+ use std::{
17
+ collections::HashMap,
18
+ fs,
19
+ io::{BufReader, Error},
20
+ sync::Arc,
21
+ };
22
+ use tokio::sync::Mutex;
23
+ use tokio_rustls::{rustls::ServerConfig, TlsAcceptor};
24
+
25
+ use crate::env::{
26
+ ITSI_ACME_CACHE_DIR, ITSI_ACME_CA_PEM_PATH, ITSI_ACME_CONTACT_EMAIL, ITSI_ACME_DIRECTORY_URL,
27
+ ITSI_LOCAL_CA_DIR,
28
+ };
29
+
30
+ mod locked_dir_cache;
31
+
32
+ #[derive(Clone)]
33
+ pub enum ItsiTlsAcceptor {
34
+ Manual(TlsAcceptor),
35
+ Automatic(
36
+ AcmeAcceptor,
37
+ Arc<Mutex<AcmeState<Error>>>,
38
+ Arc<ServerConfig>,
39
+ ),
40
+ }
41
+
42
+ /// Generates a TLS configuration based on either :
43
+ /// * Input "cert" and "key" options (either paths or Base64-encoded strings) or
44
+ /// * Performs automatic certificate generation/retrieval. Generated certs use an internal self-signed Isti CA.
45
+ ///
46
+ /// If a non-local host or optional domain parameter is provided,
47
+ /// an automated certificate will attempt to be fetched using let's encrypt.
48
+ pub fn configure_tls(
49
+ host: &str,
50
+ query_params: &HashMap<String, String>,
51
+ ) -> Result<ItsiTlsAcceptor> {
52
+ let domains = query_params
53
+ .get("domains")
54
+ .map(|v| v.split(',').map(String::from).collect::<Vec<_>>())
55
+ .or_else(|| query_params.get("domain").map(|v| vec![v.to_string()]));
56
+
57
+ if query_params.get("cert").is_some_and(|c| c == "acme") {
58
+ if let Some(domains) = domains {
59
+ let directory_url = &*ITSI_ACME_DIRECTORY_URL;
60
+ info!(
61
+ domains = format!("{:?}", domains),
62
+ directory_url, "Requesting acme cert"
63
+ );
64
+ let acme_contact_email = query_params
65
+ .get("acme_email")
66
+ .map(|s| s.to_string())
67
+ .or_else(|| (*ITSI_ACME_CONTACT_EMAIL).as_ref().ok().map(|s| s.to_string()))
68
+ .ok_or_else(|| itsi_error::ItsiError::ArgumentError(
69
+ "acme_email query param or ITSI_ACME_CONTACT_EMAIL must be set before you can auto-generate let's encrypt certificates".to_string(),
70
+ ))?;
71
+
72
+ let acme_config = AcmeConfig::new(domains)
73
+ .contact([format!("mailto:{}", acme_contact_email)])
74
+ .cache(LockedDirCache::new(&*ITSI_ACME_CACHE_DIR))
75
+ .directory(directory_url);
76
+
77
+ let acme_state = if let Ok(ca_pem_path) = &*ITSI_ACME_CA_PEM_PATH {
78
+ let mut root_cert_store = RootCertStore::empty();
79
+
80
+ let ca_pem = fs::read(ca_pem_path).expect("failed to read CA pem file");
81
+ let mut ca_reader = BufReader::new(&ca_pem[..]);
82
+ let der_certs: Vec<CertificateDer> = certs(&mut ca_reader)
83
+ .collect::<std::result::Result<Vec<CertificateDer>, _>>()
84
+ .map_err(|e| {
85
+ itsi_error::ItsiError::ArgumentError(format!(
86
+ "Invalid ACME CA Pem path {:?}",
87
+ e
88
+ ))
89
+ })?;
90
+ root_cert_store.add_parsable_certificates(der_certs);
91
+
92
+ let client_config = ClientConfig::builder()
93
+ .with_root_certificates(root_cert_store)
94
+ .with_no_client_auth();
95
+ acme_config
96
+ .client_tls_config(Arc::new(client_config))
97
+ .state()
98
+ } else {
99
+ acme_config.state()
100
+ };
101
+
102
+ let mut rustls_config = ServerConfig::builder()
103
+ .with_no_client_auth()
104
+ .with_cert_resolver(acme_state.resolver());
105
+
106
+ rustls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
107
+
108
+ let acceptor = acme_state.acceptor();
109
+ return Ok(ItsiTlsAcceptor::Automatic(
110
+ acceptor,
111
+ Arc::new(Mutex::new(acme_state)),
112
+ Arc::new(rustls_config),
113
+ ));
114
+ }
115
+ }
116
+ let (certs, key) = if let (Some(cert_path), Some(key_path)) =
117
+ (query_params.get("cert"), query_params.get("key"))
118
+ {
119
+ // Load from file or Base64
120
+ let certs = load_certs(cert_path);
121
+ let key = load_private_key(key_path);
122
+ (certs, key)
123
+ } else {
124
+ generate_ca_signed_cert(domains.unwrap_or(vec![host.to_owned()]))?
125
+ };
126
+
127
+ let mut config = ServerConfig::builder()
128
+ .with_no_client_auth()
129
+ .with_single_cert(certs, key)
130
+ .expect("Failed to build TLS config");
131
+
132
+ config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
133
+ Ok(ItsiTlsAcceptor::Manual(TlsAcceptor::from(Arc::new(config))))
134
+ }
135
+
136
+ pub fn load_certs(path: &str) -> Vec<CertificateDer<'static>> {
137
+ let data = if let Some(stripped) = path.strip_prefix("base64:") {
138
+ general_purpose::STANDARD
139
+ .decode(stripped)
140
+ .expect("Invalid base64 certificate")
141
+ } else {
142
+ fs::read(path).expect("Failed to read certificate file")
143
+ };
144
+
145
+ if data.starts_with(b"-----BEGIN ") {
146
+ let mut reader = BufReader::new(&data[..]);
147
+ let certs_der: Vec<Vec<u8>> = certs(&mut reader)
148
+ .map(|r| {
149
+ r.map(|der| der.as_ref().to_vec())
150
+ .map_err(itsi_error::ItsiError::from)
151
+ })
152
+ .collect::<Result<_>>()
153
+ .expect("Failed to parse certificate file");
154
+ certs_der
155
+ .into_iter()
156
+ .map(|vec| {
157
+ // Convert the owned Vec<u8> into a CertificateDer and force 'static.
158
+ unsafe { std::mem::transmute(CertificateDer::from(vec)) }
159
+ })
160
+ .collect()
161
+ } else {
162
+ vec![CertificateDer::from(data)]
163
+ }
164
+ }
165
+
166
+ /// Loads a private key from a file or Base64.
167
+ pub fn load_private_key(path: &str) -> PrivateKeyDer<'static> {
168
+ let key_data = if let Some(stripped) = path.strip_prefix("base64:") {
169
+ general_purpose::STANDARD
170
+ .decode(stripped)
171
+ .expect("Invalid base64 private key")
172
+ } else {
173
+ fs::read(path).expect("Failed to read private key file")
174
+ };
175
+
176
+ if key_data.starts_with(b"-----BEGIN ") {
177
+ let mut reader = BufReader::new(&key_data[..]);
178
+ let keys: Vec<Vec<u8>> = pkcs8_private_keys(&mut reader)
179
+ .map(|r| {
180
+ r.map(|key| key.secret_pkcs8_der().to_vec())
181
+ .map_err(itsi_error::ItsiError::from)
182
+ })
183
+ .collect::<Result<_>>()
184
+ .expect("Failed to parse private key");
185
+ if !keys.is_empty() {
186
+ return PrivateKeyDer::try_from(keys[0].clone()).unwrap();
187
+ }
188
+ }
189
+ PrivateKeyDer::try_from(key_data).unwrap()
190
+ }
191
+
192
+ pub fn generate_ca_signed_cert(
193
+ domains: Vec<String>,
194
+ ) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
195
+ info!(
196
+ domains = format!("{}", domains.join(", ")),
197
+ "Self signed cert",
198
+ );
199
+ info!(
200
+ "Add {} to your system's trusted cert store to resolve certificate errors.",
201
+ format!("{}/itsi_dev_ca.crt", ITSI_LOCAL_CA_DIR.to_str().unwrap())
202
+ );
203
+ info!("Dev CA path can be overridden by setting env var: `ITSI_LOCAL_CA_DIR`.");
204
+ let (ca_key_pem, ca_cert_pem) = get_or_create_local_dev_ca()?;
205
+
206
+ let ca_kp = KeyPair::from_pem(&ca_key_pem).expect("Failed to load CA key");
207
+ let ca_cert = CertificateParams::from_ca_cert_pem(&ca_cert_pem)
208
+ .expect("Failed to parse embedded CA certificate")
209
+ .self_signed(&ca_kp)
210
+ .expect("Failed to self-sign embedded CA cert");
211
+
212
+ let ee_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
213
+ let mut ee_params = CertificateParams::default();
214
+
215
+ use std::net::IpAddr;
216
+
217
+ ee_params.subject_alt_names = domains
218
+ .iter()
219
+ .map(|domain| {
220
+ if let Ok(ip) = domain.parse::<IpAddr>() {
221
+ SanType::IpAddress(ip)
222
+ } else {
223
+ SanType::DnsName(domain.clone().try_into().unwrap())
224
+ }
225
+ })
226
+ .collect();
227
+
228
+ ee_params
229
+ .distinguished_name
230
+ .push(DnType::CommonName, domains[0].clone());
231
+
232
+ ee_params.use_authority_key_identifier_extension = true;
233
+ ee_params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth];
234
+
235
+ let ee_cert = ee_params.signed_by(&ee_key, &ca_cert, &ca_kp).unwrap();
236
+ let ee_cert_der = ee_cert.der().to_vec();
237
+ let ee_cert = CertificateDer::from(ee_cert_der);
238
+
239
+ Ok((
240
+ vec![ee_cert],
241
+ PrivateKeyDer::try_from(ee_key.serialize_der()).unwrap(),
242
+ ))
243
+ }
244
+
245
+ fn get_or_create_local_dev_ca() -> Result<(String, String)> {
246
+ let ca_dir = &*ITSI_LOCAL_CA_DIR;
247
+ fs::create_dir_all(ca_dir)?;
248
+
249
+ let key_path = ca_dir.join("itsi_dev_ca.key");
250
+ let cert_path = ca_dir.join("itsi_dev_ca.crt");
251
+
252
+ if key_path.exists() && cert_path.exists() {
253
+ // Already have a local CA
254
+ let key_pem = fs::read_to_string(&key_path)?;
255
+ let cert_pem = fs::read_to_string(&cert_path)?;
256
+
257
+ Ok((key_pem, cert_pem))
258
+ } else {
259
+ let subject_alt_names = vec!["ca.itsi.fyi".to_string(), "localhost".to_string()];
260
+ let mut params = CertificateParams::new(subject_alt_names)?;
261
+ let mut distinguished_name = DistinguishedName::new();
262
+ distinguished_name.push(DnType::CommonName, "ca.itsi.fyi");
263
+ params.distinguished_name = distinguished_name;
264
+ params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
265
+ params.key_usages = vec![
266
+ KeyUsagePurpose::KeyCertSign,
267
+ KeyUsagePurpose::CrlSign,
268
+ KeyUsagePurpose::DigitalSignature, // useful for OCSP/CRL signing
269
+ ];
270
+ let key_pair = KeyPair::generate()?;
271
+ let cert = params.self_signed(&key_pair)?;
272
+
273
+ fs::write(&key_path, key_pair.serialize_pem())?;
274
+ fs::write(&cert_path, cert.pem())?;
275
+
276
+ Ok((key_pair.serialize_pem(), cert.pem()))
277
+ }
278
+ }
@@ -0,0 +1,32 @@
1
+ use std::ops::Deref;
2
+
3
+ use bytes::Bytes;
4
+
5
+ #[derive(Debug)]
6
+ pub enum ByteFrame {
7
+ Data(Bytes),
8
+ End(Bytes),
9
+ Empty,
10
+ }
11
+
12
+ impl Deref for ByteFrame {
13
+ type Target = Bytes;
14
+
15
+ fn deref(&self) -> &Self::Target {
16
+ match self {
17
+ ByteFrame::Data(data) => data,
18
+ ByteFrame::End(data) => data,
19
+ ByteFrame::Empty => unreachable!(),
20
+ }
21
+ }
22
+ }
23
+
24
+ impl From<ByteFrame> for Bytes {
25
+ fn from(frame: ByteFrame) -> Self {
26
+ match frame {
27
+ ByteFrame::Data(data) => data,
28
+ ByteFrame::End(data) => data,
29
+ ByteFrame::Empty => unreachable!(),
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,143 @@
1
+ use bytes::{Bytes, BytesMut};
2
+ use futures::Stream;
3
+ use std::convert::Infallible;
4
+ use std::future::Future;
5
+ use std::pin::Pin;
6
+ use std::task::{Context, Poll};
7
+ use std::time::Duration;
8
+ use tokio::sync::mpsc::Receiver;
9
+ use tokio::sync::watch;
10
+ use tokio::time::{sleep, Sleep};
11
+
12
+ use super::serve_strategy::single_mode::RunningPhase;
13
+
14
+ #[derive(Debug)]
15
+ pub struct FrameStream {
16
+ receiver: Receiver<Bytes>,
17
+ shutdown_rx: watch::Receiver<RunningPhase>,
18
+ drained: bool,
19
+ }
20
+
21
+ impl FrameStream {
22
+ pub fn new(receiver: Receiver<Bytes>, shutdown_rx: watch::Receiver<RunningPhase>) -> Self {
23
+ Self {
24
+ receiver,
25
+ shutdown_rx,
26
+ drained: false,
27
+ }
28
+ }
29
+ }
30
+
31
+ impl Stream for FrameStream {
32
+ type Item = Result<Bytes, Infallible>;
33
+
34
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
35
+ let this = self.get_mut();
36
+
37
+ if this.drained {
38
+ return Poll::Ready(None);
39
+ }
40
+
41
+ match Pin::new(&mut this.receiver).poll_recv(cx) {
42
+ Poll::Ready(Some(bytes)) => Poll::Ready(Some(Ok(bytes))),
43
+ Poll::Ready(None) => {
44
+ this.drained = true;
45
+ Poll::Ready(None)
46
+ }
47
+ Poll::Pending => {
48
+ if this.shutdown_rx.has_changed().unwrap_or(false)
49
+ && *this.shutdown_rx.borrow() == RunningPhase::ShutdownPending
50
+ {
51
+ if let Ok(bytes) = this.receiver.try_recv() {
52
+ return Poll::Ready(Some(Ok(bytes)));
53
+ }
54
+
55
+ this.drained = true;
56
+ return Poll::Ready(None);
57
+ }
58
+
59
+ Poll::Pending
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ /// BufferedStream wraps a stream of Bytes and coalesces chunks into a larger buffer,
66
+ /// flushing either after `max_flush_bytes` is reached or `max_flush_interval` elapses.
67
+ pub struct BufferedStream<S> {
68
+ inner: S,
69
+ buffer: BytesMut,
70
+ max_flush_bytes: usize,
71
+ max_flush_interval: Duration,
72
+ flush_deadline: Option<Pin<Box<Sleep>>>,
73
+ }
74
+
75
+ impl<S> BufferedStream<S> {
76
+ pub fn new(inner: S, max_flush_bytes: usize, max_flush_interval: Duration) -> Self {
77
+ Self {
78
+ inner,
79
+ buffer: BytesMut::with_capacity(max_flush_bytes),
80
+ max_flush_bytes,
81
+ max_flush_interval,
82
+ flush_deadline: None,
83
+ }
84
+ }
85
+ }
86
+
87
+ impl<S> Stream for BufferedStream<S>
88
+ where
89
+ S: Stream<Item = Result<Bytes, Infallible>> + Unpin,
90
+ {
91
+ type Item = Result<Bytes, Infallible>;
92
+
93
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
94
+ let this = self.get_mut();
95
+
96
+ loop {
97
+ // Flush on timer if needed
98
+ if let Some(deadline) = &mut this.flush_deadline {
99
+ if Pin::new(deadline).poll(cx).is_ready() && !this.buffer.is_empty() {
100
+ let flushed = this.buffer.split().freeze();
101
+ this.flush_deadline = None;
102
+ return Poll::Ready(Some(Ok(flushed)));
103
+ }
104
+ }
105
+
106
+ match Pin::new(&mut this.inner).poll_next(cx) {
107
+ Poll::Ready(Some(Ok(bytes))) => {
108
+ this.buffer.extend_from_slice(&bytes);
109
+
110
+ if bytes.is_empty() || this.buffer.len() >= this.max_flush_bytes {
111
+ let flushed = this.buffer.split().freeze();
112
+ this.flush_deadline = None;
113
+ return Poll::Ready(Some(Ok(flushed)));
114
+ }
115
+
116
+ if this.flush_deadline.is_none() {
117
+ this.flush_deadline = Some(Box::pin(sleep(this.max_flush_interval)));
118
+ }
119
+ }
120
+ Poll::Ready(None) => {
121
+ if this.buffer.is_empty() {
122
+ return Poll::Ready(None);
123
+ } else {
124
+ let flushed = this.buffer.split().freeze();
125
+ this.flush_deadline = None;
126
+ return Poll::Ready(Some(Ok(flushed)));
127
+ }
128
+ }
129
+ Poll::Pending => {
130
+ if let Some(deadline) = &mut this.flush_deadline {
131
+ let deadline = deadline.as_mut();
132
+ if deadline.poll(cx).is_ready() && !this.buffer.is_empty() {
133
+ let flushed = this.buffer.split().freeze();
134
+ this.flush_deadline = None;
135
+ return Poll::Ready(Some(Ok(flushed)));
136
+ }
137
+ }
138
+ return Poll::Pending;
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
@@ -0,0 +1,230 @@
1
+ use bytes::Bytes;
2
+ use core::fmt;
3
+ use futures::Stream;
4
+ use futures_util::TryStreamExt;
5
+ use http::Request;
6
+ use http_body_util::{combinators::WithTrailers, BodyExt, Either, Empty, Full, StreamBody};
7
+ use hyper::body::{Body, Frame, Incoming, SizeHint};
8
+ use std::{
9
+ convert::Infallible,
10
+ pin::Pin,
11
+ task::{Context, Poll},
12
+ };
13
+
14
+ use super::size_limited_incoming::SizeLimitedIncoming;
15
+
16
+ type Inner = Either<Full<Bytes>, Empty<Bytes>>;
17
+
18
+ type BoxStream =
19
+ Pin<Box<dyn Stream<Item = Result<Frame<Bytes>, Infallible>> + Send + Sync + 'static>>;
20
+
21
+ pub struct PlainBody(Either<StreamBody<BoxStream>, Inner>);
22
+
23
+ impl fmt::Debug for PlainBody {
24
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25
+ match &self.0 {
26
+ Either::Left(_) => f.write_str("PlainBody::Stream(..)"),
27
+ Either::Right(inner) => match inner {
28
+ Either::Left(full) => f.debug_tuple("PlainBody::Full").field(full).finish(),
29
+ Either::Right(_) => f.write_str("PlainBody::Empty"),
30
+ },
31
+ }
32
+ }
33
+ }
34
+ type DynErr = Box<dyn std::error::Error + Send + Sync>;
35
+
36
+ impl Body for PlainBody {
37
+ type Data = Bytes;
38
+ type Error = DynErr;
39
+
40
+ fn poll_frame(
41
+ self: Pin<&mut Self>,
42
+ cx: &mut Context<'_>,
43
+ ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
44
+ unsafe { self.map_unchecked_mut(|s| &mut s.0) }.poll_frame(cx)
45
+ }
46
+
47
+ fn size_hint(&self) -> SizeHint {
48
+ self.0.size_hint()
49
+ }
50
+ }
51
+
52
+ impl PlainBody {
53
+ fn stream<S>(s: S) -> Self
54
+ where
55
+ S: Stream<Item = Result<Bytes, Infallible>> + Send + Sync + 'static,
56
+ {
57
+ let boxed: BoxStream = Box::pin(s.map_ok(Frame::data));
58
+ Self(Either::Left(StreamBody::new(boxed)))
59
+ }
60
+
61
+ fn full(bytes: Bytes) -> Self {
62
+ Self(Either::Right(Either::Left(Full::new(bytes))))
63
+ }
64
+
65
+ fn empty() -> Self {
66
+ Self(Either::Right(Either::Right(Empty::new())))
67
+ }
68
+ }
69
+
70
+ type BoxTrailers = Pin<
71
+ Box<dyn std::future::Future<Output = Option<Result<http::HeaderMap, DynErr>>> + Send + Sync>,
72
+ >;
73
+
74
+ pub enum HttpBody {
75
+ Plain(PlainBody),
76
+ WithT(WithTrailers<PlainBody, BoxTrailers>),
77
+ }
78
+
79
+ impl fmt::Debug for HttpBody {
80
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81
+ match self {
82
+ HttpBody::Plain(b) => f.debug_tuple("HttpBody::Plain").field(b).finish(),
83
+ HttpBody::WithT(_) => f.write_str("HttpBody::WithT(..)"),
84
+ }
85
+ }
86
+ }
87
+
88
+ impl Body for HttpBody {
89
+ type Data = Bytes;
90
+ type Error = DynErr;
91
+
92
+ fn poll_frame(
93
+ self: Pin<&mut Self>,
94
+ cx: &mut Context<'_>,
95
+ ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
96
+ unsafe {
97
+ match self.get_unchecked_mut() {
98
+ HttpBody::Plain(b) => Pin::new_unchecked(b).poll_frame(cx),
99
+ HttpBody::WithT(b) => Pin::new_unchecked(b).poll_frame(cx),
100
+ }
101
+ }
102
+ }
103
+
104
+ fn size_hint(&self) -> SizeHint {
105
+ match self {
106
+ HttpBody::Plain(b) => b.size_hint(),
107
+ HttpBody::WithT(b) => b.size_hint(),
108
+ }
109
+ }
110
+ }
111
+
112
+ impl HttpBody {
113
+ pub fn stream<S>(s: S) -> Self
114
+ where
115
+ S: Stream<Item = Result<Bytes, Infallible>> + Send + Sync + 'static,
116
+ {
117
+ HttpBody::Plain(PlainBody::stream(s))
118
+ }
119
+
120
+ pub fn full(bytes: Bytes) -> Self {
121
+ HttpBody::Plain(PlainBody::full(bytes))
122
+ }
123
+
124
+ pub fn empty() -> Self {
125
+ HttpBody::Plain(PlainBody::empty())
126
+ }
127
+
128
+ pub fn with_trailers<Fut>(self, fut: Fut) -> Self
129
+ where
130
+ Fut: std::future::Future<Output = Option<Result<http::HeaderMap, DynErr>>>
131
+ + Send
132
+ + Sync
133
+ + 'static,
134
+ {
135
+ let boxed: BoxTrailers = Box::pin(fut);
136
+ match self {
137
+ HttpBody::Plain(p) => HttpBody::WithT(p.with_trailers(boxed)),
138
+ already @ HttpBody::WithT(_) => already,
139
+ }
140
+ }
141
+ }
142
+
143
+ pub type HttpResponse = http::Response<HttpBody>;
144
+ pub type HttpRequest = Request<SizeLimitedIncoming<Incoming>>;
145
+
146
+ pub trait ConversionExt {
147
+ fn limit(self) -> HttpRequest;
148
+ }
149
+
150
+ impl ConversionExt for Request<Incoming> {
151
+ fn limit(self) -> HttpRequest {
152
+ let (parts, body) = self.into_parts();
153
+ Request::from_parts(parts, SizeLimitedIncoming::new(body))
154
+ }
155
+ }
156
+
157
+ pub trait RequestExt {
158
+ fn content_type(&self) -> Option<&str>;
159
+ fn accept(&self) -> Option<&str>;
160
+ fn header(&self, header_name: &str) -> Option<&str>;
161
+ fn query_param(&self, query_name: &str) -> Option<&str>;
162
+ }
163
+
164
+ pub trait PathExt {
165
+ fn no_trailing_slash(&self) -> &str;
166
+ }
167
+
168
+ #[derive(Debug, Clone, Copy)]
169
+ pub enum ResponseFormat {
170
+ JSON,
171
+ HTML,
172
+ TEXT,
173
+ UNKNOWN,
174
+ }
175
+
176
+ #[derive(Debug, Clone, Default)]
177
+ pub struct SupportedEncodingSet {
178
+ pub zstd: bool,
179
+ pub br: bool,
180
+ pub deflate: bool,
181
+ pub gzip: bool,
182
+ }
183
+
184
+ impl From<Option<&str>> for ResponseFormat {
185
+ fn from(value: Option<&str>) -> Self {
186
+ match value {
187
+ Some("application/json") => ResponseFormat::JSON,
188
+ Some("text/html") => ResponseFormat::HTML,
189
+ Some("text/plain") => ResponseFormat::TEXT,
190
+ _ => ResponseFormat::UNKNOWN,
191
+ }
192
+ }
193
+ }
194
+
195
+ impl PathExt for str {
196
+ fn no_trailing_slash(&self) -> &str {
197
+ if self == "/" {
198
+ self
199
+ } else {
200
+ self.trim_end_matches('/')
201
+ }
202
+ }
203
+ }
204
+
205
+ impl RequestExt for HttpRequest {
206
+ fn content_type(&self) -> Option<&str> {
207
+ self.headers()
208
+ .get("content-type")
209
+ .map(|hv| hv.to_str().unwrap_or(""))
210
+ }
211
+
212
+ fn accept(&self) -> Option<&str> {
213
+ self.headers()
214
+ .get("accept")
215
+ .map(|hv| hv.to_str().unwrap_or(""))
216
+ }
217
+
218
+ fn header(&self, header_name: &str) -> Option<&str> {
219
+ self.headers()
220
+ .get(header_name)
221
+ .map(|hv| hv.to_str().unwrap_or(""))
222
+ }
223
+
224
+ fn query_param(&self, query_name: &str) -> Option<&str> {
225
+ self.uri()
226
+ .query()
227
+ .and_then(|q| q.split('&').find(|p| p.starts_with(query_name)))
228
+ .map(|p| p.split('=').nth(1).unwrap_or(""))
229
+ }
230
+ }