itsi-server 0.1.19 → 0.1.20

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 (174) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +950 -239
  3. data/README.md +2 -0
  4. data/exe/itsi +5 -5
  5. data/ext/itsi_acme/Cargo.toml +86 -0
  6. data/ext/itsi_acme/examples/high_level.rs +63 -0
  7. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  8. data/ext/itsi_acme/examples/low_level.rs +87 -0
  9. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  10. data/ext/itsi_acme/src/acceptor.rs +81 -0
  11. data/ext/itsi_acme/src/acme.rs +354 -0
  12. data/ext/itsi_acme/src/axum.rs +86 -0
  13. data/ext/itsi_acme/src/cache.rs +39 -0
  14. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  15. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  16. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  17. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  18. data/ext/itsi_acme/src/caches/no.rs +78 -0
  19. data/ext/itsi_acme/src/caches/test.rs +136 -0
  20. data/ext/itsi_acme/src/config.rs +172 -0
  21. data/ext/itsi_acme/src/https_helper.rs +69 -0
  22. data/ext/itsi_acme/src/incoming.rs +142 -0
  23. data/ext/itsi_acme/src/jose.rs +161 -0
  24. data/ext/itsi_acme/src/lib.rs +142 -0
  25. data/ext/itsi_acme/src/resolver.rs +59 -0
  26. data/ext/itsi_acme/src/state.rs +424 -0
  27. data/ext/itsi_server/Cargo.toml +3 -3
  28. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +2 -2
  29. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +150 -19
  30. data/ext/itsi_server/src/ruby_types/itsi_server.rs +1 -0
  31. data/ext/itsi_server/src/server/binds/listener.rs +34 -29
  32. data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +2 -2
  33. data/ext/itsi_server/src/server/binds/tls.rs +1 -1
  34. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +33 -28
  35. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +56 -3
  36. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +179 -0
  37. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +25 -2
  38. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +3 -3
  39. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +2 -1
  40. data/ext/itsi_server/src/server/middleware_stack/mod.rs +32 -34
  41. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +10 -4
  42. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +30 -7
  43. data/ext/itsi_server/src/server/thread_worker.rs +2 -2
  44. data/ext/itsi_server/src/services/static_file_server.rs +30 -28
  45. data/ext/itsi_tracing/src/lib.rs +39 -8
  46. data/lib/itsi/server/config/config_helpers.rb +93 -0
  47. data/lib/itsi/server/config/dsl.rb +81 -33
  48. data/lib/itsi/server/config/known_paths/KitchensinkDirectories.txt +2346 -0
  49. data/lib/itsi/server/config/known_paths/Randomfiles.txt +24 -0
  50. data/lib/itsi/server/config/known_paths/UnixDotfiles.txt +52 -0
  51. data/lib/itsi/server/config/known_paths/backdoors/ASP_CommonBackdoors.txt +29 -0
  52. data/lib/itsi/server/config/known_paths/backdoors/bot_control_panels.txt +1668 -0
  53. data/lib/itsi/server/config/known_paths/backdoors/shells.txt +1167 -0
  54. data/lib/itsi/server/config/known_paths/cgi/CGI_HTTP_POST.txt +7 -0
  55. data/lib/itsi/server/config/known_paths/cgi/CGI_HTTP_POST_Windows.txt +6 -0
  56. data/lib/itsi/server/config/known_paths/cgi/CGI_Microsoft.txt +79 -0
  57. data/lib/itsi/server/config/known_paths/cgi/CGI_XPlatform.txt +3948 -0
  58. data/lib/itsi/server/config/known_paths/cms/README.md +5 -0
  59. data/lib/itsi/server/config/known_paths/cms/drupal_plugins.txt +6320 -0
  60. data/lib/itsi/server/config/known_paths/cms/drupal_themes.txt +828 -0
  61. data/lib/itsi/server/config/known_paths/cms/joomla_plugins.txt +224 -0
  62. data/lib/itsi/server/config/known_paths/cms/joomla_themes.txt +30 -0
  63. data/lib/itsi/server/config/known_paths/cms/php-nuke.txt +2142 -0
  64. data/lib/itsi/server/config/known_paths/cms/wordpress.txt +1566 -0
  65. data/lib/itsi/server/config/known_paths/cms/wp_common_theme_files.txt +46 -0
  66. data/lib/itsi/server/config/known_paths/cms/wp_plugins.txt +13366 -0
  67. data/lib/itsi/server/config/known_paths/cms/wp_plugins_full.txt +68662 -0
  68. data/lib/itsi/server/config/known_paths/cms/wp_plugins_top225.txt +225 -0
  69. data/lib/itsi/server/config/known_paths/cms/wp_themes.readme +12 -0
  70. data/lib/itsi/server/config/known_paths/cms/wp_themes.txt +7336 -0
  71. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/3CharExtBrute.txt +17576 -0
  72. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/CommonWebExtensions.txt +80 -0
  73. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Backup.txt +14 -0
  74. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Common.txt +865 -0
  75. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Compressed.txt +186 -0
  76. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Mostcommon.txt +30 -0
  77. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Skipfish.txt +93 -0
  78. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/WordlistSkipfish.txt +1918 -0
  79. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/copy_of.txt +8 -0
  80. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-directories-lowercase.txt +56180 -0
  81. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-directories.txt +62290 -0
  82. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-extensions-lowercase.txt +2367 -0
  83. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-extensions.txt +2450 -0
  84. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-files-lowercase.txt +35323 -0
  85. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-files.txt +37037 -0
  86. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-words-lowercase.txt +107982 -0
  87. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-words.txt +119600 -0
  88. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-directories-lowercase.txt +26593 -0
  89. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-directories.txt +30009 -0
  90. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-extensions-lowercase.txt +1233 -0
  91. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-extensions.txt +1289 -0
  92. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-files-lowercase.txt +16243 -0
  93. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-files.txt +17128 -0
  94. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-words-lowercase.txt +56293 -0
  95. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-words.txt +63087 -0
  96. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-directories-lowercase.txt +17776 -0
  97. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-directories.txt +20122 -0
  98. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-extensions-lowercase.txt +914 -0
  99. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-extensions.txt +963 -0
  100. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-files-lowercase.txt +10848 -0
  101. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-files.txt +11424 -0
  102. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-words-lowercase.txt +38267 -0
  103. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-words.txt +43003 -0
  104. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/spanish.txt +445 -0
  105. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/test_demo.txt +36 -0
  106. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/upload_variants.txt +44 -0
  107. data/lib/itsi/server/config/known_paths/login-file-locations/Logins.txt +71 -0
  108. data/lib/itsi/server/config/known_paths/login-file-locations/cfm.txt +294 -0
  109. data/lib/itsi/server/config/known_paths/login-file-locations/html.txt +295 -0
  110. data/lib/itsi/server/config/known_paths/login-file-locations/jsp.txt +294 -0
  111. data/lib/itsi/server/config/known_paths/login-file-locations/php.txt +294 -0
  112. data/lib/itsi/server/config/known_paths/login-file-locations/windows-asp.txt +294 -0
  113. data/lib/itsi/server/config/known_paths/login-file-locations/windows-aspx.txt +294 -0
  114. data/lib/itsi/server/config/known_paths/password-file-locations/Passwords.txt +47 -0
  115. data/lib/itsi/server/config/known_paths/php/PHP.txt +30 -0
  116. data/lib/itsi/server/config/known_paths/php/PHP_CommonBackdoors.txt +5 -0
  117. data/lib/itsi/server/config/known_paths/proxy-conf.txt +31 -0
  118. data/lib/itsi/server/config/known_paths/tftp.txt +79 -0
  119. data/lib/itsi/server/config/known_paths/webservers-appservers/ADFS.txt +86 -0
  120. data/lib/itsi/server/config/known_paths/webservers-appservers/AdobeXML.txt +16 -0
  121. data/lib/itsi/server/config/known_paths/webservers-appservers/Apache.txt +101 -0
  122. data/lib/itsi/server/config/known_paths/webservers-appservers/ApacheTomcat.txt +47 -0
  123. data/lib/itsi/server/config/known_paths/webservers-appservers/Apache_Axis.txt +16 -0
  124. data/lib/itsi/server/config/known_paths/webservers-appservers/ColdFusion.txt +111 -0
  125. data/lib/itsi/server/config/known_paths/webservers-appservers/FatwireCMS.txt +390 -0
  126. data/lib/itsi/server/config/known_paths/webservers-appservers/Frontpage.txt +38 -0
  127. data/lib/itsi/server/config/known_paths/webservers-appservers/HP_System_Mgmt_Homepage.txt +239 -0
  128. data/lib/itsi/server/config/known_paths/webservers-appservers/HTTP_POST_Microsoft.txt +2 -0
  129. data/lib/itsi/server/config/known_paths/webservers-appservers/Hyperion.txt +578 -0
  130. data/lib/itsi/server/config/known_paths/webservers-appservers/IIS.txt +187 -0
  131. data/lib/itsi/server/config/known_paths/webservers-appservers/JBoss.txt +5 -0
  132. data/lib/itsi/server/config/known_paths/webservers-appservers/JRun.txt +13 -0
  133. data/lib/itsi/server/config/known_paths/webservers-appservers/JavaServlets_Common.txt +3 -0
  134. data/lib/itsi/server/config/known_paths/webservers-appservers/Joomla_exploitable.txt +1937 -0
  135. data/lib/itsi/server/config/known_paths/webservers-appservers/LotusNotes.txt +206 -0
  136. data/lib/itsi/server/config/known_paths/webservers-appservers/Netware.txt +18 -0
  137. data/lib/itsi/server/config/known_paths/webservers-appservers/Oracle9i.txt +60 -0
  138. data/lib/itsi/server/config/known_paths/webservers-appservers/OracleAppServer.txt +192 -0
  139. data/lib/itsi/server/config/known_paths/webservers-appservers/README.md +6 -0
  140. data/lib/itsi/server/config/known_paths/webservers-appservers/Ruby_Rails.txt +121 -0
  141. data/lib/itsi/server/config/known_paths/webservers-appservers/SAP.txt +463 -0
  142. data/lib/itsi/server/config/known_paths/webservers-appservers/Sharepoint.txt +1707 -0
  143. data/lib/itsi/server/config/known_paths/webservers-appservers/SiteMinder.txt +19 -0
  144. data/lib/itsi/server/config/known_paths/webservers-appservers/SunAppServerGlassfish.txt +51 -0
  145. data/lib/itsi/server/config/known_paths/webservers-appservers/SuniPlanet.txt +35 -0
  146. data/lib/itsi/server/config/known_paths/webservers-appservers/Vignette.txt +73 -0
  147. data/lib/itsi/server/config/known_paths/webservers-appservers/Weblogic.txt +160 -0
  148. data/lib/itsi/server/config/known_paths/webservers-appservers/Websphere.txt +366 -0
  149. data/lib/itsi/server/config/known_paths/wellknown-rfc5785.txt +30 -0
  150. data/lib/itsi/server/config/known_paths.rb +17 -0
  151. data/lib/itsi/server/config/middleware/_index.md +54 -0
  152. data/lib/itsi/server/config/middleware/log_requests.md +63 -0
  153. data/lib/itsi/server/config/middleware/log_requests.rb +33 -0
  154. data/lib/itsi/server/config/middleware.rb +9 -0
  155. data/lib/itsi/server/config/option.rb +9 -0
  156. data/lib/itsi/server/config/options/_index.md +36 -0
  157. data/lib/itsi/server/config/options/fiber_scheduler.md +35 -0
  158. data/lib/itsi/server/config/options/fiber_scheduler.rb +18 -0
  159. data/lib/itsi/server/config/options/threads.md +39 -0
  160. data/lib/itsi/server/config/options/threads.rb +17 -0
  161. data/lib/itsi/server/config/options/workers.md +43 -0
  162. data/lib/itsi/server/config/options/workers.rb +17 -0
  163. data/lib/itsi/server/config/typed_struct.rb +203 -0
  164. data/lib/itsi/server/config.rb +124 -30
  165. data/lib/itsi/server/signal_trap.rb +5 -1
  166. data/lib/itsi/server/typed_handlers/source_parser.rb +1 -1
  167. data/lib/itsi/server/version.rb +1 -1
  168. data/lib/itsi/server.rb +27 -6
  169. data/lib/ruby_lsp/itsi/addon.rb +64 -48
  170. metadata +141 -5
  171. data/CHANGELOG.md +0 -10
  172. data/CODE_OF_CONDUCT.md +0 -139
  173. data/LICENSE.txt +0 -21
  174. data/_index.md +0 -6
@@ -7,8 +7,9 @@ use crate::{
7
7
  },
8
8
  };
9
9
  use derive_more::Debug;
10
+ use futures::executor::block_on;
10
11
  use itsi_rb_helpers::{call_with_gvl, print_rb_backtrace, HeapValue};
11
- use itsi_tracing::{set_format, set_level, set_target};
12
+ use itsi_tracing::{set_format, set_level, set_target, set_target_filters};
12
13
  use magnus::{
13
14
  block::Proc,
14
15
  error::Result,
@@ -27,6 +28,7 @@ use std::{
27
28
  sync::{Arc, OnceLock},
28
29
  time::Duration,
29
30
  };
31
+ use tracing::{debug, error};
30
32
  static DEFAULT_BIND: &str = "http://localhost:3000";
31
33
  static ID_BUILD_CONFIG: LazyId = LazyId::new("build_config");
32
34
  static ID_RELOAD_EXEC: LazyId = LazyId::new("reload_exec");
@@ -52,7 +54,9 @@ pub struct ServerParams {
52
54
  pub preload: bool,
53
55
 
54
56
  pub request_timeout: Option<Duration>,
57
+ pub header_read_timeout: Duration,
55
58
  pub notify_watchers: Option<Vec<(String, Vec<Vec<String>>)>>,
59
+
56
60
  /// Worker params
57
61
  pub threads: u8,
58
62
  pub scheduler_threads: Option<u8>,
@@ -69,26 +73,39 @@ pub struct ServerParams {
69
73
  listener_info: Mutex<HashMap<String, i32>>,
70
74
  }
71
75
 
76
+ pub struct SocketOpts {
77
+ pub reuse_address: bool,
78
+ pub reuse_port: bool,
79
+ pub listen_backlog: usize,
80
+ pub nodelay: bool,
81
+ pub recv_buffer_size: usize,
82
+ }
83
+
72
84
  impl ServerParams {
73
85
  pub fn preload_ruby(self: &Arc<Self>) -> Result<()> {
74
86
  call_with_gvl(|ruby| -> Result<()> {
87
+ debug!("Preloading Ruby");
75
88
  if self
76
89
  .scheduler_class
77
90
  .as_ref()
78
91
  .is_some_and(|t| t == "Itsi::Scheduler")
79
92
  {
93
+ debug!("Loading Itsi Scheduler");
80
94
  ruby.require("itsi/scheduler")?;
81
95
  }
82
- let middleware = MiddlewareSet::new(
83
- self.middleware_loader
84
- .call::<_, Option<Value>>(())
85
- .inspect_err(|e| {
86
- if let Some(err_value) = e.value() {
87
- print_rb_backtrace(err_value);
88
- }
89
- })?
90
- .map(|mw| mw.into()),
91
- )?;
96
+ let routes_raw = self
97
+ .middleware_loader
98
+ .call::<_, Option<Value>>(())
99
+ .inspect_err(|e| {
100
+ eprintln!("Error loading middleware: {:?}", e);
101
+ if let Some(err_value) = e.value() {
102
+ print_rb_backtrace(err_value);
103
+ }
104
+ })?
105
+ .map(|mw| mw.into());
106
+ debug!("Middleware routes returned");
107
+ let middleware = MiddlewareSet::new(routes_raw)?;
108
+ debug!("Middleware loaded");
92
109
  self.middleware.set(middleware).map_err(|_| {
93
110
  magnus::Error::new(
94
111
  magnus::exception::runtime_error(),
@@ -100,6 +117,11 @@ impl ServerParams {
100
117
  Ok(())
101
118
  }
102
119
 
120
+ pub async fn initialize_middleware(self: &Arc<Self>) -> Result<()> {
121
+ self.middleware.get().unwrap().initialize_layers().await?;
122
+ Ok(())
123
+ }
124
+
103
125
  fn from_rb_hash(rb_param_hash: RHash) -> Result<ServerParams> {
104
126
  let workers = rb_param_hash
105
127
  .fetch::<_, Option<u8>>("workers")?
@@ -134,6 +156,10 @@ impl ServerParams {
134
156
  let preload: bool = rb_param_hash.fetch("preload")?;
135
157
  let request_timeout: Option<u64> = rb_param_hash.fetch("request_timeout")?;
136
158
  let request_timeout = request_timeout.map(Duration::from_secs);
159
+ let header_read_timeout: Duration = rb_param_hash
160
+ .fetch::<_, Option<u64>>("header_read_timeout")?
161
+ .map(Duration::from_secs)
162
+ .unwrap_or(Duration::from_secs(1));
137
163
 
138
164
  let notify_watchers: Option<Vec<(String, Vec<Vec<String>>)>> =
139
165
  rb_param_hash.fetch("notify_watchers")?;
@@ -147,6 +173,23 @@ impl ServerParams {
147
173
  let log_level: Option<String> = rb_param_hash.fetch("log_level")?;
148
174
  let log_target: Option<String> = rb_param_hash.fetch("log_target")?;
149
175
  let log_format: Option<String> = rb_param_hash.fetch("log_format")?;
176
+ let log_target_filters: Option<Vec<String>> = rb_param_hash.fetch("log_target_filters")?;
177
+
178
+ let reuse_address: bool = rb_param_hash
179
+ .fetch::<_, Option<bool>>("reuse_address")?
180
+ .unwrap_or(true);
181
+ let reuse_port: bool = rb_param_hash
182
+ .fetch::<_, Option<bool>>("reuse_port")?
183
+ .unwrap_or(true);
184
+ let listen_backlog: usize = rb_param_hash
185
+ .fetch::<_, Option<usize>>("listen_backlog")?
186
+ .unwrap_or(1024);
187
+ let nodelay: bool = rb_param_hash
188
+ .fetch::<_, Option<bool>>("nodelay")?
189
+ .unwrap_or(true);
190
+ let recv_buffer_size: usize = rb_param_hash
191
+ .fetch::<_, Option<usize>>("recv_buffer_size")?
192
+ .unwrap_or(262_144);
150
193
 
151
194
  if let Some(level) = log_level {
152
195
  set_level(&level);
@@ -160,6 +203,22 @@ impl ServerParams {
160
203
  set_format(&format);
161
204
  }
162
205
 
206
+ if let Some(target_filters) = log_target_filters {
207
+ let target_filters = target_filters
208
+ .iter()
209
+ .filter_map(|filter| {
210
+ let mut parts = filter.splitn(2, '=');
211
+ if let (Some(target), Some(level_str)) = (parts.next(), parts.next()) {
212
+ if let Ok(level) = level_str.parse::<tracing::Level>() {
213
+ return Some((target, level));
214
+ }
215
+ }
216
+ None
217
+ })
218
+ .collect::<Vec<(&str, tracing::Level)>>();
219
+ set_target_filters(target_filters);
220
+ }
221
+
163
222
  let binds: Option<Vec<String>> = rb_param_hash.fetch("binds")?;
164
223
  let binds = binds
165
224
  .unwrap_or_else(|| vec![DEFAULT_BIND.to_string()])
@@ -167,6 +226,13 @@ impl ServerParams {
167
226
  .map(|s| s.parse())
168
227
  .collect::<itsi_error::Result<Vec<Bind>>>()?;
169
228
 
229
+ let socket_opts = SocketOpts {
230
+ reuse_address,
231
+ reuse_port,
232
+ listen_backlog,
233
+ nodelay,
234
+ recv_buffer_size,
235
+ };
170
236
  let listeners = if let Some(preexisting_listeners) =
171
237
  rb_param_hash.delete::<_, Option<String>>("listeners")?
172
238
  {
@@ -183,9 +249,9 @@ impl ServerParams {
183
249
  .cloned()
184
250
  .map(|bind| {
185
251
  if let Some(fd) = bind_to_fd_map.get(&bind.listener_address_string()) {
186
- Listener::inherit_fd(bind, *fd)
252
+ Listener::inherit_fd(bind, *fd, &socket_opts)
187
253
  } else {
188
- Listener::try_from(bind)
254
+ Listener::build(bind, &socket_opts)
189
255
  }
190
256
  })
191
257
  .collect::<std::result::Result<Vec<Listener>, _>>()?
@@ -195,7 +261,7 @@ impl ServerParams {
195
261
  binds
196
262
  .iter()
197
263
  .cloned()
198
- .map(Listener::try_from)
264
+ .map(|b| Listener::build(b, &socket_opts))
199
265
  .collect::<std::result::Result<Vec<Listener>, _>>()?
200
266
  .into_iter()
201
267
  .collect::<Vec<_>>()
@@ -220,6 +286,7 @@ impl ServerParams {
220
286
  hooks,
221
287
  preload,
222
288
  request_timeout,
289
+ header_read_timeout,
223
290
  notify_watchers,
224
291
  threads,
225
292
  scheduler_threads,
@@ -249,6 +316,7 @@ impl ItsiServerConfig {
249
316
  itsifile_path.as_ref(),
250
317
  itsi_config_proc.clone(),
251
318
  )?;
319
+
252
320
  cli_params.delete::<_, Value>(Symbol::new("listeners"))?;
253
321
 
254
322
  let watcher_fd = if let Some(watchers) = server_params.notify_watchers.clone() {
@@ -276,7 +344,6 @@ impl ItsiServerConfig {
276
344
  self.itsi_config_proc.clone(),
277
345
  )
278
346
  })?;
279
-
280
347
  let is_single_mode = self.server_params.read().workers == 1;
281
348
 
282
349
  let requires_exec = if !is_single_mode && !server_params.preload {
@@ -305,10 +372,18 @@ impl ItsiServerConfig {
305
372
  .as_ref()
306
373
  .clone()
307
374
  .map(|hv| hv.clone().inner());
308
- let rb_param_hash: RHash = ruby.get_inner_ref(&ITSI_SERVER_CONFIG).funcall(
309
- *ID_BUILD_CONFIG,
310
- (cli_params, itsifile_path.cloned(), inner),
311
- )?;
375
+ let (rb_param_hash, errors): (RHash, Vec<String>) =
376
+ ruby.get_inner_ref(&ITSI_SERVER_CONFIG).funcall(
377
+ *ID_BUILD_CONFIG,
378
+ (cli_params, itsifile_path.cloned(), inner),
379
+ )?;
380
+ if !errors.is_empty() {
381
+ Self::print_config_errors(errors);
382
+ return Err(magnus::Error::new(
383
+ magnus::exception::standard_error(),
384
+ "Invalid server config",
385
+ ));
386
+ }
312
387
  Ok(Arc::new(ServerParams::from_rb_hash(rb_param_hash)?))
313
388
  }
314
389
 
@@ -322,6 +397,47 @@ impl ItsiServerConfig {
322
397
  Ok(())
323
398
  }
324
399
 
400
+ pub fn get_config_errors(&self) -> Option<Vec<String>> {
401
+ let rb_param_hash = call_with_gvl(|ruby| {
402
+ let inner = self
403
+ .itsi_config_proc
404
+ .as_ref()
405
+ .clone()
406
+ .map(|hv| hv.clone().inner());
407
+ let cli_params = self.cli_params.cloned();
408
+ let itsifile_path = self.itsifile_path.clone();
409
+
410
+ let (rb_param_hash, errors): (RHash, Vec<String>) = ruby
411
+ .get_inner_ref(&ITSI_SERVER_CONFIG)
412
+ .funcall(*ID_BUILD_CONFIG, (cli_params, itsifile_path, inner))
413
+ .unwrap();
414
+ if !errors.is_empty() {
415
+ return Err(errors);
416
+ }
417
+ Ok(rb_param_hash)
418
+ });
419
+ match rb_param_hash {
420
+ Ok(rb_param_hash) => match ServerParams::from_rb_hash(rb_param_hash) {
421
+ Ok(test_params) => {
422
+ let params_arc = Arc::new(test_params);
423
+ if let Err(err) = params_arc.clone().preload_ruby() {
424
+ let err_val = call_with_gvl(|_| format!("{}", err));
425
+ return Some(vec![err_val]);
426
+ }
427
+ if let Err(err) =
428
+ block_on(params_arc.middleware.get().unwrap().initialize_layers())
429
+ {
430
+ let err_val = call_with_gvl(|_| format!("{}", err));
431
+ return Some(vec![err_val]);
432
+ }
433
+ None
434
+ }
435
+ Err(err) => Some(vec![format!("{:?}", err)]),
436
+ },
437
+ Err(err) => Some(err),
438
+ }
439
+ }
440
+
325
441
  pub fn dup_fds(self: &Arc<Self>) -> Result<()> {
326
442
  let binding = self.server_params.read();
327
443
  let mut listener_info_guard = binding.listener_info.lock();
@@ -354,6 +470,21 @@ impl ItsiServerConfig {
354
470
  Ok(())
355
471
  }
356
472
 
473
+ pub fn print_config_errors(errors: Vec<String>) {
474
+ error!("Refusing to reload configuration due to fatal errors:");
475
+ for error in errors {
476
+ eprintln!("{}", error);
477
+ }
478
+ }
479
+
480
+ pub fn check_config(&self) -> bool {
481
+ if let Some(errors) = self.get_config_errors() {
482
+ Self::print_config_errors(errors);
483
+ return false;
484
+ }
485
+ true
486
+ }
487
+
357
488
  pub fn reload_exec(self: &Arc<Self>) -> Result<()> {
358
489
  let listener_json =
359
490
  serde_json::to_string(&self.server_params.read().listener_info.lock().clone())
@@ -72,6 +72,7 @@ impl ItsiServer {
72
72
  let strategy = self.build_strategy()?;
73
73
  if let Err(e) = strategy.clone().run() {
74
74
  error!("Error running server: {}", e);
75
+ send_lifecycle_event(LifecycleEvent::Shutdown);
75
76
  strategy.stop()?;
76
77
  }
77
78
  Ok(())
@@ -1,4 +1,5 @@
1
1
  use crate::prelude::*;
2
+ use crate::ruby_types::itsi_server::itsi_server_config::SocketOpts;
2
3
  use crate::server::io_stream::IoStream;
3
4
  use crate::server::serve_strategy::single_mode::RunningPhase;
4
5
 
@@ -326,12 +327,12 @@ impl Listener {
326
327
  }
327
328
  }
328
329
 
329
- pub fn inherit_fd(bind: Bind, fd: RawFd) -> Result<Self> {
330
+ pub fn inherit_fd(bind: Bind, fd: RawFd, socket_opts: &SocketOpts) -> Result<Self> {
330
331
  let bound = match bind.address {
331
332
  BindAddress::Ip(_) => match bind.protocol {
332
- BindProtocol::Http => Listener::Tcp(revive_tcp_socket(fd)?),
333
+ BindProtocol::Http => Listener::Tcp(revive_tcp_socket(fd, socket_opts)?),
333
334
  BindProtocol::Https => {
334
- let tcp_listener = revive_tcp_socket(fd)?;
335
+ let tcp_listener = revive_tcp_socket(fd, socket_opts)?;
335
336
  Listener::TcpTls((
336
337
  tcp_listener,
337
338
  bind.tls_config.unwrap().build_acceptor().unwrap(),
@@ -341,25 +342,25 @@ impl Listener {
341
342
  },
342
343
  BindAddress::UnixSocket(_) => match bind.tls_config {
343
344
  Some(tls_config) => Listener::UnixTls((
344
- revive_unix_socket(fd)?,
345
+ revive_unix_socket(fd, socket_opts)?,
345
346
  tls_config.build_acceptor().unwrap(),
346
347
  )),
347
- None => Listener::Unix(revive_unix_socket(fd)?),
348
+ None => Listener::Unix(revive_unix_socket(fd, socket_opts)?),
348
349
  },
349
350
  };
350
351
  Ok(bound)
351
352
  }
352
353
  }
353
354
 
354
- impl TryFrom<Bind> for Listener {
355
- type Error = itsi_error::ItsiError;
356
-
357
- fn try_from(bind: Bind) -> std::result::Result<Self, Self::Error> {
355
+ impl Listener {
356
+ pub fn build(bind: Bind, socket_opts: &SocketOpts) -> Result<Self> {
358
357
  let bound = match bind.address {
359
358
  BindAddress::Ip(addr) => match bind.protocol {
360
- BindProtocol::Http => Listener::Tcp(connect_tcp_socket(addr, bind.port.unwrap())?),
359
+ BindProtocol::Http => {
360
+ Listener::Tcp(connect_tcp_socket(addr, bind.port.unwrap(), socket_opts)?)
361
+ }
361
362
  BindProtocol::Https => {
362
- let tcp_listener = connect_tcp_socket(addr, bind.port.unwrap())?;
363
+ let tcp_listener = connect_tcp_socket(addr, bind.port.unwrap(), socket_opts)?;
363
364
  Listener::TcpTls((
364
365
  tcp_listener,
365
366
  bind.tls_config.unwrap().build_acceptor().unwrap(),
@@ -369,56 +370,60 @@ impl TryFrom<Bind> for Listener {
369
370
  },
370
371
  BindAddress::UnixSocket(path) => match bind.tls_config {
371
372
  Some(tls_config) => Listener::UnixTls((
372
- connect_unix_socket(&path)?,
373
+ connect_unix_socket(&path, socket_opts)?,
373
374
  tls_config.build_acceptor().unwrap(),
374
375
  )),
375
- None => Listener::Unix(connect_unix_socket(&path)?),
376
+ None => Listener::Unix(connect_unix_socket(&path, socket_opts)?),
376
377
  },
377
378
  };
378
379
  Ok(bound)
379
380
  }
380
381
  }
381
382
 
382
- fn revive_tcp_socket(fd: RawFd) -> Result<TcpListener> {
383
+ fn revive_tcp_socket(fd: RawFd, socket_opts: &SocketOpts) -> Result<TcpListener> {
383
384
  let socket = unsafe { Socket::from_raw_fd(fd) };
384
- socket.set_reuse_port(true).ok();
385
- socket.set_reuse_address(true).ok();
385
+ socket.set_reuse_port(socket_opts.reuse_port).ok();
386
+ socket.set_reuse_address(socket_opts.reuse_address).ok();
386
387
  socket.set_nonblocking(true).ok();
387
- socket.set_nodelay(true).ok();
388
- socket.set_recv_buffer_size(262_144).ok();
388
+ socket.set_nodelay(socket_opts.nodelay).ok();
389
+ socket
390
+ .set_recv_buffer_size(socket_opts.recv_buffer_size)
391
+ .ok();
389
392
  socket.set_cloexec(true)?;
390
- socket.listen(1024)?;
393
+ socket.listen(socket_opts.listen_backlog as i32)?;
391
394
  Ok(socket.into())
392
395
  }
393
396
 
394
- fn revive_unix_socket(fd: RawFd) -> Result<UnixListener> {
397
+ fn revive_unix_socket(fd: RawFd, socket_opts: &SocketOpts) -> Result<UnixListener> {
395
398
  let socket = unsafe { Socket::from_raw_fd(fd) };
396
399
  socket.set_nonblocking(true).ok();
397
- socket.listen(1024)?;
400
+ socket.listen(socket_opts.listen_backlog as i32)?;
398
401
  socket.set_cloexec(true)?;
399
402
 
400
403
  Ok(socket.into())
401
404
  }
402
405
 
403
- fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
406
+ fn connect_tcp_socket(addr: IpAddr, port: u16, socket_opts: &SocketOpts) -> Result<TcpListener> {
404
407
  let domain = match addr {
405
408
  IpAddr::V4(_) => Domain::IPV4,
406
409
  IpAddr::V6(_) => Domain::IPV6,
407
410
  };
408
411
  let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
409
412
  let socket_address: SocketAddr = SocketAddr::new(addr, port);
410
- socket.set_reuse_address(true).ok();
411
- socket.set_reuse_port(true).ok();
413
+ socket.set_reuse_address(socket_opts.reuse_address).ok();
414
+ socket.set_reuse_port(socket_opts.reuse_port).ok();
412
415
  socket.set_nonblocking(true).ok();
413
- socket.set_nodelay(true).ok();
414
- socket.set_recv_buffer_size(262_144).ok();
416
+ socket.set_nodelay(socket_opts.nodelay).ok();
417
+ socket
418
+ .set_recv_buffer_size(socket_opts.recv_buffer_size)
419
+ .ok();
415
420
  socket.set_only_v6(false).ok();
416
421
  socket.bind(&socket_address.into())?;
417
- socket.listen(1024)?;
422
+ socket.listen(socket_opts.listen_backlog as i32)?;
418
423
  Ok(socket.into())
419
424
  }
420
425
 
421
- fn connect_unix_socket(path: &PathBuf) -> Result<UnixListener> {
426
+ fn connect_unix_socket(path: &PathBuf, socket_opts: &SocketOpts) -> Result<UnixListener> {
422
427
  let _ = std::fs::remove_file(path);
423
428
  let socket = Socket::new(Domain::UNIX, Type::STREAM, None)?;
424
429
  socket.set_nonblocking(true).ok();
@@ -426,7 +431,7 @@ fn connect_unix_socket(path: &PathBuf) -> Result<UnixListener> {
426
431
  let socket_address = socket2::SockAddr::unix(path)?;
427
432
 
428
433
  socket.bind(&socket_address)?;
429
- socket.listen(1024)?;
434
+ socket.listen(socket_opts.listen_backlog as i32)?;
430
435
 
431
436
  Ok(socket.into())
432
437
  }
@@ -1,11 +1,11 @@
1
1
  use async_trait::async_trait;
2
2
  use fs2::FileExt;
3
+ use itsi_acme::caches::DirCache;
4
+ use itsi_acme::{AccountCache, CertCache};
3
5
  use parking_lot::Mutex;
4
6
  use std::fs::{self, OpenOptions};
5
7
  use std::io::Error as IoError;
6
8
  use std::path::{Path, PathBuf};
7
- use tokio_rustls_acme::caches::DirCache;
8
- use tokio_rustls_acme::{AccountCache, CertCache};
9
9
 
10
10
  use crate::env::ITSI_ACME_LOCK_FILE_NAME;
11
11
 
@@ -1,4 +1,5 @@
1
1
  use base64::{engine::general_purpose, Engine as _};
2
+ use itsi_acme::{AcmeAcceptor, AcmeConfig, AcmeState};
2
3
  use itsi_error::Result;
3
4
  use itsi_tracing::info;
4
5
  use locked_dir_cache::LockedDirCache;
@@ -18,7 +19,6 @@ use std::{
18
19
  };
19
20
  use tokio::sync::Mutex;
20
21
  use tokio_rustls::{rustls::ServerConfig, TlsAcceptor};
21
- use tokio_rustls_acme::{AcmeAcceptor, AcmeConfig, AcmeState};
22
22
 
23
23
  use crate::env::{
24
24
  ITSI_ACME_CACHE_DIR, ITSI_ACME_CA_PEM_PATH, ITSI_ACME_CONTACT_EMAIL, ITSI_ACME_DIRECTORY_URL,
@@ -8,30 +8,31 @@ use super::middlewares::*;
8
8
  use async_trait::async_trait;
9
9
  use either::Either;
10
10
  use magnus::error::Result;
11
- use std::cmp::Ordering;
11
+ use std::{cmp::Ordering, sync::Arc};
12
12
 
13
- #[derive(Debug)]
13
+ #[derive(Debug, Clone)]
14
14
  pub enum Middleware {
15
- AllowList(AllowList),
16
- AuthAPIKey(AuthAPIKey),
17
- AuthBasic(AuthBasic),
18
- AuthJwt(Box<AuthJwt>),
19
- CacheControl(CacheControl),
20
- Compression(Compression),
21
- Cors(Box<Cors>),
22
- DenyList(DenyList),
23
- ETag(ETag),
24
- IntrusionProtection(IntrusionProtection),
25
- LogRequests(LogRequests),
26
- MaxBody(MaxBody),
27
- Proxy(Proxy),
28
- RateLimit(RateLimit),
29
- Redirect(Redirect),
30
- RequestHeaders(RequestHeaders),
31
- ResponseHeaders(ResponseHeaders),
32
- RubyApp(RubyApp),
33
- StaticAssets(StaticAssets),
34
- StaticResponse(StaticResponse),
15
+ AllowList(Arc<AllowList>),
16
+ AuthAPIKey(Arc<AuthAPIKey>),
17
+ AuthBasic(Arc<AuthBasic>),
18
+ AuthJwt(Arc<AuthJwt>),
19
+ CacheControl(Arc<CacheControl>),
20
+ Compression(Arc<Compression>),
21
+ Cors(Arc<Cors>),
22
+ Csp(Arc<Csp>),
23
+ DenyList(Arc<DenyList>),
24
+ ETag(Arc<ETag>),
25
+ IntrusionProtection(Arc<IntrusionProtection>),
26
+ LogRequests(Arc<LogRequests>),
27
+ MaxBody(Arc<MaxBody>),
28
+ Proxy(Arc<Proxy>),
29
+ RateLimit(Arc<RateLimit>),
30
+ Redirect(Arc<Redirect>),
31
+ RequestHeaders(Arc<RequestHeaders>),
32
+ ResponseHeaders(Arc<ResponseHeaders>),
33
+ RubyApp(Arc<RubyApp>),
34
+ StaticAssets(Arc<StaticAssets>),
35
+ StaticResponse(Arc<StaticResponse>),
35
36
  }
36
37
 
37
38
  #[async_trait]
@@ -51,6 +52,7 @@ impl MiddlewareLayer for Middleware {
51
52
  Middleware::ResponseHeaders(filter) => filter.initialize().await,
52
53
  Middleware::CacheControl(filter) => filter.initialize().await,
53
54
  Middleware::Cors(filter) => filter.initialize().await,
55
+ Middleware::Csp(filter) => filter.initialize().await,
54
56
  Middleware::ETag(filter) => filter.initialize().await,
55
57
  Middleware::StaticAssets(filter) => filter.initialize().await,
56
58
  Middleware::StaticResponse(filter) => filter.initialize().await,
@@ -80,6 +82,7 @@ impl MiddlewareLayer for Middleware {
80
82
  Middleware::RateLimit(filter) => filter.before(req, context).await,
81
83
  Middleware::CacheControl(filter) => filter.before(req, context).await,
82
84
  Middleware::Cors(filter) => filter.before(req, context).await,
85
+ Middleware::Csp(filter) => filter.before(req, context).await,
83
86
  Middleware::ETag(filter) => filter.before(req, context).await,
84
87
  Middleware::StaticAssets(filter) => filter.before(req, context).await,
85
88
  Middleware::StaticResponse(filter) => filter.before(req, context).await,
@@ -104,6 +107,7 @@ impl MiddlewareLayer for Middleware {
104
107
  Middleware::RequestHeaders(filter) => filter.after(res, context).await,
105
108
  Middleware::ResponseHeaders(filter) => filter.after(res, context).await,
106
109
  Middleware::CacheControl(filter) => filter.after(res, context).await,
110
+ Middleware::Csp(filter) => filter.after(res, context).await,
107
111
  Middleware::Cors(filter) => filter.after(res, context).await,
108
112
  Middleware::ETag(filter) => filter.after(res, context).await,
109
113
  Middleware::StaticAssets(filter) => filter.after(res, context).await,
@@ -134,12 +138,13 @@ impl Middleware {
134
138
  Middleware::AuthAPIKey(_) => 11,
135
139
  Middleware::RateLimit(_) => 12,
136
140
  Middleware::ETag(_) => 13,
137
- Middleware::Compression(_) => 14,
138
- Middleware::Proxy(_) => 15,
139
- Middleware::Cors(_) => 16,
140
- Middleware::StaticResponse(_) => 17,
141
- Middleware::StaticAssets(_) => 18,
142
- Middleware::RubyApp(_) => 19,
141
+ Middleware::Csp(_) => 14,
142
+ Middleware::Compression(_) => 15,
143
+ Middleware::Proxy(_) => 16,
144
+ Middleware::Cors(_) => 17,
145
+ Middleware::StaticResponse(_) => 18,
146
+ Middleware::StaticAssets(_) => 19,
147
+ Middleware::RubyApp(_) => 20,
143
148
  }
144
149
  }
145
150
  }