itsi-server 0.1.18 → 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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +932 -382
  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 +7 -4
  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/http_request.rb +1 -0
  47. data/lib/itsi/server/config/config_helpers.rb +93 -0
  48. data/lib/itsi/server/config/dsl.rb +98 -37
  49. data/lib/itsi/server/config/known_paths/KitchensinkDirectories.txt +2346 -0
  50. data/lib/itsi/server/config/known_paths/Randomfiles.txt +24 -0
  51. data/lib/itsi/server/config/known_paths/UnixDotfiles.txt +52 -0
  52. data/lib/itsi/server/config/known_paths/backdoors/ASP_CommonBackdoors.txt +29 -0
  53. data/lib/itsi/server/config/known_paths/backdoors/bot_control_panels.txt +1668 -0
  54. data/lib/itsi/server/config/known_paths/backdoors/shells.txt +1167 -0
  55. data/lib/itsi/server/config/known_paths/cgi/CGI_HTTP_POST.txt +7 -0
  56. data/lib/itsi/server/config/known_paths/cgi/CGI_HTTP_POST_Windows.txt +6 -0
  57. data/lib/itsi/server/config/known_paths/cgi/CGI_Microsoft.txt +79 -0
  58. data/lib/itsi/server/config/known_paths/cgi/CGI_XPlatform.txt +3948 -0
  59. data/lib/itsi/server/config/known_paths/cms/README.md +5 -0
  60. data/lib/itsi/server/config/known_paths/cms/drupal_plugins.txt +6320 -0
  61. data/lib/itsi/server/config/known_paths/cms/drupal_themes.txt +828 -0
  62. data/lib/itsi/server/config/known_paths/cms/joomla_plugins.txt +224 -0
  63. data/lib/itsi/server/config/known_paths/cms/joomla_themes.txt +30 -0
  64. data/lib/itsi/server/config/known_paths/cms/php-nuke.txt +2142 -0
  65. data/lib/itsi/server/config/known_paths/cms/wordpress.txt +1566 -0
  66. data/lib/itsi/server/config/known_paths/cms/wp_common_theme_files.txt +46 -0
  67. data/lib/itsi/server/config/known_paths/cms/wp_plugins.txt +13366 -0
  68. data/lib/itsi/server/config/known_paths/cms/wp_plugins_full.txt +68662 -0
  69. data/lib/itsi/server/config/known_paths/cms/wp_plugins_top225.txt +225 -0
  70. data/lib/itsi/server/config/known_paths/cms/wp_themes.readme +12 -0
  71. data/lib/itsi/server/config/known_paths/cms/wp_themes.txt +7336 -0
  72. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/3CharExtBrute.txt +17576 -0
  73. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/CommonWebExtensions.txt +80 -0
  74. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Backup.txt +14 -0
  75. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Common.txt +865 -0
  76. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Compressed.txt +186 -0
  77. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Mostcommon.txt +30 -0
  78. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Skipfish.txt +93 -0
  79. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/WordlistSkipfish.txt +1918 -0
  80. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/copy_of.txt +8 -0
  81. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-directories-lowercase.txt +56180 -0
  82. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-directories.txt +62290 -0
  83. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-extensions-lowercase.txt +2367 -0
  84. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-extensions.txt +2450 -0
  85. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-files-lowercase.txt +35323 -0
  86. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-files.txt +37037 -0
  87. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-words-lowercase.txt +107982 -0
  88. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-words.txt +119600 -0
  89. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-directories-lowercase.txt +26593 -0
  90. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-directories.txt +30009 -0
  91. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-extensions-lowercase.txt +1233 -0
  92. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-extensions.txt +1289 -0
  93. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-files-lowercase.txt +16243 -0
  94. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-files.txt +17128 -0
  95. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-words-lowercase.txt +56293 -0
  96. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-words.txt +63087 -0
  97. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-directories-lowercase.txt +17776 -0
  98. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-directories.txt +20122 -0
  99. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-extensions-lowercase.txt +914 -0
  100. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-extensions.txt +963 -0
  101. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-files-lowercase.txt +10848 -0
  102. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-files.txt +11424 -0
  103. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-words-lowercase.txt +38267 -0
  104. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-words.txt +43003 -0
  105. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/spanish.txt +445 -0
  106. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/test_demo.txt +36 -0
  107. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/upload_variants.txt +44 -0
  108. data/lib/itsi/server/config/known_paths/login-file-locations/Logins.txt +71 -0
  109. data/lib/itsi/server/config/known_paths/login-file-locations/cfm.txt +294 -0
  110. data/lib/itsi/server/config/known_paths/login-file-locations/html.txt +295 -0
  111. data/lib/itsi/server/config/known_paths/login-file-locations/jsp.txt +294 -0
  112. data/lib/itsi/server/config/known_paths/login-file-locations/php.txt +294 -0
  113. data/lib/itsi/server/config/known_paths/login-file-locations/windows-asp.txt +294 -0
  114. data/lib/itsi/server/config/known_paths/login-file-locations/windows-aspx.txt +294 -0
  115. data/lib/itsi/server/config/known_paths/password-file-locations/Passwords.txt +47 -0
  116. data/lib/itsi/server/config/known_paths/php/PHP.txt +30 -0
  117. data/lib/itsi/server/config/known_paths/php/PHP_CommonBackdoors.txt +5 -0
  118. data/lib/itsi/server/config/known_paths/proxy-conf.txt +31 -0
  119. data/lib/itsi/server/config/known_paths/tftp.txt +79 -0
  120. data/lib/itsi/server/config/known_paths/webservers-appservers/ADFS.txt +86 -0
  121. data/lib/itsi/server/config/known_paths/webservers-appservers/AdobeXML.txt +16 -0
  122. data/lib/itsi/server/config/known_paths/webservers-appservers/Apache.txt +101 -0
  123. data/lib/itsi/server/config/known_paths/webservers-appservers/ApacheTomcat.txt +47 -0
  124. data/lib/itsi/server/config/known_paths/webservers-appservers/Apache_Axis.txt +16 -0
  125. data/lib/itsi/server/config/known_paths/webservers-appservers/ColdFusion.txt +111 -0
  126. data/lib/itsi/server/config/known_paths/webservers-appservers/FatwireCMS.txt +390 -0
  127. data/lib/itsi/server/config/known_paths/webservers-appservers/Frontpage.txt +38 -0
  128. data/lib/itsi/server/config/known_paths/webservers-appservers/HP_System_Mgmt_Homepage.txt +239 -0
  129. data/lib/itsi/server/config/known_paths/webservers-appservers/HTTP_POST_Microsoft.txt +2 -0
  130. data/lib/itsi/server/config/known_paths/webservers-appservers/Hyperion.txt +578 -0
  131. data/lib/itsi/server/config/known_paths/webservers-appservers/IIS.txt +187 -0
  132. data/lib/itsi/server/config/known_paths/webservers-appservers/JBoss.txt +5 -0
  133. data/lib/itsi/server/config/known_paths/webservers-appservers/JRun.txt +13 -0
  134. data/lib/itsi/server/config/known_paths/webservers-appservers/JavaServlets_Common.txt +3 -0
  135. data/lib/itsi/server/config/known_paths/webservers-appservers/Joomla_exploitable.txt +1937 -0
  136. data/lib/itsi/server/config/known_paths/webservers-appservers/LotusNotes.txt +206 -0
  137. data/lib/itsi/server/config/known_paths/webservers-appservers/Netware.txt +18 -0
  138. data/lib/itsi/server/config/known_paths/webservers-appservers/Oracle9i.txt +60 -0
  139. data/lib/itsi/server/config/known_paths/webservers-appservers/OracleAppServer.txt +192 -0
  140. data/lib/itsi/server/config/known_paths/webservers-appservers/README.md +6 -0
  141. data/lib/itsi/server/config/known_paths/webservers-appservers/Ruby_Rails.txt +121 -0
  142. data/lib/itsi/server/config/known_paths/webservers-appservers/SAP.txt +463 -0
  143. data/lib/itsi/server/config/known_paths/webservers-appservers/Sharepoint.txt +1707 -0
  144. data/lib/itsi/server/config/known_paths/webservers-appservers/SiteMinder.txt +19 -0
  145. data/lib/itsi/server/config/known_paths/webservers-appservers/SunAppServerGlassfish.txt +51 -0
  146. data/lib/itsi/server/config/known_paths/webservers-appservers/SuniPlanet.txt +35 -0
  147. data/lib/itsi/server/config/known_paths/webservers-appservers/Vignette.txt +73 -0
  148. data/lib/itsi/server/config/known_paths/webservers-appservers/Weblogic.txt +160 -0
  149. data/lib/itsi/server/config/known_paths/webservers-appservers/Websphere.txt +366 -0
  150. data/lib/itsi/server/config/known_paths/wellknown-rfc5785.txt +30 -0
  151. data/lib/itsi/server/config/known_paths.rb +17 -0
  152. data/lib/itsi/server/config/middleware/_index.md +54 -0
  153. data/lib/itsi/server/config/middleware/log_requests.md +63 -0
  154. data/lib/itsi/server/config/middleware/log_requests.rb +33 -0
  155. data/lib/itsi/server/config/middleware.rb +9 -0
  156. data/lib/itsi/server/config/option.rb +9 -0
  157. data/lib/itsi/server/config/options/_index.md +36 -0
  158. data/lib/itsi/server/config/options/fiber_scheduler.md +35 -0
  159. data/lib/itsi/server/config/options/fiber_scheduler.rb +18 -0
  160. data/lib/itsi/server/config/options/threads.md +39 -0
  161. data/lib/itsi/server/config/options/threads.rb +17 -0
  162. data/lib/itsi/server/config/options/workers.md +43 -0
  163. data/lib/itsi/server/config/options/workers.rb +17 -0
  164. data/lib/itsi/server/config/typed_struct.rb +203 -0
  165. data/lib/itsi/server/config.rb +124 -30
  166. data/lib/itsi/server/signal_trap.rb +5 -1
  167. data/lib/itsi/server/typed_handlers/source_parser.rb +1 -1
  168. data/lib/itsi/server/version.rb +1 -1
  169. data/lib/itsi/server.rb +27 -6
  170. data/lib/ruby_lsp/itsi/addon.rb +64 -48
  171. metadata +141 -5
  172. data/CHANGELOG.md +0 -10
  173. data/CODE_OF_CONDUCT.md +0 -139
  174. data/LICENSE.txt +0 -21
  175. data/_index.md +0 -6
@@ -90,6 +90,7 @@ impl SingleMode {
90
90
  }
91
91
 
92
92
  pub fn stop(&self) -> Result<()> {
93
+ SHUTDOWN_REQUESTED.store(true, std::sync::atomic::Ordering::SeqCst);
93
94
  self.lifecycle_channel.send(LifecycleEvent::Shutdown).ok();
94
95
  Ok(())
95
96
  }
@@ -231,14 +232,17 @@ impl SingleMode {
231
232
  );
232
233
 
233
234
  let (shutdown_sender, _) = watch::channel(RunningPhase::Running);
234
- let thread = self.clone().start_monitors(thread_workers.clone());
235
+ let monitor_thread = self.clone().start_monitors(thread_workers.clone());
235
236
  if SHUTDOWN_REQUESTED.load(Ordering::SeqCst) {
236
237
  return Ok(());
237
238
  }
238
- runtime.block_on(
239
+ let result = runtime.block_on(
239
240
  async {
240
241
  let server_params = self.server_config.server_params.read().clone();
241
- server_params.middleware.get().unwrap().initialize_layers().await?;
242
+ if let Err(err) = server_params.initialize_middleware().await {
243
+ error!("Failed to initialize middleware: {}", err);
244
+ return Err(ItsiError::new("Failed to initialize middleware"))
245
+ }
242
246
  let tokio_listeners = server_params.listeners.lock()
243
247
  .drain(..)
244
248
  .map(|list| {
@@ -310,7 +314,19 @@ impl SingleMode {
310
314
  drop(tokio_listeners);
311
315
 
312
316
  Ok::<(), ItsiError>(())
313
- })?;
317
+ });
318
+
319
+ debug!("Single mode runtime exited.");
320
+
321
+ if result.is_err() {
322
+ for _i in 0..thread_workers.len() {
323
+ job_sender.send_blocking(RequestJob::Shutdown).unwrap();
324
+ nonblocking_sender
325
+ .send_blocking(RequestJob::Shutdown)
326
+ .unwrap();
327
+ }
328
+ self.lifecycle_channel.send(LifecycleEvent::Shutdown).ok();
329
+ }
314
330
 
315
331
  shutdown_sender.send(RunningPhase::Shutdown).ok();
316
332
  let deadline = Instant::now()
@@ -318,12 +334,13 @@ impl SingleMode {
318
334
 
319
335
  runtime.shutdown_timeout(Duration::from_millis(100));
320
336
 
337
+ debug!("Shutdown timeout finished.");
321
338
  loop {
322
339
  if thread_workers
323
340
  .iter()
324
341
  .all(|worker| call_with_gvl(move |_| !worker.poll_shutdown(deadline)))
325
342
  {
326
- funcall_no_ret(thread, "join", ()).ok();
343
+ funcall_no_ret(monitor_thread, "join", ()).ok();
327
344
  break;
328
345
  }
329
346
  sleep(Duration::from_millis(50));
@@ -335,7 +352,7 @@ impl SingleMode {
335
352
  self.run()?;
336
353
  }
337
354
  debug!("Runtime has shut down");
338
- Ok(())
355
+ result
339
356
  }
340
357
 
341
358
  pub(crate) async fn serve_connection(
@@ -367,7 +384,7 @@ impl SingleMode {
367
384
  let mut serve = Box::pin(
368
385
  binding
369
386
  .timer(TokioTimer::new())
370
- .header_read_timeout(Duration::from_secs(1))
387
+ .header_read_timeout(self.server_config.server_params.read().header_read_timeout)
371
388
  .serve_connection_with_upgrades(io, service),
372
389
  );
373
390
 
@@ -401,6 +418,9 @@ impl SingleMode {
401
418
  /// Not that when running in single mode this will not unload
402
419
  /// old code. If you need a clean restart, use the `restart` (SIGHUP) method instead
403
420
  pub fn reload(&self) -> Result<()> {
421
+ if !self.server_config.check_config() {
422
+ return Ok(());
423
+ }
404
424
  let should_reexec = self.server_config.clone().reload(false)?;
405
425
  if should_reexec {
406
426
  self.server_config.dup_fds()?;
@@ -414,6 +434,9 @@ impl SingleMode {
414
434
 
415
435
  /// Restart the server while keeping connections open.
416
436
  pub fn restart(&self) -> Result<()> {
437
+ if !self.server_config.check_config() {
438
+ return Ok(());
439
+ }
417
440
  self.server_config.dup_fds()?;
418
441
  self.server_config.reload_exec()?;
419
442
  Ok(())
@@ -159,11 +159,10 @@ impl ThreadWorker {
159
159
  pub fn poll_shutdown(&self, deadline: Instant) -> bool {
160
160
  if let Some(thread) = self.thread.read().deref() {
161
161
  if Instant::now() > deadline {
162
- warn!("Worker shutdown timed out. Killing thread");
162
+ warn!("Worker shutdown timed out. Killing thread {:?}", thread);
163
163
  self.terminated.store(true, Ordering::SeqCst);
164
164
  kill_threads(vec![thread.as_value()]);
165
165
  }
166
- debug!("Checking thread status");
167
166
  if thread.funcall::<_, _, bool>(*ID_ALIVE, ()).unwrap_or(false) {
168
167
  return true;
169
168
  }
@@ -184,6 +183,7 @@ impl ThreadWorker {
184
183
  call_with_gvl(|_| {
185
184
  *self.thread.write() = Some(
186
185
  create_ruby_thread(move || {
186
+ debug!("Ruby thread worker started");
187
187
  if let Some(scheduler_class) = scheduler_class {
188
188
  if let Err(err) = self_ref.fiber_accept_loop(
189
189
  params,
@@ -50,6 +50,7 @@ pub static ROOT_STATIC_FILE_SERVER: LazyLock<StaticFileServer> = LazyLock::new(|
50
50
  serve_hidden_files: false,
51
51
  allowed_extensions: vec!["html".to_string(), "css".to_string(), "js".to_string()],
52
52
  })
53
+ .unwrap()
53
54
  });
54
55
 
55
56
  #[derive(Debug, Clone, Deserialize)]
@@ -209,14 +210,27 @@ struct ServeCacheArgs<'a>(
209
210
  );
210
211
 
211
212
  impl StaticFileServer {
212
- pub fn new(config: StaticFileServerConfig) -> Self {
213
+ pub fn new(config: StaticFileServerConfig) -> Result<Self> {
213
214
  let cache = Cache::builder().max_capacity(config.max_entries).build();
215
+ if !config.root_dir.exists() {
216
+ return Err(ItsiError::InternalError(format!(
217
+ "Root directory {} for static file server doesn't exist",
218
+ config.root_dir.display()
219
+ )));
220
+ }
221
+
222
+ if std::fs::read_dir(&config.root_dir).is_err() {
223
+ return Err(ItsiError::InternalError(format!(
224
+ "Root directory {} for static file server is not readable",
225
+ config.root_dir.display()
226
+ )));
227
+ }
214
228
 
215
- StaticFileServer {
229
+ Ok(StaticFileServer {
216
230
  config: Arc::new(config),
217
231
  cache,
218
232
  key_to_path: Arc::new(Mutex::new(HashMap::new())),
219
- }
233
+ })
220
234
  }
221
235
 
222
236
  #[allow(clippy::too_many_arguments)]
@@ -483,14 +497,19 @@ impl StaticFileServer {
483
497
 
484
498
  tokio::pin!(entries);
485
499
  while let Some(entry) = entries.next_entry().await.unwrap_or(None) {
486
- if entry
487
- .file_name()
488
- .to_str()
489
- .is_some_and(|name| name.eq_ignore_ascii_case("index.html"))
490
- && entry.metadata().await.unwrap().is_file()
491
- {
492
- index_file = Some(entry.path());
493
- break;
500
+ if let Ok(metadata) = entry.metadata().await {
501
+ if entry
502
+ .file_name()
503
+ .to_str()
504
+ .is_some_and(|name| name.eq_ignore_ascii_case("index.html"))
505
+ && metadata.is_file()
506
+ {
507
+ index_file = Some(entry.path());
508
+ break;
509
+ }
510
+ } else {
511
+ error!("Failed to retrieve metadata for entry: {:?}", entry.path());
512
+ return Err(self.config.not_found_behavior.clone());
494
513
  }
495
514
  }
496
515
  }
@@ -999,23 +1018,6 @@ impl std::fmt::Display for StaticFileServer {
999
1018
  }
1000
1019
  }
1001
1020
 
1002
- impl Default for StaticFileServer {
1003
- fn default() -> Self {
1004
- let config = StaticFileServerConfig {
1005
- root_dir: "public".into(),
1006
- max_file_size: 10 * 1024 * 1024,
1007
- max_entries: 100,
1008
- recheck_interval: Duration::from_secs(60),
1009
- try_html_extension: true,
1010
- auto_index: true,
1011
- not_found_behavior: NotFoundBehavior::Error(ErrorResponse::not_found()),
1012
- serve_hidden_files: false,
1013
- allowed_extensions: vec!["html".to_string(), "css".to_string(), "js".to_string()],
1014
- };
1015
- Self::new(config)
1016
- }
1017
- }
1018
-
1019
1021
  async fn generate_directory_listing(
1020
1022
  dir_path: &Path,
1021
1023
  config: &StaticFileServerConfig,
@@ -3,6 +3,7 @@ use std::{
3
3
  env,
4
4
  sync::{Mutex, OnceLock},
5
5
  };
6
+ use tracing::Level;
6
7
  pub use tracing::{debug, error, info, trace, warn};
7
8
  use tracing_appender::rolling;
8
9
  use tracing_subscriber::fmt::writer::BoxMakeWriter;
@@ -110,7 +111,7 @@ fn build_fmt_layer(
110
111
  .compact()
111
112
  .with_file(false)
112
113
  .with_line_number(false)
113
- .with_target(false)
114
+ .with_target(true)
114
115
  .with_thread_ids(false)
115
116
  .with_writer(BoxMakeWriter::new(std::io::stdout))
116
117
  .with_ansi(config.use_ansi)
@@ -119,7 +120,7 @@ fn build_fmt_layer(
119
120
  .compact()
120
121
  .with_file(false)
121
122
  .with_line_number(false)
122
- .with_target(false)
123
+ .with_target(true)
123
124
  .with_thread_ids(false)
124
125
  .with_writer(BoxMakeWriter::new(std::io::stdout))
125
126
  .with_ansi(config.use_ansi)
@@ -133,7 +134,7 @@ fn build_fmt_layer(
133
134
  .compact()
134
135
  .with_file(false)
135
136
  .with_line_number(false)
136
- .with_target(false)
137
+ .with_target(true)
137
138
  .with_thread_ids(false)
138
139
  .with_writer(BoxMakeWriter::new(move || {
139
140
  rolling::daily(".", file_clone.clone())
@@ -146,7 +147,7 @@ fn build_fmt_layer(
146
147
  .compact()
147
148
  .with_file(false)
148
149
  .with_line_number(false)
149
- .with_target(false)
150
+ .with_target(true)
150
151
  .with_thread_ids(false)
151
152
  .with_writer(BoxMakeWriter::new(move || {
152
153
  rolling::daily(".", file_clone.clone())
@@ -165,7 +166,7 @@ fn build_fmt_layer(
165
166
  .compact()
166
167
  .with_file(false)
167
168
  .with_line_number(false)
168
- .with_target(false)
169
+ .with_target(true)
169
170
  .with_thread_ids(false)
170
171
  .with_writer(BoxMakeWriter::new(std::io::stdout))
171
172
  .with_ansi(config.use_ansi);
@@ -173,7 +174,7 @@ fn build_fmt_layer(
173
174
  .compact()
174
175
  .with_file(false)
175
176
  .with_line_number(false)
176
- .with_target(false)
177
+ .with_target(true)
177
178
  .with_thread_ids(false)
178
179
  .with_writer(BoxMakeWriter::new(move || {
179
180
  rolling::daily(".", file_clone.clone())
@@ -186,7 +187,7 @@ fn build_fmt_layer(
186
187
  .compact()
187
188
  .with_file(false)
188
189
  .with_line_number(false)
189
- .with_target(false)
190
+ .with_target(true)
190
191
  .with_thread_ids(false)
191
192
  .with_writer(BoxMakeWriter::new(std::io::stdout))
192
193
  .with_ansi(config.use_ansi)
@@ -195,7 +196,7 @@ fn build_fmt_layer(
195
196
  .compact()
196
197
  .with_file(false)
197
198
  .with_line_number(false)
198
- .with_target(false)
199
+ .with_target(true)
199
200
  .with_thread_ids(false)
200
201
  .with_writer(BoxMakeWriter::new(move || {
201
202
  rolling::daily(".", file_clone.clone())
@@ -304,6 +305,36 @@ pub fn set_format(new_format: &str) {
304
305
  eprintln!("Current configuration not initialized; call init() first.");
305
306
  }
306
307
  }
308
+ pub fn set_target_filters(targets: Vec<(&str, Level)>) {
309
+ if let Some(reload_handle_mutex) = RELOAD_HANDLE.get() {
310
+ if let Ok(handle_guard) = reload_handle_mutex.lock() {
311
+ if let Some(handle) = handle_guard.as_ref() {
312
+ let mut new_filter = EnvFilter::new("");
313
+
314
+ if let Some(config_mutex) = CURRENT_CONFIG.get() {
315
+ if let Ok(config) = config_mutex.lock() {
316
+ if let Ok(directive) = config.level.parse() {
317
+ new_filter = new_filter.add_directive(directive);
318
+ }
319
+ }
320
+ }
321
+
322
+ for (target, level) in targets {
323
+ let directive_str = format!("{}={}", target, level);
324
+ if let Ok(directive) = directive_str.parse() {
325
+ new_filter = new_filter.add_directive(directive);
326
+ }
327
+ }
328
+
329
+ if let Err(e) = handle.modify(|filter| *filter = new_filter) {
330
+ eprintln!("Failed to update filter with target directives: {}", e);
331
+ }
332
+ }
333
+ }
334
+ } else {
335
+ eprintln!("Reload handle for filter not initialized; call init() first.");
336
+ }
337
+ }
307
338
 
308
339
  /// Run a function silently by temporarily setting a no-op subscriber.
309
340
  pub fn run_silently<F, R>(f: F) -> R
@@ -29,6 +29,7 @@ module Itsi
29
29
  path = self.path
30
30
  host = self.host
31
31
  version = self.version
32
+
32
33
  {
33
34
  "SERVER_SOFTWARE" => "Itsi",
34
35
  "SCRIPT_NAME" => script_name,
@@ -0,0 +1,93 @@
1
+ module Itsi
2
+ class Server
3
+ module Config
4
+ module ConfigHelpers
5
+
6
+ def self.load_and_register(klass)
7
+ config_type = klass.name.split("::").last.downcase
8
+ listing = [
9
+ Dir[File.expand_path(File.dirname(__FILE__) + "/#{config_type}/**.rb")],
10
+ Dir[File.expand_path(File.dirname(__FILE__) + "/#{config_type}s/**.rb")]
11
+ ].flatten
12
+
13
+ listing.each do |file|
14
+ current = klass.subclasses
15
+ require file
16
+ following = klass.subclasses
17
+ new_class = (following - current).first
18
+
19
+ documentation_file = "#{file[/(.*)\.rb/,1]}.md"
20
+ if File.exist?(documentation_file) && new_class
21
+ new_class.documentation IO.read(documentation_file)
22
+ .gsub(/^---.*?\n.*?-+/m,'') # Strip frontmatter
23
+ .gsub(/^(```.*?)\{.*?\}.*$/, "\\1") # Strip filename from code blocks
24
+ .gsub(/^\{\{[^\}]+\}\}/, "") # Strip Hugo blocks
25
+ end
26
+ end
27
+ end
28
+
29
+ def normalize_keys!(hash, expected=[])
30
+ hash.keys.each do |key|
31
+ value = hash.delete(key)
32
+ key = key.to_s.downcase.to_sym
33
+ hash[key] = value
34
+ raise "Unexpected key: #{key}" unless expected.include?(key)
35
+ expected -= [key]
36
+ end
37
+ raise "Missing required keys: #{expected.join(', ')}" unless expected.empty?
38
+ hash
39
+ end
40
+
41
+ def self.included(cls)
42
+ def cls.inherited(base)
43
+ %i[detail documentation insert_text schema].each do |attr|
44
+ base.define_singleton_method(attr) do |value=nil|
45
+ @middleware_class_attrs ||= {}
46
+ if value
47
+ @middleware_class_attrs[attr] = value
48
+ else
49
+ @middleware_class_attrs[attr]
50
+ end
51
+ end
52
+
53
+ base.define_method(attr) do |value=nil|
54
+ self.class.send(attr)
55
+ end
56
+ end
57
+
58
+ def base.schema(value=nil, &blk)
59
+ @middleware_class_attrs ||= {}
60
+ if blk
61
+ @middleware_class_attrs[:schema] = TypedStruct.new(&blk)
62
+ else
63
+ @middleware_class_attrs[:schema]
64
+ end
65
+ end
66
+ end
67
+
68
+ load_and_register(cls)
69
+
70
+ config_type = cls.name.split("::").last.downcase
71
+
72
+ cls.define_singleton_method("#{config_type}_name") do |name=self.name|
73
+ @config_name ||= name.split("::").last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
74
+ end
75
+ end
76
+
77
+ def initialize(location, params)
78
+ @location = location
79
+ @params = case self.schema
80
+ when TypedStruct::Validation
81
+ self.schema.validate!(params)
82
+ else
83
+ self.schema.new(params).to_h
84
+ end
85
+ end
86
+
87
+ def build!
88
+ @params
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end