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
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
- require 'forwardable'
2
+
3
+ require "forwardable"
3
4
  require "stringio"
4
5
  require "socket"
5
6
 
6
7
  module Itsi
7
-
8
8
  class HttpResponse
9
-
10
- def respond _body=nil, _status=200, _header=nil, status: _status, headers: _header, body: _body, hijack: false, &blk
11
-
12
- self.status = status.kind_of?(Symbol) ? HTTP_STATUS_NAME_TO_CODE_MAP.fetch(status) : status.to_i
9
+ def respond(
10
+ _body = nil, _status = 200, _header = nil, # rubocop:disable Lint/UnderscorePrefixedVariableName
11
+ status: _status, headers: _header, body: _body,
12
+ hijack: false
13
+ )
14
+ self.status = status.is_a?(Symbol) ? HTTP_STATUS_NAME_TO_CODE_MAP.fetch(status) : status.to_i
13
15
 
14
16
  body = body.to_s unless body.is_a?(String)
15
17
 
@@ -33,9 +35,9 @@ module Itsi
33
35
 
34
36
  # If you hijack the connection, you are responsible for closing it.
35
37
  # Otherwise, the response will be closed automatically.
36
- self.close unless hijack
38
+ close unless hijack
37
39
  else
38
- self.close
40
+ close
39
41
  end
40
42
  end
41
43
  end
@@ -1,8 +1,7 @@
1
1
  module Itsi
2
2
  class Server
3
-
4
3
  module Passfile
5
- require 'io/console'
4
+ require "io/console"
6
5
 
7
6
  module_function
8
7
 
@@ -18,7 +17,7 @@ module Itsi
18
17
  line.chomp!
19
18
  next if line.empty?
20
19
 
21
- user, pass = line.split(':', 2)
20
+ user, pass = line.split(":", 2)
22
21
  creds[user] = pass
23
22
  end
24
23
  end
@@ -26,14 +25,14 @@ module Itsi
26
25
  end
27
26
 
28
27
  def save(creds, filename)
29
- File.open(filename, 'w', 0o600) do |f|
28
+ File.open(filename, "w", 0o600) do |f|
30
29
  creds.each do |u, p|
31
30
  f.puts "#{u}:#{p}"
32
31
  end
33
32
  end
34
33
  end
35
34
 
36
- def echo(filename, algorithm)
35
+ def echo(_, algorithm)
37
36
  print "Enter username: "
38
37
  username = $stdin.gets.chomp
39
38
 
@@ -56,6 +55,7 @@ module Itsi
56
55
 
57
56
  def add(filename, algorithm)
58
57
  return unless (creds = load(filename))
58
+
59
59
  print "Enter username: "
60
60
  username = $stdin.gets.chomp
61
61
 
@@ -98,11 +98,11 @@ module Itsi
98
98
  def list(filename)
99
99
  puts "Current credentials in '#{filename}':"
100
100
  return unless (creds = load(filename))
101
+
101
102
  creds.each do |u, p|
102
103
  puts "#{u}:#{p}"
103
104
  end
104
105
  end
105
-
106
106
  end
107
107
  end
108
108
  end
@@ -2,7 +2,6 @@ module Itsi
2
2
  class Server
3
3
  module Config
4
4
  module ConfigHelpers
5
-
6
5
  def self.load_and_register(klass)
7
6
  config_type = klass.name.split("::").last.downcase.gsub(/([a-z]()[A-Z])/, '\1_\2')
8
7
 
@@ -17,35 +16,35 @@ module Itsi
17
16
  following = klass.subclasses
18
17
  new_class = (following - current).first
19
18
 
20
- documentation_file = "#{file[/(.*)\.rb/,1]}.md"
21
- if ! File.exist?(documentation_file)
22
- documentation_file = "#{file[/(.*)\/[^\/]+\.rb/,1]}/_index.md"
23
- end
24
- if File.exist?(documentation_file) && new_class
25
- new_class.documentation IO.read(documentation_file)
26
- .gsub(/^---.*?\n.*?-+/m,'') # Strip frontmatter
27
- .gsub(/^(```.*?)\{.*?\}.*$/, "\\1") # Strip filename from code blocks
28
- .gsub(/^\{\{[^\}]+\}\}/, "") # Strip Hugo blocks
29
- end
19
+ documentation_file = "#{file[/(.*)\.rb/, 1]}.md"
20
+ documentation_file = "#{file[%r{(.*)/[^/]+\.rb}, 1]}/_index.md" unless File.exist?(documentation_file)
21
+ next unless File.exist?(documentation_file) && new_class
22
+
23
+ new_class.documentation IO.read(documentation_file)
24
+ .gsub(/^---.*?\n.*?-+/m, "") # Strip frontmatter
25
+ .gsub(/^(```.*?)\{.*?\}.*$/, "\\1") # Strip filename from code blocks
26
+ .gsub(/^\{\{[^}]+\}\}/, "") # Strip Hugo blocks
30
27
  end
31
28
  end
32
29
 
33
- def normalize_keys!(hash, expected=[])
30
+ def normalize_keys!(hash, expected = [])
34
31
  hash.keys.each do |key|
35
32
  value = hash.delete(key)
36
33
  key = key.to_s.downcase.to_sym
37
34
  hash[key] = value
38
35
  raise "Unexpected key: #{key}" unless expected.include?(key)
36
+
39
37
  expected -= [key]
40
38
  end
41
- raise "Missing required keys: #{expected.join(', ')}" unless expected.empty?
39
+ raise "Missing required keys: #{expected.join(", ")}" unless expected.empty?
40
+
42
41
  hash
43
42
  end
44
43
 
45
- def self.included(cls)
46
- def cls.inherited(base)
44
+ def self.included(cls) # rubocop:disable Metrics/PerceivedComplexity,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
45
+ def cls.inherited(base) # rubocop:disable Metrics/MethodLength,Lint/MissingSuper,Metrics/PerceivedComplexity
47
46
  %i[detail documentation insert_text schema].each do |attr|
48
- base.define_singleton_method(attr) do |value=nil|
47
+ base.define_singleton_method(attr) do |value = nil|
49
48
  @middleware_class_attrs ||= {}
50
49
  if value
51
50
  @middleware_class_attrs[attr] = value
@@ -54,12 +53,12 @@ module Itsi
54
53
  end
55
54
  end
56
55
 
57
- base.define_method(attr) do |value=nil|
56
+ base.define_method(attr) do |_value = nil|
58
57
  self.class.send(attr)
59
58
  end
60
59
  end
61
60
 
62
- def base.schema(value=nil, &blk)
61
+ def base.schema(value = nil, &blk)
63
62
  @middleware_class_attrs ||= {}
64
63
  if blk
65
64
  @middleware_class_attrs[:schema] = TypedStruct.new(&blk)
@@ -75,29 +74,30 @@ module Itsi
75
74
 
76
75
  config_type = cls.name.split("::").last.downcase
77
76
 
78
- cls.define_singleton_method("#{config_type}_name") do |name=self.name|
77
+ cls.define_singleton_method("#{config_type}_name") do |name = self.name|
79
78
  @config_name ||= name.split("::").last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
80
79
  end
81
- cls.define_method(:opt_name){ self.class.send("#{config_type}_name") }
82
- cls.define_method(:location){ @location }
80
+ cls.define_method(:opt_name) { self.class.send("#{config_type}_name") }
81
+ cls.define_method(:location) { @location }
83
82
  end
84
83
 
85
- def initialize(location, params={})
84
+ def initialize(location, params = {})
86
85
  if !self.class.ancestors.include?(Middleware) && !location.parent.nil?
87
86
  raise "#{opt_name} must be set at the top level"
88
87
  end
88
+
89
89
  @location = location
90
- @params = case self.schema
91
- when TypedStruct::Validation
92
- self.schema.validate!(params)
93
- when Array
94
- default, validation = self.schema
95
- params ? validation.validate!(params) : default
96
- when nil
97
- nil
98
- else
99
- self.schema.new(params).to_h
100
- end
90
+ @params = case schema
91
+ when TypedStruct::Validation
92
+ schema.validate!(params)
93
+ when Array
94
+ default, validation = schema
95
+ params ? validation.validate!(params) : default
96
+ when nil
97
+ nil
98
+ else
99
+ schema.new(params).to_h
100
+ end
101
101
  end
102
102
  end
103
103
  end
@@ -9,7 +9,7 @@ module Itsi
9
9
  attr_reader :parent, :children, :middleware, :controller, :routes, :http_methods, :protocols,
10
10
  :hosts, :ports, :extensions, :content_types, :accepts, :options
11
11
 
12
- def self.evaluate(config = Itsi::Server::Config.config_file_path, &blk)
12
+ def self.evaluate(config = Itsi::Server::Config.config_file_path, &blk) # rubocop:disable Metrics/MethodLength
13
13
  config = new(routes: ["/"]) do
14
14
  if blk
15
15
  instance_exec(&blk)
@@ -17,14 +17,14 @@ module Itsi
17
17
  code = IO.read(config)
18
18
  instance_eval(code, config.to_s, 1)
19
19
  end
20
- location("*"){}
20
+ location("*") {}
21
21
  end
22
22
  [config.options, config.errors]
23
- rescue Exception => e
23
+ rescue Exception => e # rubocop:disable Lint/RescueException
24
24
  [{}, [[e, e.backtrace[0]]]]
25
25
  end
26
26
 
27
- def initialize(
27
+ def initialize( # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
28
28
  parent = nil,
29
29
  routes: [],
30
30
  methods: [],
@@ -52,17 +52,12 @@ module Itsi
52
52
  @accepts = accepts.map { |s| s.is_a?(Regexp) ? s : s.to_s }
53
53
 
54
54
  @options = {
55
- middleware_loaders: [],
55
+ nested_locations: [],
56
56
  middleware_loader: lambda do
57
- @options[:middleware_loaders].each(&:call)
57
+ @options[:nested_locations].each(&:call)
58
58
  @middleware[:app] ||= {}
59
59
  @middleware[:app][:app_proc] = @middleware[:app]&.[](:preloader)&.call || DEFAULT_APP[]
60
- if errors.any?
61
- error = errors.first.first
62
- error.set_backtrace(error.backtrace.drop_while{|r| r =~ /itsi\/server\/config/ })
63
- raise error
64
- end
65
- flatten_routes
60
+ [flatten_routes, Config.errors_to_error_lines(errors)]
66
61
  end
67
62
  }
68
63
 
@@ -78,7 +73,7 @@ module Itsi
78
73
  option_name = option.option_name
79
74
  define_method(option_name) do |*args, **kwargs, &blk|
80
75
  option.new(self, *args, **kwargs, &blk).build!
81
- rescue => e
76
+ rescue Exception => e # rubocop:disable Lint/RescueException
82
77
  @errors << [e, caller[1]]
83
78
  end
84
79
  end
@@ -89,7 +84,7 @@ module Itsi
89
84
  middleware.new(self, *args, **kwargs, &blk).build!
90
85
  rescue Config::Endpoint::InvalidHandlerException => e
91
86
  @errors << [e, "#{e.backtrace[0]}:in #{e.message}"]
92
- rescue => e
87
+ rescue Exception => e # rubocop:disable Lint/RescueException
93
88
  @errors << [e, caller[1]]
94
89
  end
95
90
  end
@@ -99,7 +94,7 @@ module Itsi
99
94
  @grpc_reflected_services.concat(handlers)
100
95
 
101
96
  location("grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo",
102
- "grpc.reflection.v1.ServerReflection/ServerReflectionInfo") do
97
+ "grpc.reflection.v1.ServerReflection/ServerReflectionInfo") do
103
98
  @middleware[:app] = { preloader: lambda {
104
99
  Itsi::Server::GrpcInterface.reflection_for(handlers)
105
100
  }, request_type: "grpc" }
@@ -141,7 +136,7 @@ module Itsi
141
136
  when Regexp
142
137
  seg.source
143
138
  else
144
- parts = seg.split('/')
139
+ parts = seg.split("/")
145
140
  parts.map do |part|
146
141
  case part
147
142
  when /^:([A-Za-z_]\w*)(?:\(([^)]*)\))?$/
@@ -184,11 +179,11 @@ module Itsi
184
179
 
185
180
  chain.each do |n|
186
181
  n.middleware.each do |k, v|
187
- if v[:combine]
188
- merged[k] = ([merged[k] || []] + [v]).flatten
189
- else
190
- merged[k] = v
191
- end
182
+ merged[k] = if v[:combine]
183
+ ([merged[k] || []] + [v]).flatten
184
+ else
185
+ v
186
+ end
192
187
  end
193
188
  end
194
189
  deep_stringify_keys(merged)
@@ -2,16 +2,20 @@ module Itsi
2
2
  class Server
3
3
  module KnownPaths
4
4
  ALL = []
5
- Dir.glob(File.join(__dir__, 'known_paths', '**', '*.txt')).each do |file|
6
- method_name = file[/known_paths\/(.*?)\.txt/,1].gsub(/([a-z])([A-Z])/, "\\1_\\2")
7
- .gsub(/-|\.|\//, "_")
8
- .gsub(/(^|\/)[0-9]/){|match| "FO"}.downcase.to_sym
5
+ Dir.glob(File.join(__dir__, "known_paths", "**", "*.txt")).each do |file|
6
+ method_name = file[%r{known_paths/(.*?)\.txt}, 1].gsub(/([a-z])([A-Z])/, "\\1_\\2")
7
+ .gsub(%r{-|\.|/}, "_")
8
+ .gsub(%r{(^|/)[0-9]}) do |match|
9
+ match.gsub(/\d/) do |digit|
10
+ %w[zero one two three four five six seven eight nine][digit.to_i]
11
+ end
12
+ end.downcase.to_sym
9
13
 
10
14
  ALL << method_name
11
- self.define_singleton_method(method_name) do
15
+ define_singleton_method(method_name) do
12
16
  File.readlines(file).map do |s|
13
- s.force_encoding('UTF-8')
14
- s.valid_encoding? ? s.strip : s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '').strip
17
+ s.force_encoding("UTF-8")
18
+ s.valid_encoding? ? s.strip : s.encode("UTF-8", invalid: :replace, undef: :replace, replace: "").strip
15
19
  end
16
20
  end
17
21
  end
@@ -84,10 +84,6 @@ module Itsi
84
84
  end
85
85
  else
86
86
  @params[:paths] << "" if @params[:paths].empty?
87
- @params[:paths] = @params[:paths].flat_map do |p|
88
- stripped_trailing = p[/(.*)\/?$/, 1]
89
- [stripped_trailing, stripped_trailing + "/"]
90
- end.uniq
91
87
  location.location(*@params[:paths], methods: @params[:http_methods]) do
92
88
  @middleware[:app] = app
93
89
  end
@@ -34,6 +34,19 @@ E.g.
34
34
  auth_api_key .. other options.., error_response: 'forbidden'
35
35
  ```
36
36
 
37
+ ## Example of built-in response
38
+ ### HTML
39
+ {{< card title="Built-in error page" image="/error_page.jpg" subtitle="Default Itsi Error Page." method="Resize" options="10x q80 webp" >}}
40
+ ### JSON
41
+ ```json
42
+ {
43
+ "error": "Too Many Requests",
44
+ "message": "Too many requests within a limited time frame.",
45
+ "code": 429,
46
+ "status": "error"
47
+ }
48
+ ```
49
+
37
50
  ## Override the error response
38
51
  You may instead wish to completely override the error response. You can provide a status code, and a message in up to three
39
52
  formats: plain-text, JSON, or HTML (at least one must be provided). Itsi will serve the appropriate type based on the `Accept` header of the incoming request, or fall back to the default if the requested type is not available.
@@ -27,7 +27,7 @@ module Itsi
27
27
  end
28
28
 
29
29
  attr_accessor :location, :routes, :block, :protocols, :hosts, :ports,
30
- :extensions, :content_types, :accepts, :block
30
+ :extensions, :content_types, :accepts
31
31
 
32
32
  def initialize(location,
33
33
  *routes,
@@ -56,13 +56,13 @@ module Itsi
56
56
  block: block
57
57
  }).to_h
58
58
  @routes = params[:routes].empty? ? ["*"] : params[:routes]
59
- @methods = params[:methods]
60
- @protocols = params[:protocols] | params[:schemes]
61
- @hosts = params[:hosts]
62
- @ports = params[:ports]
63
- @extensions = params[:extensions]
64
- @content_types = params[:content_types]
65
- @accepts = params[:accepts]
59
+ @methods = params[:methods].map { |s| s.is_a?(Regexp) ? s : s.to_s }
60
+ @protocols = (params[:protocols] | params[:schemes]).map { |s| s.is_a?(Regexp) ? s : s.to_s }
61
+ @hosts = params[:hosts].map { |s| s.is_a?(Regexp) ? s : s.to_s }
62
+ @ports = params[:ports].map { |s| s.is_a?(Regexp) ? s : s.to_s }
63
+ @extensions = params[:extensions].map { |s| s.is_a?(Regexp) ? s : s.to_s }
64
+ @content_types = params[:content_types].map { |s| s.is_a?(Regexp) ? s : s.to_s }
65
+ @accepts = params[:accepts].map { |s| s.is_a?(Regexp) ? s : s.to_s }
66
66
  @block = block
67
67
  end
68
68
 
@@ -70,27 +70,31 @@ module Itsi
70
70
  @methods
71
71
  end
72
72
 
73
+ def intersect(a, b)
74
+ return b if a.empty?
75
+ return a if b.empty?
76
+ a & b
77
+ end
78
+
73
79
  def build!
74
80
  build_child = lambda {
75
- location.children << DSL.new(
81
+ child = DSL.new(
76
82
  location,
77
83
  routes: routes,
78
- methods: Array(http_methods) | location.http_methods,
79
- protocols: Array(protocols) | location.protocols,
80
- hosts: Array(hosts) | location.hosts,
81
- ports: Array(ports) | location.ports,
82
- extensions: Array(extensions) | location.extensions,
83
- content_types: Array(content_types) | location.content_types,
84
- accepts: Array(accepts) | location.accepts,
84
+ methods: intersect(http_methods, location.http_methods),
85
+ protocols: intersect(protocols, location.protocols),
86
+ hosts: intersect(hosts, location.hosts),
87
+ ports: intersect(ports, location.ports),
88
+ extensions: intersect(extensions, location.extensions),
89
+ content_types: intersect(content_types, location.content_types),
90
+ accepts: intersect(accepts, location.accepts),
85
91
  controller: location.controller,
86
92
  &block
87
93
  )
94
+ child.options[:nested_locations].each(&:call)
95
+ location.children << child
88
96
  }
89
- if location.parent.nil?
90
- location.options[:middleware_loaders] << build_child
91
- else
92
- build_child[]
93
- end
97
+ location.options[:nested_locations] << build_child
94
98
  end
95
99
 
96
100
  end
@@ -2,17 +2,16 @@ module Itsi
2
2
  class Server
3
3
  module Config
4
4
  class Proxy < Middleware
5
-
6
5
  insert_text <<~SNIPPET
7
- proxy \\
8
- to: "${1:http://backend.example.com/api{path}{query}}", \\
9
- backends: [${2:"127.0.0.1:3001", "127.0.0.1:3002"}], \\
10
- backend_priority: ${3|"round_robin","ordered","random"|}, \\
11
- headers: { ${4| "X-Forwarded-For" => { rewrite: "{addr}" },|} }, \\
12
- verify_ssl: ${5|true,false|}, \\
13
- timeout: ${6|30,60|}, \\
14
- tls_sni: ${7|true,false|}, \\
15
- error_response: ${8|"bad_gateway", "service_unavailable", { code: 503\\, default_format: "html"\\, html: { inline: "<h1>Service Unavailable</h1>" } }|}
6
+ proxy \\
7
+ to: "${1:http://backend.example.com{path_and_query}",
8
+ backends: [${2:"127.0.0.1:3001", "127.0.0.1:3002"}],
9
+ backend_priority: ${3|"round_robin","ordered","random"|},
10
+ headers: { ${4| "X-Forwarded-For" => { rewrite: "{addr}" },|} },
11
+ verify_ssl: ${5|true,false|},
12
+ timeout: ${6|30,60|},
13
+ tls_sni: ${7|true,false|},
14
+ error_response: ${8|"bad_gateway", "service_unavailable", { code: 503\\, default_format: "html"\\, html: { inline: "<h1>Service Unavailable</h1>" } }|}
16
15
  SNIPPET
17
16
 
18
17
  detail "Forwards incoming requests to a backend server using dynamic URL rewriting. Supports various backend selection strategies and header overriding."
@@ -21,18 +20,20 @@ module Itsi
21
20
  {
22
21
  to: Type(String) & Required(),
23
22
  backends: Array(Type(String)),
24
- backend_priority: Enum(["round_robin", "ordered", "random"]).default("round_robin"),
23
+ backend_priority: Enum(%w[round_robin ordered random]).default("round_robin"),
25
24
  headers: Hash(Type(String), Type(String)).default({}),
26
25
  verify_ssl: Bool().default(true),
27
26
  tls_sni: Bool().default(true),
28
27
  timeout: Type(Integer).default(30),
29
- error_response: Type(ErrorResponseDef).default("bad_gateway"),
28
+ error_response: Type(ErrorResponseDef).default("bad_gateway")
30
29
  }
31
30
  end
32
31
 
33
32
  def build!
34
- require 'uri'
35
- @params[:backends]||= URI.extract(@params[:to]).map(&URI.method(:parse)).map{|u| "#{u.scheme}://#{u.host}:#{u.port}" }
33
+ require "uri"
34
+ @params[:backends] ||= URI.extract(@params[:to]).map(&URI.method(:parse)).map do |u|
35
+ "#{u.scheme}://#{u.host}:#{u.port}"
36
+ end
36
37
  super
37
38
  end
38
39
  end
@@ -2,12 +2,11 @@ module Itsi
2
2
  class Server
3
3
  module Config
4
4
  class RackupFile < Middleware
5
-
6
5
  insert_text <<~SNIPPET
7
- rackup_file \\
8
- "config.ru",
9
- nonblocking: ${2|true,false|},
10
- sendfile: ${3|true,false|}
6
+ rackup_file \\
7
+ "config.ru",
8
+ nonblocking: ${2|true,false|},
9
+ sendfile: ${3|true,false|}
11
10
 
12
11
  SNIPPET
13
12
 
@@ -23,20 +22,18 @@ module Itsi
23
22
  def initialize(location, app, **params)
24
23
  super(location, params)
25
24
  raise "Rackup file must be a string" unless app.is_a?(String)
25
+
26
26
  @app = Itsi::Server::RackInterface.for(app)
27
27
  end
28
28
 
29
29
  def build!
30
30
  app_args = {
31
- preloader: -> { @app},
31
+ preloader: -> { @app },
32
32
  sendfile: @params[:sendfile],
33
33
  nonblocking: @params[:nonblocking],
34
- base_path: "^(?<base_path>#{location.paths_from_parent.gsub(/\.\*\)$/, ')')}).*$"
34
+ base_path: "^(?<base_path>#{location.paths_from_parent.gsub(/\.\*\)$/, ")")}).*$"
35
35
  }
36
36
  location.middleware[:app] = app_args
37
- location.location("*") do
38
- @middleware[:app] = app_args
39
- end
40
37
  end
41
38
  end
42
39
  end
@@ -20,6 +20,46 @@ It can auto-index directories for simple directory listings.
20
20
  static_assets root_dir: "./"
21
21
  ```
22
22
 
23
+ ### Directory Index
24
+ #### HTML
25
+
26
+ {{< card link="/" title="Static File Server" image="/directory_listing.jpg" subtitle="Static File Listing, Powered by Itsi." method="Resize" options="500x q80 webp" >}}
27
+
28
+ #### JSON
29
+ Directory indexes also support responding in JSON format. E.g.
30
+
31
+ `curl -H "Accept: application/json" http://0.0.0.0`
32
+
33
+ ```json
34
+ {
35
+ "directory": ".",
36
+ "items": [
37
+ {
38
+ "is_dir": false,
39
+ "modified": "2025-04-22 04:21:43",
40
+ "name": "Gemfile",
41
+ "path": "Gemfile",
42
+ "size": "42 B"
43
+ },
44
+ {
45
+ "is_dir": false,
46
+ "modified": "2025-04-22 04:21:48",
47
+ "name": "Gemfile.lock",
48
+ "path": "Gemfile%2Elock",
49
+ "size": "463 B"
50
+ },
51
+ {
52
+ "is_dir": false,
53
+ "modified": "2025-04-22 02:46:45",
54
+ "name": "Itsi.rb",
55
+ "path": "Itsi%2Erb",
56
+ "size": "80 B"
57
+ }
58
+ ],
59
+ "title": "Directory listing for ."
60
+ }
61
+ ```
62
+
23
63
  ## Configuration Options
24
64
 
25
65
 
@@ -75,11 +75,15 @@ module Itsi
75
75
  @params[:allowed_extensions] << ""
76
76
  end
77
77
 
78
- @params[:base_path] = "^(?<base_path>#{location.paths_from_parent}).*$"
79
- params = @params
80
- location.location("*", extensions: @params[:allowed_extensions]) do
81
- @middleware[:static_assets] = params
78
+ if @params[:allowed_extensions].any? && @params[:auto_index]
79
+ @params[:allowed_extensions] |= ["html"]
80
+ @params[:allowed_extensions] |= [""]
82
81
  end
82
+
83
+ @params[:base_path] = "^(?<base_path>#{location.paths_from_parent.gsub(/\.\*\)$/,")")}).*$"
84
+ params = @params
85
+
86
+ location.middleware[:static_assets] = params
83
87
  end
84
88
  end
85
89
  end
@@ -13,6 +13,20 @@ The String Rewrite mechanism is used when configuring Itsi for
13
13
 
14
14
  It allows you to create dynamic strings from a template by combining literal text with placeholders. Placeholders (denoted using curly braces: `{}`) are replaced at runtime with data derived from the HTTP request, response, or context.
15
15
 
16
+ Modifiers can be appended after a pipe | to transform the substituted value.
17
+
18
+ ## Modifiers
19
+
20
+ After a placeholder name, add |<modifier>:<arg> (or for replace, |replace:<from>,<to>). Available modifiers:
21
+
22
+ `strip_prefix:<text>` If the substituted value starts with <text>, remove that prefix.
23
+
24
+ `strip_suffix:<text>` If the substituted value ends with <text>, remove that suffix.
25
+
26
+ `replace:<from>,<to>` Replace all occurrences of <from> in the substituted value with <to>.
27
+
28
+ Modifiers are applied in the order they appear. You can chain multiple modifiers by repeating the |<modifier>:<arg> syntax (e.g. `{path|strip_prefix:/rails|replace:old,new}`).
29
+
16
30
  ### Rewriting a Request
17
31
 
18
32
  The following placeholders are supported:
@@ -4,7 +4,6 @@ module Itsi
4
4
  class Option
5
5
  include ConfigHelpers
6
6
 
7
-
8
7
  def build!
9
8
  location.options[self.class.option_name] = @params
10
9
  end
@@ -3,7 +3,7 @@ module Itsi
3
3
  module Config
4
4
  class Include < Option
5
5
 
6
- insert_text "include \"${1|other_file.rb|}\" # Include another file to be loaded within the current configuration"
6
+ insert_text "include \"${1|other_file|}\" # Include another file to be loaded within the current configuration"
7
7
 
8
8
  detail "Include another file to be loaded within the current configuration"
9
9