itsi 0.1.20 → 0.2.2

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 (319) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -8
  3. data/Cargo.lock +2 -2
  4. data/LICENSE.txt +698 -0
  5. data/README.md +15 -4
  6. data/Rakefile +9 -5
  7. data/crates/itsi_acme/.gitignore +4 -0
  8. data/crates/itsi_acme/Cargo.toml +86 -0
  9. data/crates/itsi_acme/LICENSE-APACHE +201 -0
  10. data/crates/itsi_acme/LICENSE-MIT +23 -0
  11. data/crates/itsi_acme/README.md +9 -0
  12. data/crates/itsi_acme/examples/high_level.rs +63 -0
  13. data/crates/itsi_acme/examples/high_level_warp.rs +52 -0
  14. data/crates/itsi_acme/examples/low_level.rs +87 -0
  15. data/crates/itsi_acme/examples/low_level_axum.rs +66 -0
  16. data/crates/itsi_acme/src/acceptor.rs +81 -0
  17. data/crates/itsi_acme/src/acme.rs +354 -0
  18. data/crates/itsi_acme/src/axum.rs +86 -0
  19. data/crates/itsi_acme/src/cache.rs +39 -0
  20. data/crates/itsi_acme/src/caches/boxed.rs +80 -0
  21. data/crates/itsi_acme/src/caches/composite.rs +69 -0
  22. data/crates/itsi_acme/src/caches/dir.rs +106 -0
  23. data/crates/itsi_acme/src/caches/mod.rs +11 -0
  24. data/crates/itsi_acme/src/caches/no.rs +78 -0
  25. data/crates/itsi_acme/src/caches/test.rs +136 -0
  26. data/crates/itsi_acme/src/config.rs +172 -0
  27. data/crates/itsi_acme/src/https_helper.rs +69 -0
  28. data/crates/itsi_acme/src/incoming.rs +142 -0
  29. data/crates/itsi_acme/src/jose.rs +161 -0
  30. data/crates/itsi_acme/src/lib.rs +142 -0
  31. data/crates/itsi_acme/src/resolver.rs +59 -0
  32. data/crates/itsi_acme/src/state.rs +424 -0
  33. data/crates/itsi_rb_helpers/src/lib.rs +4 -3
  34. data/crates/itsi_scheduler/Cargo.toml +1 -1
  35. data/crates/itsi_scheduler/src/itsi_scheduler.rs +8 -2
  36. data/crates/itsi_scheduler/src/lib.rs +1 -0
  37. data/crates/itsi_server/Cargo.toml +1 -1
  38. data/crates/itsi_server/src/lib.rs +2 -1
  39. data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +18 -1
  40. data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +11 -3
  41. data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +122 -63
  42. data/crates/itsi_server/src/ruby_types/itsi_server.rs +2 -0
  43. data/crates/itsi_server/src/server/binds/bind.rs +3 -0
  44. data/crates/itsi_server/src/server/binds/listener.rs +12 -5
  45. data/crates/itsi_server/src/server/binds/tls.rs +13 -5
  46. data/crates/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +12 -5
  47. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +8 -1
  48. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +9 -1
  49. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +48 -43
  50. data/crates/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +11 -2
  51. data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +39 -12
  52. data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +36 -27
  53. data/crates/itsi_server/src/server/middleware_stack/middlewares/csp.rs +25 -11
  54. data/crates/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +12 -3
  55. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +74 -72
  56. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +15 -1
  57. data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +11 -8
  58. data/crates/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +19 -11
  59. data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +5 -5
  60. data/crates/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +2 -2
  61. data/crates/itsi_server/src/server/middleware_stack/middlewares/mod.rs +11 -5
  62. data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +17 -20
  63. data/crates/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +19 -8
  64. data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +16 -37
  65. data/crates/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +22 -12
  66. data/crates/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +26 -11
  67. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +7 -1
  68. data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +14 -4
  69. data/crates/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +19 -0
  70. data/crates/itsi_server/src/server/middleware_stack/mod.rs +49 -13
  71. data/crates/itsi_server/src/server/mod.rs +1 -0
  72. data/crates/itsi_server/src/server/redirect_type.rs +26 -0
  73. data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +22 -16
  74. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +49 -12
  75. data/crates/itsi_server/src/server/signal.rs +1 -0
  76. data/crates/itsi_server/src/server/size_limited_incoming.rs +6 -0
  77. data/crates/itsi_server/src/server/thread_worker.rs +5 -1
  78. data/crates/itsi_server/src/services/itsi_http_service.rs +20 -2
  79. data/crates/itsi_server/src/services/rate_limiter.rs +15 -4
  80. data/crates/itsi_server/src/services/static_file_server.rs +33 -19
  81. data/crates/itsi_tracing/src/lib.rs +42 -22
  82. data/docs/content/_index.md +1 -2
  83. data/docs/content/acknowledgements/_index.md +5 -2
  84. data/docs/content/configuration/_index.md +8 -5
  85. data/docs/content/contact/_index.md +8 -1
  86. data/docs/content/faqs/_index.md +5 -3
  87. data/docs/content/features/_index.md +56 -50
  88. data/docs/content/getting_started/_index.md +8 -5
  89. data/docs/content/getting_started/local_development.md +68 -8
  90. data/docs/content/getting_started/logging.md +16 -9
  91. data/docs/content/getting_started/running_itsi_in_production.md +5 -3
  92. data/docs/content/getting_started/signals.md +38 -0
  93. data/docs/content/itsi_scheduler/_index.md +8 -7
  94. data/docs/content/utilities/_index.md +13 -0
  95. data/docs/content/utilities/config_file_testing.md +17 -0
  96. data/docs/content/utilities/passfile_generator.md +41 -0
  97. data/docs/content/utilities/route_testing.md +27 -0
  98. data/docs/content/utilities/secrets_management.md +30 -0
  99. data/docs/hugo.yaml +1 -1
  100. data/fairytale.txt +3 -4
  101. data/gems/scheduler/Cargo.lock +1 -1
  102. data/gems/scheduler/README.md +4 -5
  103. data/gems/scheduler/Rakefile +0 -4
  104. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  105. data/gems/scheduler/lib/itsi/scheduler.rb +9 -4
  106. data/gems/scheduler/test/test_active_record.rb +12 -7
  107. data/gems/server/Cargo.lock +1 -1
  108. data/gems/server/Rakefile +0 -4
  109. data/gems/server/exe/itsi +13 -2
  110. data/gems/server/lib/itsi/http_request/response_status_shortcodes.rb +2 -0
  111. data/gems/server/lib/itsi/http_request.rb +40 -9
  112. data/gems/server/lib/itsi/http_response.rb +2 -1
  113. data/gems/server/lib/itsi/passfile.rb +0 -1
  114. data/gems/server/lib/itsi/server/config/config_helpers.rb +20 -8
  115. data/gems/server/lib/itsi/server/config/dsl.rb +20 -435
  116. data/gems/server/lib/itsi/server/config/known_paths.rb +4 -1
  117. data/gems/server/lib/itsi/server/config/middleware/_index.md +6 -4
  118. data/gems/server/lib/itsi/server/config/middleware/allow_list.md +46 -0
  119. data/gems/server/lib/itsi/server/config/middleware/allow_list.rb +42 -0
  120. data/gems/server/lib/itsi/server/config/middleware/auth_api_key.md +90 -0
  121. data/gems/server/lib/itsi/server/config/middleware/auth_api_key.rb +51 -0
  122. data/gems/server/lib/itsi/server/config/middleware/auth_basic.md +45 -0
  123. data/gems/server/lib/itsi/server/config/middleware/auth_basic.rb +44 -0
  124. data/gems/server/lib/itsi/server/config/middleware/auth_jwt.md +82 -0
  125. data/gems/server/lib/itsi/server/config/middleware/auth_jwt.rb +38 -0
  126. data/gems/server/lib/itsi/server/config/middleware/cache_control.md +78 -0
  127. data/gems/server/lib/itsi/server/config/middleware/cache_control.rb +45 -0
  128. data/gems/server/lib/itsi/server/config/middleware/cidr_to_regex.rb +50 -0
  129. data/gems/server/lib/itsi/server/config/middleware/compression.md +50 -0
  130. data/gems/server/lib/itsi/server/config/middleware/compression.rb +37 -0
  131. data/gems/server/lib/itsi/server/config/middleware/cors.md +93 -0
  132. data/gems/server/lib/itsi/server/config/middleware/cors.rb +32 -0
  133. data/gems/server/lib/itsi/server/config/middleware/csp.md +37 -0
  134. data/gems/server/lib/itsi/server/config/middleware/csp.rb +44 -0
  135. data/gems/server/lib/itsi/server/config/middleware/deny_list.md +45 -0
  136. data/gems/server/lib/itsi/server/config/middleware/deny_list.rb +42 -0
  137. data/gems/server/lib/itsi/server/config/middleware/endpoint/_index.md +159 -0
  138. data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.md +186 -0
  139. data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.rb +33 -0
  140. data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.md +12 -0
  141. data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.rb +42 -0
  142. data/gems/server/lib/itsi/server/config/middleware/endpoint/endpoint.rb +99 -0
  143. data/gems/server/lib/itsi/server/config/middleware/endpoint/get.md +12 -0
  144. data/gems/server/lib/itsi/server/config/middleware/endpoint/get.rb +42 -0
  145. data/gems/server/lib/itsi/server/config/middleware/endpoint/http_request.md +44 -0
  146. data/gems/server/lib/itsi/server/config/middleware/endpoint/http_response.md +39 -0
  147. data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.md +12 -0
  148. data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.rb +42 -0
  149. data/gems/server/lib/itsi/server/config/middleware/endpoint/post.md +12 -0
  150. data/gems/server/lib/itsi/server/config/middleware/endpoint/post.rb +42 -0
  151. data/gems/server/lib/itsi/server/config/middleware/endpoint/put.md +12 -0
  152. data/gems/server/lib/itsi/server/config/middleware/endpoint/put.rb +42 -0
  153. data/gems/server/lib/itsi/server/config/middleware/endpoint/schemas.md +122 -0
  154. data/gems/server/lib/itsi/server/config/middleware/error_response.md +61 -0
  155. data/gems/server/lib/itsi/server/config/middleware/error_response.rb +36 -0
  156. data/gems/server/lib/itsi/server/config/middleware/etag.md +59 -0
  157. data/gems/server/lib/itsi/server/config/middleware/etag.rb +27 -0
  158. data/gems/server/lib/itsi/server/config/middleware/grpc.md +172 -0
  159. data/gems/server/lib/itsi/server/config/middleware/grpc.rb +54 -0
  160. data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.md +124 -0
  161. data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.rb +61 -0
  162. data/gems/server/lib/itsi/server/config/middleware/location.md +107 -0
  163. data/gems/server/lib/itsi/server/config/middleware/location.rb +99 -0
  164. data/gems/server/lib/itsi/server/config/middleware/log_requests.md +13 -11
  165. data/gems/server/lib/itsi/server/config/middleware/log_requests.rb +1 -3
  166. data/gems/server/lib/itsi/server/config/middleware/max_body.md +18 -0
  167. data/gems/server/lib/itsi/server/config/middleware/max_body.rb +21 -0
  168. data/gems/server/lib/itsi/server/config/middleware/proxy.md +62 -0
  169. data/gems/server/lib/itsi/server/config/middleware/proxy.rb +41 -0
  170. data/gems/server/lib/itsi/server/config/middleware/rackup_file.md +54 -0
  171. data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +44 -0
  172. data/gems/server/lib/itsi/server/config/middleware/rate_limit.md +126 -0
  173. data/gems/server/lib/itsi/server/config/middleware/rate_limit.rb +34 -0
  174. data/gems/server/lib/itsi/server/config/middleware/rate_limit_store.rb +25 -0
  175. data/gems/server/lib/itsi/server/config/middleware/redirect.md +55 -0
  176. data/gems/server/lib/itsi/server/config/middleware/redirect.rb +25 -0
  177. data/gems/server/lib/itsi/server/config/middleware/request_headers.md +34 -0
  178. data/gems/server/lib/itsi/server/config/middleware/request_headers.rb +24 -0
  179. data/gems/server/lib/itsi/server/config/middleware/response_headers.md +33 -0
  180. data/gems/server/lib/itsi/server/config/middleware/response_headers.rb +25 -0
  181. data/gems/server/lib/itsi/server/config/middleware/run.md +60 -0
  182. data/gems/server/lib/itsi/server/config/middleware/run.rb +43 -0
  183. data/gems/server/lib/itsi/server/config/middleware/static_assets.md +73 -0
  184. data/gems/server/lib/itsi/server/config/middleware/static_assets.rb +87 -0
  185. data/gems/server/lib/itsi/server/config/middleware/static_response.md +44 -0
  186. data/gems/server/lib/itsi/server/config/middleware/static_response.rb +29 -0
  187. data/gems/server/lib/itsi/server/config/middleware/string_rewrite.md +67 -0
  188. data/gems/server/lib/itsi/server/config/middleware/token_source.rb +32 -0
  189. data/gems/server/lib/itsi/server/config/middleware.rb +4 -0
  190. data/gems/server/lib/itsi/server/config/option.rb +5 -0
  191. data/gems/server/lib/itsi/server/config/options/_index.md +3 -2
  192. data/gems/server/lib/itsi/server/config/options/auto_reload_config.md +13 -0
  193. data/gems/server/lib/itsi/server/config/options/auto_reload_config.rb +41 -0
  194. data/gems/server/lib/itsi/server/config/options/bind.md +71 -0
  195. data/gems/server/lib/itsi/server/config/options/bind.rb +26 -0
  196. data/gems/server/lib/itsi/server/config/options/certificates.md +65 -0
  197. data/gems/server/lib/itsi/server/config/options/daemonize.md +14 -0
  198. data/gems/server/lib/itsi/server/config/options/daemonize.rb +19 -0
  199. data/gems/server/lib/itsi/server/config/options/fiber_scheduler.md +1 -2
  200. data/gems/server/lib/itsi/server/config/options/fiber_scheduler.rb +6 -3
  201. data/gems/server/lib/itsi/server/config/options/header_read_timeout.md +17 -0
  202. data/gems/server/lib/itsi/server/config/options/header_read_timeout.rb +19 -0
  203. data/gems/server/lib/itsi/server/config/options/hooks/_index.md +11 -0
  204. data/gems/server/lib/itsi/server/config/options/hooks/after_fork.md +13 -0
  205. data/gems/server/lib/itsi/server/config/options/hooks/after_fork.rb +28 -0
  206. data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.md +14 -0
  207. data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.rb +28 -0
  208. data/gems/server/lib/itsi/server/config/options/hooks/after_start.md +12 -0
  209. data/gems/server/lib/itsi/server/config/options/hooks/after_start.rb +28 -0
  210. data/gems/server/lib/itsi/server/config/options/hooks/before_fork.md +13 -0
  211. data/gems/server/lib/itsi/server/config/options/hooks/before_fork.rb +28 -0
  212. data/gems/server/lib/itsi/server/config/options/hooks/before_restart.md +12 -0
  213. data/gems/server/lib/itsi/server/config/options/hooks/before_restart.rb +28 -0
  214. data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.md +12 -0
  215. data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.rb +28 -0
  216. data/gems/server/lib/itsi/server/config/options/include.md +20 -0
  217. data/gems/server/lib/itsi/server/config/options/include.rb +36 -0
  218. data/gems/server/lib/itsi/server/config/options/listen_backlog.md +11 -0
  219. data/gems/server/lib/itsi/server/config/options/listen_backlog.rb +19 -0
  220. data/gems/server/lib/itsi/server/config/options/log_format.md +18 -0
  221. data/gems/server/lib/itsi/server/config/options/log_format.rb +19 -0
  222. data/gems/server/lib/itsi/server/config/options/log_level.md +34 -0
  223. data/gems/server/lib/itsi/server/config/options/log_level.rb +20 -0
  224. data/gems/server/lib/itsi/server/config/options/log_target.md +38 -0
  225. data/gems/server/lib/itsi/server/config/options/log_target.rb +19 -0
  226. data/gems/server/lib/itsi/server/config/options/log_target_filters.md +17 -0
  227. data/gems/server/lib/itsi/server/config/options/log_target_filters.rb +19 -0
  228. data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.md +27 -0
  229. data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.rb +24 -0
  230. data/gems/server/lib/itsi/server/config/options/nodelay.md +16 -0
  231. data/gems/server/lib/itsi/server/config/options/nodelay.rb +19 -0
  232. data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.md +19 -0
  233. data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.rb +18 -0
  234. data/gems/server/lib/itsi/server/config/options/pin_worker_cores.md +17 -0
  235. data/gems/server/lib/itsi/server/config/options/pin_worker_cores.rb +19 -0
  236. data/gems/server/lib/itsi/server/config/options/preload.md +21 -0
  237. data/gems/server/lib/itsi/server/config/options/preload.rb +18 -0
  238. data/gems/server/lib/itsi/server/config/options/recv_buffer_size.md +15 -0
  239. data/gems/server/lib/itsi/server/config/options/recv_buffer_size.rb +19 -0
  240. data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.md +21 -0
  241. data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.rb +30 -0
  242. data/gems/server/lib/itsi/server/config/options/request_timeout.md +23 -0
  243. data/gems/server/lib/itsi/server/config/options/request_timeout.rb +19 -0
  244. data/gems/server/lib/itsi/server/config/options/reuse_address.md +16 -0
  245. data/gems/server/lib/itsi/server/config/options/reuse_address.rb +19 -0
  246. data/gems/server/lib/itsi/server/config/options/reuse_port.md +16 -0
  247. data/gems/server/lib/itsi/server/config/options/reuse_port.rb +19 -0
  248. data/gems/server/lib/itsi/server/config/options/scheduler_threads.md +34 -0
  249. data/gems/server/lib/itsi/server/config/options/scheduler_threads.rb +17 -0
  250. data/gems/server/lib/itsi/server/config/options/shutdown_timeout.md +17 -0
  251. data/gems/server/lib/itsi/server/config/options/shutdown_timeout.rb +19 -0
  252. data/gems/server/lib/itsi/server/config/options/stream_body.md +32 -0
  253. data/gems/server/lib/itsi/server/config/options/stream_body.rb +18 -0
  254. data/gems/server/lib/itsi/server/config/options/threads.md +7 -2
  255. data/gems/server/lib/itsi/server/config/options/threads.rb +1 -1
  256. data/gems/server/lib/itsi/server/config/options/watch.md +16 -0
  257. data/gems/server/lib/itsi/server/config/options/watch.rb +28 -0
  258. data/gems/server/lib/itsi/server/config/options/worker_memory_limit.md +22 -0
  259. data/gems/server/lib/itsi/server/config/options/worker_memory_limit.rb +18 -0
  260. data/gems/server/lib/itsi/server/config/options/workers.md +1 -2
  261. data/gems/server/lib/itsi/server/config/options/workers.rb +1 -1
  262. data/gems/server/lib/itsi/server/config/typed_struct.rb +59 -20
  263. data/gems/server/lib/itsi/server/config.rb +77 -48
  264. data/gems/server/lib/itsi/server/default_config/Itsi.rb +3 -3
  265. data/gems/server/lib/itsi/server/grpc/grpc_call.rb +1 -1
  266. data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +11 -4
  267. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -3
  268. data/gems/server/lib/itsi/server/route_tester.rb +58 -8
  269. data/gems/server/lib/itsi/server/signal_trap.rb +1 -1
  270. data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +14 -18
  271. data/gems/server/lib/itsi/server/typed_handlers/source_parser.rb +5 -4
  272. data/gems/server/lib/itsi/server/typed_handlers.rb +12 -4
  273. data/gems/server/lib/itsi/server/version.rb +1 -1
  274. data/gems/server/lib/itsi/server.rb +98 -14
  275. data/gems/server/lib/ruby_lsp/itsi/addon.rb +20 -18
  276. data/gems/server/test/helpers/test_helper.rb +89 -29
  277. data/gems/server/test/middleware/allow_list.rb +128 -0
  278. data/gems/server/test/middleware/auth_api_key.rb +141 -0
  279. data/gems/server/test/middleware/auth_basic.rb +91 -0
  280. data/gems/server/test/middleware/auth_jwt.rb +214 -0
  281. data/gems/server/test/middleware/cache_control.rb +82 -0
  282. data/gems/server/test/middleware/cidr_to_regex.rb +46 -0
  283. data/gems/server/test/middleware/compression.rb +89 -0
  284. data/gems/server/test/middleware/cors.rb +113 -0
  285. data/gems/server/test/middleware/csp.rb +62 -0
  286. data/gems/server/test/middleware/deny_list.rb +131 -0
  287. data/gems/server/test/middleware/endpoint.rb +300 -0
  288. data/gems/server/test/middleware/etag.rb +75 -0
  289. data/gems/server/test/middleware/grpc/grpc.rb +158 -0
  290. data/gems/server/test/middleware/grpc/test_service.proto +32 -0
  291. data/gems/server/test/middleware/grpc/test_service_impl.rb +28 -0
  292. data/gems/server/test/middleware/grpc/test_service_pb.rb +18 -0
  293. data/gems/server/test/middleware/grpc/test_service_services_pb.rb +30 -0
  294. data/gems/server/test/middleware/header_interpolation.rb +35 -0
  295. data/gems/server/test/middleware/intrusion_protection.rb +259 -0
  296. data/gems/server/test/middleware/location.rb +220 -0
  297. data/gems/server/test/middleware/max_body.rb +20 -0
  298. data/gems/server/test/middleware/proxy.rb +415 -0
  299. data/gems/server/test/middleware/rate_limit.rb +211 -0
  300. data/gems/server/test/middleware/redirect.rb +85 -0
  301. data/gems/server/test/middleware/request_headers.rb +50 -0
  302. data/gems/server/test/middleware/response_headers.rb +50 -0
  303. data/gems/server/test/middleware/static_assets.rb +374 -0
  304. data/gems/server/test/middleware/static_response.rb +56 -0
  305. data/gems/server/test/middleware/string_rewrite.rb +112 -0
  306. data/gems/server/test/options/bind.rb +47 -0
  307. data/gems/server/test/options/header_read_timeout.rb +23 -0
  308. data/gems/server/test/options/test_request_timeout.rb +16 -0
  309. data/gems/server/test/options/test_workers.rb +2 -4
  310. data/gems/server/test/{test_itsi_server.rb → rack/test_rack_server.rb} +2 -2
  311. data/grpc_test/Itsi.rb +11 -0
  312. data/grpc_test/echo.proto +14 -0
  313. data/grpc_test/echo_pb.rb +16 -0
  314. data/grpc_test/echo_service_impl.rb +8 -0
  315. data/grpc_test/echo_services_pb.rb +22 -0
  316. data/lib/itsi/version.rb +1 -1
  317. data/tasks.txt +15 -72
  318. metadata +210 -7
  319. data/gems/server/lib/itsi/server/default_config/Itsi-rackup.rb +0 -119
@@ -0,0 +1,50 @@
1
+ require_relative "../helpers/test_helper"
2
+
3
+ class TestRequestHeaders < Minitest::Test
4
+ def test_add_and_remove_static
5
+ server(
6
+ itsi_rb: lambda do
7
+ request_headers \
8
+ additions: { "X-Test" => ["hello"] },
9
+ removals: ["X-Remove-Me"]
10
+ get("/foo") do |r|
11
+ r.ok r.header("X-Test").first + "|" + (r.header("X-Remove-Me").empty? ? "gone" : "here")
12
+ end
13
+ end
14
+ ) do
15
+ res = get_resp("/foo", { "X-Remove-Me" => "bye" })
16
+ assert_equal "200", res.code
17
+ assert_equal "hello|gone", res.body
18
+ end
19
+ end
20
+
21
+ def test_dynamic_string_rewrite
22
+ server(
23
+ itsi_rb: lambda do
24
+ request_headers \
25
+ additions: { "X-Path" => ["{path_and_query}"] },
26
+ removals: []
27
+ get("/bar") { |r| r.ok r.header("X-Path").first }
28
+ end
29
+ ) do
30
+ res = get_resp("/bar?x=1")
31
+ assert_equal "200", res.code
32
+ assert_equal "/bar?x=1", res.body
33
+ end
34
+ end
35
+
36
+ def test_override_existing_header
37
+ server(
38
+ itsi_rb: lambda do
39
+ request_headers \
40
+ additions: { "User-Agent" => ["ItsiTester"] },
41
+ removals: ["User-Agent"]
42
+ get("/u") { |r| r.ok r.header("User-Agent").first }
43
+ end
44
+ ) do
45
+ res = get_resp("/u", { "User-Agent" => "orig" })
46
+ assert_equal "200", res.code
47
+ assert_equal "ItsiTester", res.body
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,50 @@
1
+ require_relative "../helpers/test_helper"
2
+
3
+ class TestResponseHeaders < Minitest::Test
4
+ def test_add_and_remove_static
5
+ server(
6
+ itsi_rb: lambda do
7
+ response_headers \
8
+ additions: { "X-Test" => ["world"] }
9
+ # removals: ["X-Custom"]
10
+ get("/foo") { |r| r.ok "hi", {"X-Custom" => "value"} }
11
+ end
12
+ ) do
13
+ res = get_resp("/foo")
14
+ assert_equal "200", res.code
15
+ assert_equal "world", res["X-Test"]
16
+ refute res.key?("X-Custom")
17
+ end
18
+ end
19
+
20
+ def test_dynamic_response_rewrite
21
+ server(
22
+ itsi_rb: lambda do
23
+ response_headers \
24
+ additions: { "X-Addr" => ["{addr}"] },
25
+ removals: []
26
+ get("/addr") { |r| r.ok "12345" }
27
+ end
28
+ ) do
29
+ res = get_resp("/addr")
30
+ assert_equal "200", res.code
31
+ assert_equal "127.0.0.1", res["X-Addr"]
32
+ end
33
+ end
34
+
35
+ def test_override_multiple_values
36
+ server(
37
+ itsi_rb: lambda do
38
+ response_headers \
39
+ additions: { "Set-Cookie" => ["a=1", "b=2"] },
40
+ removals: ["Set-Cookie"]
41
+ get("/cookie") { |r| r.ok "ok" }
42
+ end
43
+ ) do
44
+ res = get_resp("/cookie")
45
+ cookies = res.get_fields("Set-Cookie")
46
+ assert_includes cookies, "a=1"
47
+ assert_includes cookies, "b=2"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,374 @@
1
+ require_relative "../helpers/test_helper"
2
+ require "tmpdir"
3
+
4
+ class TestStaticAssets < Minitest::Test
5
+ # Test serving an existing file via GET.
6
+ def test_serving_existing_file
7
+ Dir.mktmpdir do |dir|
8
+ file_path = File.join(dir, "test.txt")
9
+ File.write(file_path, "Hello world")
10
+ server(
11
+ itsi_rb: lambda do
12
+ static_assets \
13
+ root_dir: "#{dir}",
14
+ not_found_behavior: "fallthrough",
15
+ headers: { "Cache-Control" => "max-age=3600" },
16
+ allowed_extensions: ["txt"]
17
+
18
+ get("/test.txt") { |r| r.ok "fallback" }
19
+ end
20
+ ) do
21
+ res = get_resp("/test.txt")
22
+ assert_equal "200", res.code, "Expected 200 OK for an existing file"
23
+ assert_equal "Hello world", res.body
24
+ assert_match /max-age=3600/, res["Cache-Control"]
25
+ end
26
+ end
27
+ end
28
+
29
+ # Test HEAD request returns headers with no body.
30
+ def test_head_request
31
+ Dir.mktmpdir do |dir|
32
+ file_path = File.join(dir, "test.txt")
33
+ File.write(file_path, "Hello world")
34
+ server(
35
+ itsi_rb: lambda do
36
+ static_assets \
37
+ root_dir: "#{dir}",
38
+ not_found_behavior: "fallthrough",
39
+ allowed_extensions: ["txt"]
40
+
41
+ get("/test.txt") { |r| r.ok "fallback" }
42
+ end
43
+ ) do
44
+ res = head("/test.txt")
45
+ assert_equal "200", res.code, "Expected 200 OK for HEAD request"
46
+ assert(res.body.nil? || res.body.empty?, "Expected empty body for HEAD request")
47
+ end
48
+ end
49
+ end
50
+
51
+ # Test that non-GET/HEAD requests are not handled by static asset middleware.
52
+ def test_non_get_head_request_passes_through
53
+ Dir.mktmpdir do |dir|
54
+ File.write(File.join(dir, "test.txt"), "Hello world")
55
+ server(
56
+ itsi_rb: lambda do
57
+ static_assets \
58
+ root_dir: "#{dir}",
59
+ not_found_behavior: "fallthrough",
60
+ allowed_extensions: ["txt"]
61
+
62
+ endpoint { |r| r.ok "fallback" }
63
+ end
64
+ ) do
65
+ res = post("/test.txt","")
66
+ # Since POST is not handled by static assets, expect the fallback.
67
+ assert_equal "200", res.code, "Expected fallback response for non-GET/HEAD request"
68
+ assert_equal "fallback", res.body
69
+ end
70
+ end
71
+ end
72
+
73
+ # Test try_html_extension: If "test.html" exists, a request to "/test" returns its content.
74
+ def test_try_html_extension
75
+ Dir.mktmpdir do |dir|
76
+ File.write(File.join(dir, "test.html"), "HTML content")
77
+ server(
78
+ itsi_rb: lambda do
79
+ static_assets \
80
+ root_dir: "#{dir}",
81
+ not_found_behavior: "fallthrough",
82
+ try_html_extension: true,
83
+ allowed_extensions: ["html"]
84
+
85
+ get("/test") { |r| r.ok "fallback" }
86
+ end
87
+ ) do
88
+ res = get_resp("/test")
89
+ assert_equal "200", res.code, "Expected 200 OK with try_html_extension"
90
+ assert_equal "HTML content", res.body
91
+ end
92
+ end
93
+ end
94
+
95
+ # Test a Range request for partial content.
96
+ def test_range_request
97
+ Dir.mktmpdir do |dir|
98
+ File.write(File.join(dir, "test.txt"), "Hello world")
99
+ server(
100
+ itsi_rb: lambda do
101
+ static_assets \
102
+ root_dir: "#{dir}",
103
+ not_found_behavior: "fallthrough",
104
+ allowed_extensions: ["txt"]
105
+
106
+ get("/test.txt") { |r| r.ok "fallback" }
107
+ end
108
+ ) do
109
+ res = get_resp("/test.txt", { "Range" => "bytes=0-4" })
110
+ # Expecting 206 Partial Content with "Hello" as the body.
111
+ assert_equal "206", res.code, "Expected 206 Partial Content for valid Range request"
112
+ assert_equal "Hello", res.body
113
+ end
114
+ end
115
+ end
116
+
117
+ # Test that a non-existent file passes through to the fallback handler.
118
+ def test_file_not_found_passes_through
119
+ Dir.mktmpdir do |dir|
120
+ server(
121
+ itsi_rb: lambda do
122
+ static_assets \
123
+ root_dir: "#{dir}",
124
+ not_found_behavior: "fallthrough",
125
+ allowed_extensions: ["txt"]
126
+
127
+ get("/nonexistent") { |r| r.ok "fallback" }
128
+ end
129
+ ) do
130
+ res = get_resp("/nonexistent")
131
+ assert_equal "200", res.code, "Expected fallback response for non-existent file"
132
+ assert_equal "fallback", res.body
133
+ end
134
+ end
135
+ end
136
+ def test_relative_path_true
137
+ Dir.mktmpdir do |dir|
138
+ # Create file "test.txt" in root_dir.
139
+ dir = FileUtils.mkdir_p(File.join(dir, "foo", "bar", "baz")).first
140
+ file_path = File.join(dir, "test.txt")
141
+ File.write(file_path, "relative true")
142
+ server(
143
+ itsi_rb: lambda do
144
+ location "foo/bar/baz" do
145
+ static_assets \
146
+ root_dir: "#{dir}",
147
+ not_found_behavior: "fallthrough",
148
+ allowed_extensions: ["txt"],
149
+ relative_path: true
150
+
151
+ get("/foo/bar/baz/test.txt") { |r| r.ok "fallback" }
152
+ end
153
+ end
154
+ ) do
155
+ res = get_resp("/foo/bar/baz/test.txt")
156
+ assert_equal "200", res.code, "Expected 200 OK with relative_path: true"
157
+ assert_equal "relative true", res.body
158
+ end
159
+ end
160
+ end
161
+
162
+ def test_relative_path_false
163
+ Dir.mktmpdir do |dir|
164
+ # Create file "test.txt" in root_dir.
165
+ child_dir = FileUtils.mkdir_p(File.join(dir, "foo", "bar", "baz")).first
166
+ file_path = File.join(child_dir, "test.txt")
167
+
168
+ File.write(file_path, "relative false")
169
+ server(
170
+ itsi_rb: lambda do
171
+
172
+ location "/foo/bar/baz/" do
173
+ static_assets \
174
+ root_dir: "#{dir}",
175
+ not_found_behavior: "fallthrough",
176
+ allowed_extensions: ["txt"],
177
+ relative_path: false
178
+
179
+ get("/foo/bar/baz/test.txt") { |r| r.ok "fallback" }
180
+ end
181
+ end
182
+ ) do
183
+ res = get_resp("/foo/bar/baz/test.txt")
184
+
185
+ assert_equal "200", res.code, "Expected 200 OK with relative_path: false"
186
+ assert_equal "relative false", res.body
187
+ end
188
+ end
189
+ end
190
+
191
+ # 2. Allowed Extensions Test
192
+ def test_file_not_in_allowed_extensions
193
+ Dir.mktmpdir do |dir|
194
+ file_path = File.join(dir, "file.xyz")
195
+ File.write(file_path, "not allowed")
196
+ server(
197
+ itsi_rb: lambda do
198
+ static_assets \
199
+ root_dir: "#{dir}",
200
+ not_found_behavior: "fallthrough",
201
+ allowed_extensions: ["txt"]
202
+ get("/file.xyz") { |r| r.ok "fallback" }
203
+ end
204
+ ) do
205
+ res = get_resp("/file.xyz")
206
+ assert_equal "200", res.code, "Expected fallback when file extension is not allowed"
207
+ assert_equal "fallback", res.body
208
+ end
209
+ end
210
+ end
211
+
212
+ def test_hidden_files_not_served_when_disabled
213
+ Dir.mktmpdir do |dir|
214
+ file_path = File.join(dir, ".secret.txt")
215
+ File.write(file_path, "hidden content")
216
+ server(
217
+ itsi_rb: lambda do
218
+ static_assets \
219
+ root_dir: "#{dir}",
220
+ not_found_behavior: "fallthrough"
221
+ endpoint { |r| r.ok "fallback" }
222
+ end
223
+ ) do
224
+ res = get_resp("/.secret.txt")
225
+
226
+ assert_equal "200", res.code, "Expected fallback when hidden files are disabled"
227
+ assert_equal "fallback", res.body
228
+ end
229
+ end
230
+ end
231
+
232
+ def test_hidden_files_served_when_enabled
233
+ Dir.mktmpdir do |dir|
234
+ file_path = File.join(dir, ".secret.txt")
235
+ File.write(file_path, "hidden content")
236
+ server(
237
+ itsi_rb: lambda do
238
+ static_assets \
239
+ root_dir: "#{dir}",
240
+ not_found_behavior: "fallthrough",
241
+ allowed_extensions: ["txt"],
242
+ serve_hidden_files: true
243
+ get("/.secret.txt") { |r| r.ok "fallback" }
244
+ end
245
+ ) do
246
+ res = get_resp("/.secret.txt")
247
+ assert_equal "200", res.code, "Expected 200 OK when hidden files are enabled"
248
+ assert_equal "hidden content", res.body
249
+ end
250
+ end
251
+ end
252
+
253
+ # 4. Auto Index Tests
254
+ def test_auto_index_json
255
+ Dir.mktmpdir do |dir|
256
+ File.write(File.join(dir, "file1.txt"), "content1")
257
+ File.write(File.join(dir, "file2.txt"), "content2")
258
+ server(
259
+ itsi_rb: lambda do
260
+ static_assets \
261
+ root_dir: "#{dir}",
262
+ not_found_behavior: "fallthrough",
263
+ auto_index: true,
264
+ allowed_extensions: []
265
+ get("/") { |r| r.ok "fallback" }
266
+ end
267
+ ) do
268
+ res = get_resp("/", { "Accept" => "application/json" })
269
+
270
+ assert_equal "200", res.code, "Expected 200 OK for JSON auto index"
271
+ assert_includes res.body, "[", "Expected JSON listing in auto index"
272
+ end
273
+ end
274
+ end
275
+
276
+ def test_auto_index_html
277
+ Dir.mktmpdir do |dir|
278
+ File.write(File.join(dir, "file1.txt"), "content1")
279
+ File.write(File.join(dir, "file2.txt"), "content2")
280
+ server(
281
+ itsi_rb: lambda do
282
+ static_assets \
283
+ root_dir: "#{dir}",
284
+ not_found_behavior: "fallthrough",
285
+ auto_index: true,
286
+ allowed_extensions: []
287
+ get("/") { |r| r.ok "fallback" }
288
+ end
289
+ ) do
290
+ res = get_resp("/", { "Accept" => "text/html" })
291
+ assert_equal "200", res.code, "Expected 200 OK for HTML auto index"
292
+ assert_includes res.body.downcase, "<html", "Expected HTML structure in auto index"
293
+ end
294
+ end
295
+ end
296
+
297
+ # 5. Not Found Behavior Tests
298
+ def test_not_found_behavior_index
299
+ Dir.mktmpdir do |dir|
300
+ # Create an index file to be used as fallback.
301
+ File.write(File.join(dir, "index.html"), "SPA Index")
302
+ server(
303
+ itsi_rb: lambda do
304
+ static_assets \
305
+ root_dir: "#{dir}",
306
+ not_found_behavior: { index: "index.html" },
307
+ allowed_extensions: ["html"]
308
+ get("/nonexistent") { |r| r.ok "fallback" }
309
+ end
310
+ ) do
311
+ res = get_resp("/nonexistent")
312
+ assert_equal "200", res.code, "Expected index to be served for index not found behaviour"
313
+ assert_equal "SPA Index", res.body
314
+ end
315
+ end
316
+ end
317
+
318
+ def test_not_found_behavior_redirect
319
+ Dir.mktmpdir do |dir|
320
+ server(
321
+ itsi_rb: lambda do
322
+ static_assets \
323
+ root_dir: "#{dir}",
324
+ not_found_behavior: { redirect: { to: "https://example.com", type: "permanent" } }
325
+ get("/nonexistent") { |r| r.ok "fallback" }
326
+ end
327
+ ) do
328
+ res = get_resp("/nonexistent")
329
+ assert_equal "308", res.code, "Expected 308 for redirect not found behaviour"
330
+ assert_equal "https://example.com", res["Location"]
331
+ end
332
+ end
333
+ end
334
+
335
+ def test_not_found_behavior_error_builtin
336
+ Dir.mktmpdir do |dir|
337
+ server(
338
+ itsi_rb: lambda do
339
+ static_assets \
340
+ root_dir: "#{dir}",
341
+ not_found_behavior: {error: "not_found"}
342
+ get("/nonexistent") { |r| r.ok "fallback" }
343
+ end
344
+ ) do
345
+ res = get_resp("/nonexistent")
346
+ assert_equal "404", res.code, "Expected 404 for built-in not_found behaviour"
347
+ end
348
+ end
349
+ end
350
+
351
+ def test_not_found_behavior_custom_error
352
+ Dir.mktmpdir do |dir|
353
+ server(
354
+ itsi_rb: lambda do
355
+ static_assets \
356
+ root_dir: "#{dir}",
357
+ not_found_behavior: {
358
+ error: {
359
+ code: 404,
360
+ plaintext: { inline: "Not Found" },
361
+ html: { inline: "<h1>Not Found</h1>" },
362
+ default: "plaintext"
363
+ },
364
+ }
365
+ get("/nonexistent") { |r| r.ok "fallback" }
366
+ end
367
+ ) do
368
+ res = get_resp("/nonexistent")
369
+ assert_equal "404", res.code, "Expected 404 for custom not_found behaviour"
370
+ assert_equal "Not Found", res.body
371
+ end
372
+ end
373
+ end
374
+ end
@@ -0,0 +1,56 @@
1
+ # test_static_response.rb
2
+ require_relative "../helpers/test_helper"
3
+
4
+ class TestStaticResponse < Minitest::Test
5
+ def test_basic_response_code_and_body
6
+ server(
7
+ itsi_rb: lambda do
8
+ static_response \
9
+ code: 204,
10
+ headers: [],
11
+ body: ""
12
+ end
13
+ ) do
14
+ res = get_resp("/any")
15
+ assert_equal "204", res.code
16
+ assert_nil res.body # 204 should have no body
17
+ end
18
+ end
19
+
20
+ def test_custom_headers_and_body
21
+ server(
22
+ itsi_rb: lambda do
23
+ static_response \
24
+ code: 418,
25
+ headers: [
26
+ ["Content-Type","text/teapot"],
27
+ ["X-Test","value"]
28
+ ],
29
+ body: "I'm a teapot"
30
+ end
31
+ ) do
32
+ res = get_resp("/brew")
33
+ assert_equal "418", res.code
34
+ assert_equal "text/teapot", res["Content-Type"]
35
+ assert_equal "value", res["X-Test"]
36
+ assert_equal "I'm a teapot", res.body
37
+ end
38
+ end
39
+
40
+ def test_binary_body
41
+ data = [0x00,0xFF,0x7F]
42
+ server(
43
+ itsi_rb: lambda do
44
+ static_response \
45
+ code: 200,
46
+ headers: [["Content-Type","application/octet-stream"]],
47
+ body: data.pack("C*")
48
+ end
49
+ ) do
50
+ res = get_resp("/bin")
51
+ # Ensure raw bytes round‑trip
52
+ assert_equal data.pack("C*"), res.body
53
+ assert_equal "application/octet-stream", res["Content-Type"]
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,112 @@
1
+ require_relative "../helpers/test_helper"
2
+
3
+ class TestStringRewrite < Minitest::Test
4
+
5
+ def test_interpolates_method
6
+ server(
7
+ itsi_rb: lambda do
8
+ redirect to: "https://example.com/{method}/redirect", type: "temporary"
9
+ get("/foo") { |r| r.ok "should not get here" }
10
+ end
11
+ ) do
12
+ res = get_resp("/foo")
13
+ assert_equal "307", res.code, "Expected status 307 for temporary redirect"
14
+ assert_equal "https://example.com/GET/redirect", res["Location"]
15
+ end
16
+ end
17
+
18
+ def test_interpolates_path
19
+ server(
20
+ itsi_rb: lambda do
21
+ redirect to: "https://example.com{path}", type: "found"
22
+ get("/bar") { |r| r.ok "should not get here" }
23
+ end
24
+ ) do
25
+ res = get_resp("/bar")
26
+ assert_equal "302", res.code, "Expected status 302 for found redirect"
27
+ assert_equal "https://example.com/bar", res["Location"]
28
+ end
29
+ end
30
+
31
+ def test_interpolates_addr
32
+ server(
33
+ itsi_rb: lambda do
34
+ redirect to: "https://{addr}/redirect", type: "moved_permanently"
35
+ get("/foo") { |r| r.ok "should not get here" }
36
+ end
37
+ ) do
38
+ res = get_resp("/foo")
39
+ assert_equal "301", res.code, "Expected status 301 for moved permanently redirect"
40
+ # In tests, the context's address is typically set to "127.0.0.1"
41
+ assert_equal "https://127.0.0.1/redirect", res["Location"]
42
+ end
43
+ end
44
+
45
+ def test_interpolates_host
46
+ server(
47
+ itsi_rb: lambda do
48
+ redirect to: "https://{host}/", type: "permanent"
49
+ get("/foo") { |r| r.ok "should not get here" }
50
+ end
51
+ ) do
52
+ res = get_resp("/foo")
53
+ assert_equal "308", res.code, "Expected status 308 for permanent redirect"
54
+ # When no host is explicitly specified in the URL, it defaults to "localhost"
55
+ assert_equal "https://localhost/", res["Location"]
56
+ end
57
+ end
58
+
59
+ def test_interpolates_path_and_query
60
+ server(
61
+ itsi_rb: lambda do
62
+ redirect to: "https://example.com{path_and_query}", type: "temporary"
63
+ get("/foo") { |r| r.ok "should not get here" }
64
+ end
65
+ ) do
66
+ res = get_resp("/foo?bar=baz")
67
+ assert_equal "307", res.code, "Expected status 307 for temporary redirect"
68
+ assert_equal "https://example.com/foo?bar=baz", res["Location"]
69
+ end
70
+ end
71
+
72
+ def test_interpolates_query
73
+ server(
74
+ itsi_rb: lambda do
75
+ redirect to: "https://example.com{query}", type: "found"
76
+ get("/foo?x=y") { |r| r.ok "should not get here" }
77
+ end
78
+ ) do
79
+ res = get_resp("/foo?x=y")
80
+ assert_equal "302", res.code, "Expected status 302 for found redirect"
81
+ assert_equal "https://example.com?x=y", res["Location"]
82
+ end
83
+ end
84
+
85
+ def test_interpolates_port
86
+ server(
87
+ itsi_rb: lambda do
88
+ redirect to: "https://example.com:{port}/", type: "moved_permanently"
89
+ # We assume that if no port is provided in the request URI, it defaults to "80"
90
+ get("/foo") { |r| r.ok "should not get here" }
91
+ end
92
+ ) do
93
+ res = get_resp("/foo")
94
+ assert_equal "301", res.code, "Expected status 301 for moved permanently redirect"
95
+ assert_equal "https://example.com:80/", res["Location"]
96
+ end
97
+ end
98
+
99
+ def test_unknown_placeholder_remains_unmodified
100
+ server(
101
+ itsi_rb: lambda do
102
+ redirect to: "https://example.com/{unknown}", type: "permanent"
103
+ get("/foo") { |r| r.ok "should not get here" }
104
+ end
105
+ ) do
106
+ res = get_resp("/foo")
107
+ # The unknown placeholder is not substituted and remains as '{unknown}'.
108
+ assert_equal "308", res.code, "Expected status 308 for permanent redirect"
109
+ assert_equal "https://example.com/{unknown}", res["Location"]
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,47 @@
1
+ require_relative "../helpers/test_helper"
2
+
3
+ class TestBind < Minitest::Test
4
+ def test_http
5
+ server(
6
+ app: lambda do |env|
7
+ [200, { "Content-Type" => "text/plain" }, ["Hello, World!"]]
8
+ end) do
9
+ assert_equal "Hello, World!", get("/")
10
+ end
11
+ end
12
+
13
+
14
+ def test_https
15
+ server(app: lambda do |env|
16
+ [200, { "Content-Type" => "text/plain" }, ["Hello, HTTPS!"]]
17
+ end, protocol: "https") do |uri|
18
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true,
19
+ verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
20
+ http.request(Net::HTTP::Get.new("/"))
21
+ end
22
+ assert_equal "200", response.code
23
+ assert_equal "Hello, HTTPS!", response.body
24
+ end
25
+ end
26
+
27
+ def test_unix_socket_http
28
+ server(
29
+ bind: free_bind("http", unix_socket: true),
30
+ app: lambda do |env|
31
+ [200, { "Content-Type" => "text/plain" }, ["Hello, World!"]]
32
+ end) do
33
+ assert_equal "Hello, World!", get("/")
34
+ end
35
+ end
36
+
37
+ def test_unix_socket_https
38
+ server(
39
+ bind: free_bind("http", unix_socket: true),
40
+ app: lambda do |env|
41
+ [200, { "Content-Type" => "text/plain" }, ["Hello, World!"]]
42
+ end) do
43
+ assert_equal "Hello, World!", get("/")
44
+ end
45
+ end
46
+
47
+ end