itsi 0.2.2 → 0.2.4

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 (498) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/Cargo.lock +29 -30
  4. data/README.md +1 -0
  5. data/Rakefile +2 -2
  6. data/crates/itsi_scheduler/Cargo.toml +1 -1
  7. data/crates/itsi_server/Cargo.toml +1 -1
  8. data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +26 -3
  9. data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +28 -11
  10. data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +1 -1
  11. data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +1 -2
  12. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +14 -2
  13. data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +86 -41
  14. data/crates/itsi_server/src/services/itsi_http_service.rs +46 -35
  15. data/crates/itsi_server/src/services/static_file_server.rs +31 -3
  16. data/docs/content/_index.md +1 -1
  17. data/docs/content/contact/_index.md +1 -1
  18. data/docs/content/directory_listing.jpg +0 -0
  19. data/docs/content/error_page.jpg +0 -0
  20. data/docs/content/getting_started/local_development.md +9 -2
  21. data/docs/content/getting_started/logging.md +1 -2
  22. data/docs/content/getting_started/signals.md +0 -1
  23. data/docs/hugo.yaml +3 -0
  24. data/examples/api_with_schema_and_controllers/Itsi.rb +35 -0
  25. data/examples/api_with_schema_and_controllers/README.md +10 -0
  26. data/examples/api_with_schema_and_controllers/controllers.rb +66 -0
  27. data/examples/api_with_schema_and_controllers/schemas.rb +25 -0
  28. data/examples/file_server/Itsi.rb +30 -0
  29. data/examples/file_server/README.md +3 -0
  30. data/examples/file_server/about.html +80 -0
  31. data/examples/file_server/admin/secrets.txt +1 -0
  32. data/examples/file_server/fairytale.txt +33 -0
  33. data/examples/file_server/itsi-server-100.png +0 -0
  34. data/examples/file_server/subdirectory/movies.csv +21 -0
  35. data/examples/helpers/datastore.rb +84 -0
  36. data/examples/media_server/Itsi.rb +5 -0
  37. data/examples/media_server/README.md +3 -0
  38. data/examples/media_server/images/itsi-server-100.png +0 -0
  39. data/examples/media_server/index.html +82 -0
  40. data/examples/multi_rack_rails_sinatra/Gemfile +66 -0
  41. data/examples/multi_rack_rails_sinatra/Gemfile.lock +416 -0
  42. data/examples/multi_rack_rails_sinatra/Itsi.rb +44 -0
  43. data/examples/multi_rack_rails_sinatra/README.md +7 -0
  44. data/examples/multi_rack_rails_sinatra/index.html +119 -0
  45. data/examples/multi_rack_rails_sinatra/rails_subapp/.dockerignore +51 -0
  46. data/examples/multi_rack_rails_sinatra/rails_subapp/.gitattributes +9 -0
  47. data/examples/multi_rack_rails_sinatra/rails_subapp/.github/dependabot.yml +12 -0
  48. data/examples/multi_rack_rails_sinatra/rails_subapp/.github/workflows/ci.yml +90 -0
  49. data/examples/multi_rack_rails_sinatra/rails_subapp/.gitignore +34 -0
  50. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/docker-setup.sample +3 -0
  51. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/post-app-boot.sample +3 -0
  52. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/post-deploy.sample +14 -0
  53. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/post-proxy-reboot.sample +3 -0
  54. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-app-boot.sample +3 -0
  55. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-build.sample +51 -0
  56. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-connect.sample +47 -0
  57. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-deploy.sample +109 -0
  58. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  59. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/secrets +17 -0
  60. data/examples/multi_rack_rails_sinatra/rails_subapp/.rubocop.yml +8 -0
  61. data/examples/multi_rack_rails_sinatra/rails_subapp/.ruby-version +1 -0
  62. data/examples/multi_rack_rails_sinatra/rails_subapp/Dockerfile +72 -0
  63. data/examples/multi_rack_rails_sinatra/rails_subapp/Gemfile +66 -0
  64. data/examples/multi_rack_rails_sinatra/rails_subapp/Gemfile.lock +416 -0
  65. data/examples/multi_rack_rails_sinatra/rails_subapp/README.md +24 -0
  66. data/examples/multi_rack_rails_sinatra/rails_subapp/Rakefile +6 -0
  67. data/examples/multi_rack_rails_sinatra/rails_subapp/app/assets/images/.keep +0 -0
  68. data/examples/multi_rack_rails_sinatra/rails_subapp/app/assets/stylesheets/application.css +161 -0
  69. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/application_controller.rb +4 -0
  70. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/articles_controller.rb +70 -0
  71. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/concerns/.keep +0 -0
  72. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/home_controller.rb +4 -0
  73. data/examples/multi_rack_rails_sinatra/rails_subapp/app/helpers/application_helper.rb +2 -0
  74. data/examples/multi_rack_rails_sinatra/rails_subapp/app/helpers/articles_helper.rb +2 -0
  75. data/examples/multi_rack_rails_sinatra/rails_subapp/app/helpers/home_helper.rb +2 -0
  76. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/application.js +3 -0
  77. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/controllers/application.js +9 -0
  78. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/controllers/hello_controller.js +7 -0
  79. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/controllers/index.js +4 -0
  80. data/examples/multi_rack_rails_sinatra/rails_subapp/app/jobs/application_job.rb +7 -0
  81. data/examples/multi_rack_rails_sinatra/rails_subapp/app/mailers/application_mailer.rb +4 -0
  82. data/examples/multi_rack_rails_sinatra/rails_subapp/app/models/application_record.rb +3 -0
  83. data/examples/multi_rack_rails_sinatra/rails_subapp/app/models/article.rb +2 -0
  84. data/examples/multi_rack_rails_sinatra/rails_subapp/app/models/concerns/.keep +0 -0
  85. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/_article.html.erb +14 -0
  86. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/_article.json.jbuilder +2 -0
  87. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/_form.html.erb +30 -0
  88. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/edit.html.erb +12 -0
  89. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/index.html.erb +29 -0
  90. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/index.json.jbuilder +1 -0
  91. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/new.html.erb +11 -0
  92. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/show.html.erb +10 -0
  93. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/show.json.jbuilder +1 -0
  94. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/home/index.html.erb +8 -0
  95. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/layouts/application.html.erb +34 -0
  96. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/layouts/mailer.html.erb +13 -0
  97. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/layouts/mailer.text.erb +1 -0
  98. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/pwa/manifest.json.erb +22 -0
  99. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/pwa/service-worker.js +26 -0
  100. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/brakeman +7 -0
  101. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/bundle +109 -0
  102. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/dev +2 -0
  103. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/docker-entrypoint +14 -0
  104. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/importmap +4 -0
  105. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/jobs +6 -0
  106. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/kamal +27 -0
  107. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/rails +4 -0
  108. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/rake +4 -0
  109. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/rubocop +8 -0
  110. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/setup +34 -0
  111. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/thrust +5 -0
  112. data/examples/multi_rack_rails_sinatra/rails_subapp/config/application.rb +27 -0
  113. data/examples/multi_rack_rails_sinatra/rails_subapp/config/boot.rb +4 -0
  114. data/examples/multi_rack_rails_sinatra/rails_subapp/config/cable.yml +17 -0
  115. data/examples/multi_rack_rails_sinatra/rails_subapp/config/cache.yml +16 -0
  116. data/examples/multi_rack_rails_sinatra/rails_subapp/config/credentials.yml.enc +1 -0
  117. data/examples/multi_rack_rails_sinatra/rails_subapp/config/database.yml +41 -0
  118. data/examples/multi_rack_rails_sinatra/rails_subapp/config/deploy.yml +116 -0
  119. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environment.rb +5 -0
  120. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environments/development.rb +72 -0
  121. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environments/production.rb +90 -0
  122. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environments/test.rb +53 -0
  123. data/examples/multi_rack_rails_sinatra/rails_subapp/config/importmap.rb +7 -0
  124. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/assets.rb +7 -0
  125. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/content_security_policy.rb +25 -0
  126. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/filter_parameter_logging.rb +8 -0
  127. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/inflections.rb +16 -0
  128. data/examples/multi_rack_rails_sinatra/rails_subapp/config/locales/en.yml +31 -0
  129. data/examples/multi_rack_rails_sinatra/rails_subapp/config/puma.rb +41 -0
  130. data/examples/multi_rack_rails_sinatra/rails_subapp/config/queue.yml +18 -0
  131. data/examples/multi_rack_rails_sinatra/rails_subapp/config/recurring.yml +10 -0
  132. data/examples/multi_rack_rails_sinatra/rails_subapp/config/routes.rb +17 -0
  133. data/examples/multi_rack_rails_sinatra/rails_subapp/config/storage.yml +34 -0
  134. data/examples/multi_rack_rails_sinatra/rails_subapp/config.ru +6 -0
  135. data/examples/multi_rack_rails_sinatra/rails_subapp/db/cable_schema.rb +11 -0
  136. data/examples/multi_rack_rails_sinatra/rails_subapp/db/cache_schema.rb +14 -0
  137. data/examples/multi_rack_rails_sinatra/rails_subapp/db/migrate/20250422211855_create_articles.rb +10 -0
  138. data/examples/multi_rack_rails_sinatra/rails_subapp/db/queue_schema.rb +129 -0
  139. data/examples/multi_rack_rails_sinatra/rails_subapp/db/schema.rb +20 -0
  140. data/examples/multi_rack_rails_sinatra/rails_subapp/db/seeds.rb +9 -0
  141. data/examples/multi_rack_rails_sinatra/rails_subapp/lib/tasks/.keep +0 -0
  142. data/examples/multi_rack_rails_sinatra/rails_subapp/log/.keep +0 -0
  143. data/examples/multi_rack_rails_sinatra/rails_subapp/public/400.html +114 -0
  144. data/examples/multi_rack_rails_sinatra/rails_subapp/public/404.html +114 -0
  145. data/examples/multi_rack_rails_sinatra/rails_subapp/public/406-unsupported-browser.html +114 -0
  146. data/examples/multi_rack_rails_sinatra/rails_subapp/public/422.html +114 -0
  147. data/examples/multi_rack_rails_sinatra/rails_subapp/public/500.html +114 -0
  148. data/examples/multi_rack_rails_sinatra/rails_subapp/public/icon.png +0 -0
  149. data/examples/multi_rack_rails_sinatra/rails_subapp/public/icon.svg +3 -0
  150. data/examples/multi_rack_rails_sinatra/rails_subapp/public/robots.txt +1 -0
  151. data/examples/multi_rack_rails_sinatra/rails_subapp/script/.keep +0 -0
  152. data/examples/multi_rack_rails_sinatra/rails_subapp/storage/.keep +0 -0
  153. data/examples/multi_rack_rails_sinatra/rails_subapp/test/application_system_test_case.rb +5 -0
  154. data/examples/multi_rack_rails_sinatra/rails_subapp/test/controllers/.keep +0 -0
  155. data/examples/multi_rack_rails_sinatra/rails_subapp/test/controllers/articles_controller_test.rb +48 -0
  156. data/examples/multi_rack_rails_sinatra/rails_subapp/test/controllers/home_controller_test.rb +8 -0
  157. data/examples/multi_rack_rails_sinatra/rails_subapp/test/fixtures/articles.yml +9 -0
  158. data/examples/multi_rack_rails_sinatra/rails_subapp/test/fixtures/files/.keep +0 -0
  159. data/examples/multi_rack_rails_sinatra/rails_subapp/test/helpers/.keep +0 -0
  160. data/examples/multi_rack_rails_sinatra/rails_subapp/test/integration/.keep +0 -0
  161. data/examples/multi_rack_rails_sinatra/rails_subapp/test/mailers/.keep +0 -0
  162. data/examples/multi_rack_rails_sinatra/rails_subapp/test/models/.keep +0 -0
  163. data/examples/multi_rack_rails_sinatra/rails_subapp/test/models/article_test.rb +7 -0
  164. data/examples/multi_rack_rails_sinatra/rails_subapp/test/system/.keep +0 -0
  165. data/examples/multi_rack_rails_sinatra/rails_subapp/test/system/articles_test.rb +43 -0
  166. data/examples/multi_rack_rails_sinatra/rails_subapp/test/test_helper.rb +15 -0
  167. data/examples/multi_rack_rails_sinatra/rails_subapp/tmp/.keep +0 -0
  168. data/examples/multi_rack_rails_sinatra/rails_subapp/tmp/pids/.keep +0 -0
  169. data/examples/multi_rack_rails_sinatra/rails_subapp/tmp/storage/.keep +0 -0
  170. data/examples/multi_rack_rails_sinatra/rails_subapp/vendor/.keep +0 -0
  171. data/examples/multi_rack_rails_sinatra/rails_subapp/vendor/javascript/.keep +0 -0
  172. data/examples/multi_rack_rails_sinatra/sinatra_subapp/config.ru +2 -0
  173. data/examples/multi_rack_rails_sinatra/sinatra_subapp/my_app.rb +96 -0
  174. data/examples/rails_with_static_assets/.dockerignore +51 -0
  175. data/examples/rails_with_static_assets/.gitattributes +9 -0
  176. data/examples/rails_with_static_assets/.github/dependabot.yml +12 -0
  177. data/examples/rails_with_static_assets/.github/workflows/ci.yml +90 -0
  178. data/examples/rails_with_static_assets/.gitignore +34 -0
  179. data/examples/rails_with_static_assets/.kamal/hooks/docker-setup.sample +3 -0
  180. data/examples/rails_with_static_assets/.kamal/hooks/post-app-boot.sample +3 -0
  181. data/examples/rails_with_static_assets/.kamal/hooks/post-deploy.sample +14 -0
  182. data/examples/rails_with_static_assets/.kamal/hooks/post-proxy-reboot.sample +3 -0
  183. data/examples/rails_with_static_assets/.kamal/hooks/pre-app-boot.sample +3 -0
  184. data/examples/rails_with_static_assets/.kamal/hooks/pre-build.sample +51 -0
  185. data/examples/rails_with_static_assets/.kamal/hooks/pre-connect.sample +47 -0
  186. data/examples/rails_with_static_assets/.kamal/hooks/pre-deploy.sample +109 -0
  187. data/examples/rails_with_static_assets/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  188. data/examples/rails_with_static_assets/.kamal/secrets +17 -0
  189. data/examples/rails_with_static_assets/.rubocop.yml +8 -0
  190. data/examples/rails_with_static_assets/.ruby-version +1 -0
  191. data/examples/rails_with_static_assets/Dockerfile +72 -0
  192. data/examples/rails_with_static_assets/Gemfile +66 -0
  193. data/examples/rails_with_static_assets/Gemfile.lock +416 -0
  194. data/examples/rails_with_static_assets/Itsi.rb +10 -0
  195. data/examples/rails_with_static_assets/README.md +14 -0
  196. data/examples/rails_with_static_assets/Rakefile +6 -0
  197. data/examples/rails_with_static_assets/app/assets/images/.keep +0 -0
  198. data/examples/rails_with_static_assets/app/assets/stylesheets/application.css +161 -0
  199. data/examples/rails_with_static_assets/app/controllers/application_controller.rb +4 -0
  200. data/examples/rails_with_static_assets/app/controllers/articles_controller.rb +70 -0
  201. data/examples/rails_with_static_assets/app/controllers/concerns/.keep +0 -0
  202. data/examples/rails_with_static_assets/app/controllers/home_controller.rb +4 -0
  203. data/examples/rails_with_static_assets/app/helpers/application_helper.rb +2 -0
  204. data/examples/rails_with_static_assets/app/helpers/articles_helper.rb +2 -0
  205. data/examples/rails_with_static_assets/app/helpers/home_helper.rb +2 -0
  206. data/examples/rails_with_static_assets/app/javascript/application.js +3 -0
  207. data/examples/rails_with_static_assets/app/javascript/controllers/application.js +9 -0
  208. data/examples/rails_with_static_assets/app/javascript/controllers/hello_controller.js +7 -0
  209. data/examples/rails_with_static_assets/app/javascript/controllers/index.js +4 -0
  210. data/examples/rails_with_static_assets/app/jobs/application_job.rb +7 -0
  211. data/examples/rails_with_static_assets/app/mailers/application_mailer.rb +4 -0
  212. data/examples/rails_with_static_assets/app/models/application_record.rb +3 -0
  213. data/examples/rails_with_static_assets/app/models/article.rb +2 -0
  214. data/examples/rails_with_static_assets/app/models/concerns/.keep +0 -0
  215. data/examples/rails_with_static_assets/app/views/articles/_article.html.erb +14 -0
  216. data/examples/rails_with_static_assets/app/views/articles/_article.json.jbuilder +2 -0
  217. data/examples/rails_with_static_assets/app/views/articles/_form.html.erb +30 -0
  218. data/examples/rails_with_static_assets/app/views/articles/edit.html.erb +12 -0
  219. data/examples/rails_with_static_assets/app/views/articles/index.html.erb +29 -0
  220. data/examples/rails_with_static_assets/app/views/articles/index.json.jbuilder +1 -0
  221. data/examples/rails_with_static_assets/app/views/articles/new.html.erb +11 -0
  222. data/examples/rails_with_static_assets/app/views/articles/show.html.erb +10 -0
  223. data/examples/rails_with_static_assets/app/views/articles/show.json.jbuilder +1 -0
  224. data/examples/rails_with_static_assets/app/views/home/index.html.erb +8 -0
  225. data/examples/rails_with_static_assets/app/views/layouts/application.html.erb +34 -0
  226. data/examples/rails_with_static_assets/app/views/layouts/mailer.html.erb +13 -0
  227. data/examples/rails_with_static_assets/app/views/layouts/mailer.text.erb +1 -0
  228. data/examples/rails_with_static_assets/app/views/pwa/manifest.json.erb +22 -0
  229. data/examples/rails_with_static_assets/app/views/pwa/service-worker.js +26 -0
  230. data/examples/rails_with_static_assets/bin/brakeman +7 -0
  231. data/examples/rails_with_static_assets/bin/bundle +109 -0
  232. data/examples/rails_with_static_assets/bin/dev +2 -0
  233. data/examples/rails_with_static_assets/bin/docker-entrypoint +14 -0
  234. data/examples/rails_with_static_assets/bin/importmap +4 -0
  235. data/examples/rails_with_static_assets/bin/jobs +6 -0
  236. data/examples/rails_with_static_assets/bin/kamal +27 -0
  237. data/examples/rails_with_static_assets/bin/rails +4 -0
  238. data/examples/rails_with_static_assets/bin/rake +4 -0
  239. data/examples/rails_with_static_assets/bin/rubocop +8 -0
  240. data/examples/rails_with_static_assets/bin/setup +34 -0
  241. data/examples/rails_with_static_assets/bin/thrust +5 -0
  242. data/examples/rails_with_static_assets/config/application.rb +26 -0
  243. data/examples/rails_with_static_assets/config/boot.rb +4 -0
  244. data/examples/rails_with_static_assets/config/cable.yml +17 -0
  245. data/examples/rails_with_static_assets/config/cache.yml +16 -0
  246. data/examples/rails_with_static_assets/config/credentials.yml.enc +1 -0
  247. data/examples/rails_with_static_assets/config/database.yml +41 -0
  248. data/examples/rails_with_static_assets/config/deploy.yml +116 -0
  249. data/examples/rails_with_static_assets/config/environment.rb +5 -0
  250. data/examples/rails_with_static_assets/config/environments/development.rb +74 -0
  251. data/examples/rails_with_static_assets/config/environments/production.rb +90 -0
  252. data/examples/rails_with_static_assets/config/environments/test.rb +53 -0
  253. data/examples/rails_with_static_assets/config/importmap.rb +7 -0
  254. data/examples/rails_with_static_assets/config/initializers/assets.rb +7 -0
  255. data/examples/rails_with_static_assets/config/initializers/content_security_policy.rb +25 -0
  256. data/examples/rails_with_static_assets/config/initializers/filter_parameter_logging.rb +8 -0
  257. data/examples/rails_with_static_assets/config/initializers/inflections.rb +16 -0
  258. data/examples/rails_with_static_assets/config/locales/en.yml +31 -0
  259. data/examples/rails_with_static_assets/config/puma.rb +41 -0
  260. data/examples/rails_with_static_assets/config/queue.yml +18 -0
  261. data/examples/rails_with_static_assets/config/recurring.yml +10 -0
  262. data/examples/rails_with_static_assets/config/routes.rb +17 -0
  263. data/examples/rails_with_static_assets/config/storage.yml +34 -0
  264. data/examples/rails_with_static_assets/config.ru +6 -0
  265. data/examples/rails_with_static_assets/db/cable_schema.rb +11 -0
  266. data/examples/rails_with_static_assets/db/cache_schema.rb +14 -0
  267. data/examples/rails_with_static_assets/db/migrate/20250422211855_create_articles.rb +10 -0
  268. data/examples/rails_with_static_assets/db/queue_schema.rb +129 -0
  269. data/examples/rails_with_static_assets/db/schema.rb +20 -0
  270. data/examples/rails_with_static_assets/db/seeds.rb +9 -0
  271. data/examples/rails_with_static_assets/lib/tasks/.keep +0 -0
  272. data/examples/rails_with_static_assets/log/.keep +0 -0
  273. data/examples/rails_with_static_assets/public/400.html +114 -0
  274. data/examples/rails_with_static_assets/public/404.html +114 -0
  275. data/examples/rails_with_static_assets/public/406-unsupported-browser.html +114 -0
  276. data/examples/rails_with_static_assets/public/422.html +114 -0
  277. data/examples/rails_with_static_assets/public/500.html +114 -0
  278. data/examples/rails_with_static_assets/public/icon.png +0 -0
  279. data/examples/rails_with_static_assets/public/icon.svg +3 -0
  280. data/examples/rails_with_static_assets/public/robots.txt +1 -0
  281. data/examples/rails_with_static_assets/script/.keep +0 -0
  282. data/examples/rails_with_static_assets/storage/.keep +0 -0
  283. data/examples/rails_with_static_assets/test/application_system_test_case.rb +5 -0
  284. data/examples/rails_with_static_assets/test/controllers/.keep +0 -0
  285. data/examples/rails_with_static_assets/test/controllers/articles_controller_test.rb +48 -0
  286. data/examples/rails_with_static_assets/test/controllers/home_controller_test.rb +8 -0
  287. data/examples/rails_with_static_assets/test/fixtures/articles.yml +9 -0
  288. data/examples/rails_with_static_assets/test/fixtures/files/.keep +0 -0
  289. data/examples/rails_with_static_assets/test/helpers/.keep +0 -0
  290. data/examples/rails_with_static_assets/test/integration/.keep +0 -0
  291. data/examples/rails_with_static_assets/test/mailers/.keep +0 -0
  292. data/examples/rails_with_static_assets/test/models/.keep +0 -0
  293. data/examples/rails_with_static_assets/test/models/article_test.rb +7 -0
  294. data/examples/rails_with_static_assets/test/system/.keep +0 -0
  295. data/examples/rails_with_static_assets/test/system/articles_test.rb +43 -0
  296. data/examples/rails_with_static_assets/test/test_helper.rb +15 -0
  297. data/examples/rails_with_static_assets/tmp/.keep +0 -0
  298. data/examples/rails_with_static_assets/tmp/pids/.keep +0 -0
  299. data/examples/rails_with_static_assets/tmp/storage/.keep +0 -0
  300. data/examples/rails_with_static_assets/vendor/.keep +0 -0
  301. data/examples/rails_with_static_assets/vendor/javascript/.keep +0 -0
  302. data/examples/reverse_proxy/Itsi.rb +28 -0
  303. data/examples/reverse_proxy/README.md +14 -0
  304. data/examples/reverse_proxy/index.html +119 -0
  305. data/examples/reverse_proxy/rails_subapp/.dockerignore +51 -0
  306. data/examples/reverse_proxy/rails_subapp/.gitattributes +9 -0
  307. data/examples/reverse_proxy/rails_subapp/.github/dependabot.yml +12 -0
  308. data/examples/reverse_proxy/rails_subapp/.github/workflows/ci.yml +90 -0
  309. data/examples/reverse_proxy/rails_subapp/.gitignore +34 -0
  310. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/docker-setup.sample +3 -0
  311. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/post-app-boot.sample +3 -0
  312. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/post-deploy.sample +14 -0
  313. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/post-proxy-reboot.sample +3 -0
  314. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-app-boot.sample +3 -0
  315. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-build.sample +51 -0
  316. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-connect.sample +47 -0
  317. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-deploy.sample +109 -0
  318. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  319. data/examples/reverse_proxy/rails_subapp/.kamal/secrets +17 -0
  320. data/examples/reverse_proxy/rails_subapp/.rubocop.yml +8 -0
  321. data/examples/reverse_proxy/rails_subapp/.ruby-version +1 -0
  322. data/examples/reverse_proxy/rails_subapp/Dockerfile +72 -0
  323. data/examples/reverse_proxy/rails_subapp/Gemfile +66 -0
  324. data/examples/reverse_proxy/rails_subapp/Gemfile.lock +416 -0
  325. data/examples/reverse_proxy/rails_subapp/README.md +24 -0
  326. data/examples/reverse_proxy/rails_subapp/Rakefile +6 -0
  327. data/examples/reverse_proxy/rails_subapp/app/assets/images/.keep +0 -0
  328. data/examples/reverse_proxy/rails_subapp/app/assets/stylesheets/application.css +161 -0
  329. data/examples/reverse_proxy/rails_subapp/app/controllers/application_controller.rb +4 -0
  330. data/examples/reverse_proxy/rails_subapp/app/controllers/articles_controller.rb +70 -0
  331. data/examples/reverse_proxy/rails_subapp/app/controllers/concerns/.keep +0 -0
  332. data/examples/reverse_proxy/rails_subapp/app/controllers/home_controller.rb +4 -0
  333. data/examples/reverse_proxy/rails_subapp/app/helpers/application_helper.rb +2 -0
  334. data/examples/reverse_proxy/rails_subapp/app/helpers/articles_helper.rb +2 -0
  335. data/examples/reverse_proxy/rails_subapp/app/helpers/home_helper.rb +2 -0
  336. data/examples/reverse_proxy/rails_subapp/app/javascript/application.js +3 -0
  337. data/examples/reverse_proxy/rails_subapp/app/javascript/controllers/application.js +9 -0
  338. data/examples/reverse_proxy/rails_subapp/app/javascript/controllers/hello_controller.js +7 -0
  339. data/examples/reverse_proxy/rails_subapp/app/javascript/controllers/index.js +4 -0
  340. data/examples/reverse_proxy/rails_subapp/app/jobs/application_job.rb +7 -0
  341. data/examples/reverse_proxy/rails_subapp/app/mailers/application_mailer.rb +4 -0
  342. data/examples/reverse_proxy/rails_subapp/app/models/application_record.rb +3 -0
  343. data/examples/reverse_proxy/rails_subapp/app/models/article.rb +2 -0
  344. data/examples/reverse_proxy/rails_subapp/app/models/concerns/.keep +0 -0
  345. data/examples/reverse_proxy/rails_subapp/app/views/articles/_article.html.erb +14 -0
  346. data/examples/reverse_proxy/rails_subapp/app/views/articles/_article.json.jbuilder +2 -0
  347. data/examples/reverse_proxy/rails_subapp/app/views/articles/_form.html.erb +30 -0
  348. data/examples/reverse_proxy/rails_subapp/app/views/articles/edit.html.erb +12 -0
  349. data/examples/reverse_proxy/rails_subapp/app/views/articles/index.html.erb +29 -0
  350. data/examples/reverse_proxy/rails_subapp/app/views/articles/index.json.jbuilder +1 -0
  351. data/examples/reverse_proxy/rails_subapp/app/views/articles/new.html.erb +11 -0
  352. data/examples/reverse_proxy/rails_subapp/app/views/articles/show.html.erb +10 -0
  353. data/examples/reverse_proxy/rails_subapp/app/views/articles/show.json.jbuilder +1 -0
  354. data/examples/reverse_proxy/rails_subapp/app/views/home/index.html.erb +8 -0
  355. data/examples/reverse_proxy/rails_subapp/app/views/layouts/application.html.erb +34 -0
  356. data/examples/reverse_proxy/rails_subapp/app/views/layouts/mailer.html.erb +13 -0
  357. data/examples/reverse_proxy/rails_subapp/app/views/layouts/mailer.text.erb +1 -0
  358. data/examples/reverse_proxy/rails_subapp/app/views/pwa/manifest.json.erb +22 -0
  359. data/examples/reverse_proxy/rails_subapp/app/views/pwa/service-worker.js +26 -0
  360. data/examples/reverse_proxy/rails_subapp/bin/brakeman +7 -0
  361. data/examples/reverse_proxy/rails_subapp/bin/bundle +109 -0
  362. data/examples/reverse_proxy/rails_subapp/bin/dev +2 -0
  363. data/examples/reverse_proxy/rails_subapp/bin/docker-entrypoint +14 -0
  364. data/examples/reverse_proxy/rails_subapp/bin/importmap +4 -0
  365. data/examples/reverse_proxy/rails_subapp/bin/jobs +6 -0
  366. data/examples/reverse_proxy/rails_subapp/bin/kamal +27 -0
  367. data/examples/reverse_proxy/rails_subapp/bin/rails +4 -0
  368. data/examples/reverse_proxy/rails_subapp/bin/rake +4 -0
  369. data/examples/reverse_proxy/rails_subapp/bin/rubocop +8 -0
  370. data/examples/reverse_proxy/rails_subapp/bin/setup +34 -0
  371. data/examples/reverse_proxy/rails_subapp/bin/thrust +5 -0
  372. data/examples/reverse_proxy/rails_subapp/config/application.rb +26 -0
  373. data/examples/reverse_proxy/rails_subapp/config/boot.rb +4 -0
  374. data/examples/reverse_proxy/rails_subapp/config/cable.yml +17 -0
  375. data/examples/reverse_proxy/rails_subapp/config/cache.yml +16 -0
  376. data/examples/reverse_proxy/rails_subapp/config/credentials.yml.enc +1 -0
  377. data/examples/reverse_proxy/rails_subapp/config/database.yml +41 -0
  378. data/examples/reverse_proxy/rails_subapp/config/deploy.yml +116 -0
  379. data/examples/reverse_proxy/rails_subapp/config/environment.rb +5 -0
  380. data/examples/reverse_proxy/rails_subapp/config/environments/development.rb +72 -0
  381. data/examples/reverse_proxy/rails_subapp/config/environments/production.rb +90 -0
  382. data/examples/reverse_proxy/rails_subapp/config/environments/test.rb +53 -0
  383. data/examples/reverse_proxy/rails_subapp/config/importmap.rb +7 -0
  384. data/examples/reverse_proxy/rails_subapp/config/initializers/assets.rb +7 -0
  385. data/examples/reverse_proxy/rails_subapp/config/initializers/content_security_policy.rb +25 -0
  386. data/examples/reverse_proxy/rails_subapp/config/initializers/filter_parameter_logging.rb +8 -0
  387. data/examples/reverse_proxy/rails_subapp/config/initializers/inflections.rb +16 -0
  388. data/examples/reverse_proxy/rails_subapp/config/locales/en.yml +31 -0
  389. data/examples/reverse_proxy/rails_subapp/config/puma.rb +41 -0
  390. data/examples/reverse_proxy/rails_subapp/config/queue.yml +18 -0
  391. data/examples/reverse_proxy/rails_subapp/config/recurring.yml +10 -0
  392. data/examples/reverse_proxy/rails_subapp/config/routes.rb +17 -0
  393. data/examples/reverse_proxy/rails_subapp/config/storage.yml +34 -0
  394. data/examples/reverse_proxy/rails_subapp/config.ru +6 -0
  395. data/examples/reverse_proxy/rails_subapp/db/cable_schema.rb +11 -0
  396. data/examples/reverse_proxy/rails_subapp/db/cache_schema.rb +14 -0
  397. data/examples/reverse_proxy/rails_subapp/db/migrate/20250422211855_create_articles.rb +10 -0
  398. data/examples/reverse_proxy/rails_subapp/db/queue_schema.rb +129 -0
  399. data/examples/reverse_proxy/rails_subapp/db/schema.rb +20 -0
  400. data/examples/reverse_proxy/rails_subapp/db/seeds.rb +9 -0
  401. data/examples/reverse_proxy/rails_subapp/lib/tasks/.keep +0 -0
  402. data/examples/reverse_proxy/rails_subapp/log/.keep +0 -0
  403. data/examples/reverse_proxy/rails_subapp/public/400.html +114 -0
  404. data/examples/reverse_proxy/rails_subapp/public/404.html +114 -0
  405. data/examples/reverse_proxy/rails_subapp/public/406-unsupported-browser.html +114 -0
  406. data/examples/reverse_proxy/rails_subapp/public/422.html +114 -0
  407. data/examples/reverse_proxy/rails_subapp/public/500.html +114 -0
  408. data/examples/reverse_proxy/rails_subapp/public/icon.png +0 -0
  409. data/examples/reverse_proxy/rails_subapp/public/icon.svg +3 -0
  410. data/examples/reverse_proxy/rails_subapp/public/robots.txt +1 -0
  411. data/examples/reverse_proxy/rails_subapp/script/.keep +0 -0
  412. data/examples/reverse_proxy/rails_subapp/storage/.keep +0 -0
  413. data/examples/reverse_proxy/rails_subapp/test/application_system_test_case.rb +5 -0
  414. data/examples/reverse_proxy/rails_subapp/test/controllers/.keep +0 -0
  415. data/examples/reverse_proxy/rails_subapp/test/controllers/articles_controller_test.rb +48 -0
  416. data/examples/reverse_proxy/rails_subapp/test/controllers/home_controller_test.rb +8 -0
  417. data/examples/reverse_proxy/rails_subapp/test/fixtures/articles.yml +9 -0
  418. data/examples/reverse_proxy/rails_subapp/test/fixtures/files/.keep +0 -0
  419. data/examples/reverse_proxy/rails_subapp/test/helpers/.keep +0 -0
  420. data/examples/reverse_proxy/rails_subapp/test/integration/.keep +0 -0
  421. data/examples/reverse_proxy/rails_subapp/test/mailers/.keep +0 -0
  422. data/examples/reverse_proxy/rails_subapp/test/models/.keep +0 -0
  423. data/examples/reverse_proxy/rails_subapp/test/models/article_test.rb +7 -0
  424. data/examples/reverse_proxy/rails_subapp/test/system/.keep +0 -0
  425. data/examples/reverse_proxy/rails_subapp/test/system/articles_test.rb +43 -0
  426. data/examples/reverse_proxy/rails_subapp/test/test_helper.rb +15 -0
  427. data/examples/reverse_proxy/rails_subapp/tmp/.keep +0 -0
  428. data/examples/reverse_proxy/rails_subapp/tmp/pids/.keep +0 -0
  429. data/examples/reverse_proxy/rails_subapp/tmp/storage/.keep +0 -0
  430. data/examples/reverse_proxy/rails_subapp/vendor/.keep +0 -0
  431. data/examples/reverse_proxy/rails_subapp/vendor/javascript/.keep +0 -0
  432. data/examples/reverse_proxy/sinatra_subapp/Gemfile +2 -0
  433. data/examples/reverse_proxy/sinatra_subapp/Gemfile.lock +46 -0
  434. data/examples/reverse_proxy/sinatra_subapp/config.ru +2 -0
  435. data/examples/reverse_proxy/sinatra_subapp/my_app.rb +96 -0
  436. data/examples/simple_api/Itsi.rb +127 -0
  437. data/examples/simple_api_no_schema/Itsi.rb +86 -0
  438. data/examples/spa/Itsi.rb +5 -0
  439. data/examples/spa/README.md +3 -0
  440. data/examples/spa/dist/assets/index-2cf7be43.js +40 -0
  441. data/examples/spa/dist/index.html +13 -0
  442. data/examples/spa/index.html +12 -0
  443. data/examples/spa/package-lock.json +1265 -0
  444. data/examples/spa/package.json +18 -0
  445. data/examples/spa/src/App.jsx +49 -0
  446. data/examples/spa/src/main.jsx +6 -0
  447. data/examples/spa/src/pages/About.jsx +9 -0
  448. data/examples/spa/src/pages/Home.jsx +9 -0
  449. data/examples/spa/vite.config.js +9 -0
  450. data/gems/scheduler/Cargo.lock +74 -17
  451. data/gems/scheduler/itsi-scheduler.gemspec +2 -2
  452. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  453. data/gems/server/Cargo.lock +28 -29
  454. data/gems/server/itsi-server.gemspec +2 -2
  455. data/gems/server/lib/itsi/http_request.rb +31 -34
  456. data/gems/server/lib/itsi/http_response.rb +10 -8
  457. data/gems/server/lib/itsi/passfile.rb +6 -6
  458. data/gems/server/lib/itsi/server/config/config_helpers.rb +33 -33
  459. data/gems/server/lib/itsi/server/config/dsl.rb +16 -21
  460. data/gems/server/lib/itsi/server/config/known_paths.rb +11 -7
  461. data/gems/server/lib/itsi/server/config/middleware/endpoint/endpoint.rb +0 -4
  462. data/gems/server/lib/itsi/server/config/middleware/error_response.md +13 -0
  463. data/gems/server/lib/itsi/server/config/middleware/location.rb +25 -21
  464. data/gems/server/lib/itsi/server/config/middleware/proxy.rb +15 -14
  465. data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +7 -10
  466. data/gems/server/lib/itsi/server/config/middleware/static_assets.md +40 -0
  467. data/gems/server/lib/itsi/server/config/middleware/static_assets.rb +8 -4
  468. data/gems/server/lib/itsi/server/config/middleware/string_rewrite.md +14 -0
  469. data/gems/server/lib/itsi/server/config/option.rb +0 -1
  470. data/gems/server/lib/itsi/server/config/options/include.rb +1 -1
  471. data/gems/server/lib/itsi/server/config/options/nodelay.md +2 -2
  472. data/gems/server/lib/itsi/server/config/options/reuse_address.md +1 -1
  473. data/gems/server/lib/itsi/server/config/typed_struct.rb +32 -35
  474. data/gems/server/lib/itsi/server/config.rb +107 -92
  475. data/gems/server/lib/itsi/server/default_app/default_app.rb +1 -1
  476. data/gems/server/lib/itsi/server/grpc/grpc_call.rb +4 -5
  477. data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +6 -7
  478. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +0 -1
  479. data/gems/server/lib/itsi/server/rack_interface.rb +1 -2
  480. data/gems/server/lib/itsi/server/route_tester.rb +26 -24
  481. data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +25 -0
  482. data/gems/server/lib/itsi/server/typed_handlers/source_parser.rb +9 -7
  483. data/gems/server/lib/itsi/server/version.rb +1 -1
  484. data/gems/server/lib/itsi/server.rb +22 -22
  485. data/gems/server/lib/itsi/standard_headers.rb +80 -80
  486. data/gems/server/test/helpers/test_helper.rb +17 -16
  487. data/gems/server/test/middleware/static_assets.rb +6 -4
  488. data/gems/server/test/middleware/string_rewrite.rb +54 -0
  489. data/gems/server/test/middleware/test_log_requests.rb +66 -2
  490. data/gems/server/test/options/test_workers.rb +12 -5
  491. data/lib/itsi/version.rb +1 -1
  492. metadata +435 -13
  493. data/examples/static_assets_example.rb +0 -83
  494. data/grpc_test/Itsi.rb +0 -11
  495. data/grpc_test/echo.proto +0 -14
  496. data/grpc_test/echo_pb.rb +0 -16
  497. data/grpc_test/echo_service_impl.rb +0 -8
  498. data/grpc_test/echo_services_pb.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2fc0853d34dfcbdef3c9d065d4f1e63d78eb16185891280586a736aabc265710
4
- data.tar.gz: '0074897dc1785778b1a7102b78e8c78b2f75fdbdd484420cc521025205eba8e7'
3
+ metadata.gz: c0f48a4cf96ec2afd196a828cd9b264a019673ce1cb690229c57a4f113f47e52
4
+ data.tar.gz: 6119bc422b0ec00e7d4d9c52314d725c441e2117275402c838f1ee47166b000a
5
5
  SHA512:
6
- metadata.gz: f408680c69ebda75675b9da1f08cf300a79605b4f043b9427ec2707b325424de6ce87815ed29ec140ab8db0130faabd5cc740ecab19c726399291dc1818f343e
7
- data.tar.gz: ecdd5a1a2e15939921460d938584b912eb1417be8b34af58772d67214be5013d67979a0ef18c12f156ffcb1c7ebeacb4874604f7f187e673e8fc32f660a600a1
6
+ metadata.gz: 7d572b3bad1e3156eb2e0c5da02d9ac5b988f37b19d33fdac7f475cd6161563c4d5befaae7d5776fc30e1089fe8f2d025e9557978f618980260e6d1715f2783c
7
+ data.tar.gz: 3913fb88decca4a0fc83e2c0136056d5eff6b703c498cb0d160c9bf2accadbe50a01c23d6380eb0a87a41e7901f2dbac6cf1050c566f2250a91fc51991a4749d
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ## [0.2.2] - 2025-04-22
1
+ ## [0.2.3] - 2025-04-22
2
2
 
3
3
  - Public release!
4
4
  - https://itsi.fyi
data/Cargo.lock CHANGED
@@ -117,9 +117,9 @@ dependencies = [
117
117
 
118
118
  [[package]]
119
119
  name = "anyhow"
120
- version = "1.0.97"
120
+ version = "1.0.98"
121
121
  source = "registry+https://github.com/rust-lang/crates.io-index"
122
- checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
122
+ checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
123
123
 
124
124
  [[package]]
125
125
  name = "arc-swap"
@@ -253,9 +253,9 @@ dependencies = [
253
253
 
254
254
  [[package]]
255
255
  name = "aws-lc-sys"
256
- version = "0.28.0"
256
+ version = "0.28.2"
257
257
  source = "registry+https://github.com/rust-lang/crates.io-index"
258
- checksum = "b9f7720b74ed28ca77f90769a71fd8c637a0137f6fae4ae947e1050229cff57f"
258
+ checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1"
259
259
  dependencies = [
260
260
  "bindgen",
261
261
  "cc",
@@ -467,9 +467,9 @@ dependencies = [
467
467
 
468
468
  [[package]]
469
469
  name = "brotli-decompressor"
470
- version = "4.0.2"
470
+ version = "4.0.3"
471
471
  source = "registry+https://github.com/rust-lang/crates.io-index"
472
- checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37"
472
+ checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd"
473
473
  dependencies = [
474
474
  "alloc-no-stdlib",
475
475
  "alloc-stdlib",
@@ -587,9 +587,9 @@ dependencies = [
587
587
 
588
588
  [[package]]
589
589
  name = "clap"
590
- version = "4.5.36"
590
+ version = "4.5.37"
591
591
  source = "registry+https://github.com/rust-lang/crates.io-index"
592
- checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
592
+ checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
593
593
  dependencies = [
594
594
  "clap_builder",
595
595
  "clap_derive",
@@ -597,9 +597,9 @@ dependencies = [
597
597
 
598
598
  [[package]]
599
599
  name = "clap_builder"
600
- version = "4.5.36"
600
+ version = "4.5.37"
601
601
  source = "registry+https://github.com/rust-lang/crates.io-index"
602
- checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
602
+ checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
603
603
  dependencies = [
604
604
  "anstream",
605
605
  "anstyle",
@@ -764,9 +764,9 @@ dependencies = [
764
764
 
765
765
  [[package]]
766
766
  name = "data-encoding"
767
- version = "2.8.0"
767
+ version = "2.9.0"
768
768
  source = "registry+https://github.com/rust-lang/crates.io-index"
769
- checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
769
+ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
770
770
 
771
771
  [[package]]
772
772
  name = "der-parser"
@@ -1176,9 +1176,9 @@ dependencies = [
1176
1176
 
1177
1177
  [[package]]
1178
1178
  name = "h2"
1179
- version = "0.4.8"
1179
+ version = "0.4.9"
1180
1180
  source = "registry+https://github.com/rust-lang/crates.io-index"
1181
- checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
1181
+ checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
1182
1182
  dependencies = [
1183
1183
  "atomic-waker",
1184
1184
  "bytes",
@@ -1363,7 +1363,7 @@ dependencies = [
1363
1363
  "bytes",
1364
1364
  "futures-channel",
1365
1365
  "futures-util",
1366
- "h2 0.4.8",
1366
+ "h2 0.4.9",
1367
1367
  "http 1.3.1",
1368
1368
  "http-body 1.0.1",
1369
1369
  "httparse",
@@ -1644,7 +1644,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1644
1644
 
1645
1645
  [[package]]
1646
1646
  name = "itsi-scheduler"
1647
- version = "0.2.2"
1647
+ version = "0.2.4"
1648
1648
  dependencies = [
1649
1649
  "bytes",
1650
1650
  "derive_more",
@@ -1662,7 +1662,7 @@ dependencies = [
1662
1662
 
1663
1663
  [[package]]
1664
1664
  name = "itsi-server"
1665
- version = "0.2.2"
1665
+ version = "0.2.4"
1666
1666
  dependencies = [
1667
1667
  "argon2",
1668
1668
  "async-channel",
@@ -1699,7 +1699,7 @@ dependencies = [
1699
1699
  "parking_lot",
1700
1700
  "percent-encoding",
1701
1701
  "pin-project",
1702
- "rand 0.9.0",
1702
+ "rand 0.9.1",
1703
1703
  "rcgen",
1704
1704
  "redis",
1705
1705
  "regex",
@@ -1731,7 +1731,7 @@ dependencies = [
1731
1731
  "axum-server",
1732
1732
  "base64 0.22.1",
1733
1733
  "chrono",
1734
- "clap 4.5.36",
1734
+ "clap 4.5.37",
1735
1735
  "futures",
1736
1736
  "log",
1737
1737
  "num-bigint",
@@ -1868,9 +1868,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
1868
1868
 
1869
1869
  [[package]]
1870
1870
  name = "libc"
1871
- version = "0.2.171"
1871
+ version = "0.2.172"
1872
1872
  source = "registry+https://github.com/rust-lang/crates.io-index"
1873
- checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
1873
+ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
1874
1874
 
1875
1875
  [[package]]
1876
1876
  name = "libloading"
@@ -1879,7 +1879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1879
1879
  checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
1880
1880
  dependencies = [
1881
1881
  "cfg-if",
1882
- "windows-targets 0.52.6",
1882
+ "windows-targets 0.48.5",
1883
1883
  ]
1884
1884
 
1885
1885
  [[package]]
@@ -2393,9 +2393,9 @@ dependencies = [
2393
2393
 
2394
2394
  [[package]]
2395
2395
  name = "proc-macro2"
2396
- version = "1.0.94"
2396
+ version = "1.0.95"
2397
2397
  source = "registry+https://github.com/rust-lang/crates.io-index"
2398
- checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
2398
+ checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
2399
2399
  dependencies = [
2400
2400
  "unicode-ident",
2401
2401
  ]
@@ -2428,7 +2428,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
2428
2428
  dependencies = [
2429
2429
  "bytes",
2430
2430
  "getrandom 0.3.2",
2431
- "rand 0.9.0",
2431
+ "rand 0.9.1",
2432
2432
  "ring",
2433
2433
  "rustc-hash 2.1.1",
2434
2434
  "rustls",
@@ -2493,13 +2493,12 @@ dependencies = [
2493
2493
 
2494
2494
  [[package]]
2495
2495
  name = "rand"
2496
- version = "0.9.0"
2496
+ version = "0.9.1"
2497
2497
  source = "registry+https://github.com/rust-lang/crates.io-index"
2498
- checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
2498
+ checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
2499
2499
  dependencies = [
2500
2500
  "rand_chacha 0.9.0",
2501
2501
  "rand_core 0.9.3",
2502
- "zerocopy",
2503
2502
  ]
2504
2503
 
2505
2504
  [[package]]
@@ -3089,9 +3088,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
3089
3088
 
3090
3089
  [[package]]
3091
3090
  name = "signal-hook-registry"
3092
- version = "1.4.2"
3091
+ version = "1.4.5"
3093
3092
  source = "registry+https://github.com/rust-lang/crates.io-index"
3094
- checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
3093
+ checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
3095
3094
  dependencies = [
3096
3095
  "libc",
3097
3096
  ]
data/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  > The Serious Web Server, for Serious People
5
5
 
6
6
  [![Test](https://github.com/wouterken/itsi/actions/workflows/test.yml/badge.svg)](https://github.com/wouterken/itsi/actions/workflows/test.yml)
7
+ [![Gem Version](https://img.shields.io/gem/v/itsi)](https://rubygems.org/gems/itsi)
7
8
 
8
9
  Itsi is a feature-packed, high performance web and application server, with first-class support for Ruby applications.
9
10
  It's a compliant Rack server. It’s also a well-equipped reverse proxy, API gateway, and static file server, controlled by an intuitive and elegant configuration API and DSL.
data/Rakefile CHANGED
@@ -84,11 +84,11 @@ task :build_all do
84
84
  end
85
85
 
86
86
  task :test_env_up do
87
- system('terraform -chdir=sandbox/deploy apply')
87
+ system('terraform -chdir=tmp/sandbox/deploy apply')
88
88
  end
89
89
 
90
90
  task :test_env_down do
91
- system('terraform -chdir=sandbox/deploy destroy')
91
+ system('terraform -chdir=tmp/sandbox/deploy destroy')
92
92
  end
93
93
  %i[itsi puma iodine falcon unicorn].each do |server|
94
94
  namespace server do
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-scheduler"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-server"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -14,7 +14,7 @@ use magnus::{
14
14
  block::Proc,
15
15
  error::Result,
16
16
  value::{LazyId, ReprValue},
17
- RArray, RHash, Ruby, Symbol, Value,
17
+ RArray, RHash, Ruby, Symbol, TryConvert, Value,
18
18
  };
19
19
  use nix::{
20
20
  fcntl::{fcntl, FcntlArg, FdFlag},
@@ -124,9 +124,17 @@ impl ServerParams {
124
124
  debug!("Loading Itsi Scheduler");
125
125
  ruby.require("itsi/scheduler")?;
126
126
  }
127
- let routes_raw = self
127
+ let result_pair = self
128
128
  .middleware_loader
129
- .call::<_, Option<Value>>(())
129
+ .call::<(), RArray>(())
130
+ .inspect_err(|e| {
131
+ eprintln!("Error loading middleware: {:?}", e);
132
+ if let Some(err_value) = e.value() {
133
+ print_rb_backtrace(err_value);
134
+ }
135
+ })?;
136
+ let routes_raw = result_pair
137
+ .entry::<Option<Value>>(0)
130
138
  .inspect_err(|e| {
131
139
  eprintln!("Error loading middleware: {:?}", e);
132
140
  if let Some(err_value) = e.value() {
@@ -134,6 +142,21 @@ impl ServerParams {
134
142
  }
135
143
  })?
136
144
  .map(|mw| mw.into());
145
+ let error_lines = result_pair.entry::<Option<RArray>>(1).inspect_err(|e| {
146
+ eprintln!("Error loading middleware: {:?}", e);
147
+ if let Some(err_value) = e.value() {
148
+ print_rb_backtrace(err_value);
149
+ }
150
+ })?;
151
+ if error_lines.is_some_and(|r| !r.is_empty()) {
152
+ let errors: Vec<String> =
153
+ Vec::<String>::try_convert(error_lines.unwrap().as_value())?;
154
+ ItsiServerConfig::print_config_errors(errors);
155
+ return Err(magnus::Error::new(
156
+ magnus::exception::runtime_error(),
157
+ "Failed to set middleware",
158
+ ));
159
+ }
137
160
  let middleware = MiddlewareSet::new(routes_raw)?;
138
161
  self.middleware.set(middleware).map_err(|_| {
139
162
  magnus::Error::new(
@@ -1,5 +1,6 @@
1
1
  use crate::{
2
- server::http_message_types::HttpResponse, services::itsi_http_service::HttpRequestContext,
2
+ server::http_message_types::{HttpRequest, HttpResponse},
3
+ services::itsi_http_service::HttpRequestContext,
3
4
  };
4
5
 
5
6
  use super::{
@@ -13,6 +14,7 @@ use async_compression::{
13
14
  };
14
15
  use async_trait::async_trait;
15
16
  use bytes::{Bytes, BytesMut};
17
+ use either::Either;
16
18
  use futures::TryStreamExt;
17
19
  use http::{
18
20
  header::{GetAll, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TYPE},
@@ -20,6 +22,7 @@ use http::{
20
22
  };
21
23
  use http_body_util::{combinators::BoxBody, BodyExt, Full, StreamBody};
22
24
  use hyper::body::{Body, Frame};
25
+ use magnus::error::Result;
23
26
  use serde::{Deserialize, Serialize};
24
27
  use std::convert::Infallible;
25
28
  use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
@@ -151,6 +154,15 @@ fn update_content_encoding(parts: &mut http::response::Parts, new_encoding: Head
151
154
 
152
155
  #[async_trait]
153
156
  impl MiddlewareLayer for Compression {
157
+ async fn before(
158
+ &self,
159
+ req: HttpRequest,
160
+ context: &mut HttpRequestContext,
161
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
162
+ context.set_supported_encoding_set(&req);
163
+ Ok(Either::Left(req))
164
+ }
165
+
154
166
  /// We'll apply compression on the response, where appropriate.
155
167
  /// This is if:
156
168
  /// * The response body is larger than the minimum size.
@@ -207,16 +219,21 @@ impl MiddlewareLayer for Compression {
207
219
  }
208
220
  }
209
221
 
210
- let compression_method = match find_first_supported(
211
- &context.supported_encoding_set,
212
- self.algorithms.iter().map(|algo| algo.as_str()),
213
- ) {
214
- Some("gzip") => CompressionAlgorithm::Gzip,
215
- Some("br") => CompressionAlgorithm::Brotli,
216
- Some("deflate") => CompressionAlgorithm::Deflate,
217
- Some("zstd") => CompressionAlgorithm::Zstd,
218
- _ => CompressionAlgorithm::Identity,
219
- };
222
+ let compression_method =
223
+ if let Some(supported_encoding_set) = context.supported_encoding_set() {
224
+ match find_first_supported(
225
+ supported_encoding_set,
226
+ self.algorithms.iter().map(|algo| algo.as_str()),
227
+ ) {
228
+ Some("gzip") => CompressionAlgorithm::Gzip,
229
+ Some("br") => CompressionAlgorithm::Brotli,
230
+ Some("deflate") => CompressionAlgorithm::Deflate,
231
+ Some("zstd") => CompressionAlgorithm::Zstd,
232
+ _ => CompressionAlgorithm::Identity,
233
+ }
234
+ } else {
235
+ CompressionAlgorithm::Identity
236
+ };
220
237
 
221
238
  debug!(
222
239
  target: "middleware::compress",
@@ -62,7 +62,7 @@ impl MiddlewareLayer for LogRequests {
62
62
  req: HttpRequest,
63
63
  context: &mut HttpRequestContext,
64
64
  ) -> Result<Either<HttpRequest, HttpResponse>> {
65
- context.track_start_time();
65
+ context.init_logging_params();
66
66
  if let Some(LogConfig { level, format }) = self.before.as_ref() {
67
67
  level.log(format.rewrite_request(&req, context));
68
68
  }
@@ -33,7 +33,7 @@ use reqwest::{
33
33
  Body, Client, Url,
34
34
  };
35
35
  use serde::Deserialize;
36
- use tracing::{debug, info};
36
+ use tracing::debug;
37
37
 
38
38
  #[derive(Debug, Clone, Deserialize)]
39
39
  pub struct Proxy {
@@ -324,7 +324,6 @@ impl MiddlewareLayer for Proxy {
324
324
  .unwrap_or("")
325
325
  });
326
326
 
327
- info!("Extracted host str is {}", host_str);
328
327
  let req_info = RequestInfo {
329
328
  method: req.method().clone(),
330
329
  headers: req_headers.clone(),
@@ -12,10 +12,11 @@ use async_trait::async_trait;
12
12
  use either::Either;
13
13
  use http::{
14
14
  header::{IF_MODIFIED_SINCE, RANGE},
15
- HeaderMap, Method,
15
+ HeaderMap, HeaderValue, Method,
16
16
  };
17
17
  use itsi_error::ItsiError;
18
18
  use magnus::error::Result;
19
+ use moka::sync::Cache;
19
20
  use regex::Regex;
20
21
  use serde::Deserialize;
21
22
  use std::{collections::HashMap, path::PathBuf, sync::OnceLock, time::Duration};
@@ -75,6 +76,10 @@ impl MiddlewareLayer for StaticAssets {
75
76
  recheck_interval: Duration::from_secs(self.file_check_interval),
76
77
  serve_hidden_files: self.serve_hidden_files,
77
78
  allowed_extensions: self.allowed_extensions.clone(),
79
+ miss_cache: Cache::builder()
80
+ .max_capacity(self.max_files_in_memory)
81
+ .time_to_live(Duration::from_secs(self.file_check_interval))
82
+ .build(),
78
83
  })?)
79
84
  .map_err(ItsiError::new)?;
80
85
  Ok(())
@@ -90,6 +95,8 @@ impl MiddlewareLayer for StaticAssets {
90
95
  debug!(target: "middleware::static_assets", "Refusing to handle non-GET/HEAD request");
91
96
  return Ok(Either::Left(req));
92
97
  }
98
+
99
+ context.set_supported_encoding_set(&req);
93
100
  let abs_path = req.uri().path();
94
101
  let rel_path = if !self.relative_path {
95
102
  abs_path.trim_start_matches("/")
@@ -123,6 +130,10 @@ impl MiddlewareLayer for StaticAssets {
123
130
 
124
131
  // Let the file server handle everything
125
132
  let file_server = self.file_server.get().unwrap();
133
+ let encodings: &[HeaderValue] = context
134
+ .supported_encoding_set()
135
+ .map(Vec::as_slice)
136
+ .unwrap_or(&[] as &[HeaderValue]);
126
137
  let response = file_server
127
138
  .serve(
128
139
  &req,
@@ -131,9 +142,10 @@ impl MiddlewareLayer for StaticAssets {
131
142
  serve_range,
132
143
  if_modified_since,
133
144
  is_head_request,
134
- &context.supported_encoding_set,
145
+ encodings,
135
146
  )
136
147
  .await;
148
+
137
149
  if response.is_none() {
138
150
  Ok(Either::Left(req))
139
151
  } else {
@@ -50,6 +50,35 @@ pub fn parse_template(template: &str) -> Vec<Segment> {
50
50
  }
51
51
 
52
52
  impl StringRewrite {
53
+ /// Apply a single modifier of the form `op:arg` (or for replace `op:from,to`)
54
+ #[inline]
55
+ fn apply_modifier(s: &mut String, mod_str: &str) {
56
+ if let Some((op, arg)) = mod_str.split_once(':') {
57
+ match op {
58
+ "strip_prefix" => {
59
+ if s.starts_with(arg) {
60
+ let _ = s.drain(..arg.len());
61
+ }
62
+ }
63
+ "strip_suffix" => {
64
+ if s.ends_with(arg) {
65
+ let len = s.len();
66
+ let start = len.saturating_sub(arg.len());
67
+ let _ = s.drain(start..);
68
+ }
69
+ }
70
+ "replace" => {
71
+ if let Some((from, to)) = arg.split_once(',') {
72
+ if s.contains(from) {
73
+ *s = s.replace(from, to);
74
+ }
75
+ }
76
+ }
77
+ _ => {}
78
+ }
79
+ }
80
+ }
81
+
53
82
  pub fn rewrite_request(&self, req: &HttpRequest, context: &HttpRequestContext) -> String {
54
83
  let segments = self
55
84
  .segments
@@ -63,9 +92,17 @@ impl StringRewrite {
63
92
 
64
93
  for segment in segments {
65
94
  match segment {
66
- Segment::Literal(text) => result.push_str(text),
67
- Segment::Placeholder(placeholder) => {
68
- let replacement = match placeholder.as_str() {
95
+ Segment::Literal(text) => {
96
+ result.push_str(text);
97
+ }
98
+ Segment::Placeholder(raw) => {
99
+ // split into key and optional modifier
100
+ let mut parts = raw.split('|');
101
+ let key = parts.next().unwrap();
102
+ let modifiers = parts; // zero o
103
+
104
+ // 1) lookup the raw replacement
105
+ let mut replacement = match key {
69
106
  "request_id" => context.short_request_id(),
70
107
  "request_id_full" => context.request_id(),
71
108
  "method" => req.method().as_str().to_string(),
@@ -76,13 +113,13 @@ impl StringRewrite {
76
113
  .uri()
77
114
  .path_and_query()
78
115
  .map(|pq| pq.to_string())
79
- .unwrap_or("".to_string()),
116
+ .unwrap_or_default(),
80
117
  "query" => {
81
- let query = req.uri().query().unwrap_or("").to_string();
82
- if query.is_empty() {
83
- query
118
+ let q = req.uri().query().unwrap_or("");
119
+ if q.is_empty() {
120
+ "".to_string()
84
121
  } else {
85
- format!("?{}", query)
122
+ format!("?{}", q)
86
123
  }
87
124
  }
88
125
  "port" => req
@@ -91,31 +128,34 @@ impl StringRewrite {
91
128
  .map(|p| p.to_string())
92
129
  .unwrap_or_else(|| "80".to_string()),
93
130
  "start_time" => {
94
- if let Some(start_time) = context.start_time() {
95
- start_time.format("%Y-%m-%d:%H:%M:%S:%3f").to_string()
131
+ if let Some(ts) = context.start_time() {
132
+ ts.format("%Y-%m-%d:%H:%M:%S:%3f").to_string()
96
133
  } else {
97
134
  "N/A".to_string()
98
135
  }
99
136
  }
100
137
  other => {
101
- if let Some(header_val) = req.headers().get(other) {
102
- if let Ok(s) = header_val.to_str() {
103
- s.to_string()
104
- } else {
105
- "".to_string()
106
- }
107
- } else if let Some(caps) = &captures {
108
- if let Some(m) = caps.name(other) {
109
- m.as_str().to_string()
110
- } else {
111
- // Fallback: leave the placeholder as is.
112
- format!("{{{}}}", other)
113
- }
114
- } else {
138
+ // headers first
139
+ if let Some(hv) = req.headers().get(other) {
140
+ hv.to_str().unwrap_or("").to_string()
141
+ }
142
+ // then any regex‐capture
143
+ else if let Some(caps) = &captures {
144
+ caps.name(other)
145
+ .map(|m| m.as_str().to_string())
146
+ .unwrap_or_else(|| format!("{{{}}}", other))
147
+ }
148
+ // fallback: leave placeholder intact
149
+ else {
115
150
  format!("{{{}}}", other)
116
151
  }
117
152
  }
118
153
  };
154
+
155
+ for m in modifiers {
156
+ Self::apply_modifier(&mut replacement, m);
157
+ }
158
+
119
159
  result.push_str(&replacement);
120
160
  }
121
161
  }
@@ -132,37 +172,42 @@ impl StringRewrite {
132
172
  let mut result = String::with_capacity(self.template_string.len());
133
173
  for segment in segments {
134
174
  match segment {
135
- Segment::Literal(text) => result.push_str(text),
136
- Segment::Placeholder(placeholder) => {
137
- let replacement = match placeholder.as_str() {
175
+ Segment::Literal(text) => {
176
+ result.push_str(text);
177
+ }
178
+ Segment::Placeholder(raw) => {
179
+ let mut parts = raw.split('|');
180
+ let key = parts.next().unwrap();
181
+ let modifiers = parts; // zero o
182
+
183
+ let mut replacement = match key {
138
184
  "request_id" => context.short_request_id(),
139
185
  "request_id_full" => context.request_id(),
140
186
  "status" => resp.status().as_str().to_string(),
141
187
  "addr" => context.addr.to_owned(),
142
188
  "response_time" => {
143
- if let Some(response_time) = context.get_response_time() {
144
- if let Some(microseconds) = response_time.num_microseconds() {
145
- format!("{:.3}ms", microseconds as f64 / 1000.0)
146
- } else {
147
- format!("{}ms", response_time.num_milliseconds())
148
- }
189
+ let dur = context.get_response_time();
190
+ let micros = dur.as_micros();
191
+ if micros < 1_000 {
192
+ format!("{}µs", micros)
149
193
  } else {
150
- "-".to_string()
194
+ let ms = dur.as_secs_f64() * 1_000.0;
195
+ format!("{:.3}ms", ms)
151
196
  }
152
197
  }
153
198
  other => {
154
- // Try pulling from response headers first
155
- if let Some(val) = resp.headers().get(other) {
156
- if let Ok(s) = val.to_str() {
157
- s.to_string()
158
- } else {
159
- "".to_string()
160
- }
199
+ if let Some(hv) = resp.headers().get(other) {
200
+ hv.to_str().unwrap_or("").to_string()
161
201
  } else {
162
202
  format!("{{{}}}", other)
163
203
  }
164
204
  }
165
205
  };
206
+
207
+ for m in modifiers {
208
+ Self::apply_modifier(&mut replacement, m);
209
+ }
210
+
166
211
  result.push_str(&replacement);
167
212
  }
168
213
  }