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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -8
- data/Cargo.lock +2 -2
- data/LICENSE.txt +698 -0
- data/README.md +15 -4
- data/Rakefile +9 -5
- data/crates/itsi_acme/.gitignore +4 -0
- data/crates/itsi_acme/Cargo.toml +86 -0
- data/crates/itsi_acme/LICENSE-APACHE +201 -0
- data/crates/itsi_acme/LICENSE-MIT +23 -0
- data/crates/itsi_acme/README.md +9 -0
- data/crates/itsi_acme/examples/high_level.rs +63 -0
- data/crates/itsi_acme/examples/high_level_warp.rs +52 -0
- data/crates/itsi_acme/examples/low_level.rs +87 -0
- data/crates/itsi_acme/examples/low_level_axum.rs +66 -0
- data/crates/itsi_acme/src/acceptor.rs +81 -0
- data/crates/itsi_acme/src/acme.rs +354 -0
- data/crates/itsi_acme/src/axum.rs +86 -0
- data/crates/itsi_acme/src/cache.rs +39 -0
- data/crates/itsi_acme/src/caches/boxed.rs +80 -0
- data/crates/itsi_acme/src/caches/composite.rs +69 -0
- data/crates/itsi_acme/src/caches/dir.rs +106 -0
- data/crates/itsi_acme/src/caches/mod.rs +11 -0
- data/crates/itsi_acme/src/caches/no.rs +78 -0
- data/crates/itsi_acme/src/caches/test.rs +136 -0
- data/crates/itsi_acme/src/config.rs +172 -0
- data/crates/itsi_acme/src/https_helper.rs +69 -0
- data/crates/itsi_acme/src/incoming.rs +142 -0
- data/crates/itsi_acme/src/jose.rs +161 -0
- data/crates/itsi_acme/src/lib.rs +142 -0
- data/crates/itsi_acme/src/resolver.rs +59 -0
- data/crates/itsi_acme/src/state.rs +424 -0
- data/crates/itsi_rb_helpers/src/lib.rs +4 -3
- data/crates/itsi_scheduler/Cargo.toml +1 -1
- data/crates/itsi_scheduler/src/itsi_scheduler.rs +8 -2
- data/crates/itsi_scheduler/src/lib.rs +1 -0
- data/crates/itsi_server/Cargo.toml +1 -1
- data/crates/itsi_server/src/lib.rs +2 -1
- data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +18 -1
- data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +11 -3
- data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +122 -63
- data/crates/itsi_server/src/ruby_types/itsi_server.rs +2 -0
- data/crates/itsi_server/src/server/binds/bind.rs +3 -0
- data/crates/itsi_server/src/server/binds/listener.rs +12 -5
- data/crates/itsi_server/src/server/binds/tls.rs +13 -5
- data/crates/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +12 -5
- data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +8 -1
- data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +9 -1
- data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +48 -43
- data/crates/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +11 -2
- data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +39 -12
- data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +36 -27
- data/crates/itsi_server/src/server/middleware_stack/middlewares/csp.rs +25 -11
- data/crates/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +12 -3
- data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +74 -72
- data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +15 -1
- data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +11 -8
- data/crates/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +19 -11
- data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +5 -5
- data/crates/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +2 -2
- data/crates/itsi_server/src/server/middleware_stack/middlewares/mod.rs +11 -5
- data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +17 -20
- data/crates/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +19 -8
- data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +16 -37
- data/crates/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +22 -12
- data/crates/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +26 -11
- data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +7 -1
- data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +14 -4
- data/crates/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +19 -0
- data/crates/itsi_server/src/server/middleware_stack/mod.rs +49 -13
- data/crates/itsi_server/src/server/mod.rs +1 -0
- data/crates/itsi_server/src/server/redirect_type.rs +26 -0
- data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +22 -16
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +49 -12
- data/crates/itsi_server/src/server/signal.rs +1 -0
- data/crates/itsi_server/src/server/size_limited_incoming.rs +6 -0
- data/crates/itsi_server/src/server/thread_worker.rs +5 -1
- data/crates/itsi_server/src/services/itsi_http_service.rs +20 -2
- data/crates/itsi_server/src/services/rate_limiter.rs +15 -4
- data/crates/itsi_server/src/services/static_file_server.rs +33 -19
- data/crates/itsi_tracing/src/lib.rs +42 -22
- data/docs/content/_index.md +1 -2
- data/docs/content/acknowledgements/_index.md +5 -2
- data/docs/content/configuration/_index.md +8 -5
- data/docs/content/contact/_index.md +8 -1
- data/docs/content/faqs/_index.md +5 -3
- data/docs/content/features/_index.md +56 -50
- data/docs/content/getting_started/_index.md +8 -5
- data/docs/content/getting_started/local_development.md +68 -8
- data/docs/content/getting_started/logging.md +16 -9
- data/docs/content/getting_started/running_itsi_in_production.md +5 -3
- data/docs/content/getting_started/signals.md +38 -0
- data/docs/content/itsi_scheduler/_index.md +8 -7
- data/docs/content/utilities/_index.md +13 -0
- data/docs/content/utilities/config_file_testing.md +17 -0
- data/docs/content/utilities/passfile_generator.md +41 -0
- data/docs/content/utilities/route_testing.md +27 -0
- data/docs/content/utilities/secrets_management.md +30 -0
- data/docs/hugo.yaml +1 -1
- data/fairytale.txt +3 -4
- data/gems/scheduler/Cargo.lock +1 -1
- data/gems/scheduler/README.md +4 -5
- data/gems/scheduler/Rakefile +0 -4
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/lib/itsi/scheduler.rb +9 -4
- data/gems/scheduler/test/test_active_record.rb +12 -7
- data/gems/server/Cargo.lock +1 -1
- data/gems/server/Rakefile +0 -4
- data/gems/server/exe/itsi +13 -2
- data/gems/server/lib/itsi/http_request/response_status_shortcodes.rb +2 -0
- data/gems/server/lib/itsi/http_request.rb +40 -9
- data/gems/server/lib/itsi/http_response.rb +2 -1
- data/gems/server/lib/itsi/passfile.rb +0 -1
- data/gems/server/lib/itsi/server/config/config_helpers.rb +20 -8
- data/gems/server/lib/itsi/server/config/dsl.rb +20 -435
- data/gems/server/lib/itsi/server/config/known_paths.rb +4 -1
- data/gems/server/lib/itsi/server/config/middleware/_index.md +6 -4
- data/gems/server/lib/itsi/server/config/middleware/allow_list.md +46 -0
- data/gems/server/lib/itsi/server/config/middleware/allow_list.rb +42 -0
- data/gems/server/lib/itsi/server/config/middleware/auth_api_key.md +90 -0
- data/gems/server/lib/itsi/server/config/middleware/auth_api_key.rb +51 -0
- data/gems/server/lib/itsi/server/config/middleware/auth_basic.md +45 -0
- data/gems/server/lib/itsi/server/config/middleware/auth_basic.rb +44 -0
- data/gems/server/lib/itsi/server/config/middleware/auth_jwt.md +82 -0
- data/gems/server/lib/itsi/server/config/middleware/auth_jwt.rb +38 -0
- data/gems/server/lib/itsi/server/config/middleware/cache_control.md +78 -0
- data/gems/server/lib/itsi/server/config/middleware/cache_control.rb +45 -0
- data/gems/server/lib/itsi/server/config/middleware/cidr_to_regex.rb +50 -0
- data/gems/server/lib/itsi/server/config/middleware/compression.md +50 -0
- data/gems/server/lib/itsi/server/config/middleware/compression.rb +37 -0
- data/gems/server/lib/itsi/server/config/middleware/cors.md +93 -0
- data/gems/server/lib/itsi/server/config/middleware/cors.rb +32 -0
- data/gems/server/lib/itsi/server/config/middleware/csp.md +37 -0
- data/gems/server/lib/itsi/server/config/middleware/csp.rb +44 -0
- data/gems/server/lib/itsi/server/config/middleware/deny_list.md +45 -0
- data/gems/server/lib/itsi/server/config/middleware/deny_list.rb +42 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/_index.md +159 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.md +186 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.rb +33 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.md +12 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.rb +42 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/endpoint.rb +99 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/get.md +12 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/get.rb +42 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/http_request.md +44 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/http_response.md +39 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.md +12 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.rb +42 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/post.md +12 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/post.rb +42 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/put.md +12 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/put.rb +42 -0
- data/gems/server/lib/itsi/server/config/middleware/endpoint/schemas.md +122 -0
- data/gems/server/lib/itsi/server/config/middleware/error_response.md +61 -0
- data/gems/server/lib/itsi/server/config/middleware/error_response.rb +36 -0
- data/gems/server/lib/itsi/server/config/middleware/etag.md +59 -0
- data/gems/server/lib/itsi/server/config/middleware/etag.rb +27 -0
- data/gems/server/lib/itsi/server/config/middleware/grpc.md +172 -0
- data/gems/server/lib/itsi/server/config/middleware/grpc.rb +54 -0
- data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.md +124 -0
- data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.rb +61 -0
- data/gems/server/lib/itsi/server/config/middleware/location.md +107 -0
- data/gems/server/lib/itsi/server/config/middleware/location.rb +99 -0
- data/gems/server/lib/itsi/server/config/middleware/log_requests.md +13 -11
- data/gems/server/lib/itsi/server/config/middleware/log_requests.rb +1 -3
- data/gems/server/lib/itsi/server/config/middleware/max_body.md +18 -0
- data/gems/server/lib/itsi/server/config/middleware/max_body.rb +21 -0
- data/gems/server/lib/itsi/server/config/middleware/proxy.md +62 -0
- data/gems/server/lib/itsi/server/config/middleware/proxy.rb +41 -0
- data/gems/server/lib/itsi/server/config/middleware/rackup_file.md +54 -0
- data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +44 -0
- data/gems/server/lib/itsi/server/config/middleware/rate_limit.md +126 -0
- data/gems/server/lib/itsi/server/config/middleware/rate_limit.rb +34 -0
- data/gems/server/lib/itsi/server/config/middleware/rate_limit_store.rb +25 -0
- data/gems/server/lib/itsi/server/config/middleware/redirect.md +55 -0
- data/gems/server/lib/itsi/server/config/middleware/redirect.rb +25 -0
- data/gems/server/lib/itsi/server/config/middleware/request_headers.md +34 -0
- data/gems/server/lib/itsi/server/config/middleware/request_headers.rb +24 -0
- data/gems/server/lib/itsi/server/config/middleware/response_headers.md +33 -0
- data/gems/server/lib/itsi/server/config/middleware/response_headers.rb +25 -0
- data/gems/server/lib/itsi/server/config/middleware/run.md +60 -0
- data/gems/server/lib/itsi/server/config/middleware/run.rb +43 -0
- data/gems/server/lib/itsi/server/config/middleware/static_assets.md +73 -0
- data/gems/server/lib/itsi/server/config/middleware/static_assets.rb +87 -0
- data/gems/server/lib/itsi/server/config/middleware/static_response.md +44 -0
- data/gems/server/lib/itsi/server/config/middleware/static_response.rb +29 -0
- data/gems/server/lib/itsi/server/config/middleware/string_rewrite.md +67 -0
- data/gems/server/lib/itsi/server/config/middleware/token_source.rb +32 -0
- data/gems/server/lib/itsi/server/config/middleware.rb +4 -0
- data/gems/server/lib/itsi/server/config/option.rb +5 -0
- data/gems/server/lib/itsi/server/config/options/_index.md +3 -2
- data/gems/server/lib/itsi/server/config/options/auto_reload_config.md +13 -0
- data/gems/server/lib/itsi/server/config/options/auto_reload_config.rb +41 -0
- data/gems/server/lib/itsi/server/config/options/bind.md +71 -0
- data/gems/server/lib/itsi/server/config/options/bind.rb +26 -0
- data/gems/server/lib/itsi/server/config/options/certificates.md +65 -0
- data/gems/server/lib/itsi/server/config/options/daemonize.md +14 -0
- data/gems/server/lib/itsi/server/config/options/daemonize.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/fiber_scheduler.md +1 -2
- data/gems/server/lib/itsi/server/config/options/fiber_scheduler.rb +6 -3
- data/gems/server/lib/itsi/server/config/options/header_read_timeout.md +17 -0
- data/gems/server/lib/itsi/server/config/options/header_read_timeout.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/hooks/_index.md +11 -0
- data/gems/server/lib/itsi/server/config/options/hooks/after_fork.md +13 -0
- data/gems/server/lib/itsi/server/config/options/hooks/after_fork.rb +28 -0
- data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.md +14 -0
- data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.rb +28 -0
- data/gems/server/lib/itsi/server/config/options/hooks/after_start.md +12 -0
- data/gems/server/lib/itsi/server/config/options/hooks/after_start.rb +28 -0
- data/gems/server/lib/itsi/server/config/options/hooks/before_fork.md +13 -0
- data/gems/server/lib/itsi/server/config/options/hooks/before_fork.rb +28 -0
- data/gems/server/lib/itsi/server/config/options/hooks/before_restart.md +12 -0
- data/gems/server/lib/itsi/server/config/options/hooks/before_restart.rb +28 -0
- data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.md +12 -0
- data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.rb +28 -0
- data/gems/server/lib/itsi/server/config/options/include.md +20 -0
- data/gems/server/lib/itsi/server/config/options/include.rb +36 -0
- data/gems/server/lib/itsi/server/config/options/listen_backlog.md +11 -0
- data/gems/server/lib/itsi/server/config/options/listen_backlog.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/log_format.md +18 -0
- data/gems/server/lib/itsi/server/config/options/log_format.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/log_level.md +34 -0
- data/gems/server/lib/itsi/server/config/options/log_level.rb +20 -0
- data/gems/server/lib/itsi/server/config/options/log_target.md +38 -0
- data/gems/server/lib/itsi/server/config/options/log_target.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/log_target_filters.md +17 -0
- data/gems/server/lib/itsi/server/config/options/log_target_filters.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.md +27 -0
- data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.rb +24 -0
- data/gems/server/lib/itsi/server/config/options/nodelay.md +16 -0
- data/gems/server/lib/itsi/server/config/options/nodelay.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.md +19 -0
- data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.rb +18 -0
- data/gems/server/lib/itsi/server/config/options/pin_worker_cores.md +17 -0
- data/gems/server/lib/itsi/server/config/options/pin_worker_cores.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/preload.md +21 -0
- data/gems/server/lib/itsi/server/config/options/preload.rb +18 -0
- data/gems/server/lib/itsi/server/config/options/recv_buffer_size.md +15 -0
- data/gems/server/lib/itsi/server/config/options/recv_buffer_size.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.md +21 -0
- data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.rb +30 -0
- data/gems/server/lib/itsi/server/config/options/request_timeout.md +23 -0
- data/gems/server/lib/itsi/server/config/options/request_timeout.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/reuse_address.md +16 -0
- data/gems/server/lib/itsi/server/config/options/reuse_address.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/reuse_port.md +16 -0
- data/gems/server/lib/itsi/server/config/options/reuse_port.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/scheduler_threads.md +34 -0
- data/gems/server/lib/itsi/server/config/options/scheduler_threads.rb +17 -0
- data/gems/server/lib/itsi/server/config/options/shutdown_timeout.md +17 -0
- data/gems/server/lib/itsi/server/config/options/shutdown_timeout.rb +19 -0
- data/gems/server/lib/itsi/server/config/options/stream_body.md +32 -0
- data/gems/server/lib/itsi/server/config/options/stream_body.rb +18 -0
- data/gems/server/lib/itsi/server/config/options/threads.md +7 -2
- data/gems/server/lib/itsi/server/config/options/threads.rb +1 -1
- data/gems/server/lib/itsi/server/config/options/watch.md +16 -0
- data/gems/server/lib/itsi/server/config/options/watch.rb +28 -0
- data/gems/server/lib/itsi/server/config/options/worker_memory_limit.md +22 -0
- data/gems/server/lib/itsi/server/config/options/worker_memory_limit.rb +18 -0
- data/gems/server/lib/itsi/server/config/options/workers.md +1 -2
- data/gems/server/lib/itsi/server/config/options/workers.rb +1 -1
- data/gems/server/lib/itsi/server/config/typed_struct.rb +59 -20
- data/gems/server/lib/itsi/server/config.rb +77 -48
- data/gems/server/lib/itsi/server/default_config/Itsi.rb +3 -3
- data/gems/server/lib/itsi/server/grpc/grpc_call.rb +1 -1
- data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +11 -4
- data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -3
- data/gems/server/lib/itsi/server/route_tester.rb +58 -8
- data/gems/server/lib/itsi/server/signal_trap.rb +1 -1
- data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +14 -18
- data/gems/server/lib/itsi/server/typed_handlers/source_parser.rb +5 -4
- data/gems/server/lib/itsi/server/typed_handlers.rb +12 -4
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +98 -14
- data/gems/server/lib/ruby_lsp/itsi/addon.rb +20 -18
- data/gems/server/test/helpers/test_helper.rb +89 -29
- data/gems/server/test/middleware/allow_list.rb +128 -0
- data/gems/server/test/middleware/auth_api_key.rb +141 -0
- data/gems/server/test/middleware/auth_basic.rb +91 -0
- data/gems/server/test/middleware/auth_jwt.rb +214 -0
- data/gems/server/test/middleware/cache_control.rb +82 -0
- data/gems/server/test/middleware/cidr_to_regex.rb +46 -0
- data/gems/server/test/middleware/compression.rb +89 -0
- data/gems/server/test/middleware/cors.rb +113 -0
- data/gems/server/test/middleware/csp.rb +62 -0
- data/gems/server/test/middleware/deny_list.rb +131 -0
- data/gems/server/test/middleware/endpoint.rb +300 -0
- data/gems/server/test/middleware/etag.rb +75 -0
- data/gems/server/test/middleware/grpc/grpc.rb +158 -0
- data/gems/server/test/middleware/grpc/test_service.proto +32 -0
- data/gems/server/test/middleware/grpc/test_service_impl.rb +28 -0
- data/gems/server/test/middleware/grpc/test_service_pb.rb +18 -0
- data/gems/server/test/middleware/grpc/test_service_services_pb.rb +30 -0
- data/gems/server/test/middleware/header_interpolation.rb +35 -0
- data/gems/server/test/middleware/intrusion_protection.rb +259 -0
- data/gems/server/test/middleware/location.rb +220 -0
- data/gems/server/test/middleware/max_body.rb +20 -0
- data/gems/server/test/middleware/proxy.rb +415 -0
- data/gems/server/test/middleware/rate_limit.rb +211 -0
- data/gems/server/test/middleware/redirect.rb +85 -0
- data/gems/server/test/middleware/request_headers.rb +50 -0
- data/gems/server/test/middleware/response_headers.rb +50 -0
- data/gems/server/test/middleware/static_assets.rb +374 -0
- data/gems/server/test/middleware/static_response.rb +56 -0
- data/gems/server/test/middleware/string_rewrite.rb +112 -0
- data/gems/server/test/options/bind.rb +47 -0
- data/gems/server/test/options/header_read_timeout.rb +23 -0
- data/gems/server/test/options/test_request_timeout.rb +16 -0
- data/gems/server/test/options/test_workers.rb +2 -4
- data/gems/server/test/{test_itsi_server.rb → rack/test_rack_server.rb} +2 -2
- data/grpc_test/Itsi.rb +11 -0
- data/grpc_test/echo.proto +14 -0
- data/grpc_test/echo_pb.rb +16 -0
- data/grpc_test/echo_service_impl.rb +8 -0
- data/grpc_test/echo_services_pb.rb +22 -0
- data/lib/itsi/version.rb +1 -1
- data/tasks.txt +15 -72
- metadata +210 -7
- data/gems/server/lib/itsi/server/default_config/Itsi-rackup.rb +0 -119
@@ -2,9 +2,7 @@ use crate::ruby_types::itsi_server::itsi_server_config::ItsiServerConfig;
|
|
2
2
|
use crate::server::signal::SIGNAL_HANDLER_CHANNEL;
|
3
3
|
use crate::server::{lifecycle_event::LifecycleEvent, process_worker::ProcessWorker};
|
4
4
|
use itsi_error::{ItsiError, Result};
|
5
|
-
use itsi_rb_helpers::{
|
6
|
-
call_proc_and_log_errors, call_with_gvl, call_without_gvl, create_ruby_thread,
|
7
|
-
};
|
5
|
+
use itsi_rb_helpers::{call_with_gvl, call_without_gvl, create_ruby_thread};
|
8
6
|
use itsi_tracing::{error, info, warn};
|
9
7
|
use magnus::Value;
|
10
8
|
use nix::{libc::exit, unistd::Pid};
|
@@ -56,6 +54,12 @@ impl ClusterMode {
|
|
56
54
|
.expect("Failed to build Tokio runtime")
|
57
55
|
}
|
58
56
|
|
57
|
+
pub fn invoke_hook(&self, hook_name: &str) {
|
58
|
+
if let Some(hook) = self.server_config.server_params.read().hooks.get(hook_name) {
|
59
|
+
call_with_gvl(|_| hook.call::<_, Value>(()).ok());
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
59
63
|
#[allow(clippy::await_holding_lock)]
|
60
64
|
pub async fn handle_lifecycle_event(
|
61
65
|
self: Arc<Self>,
|
@@ -70,10 +74,12 @@ impl ClusterMode {
|
|
70
74
|
LifecycleEvent::Shutdown => {
|
71
75
|
self.server_config.stop_watcher()?;
|
72
76
|
self.shutdown().await?;
|
77
|
+
self.invoke_hook("before_shutdown");
|
73
78
|
Ok(())
|
74
79
|
}
|
75
80
|
LifecycleEvent::Restart => {
|
76
|
-
if self.server_config.check_config() {
|
81
|
+
if self.server_config.check_config().await {
|
82
|
+
self.invoke_hook("before_restart");
|
77
83
|
self.server_config.dup_fds()?;
|
78
84
|
self.shutdown().await.ok();
|
79
85
|
info!("Shutdown complete. Calling reload exec");
|
@@ -82,7 +88,7 @@ impl ClusterMode {
|
|
82
88
|
Ok(())
|
83
89
|
}
|
84
90
|
LifecycleEvent::Reload => {
|
85
|
-
if !self.server_config.check_config() {
|
91
|
+
if !self.server_config.check_config().await {
|
86
92
|
return Ok(());
|
87
93
|
}
|
88
94
|
let should_reexec = self.server_config.clone().reload(true)?;
|
@@ -268,15 +274,7 @@ impl ClusterMode {
|
|
268
274
|
#[instrument(skip(self), fields(mode = "cluster", pid=format!("{:?}", Pid::this())))]
|
269
275
|
pub fn run(self: Arc<Self>) -> Result<()> {
|
270
276
|
info!("Starting in Cluster mode");
|
271
|
-
|
272
|
-
.server_config
|
273
|
-
.server_params
|
274
|
-
.read()
|
275
|
-
.hooks
|
276
|
-
.get("before_fork")
|
277
|
-
{
|
278
|
-
call_with_gvl(|_| call_proc_and_log_errors(proc.clone()))
|
279
|
-
}
|
277
|
+
self.invoke_hook("before_fork");
|
280
278
|
self.process_workers
|
281
279
|
.lock()
|
282
280
|
.iter()
|
@@ -290,7 +288,15 @@ impl ClusterMode {
|
|
290
288
|
|
291
289
|
self.build_runtime().block_on(async {
|
292
290
|
let self_ref = self_ref.clone();
|
293
|
-
let
|
291
|
+
let memory_check_duration = if self_ref.server_config.server_params.read().worker_memory_limit.is_some(){
|
292
|
+
time::Duration::from_secs(15)
|
293
|
+
} else {
|
294
|
+
time::Duration::from_secs(60 * 60 * 24 * 365 * 100)
|
295
|
+
};
|
296
|
+
|
297
|
+
let mut memory_check_interval = time::interval(memory_check_duration);
|
298
|
+
|
299
|
+
self.invoke_hook("after_start");
|
294
300
|
|
295
301
|
loop {
|
296
302
|
tokio::select! {
|
@@ -314,7 +320,7 @@ impl ClusterMode {
|
|
314
320
|
if let Some(current_mem_usage) = largest_worker.memory_usage(){
|
315
321
|
if current_mem_usage > memory_limit {
|
316
322
|
largest_worker.reboot(self_ref.clone()).await.ok();
|
317
|
-
if let Some(hook) = self_ref.server_config.server_params.read().hooks.get("
|
323
|
+
if let Some(hook) = self_ref.server_config.server_params.read().hooks.get("after_memory_limit_reached") {
|
318
324
|
call_with_gvl(|_| hook.call::<_, Value>((largest_worker.pid(),)).ok() );
|
319
325
|
}
|
320
326
|
}
|
@@ -19,7 +19,7 @@ use itsi_rb_helpers::{
|
|
19
19
|
call_with_gvl, call_without_gvl, create_ruby_thread, funcall_no_ret, print_rb_backtrace,
|
20
20
|
};
|
21
21
|
use itsi_tracing::{debug, error, info};
|
22
|
-
use magnus::value::ReprValue;
|
22
|
+
use magnus::{value::ReprValue, Value};
|
23
23
|
use nix::unistd::Pid;
|
24
24
|
use parking_lot::RwLock;
|
25
25
|
use std::{
|
@@ -83,8 +83,7 @@ impl SingleMode {
|
|
83
83
|
builder
|
84
84
|
.thread_name("itsi-server-accept-loop")
|
85
85
|
.thread_stack_size(3 * 1024 * 1024)
|
86
|
-
.
|
87
|
-
.enable_time()
|
86
|
+
.enable_all()
|
88
87
|
.build()
|
89
88
|
.expect("Failed to build Tokio runtime")
|
90
89
|
}
|
@@ -160,12 +159,12 @@ impl SingleMode {
|
|
160
159
|
pub fn start_monitors(
|
161
160
|
self: Arc<Self>,
|
162
161
|
thread_workers: Arc<Vec<Arc<ThreadWorker>>>,
|
163
|
-
) -> magnus::Thread {
|
162
|
+
) -> Option<magnus::Thread> {
|
164
163
|
call_with_gvl(move |_| {
|
165
164
|
create_ruby_thread(move || {
|
166
165
|
call_without_gvl(move || {
|
167
166
|
let monitor_runtime = RuntimeBuilder::new_current_thread()
|
168
|
-
.
|
167
|
+
.enable_all()
|
169
168
|
.build()
|
170
169
|
.unwrap();
|
171
170
|
let receiver = self.clone();
|
@@ -190,10 +189,10 @@ impl SingleMode {
|
|
190
189
|
lifecycle_event = lifecycle_rx.recv() => {
|
191
190
|
match lifecycle_event {
|
192
191
|
Ok(LifecycleEvent::Restart) => {
|
193
|
-
receiver.restart().ok();
|
192
|
+
receiver.restart().await.ok();
|
194
193
|
}
|
195
194
|
Ok(LifecycleEvent::Reload) => {
|
196
|
-
receiver.reload().ok();
|
195
|
+
receiver.reload().await.ok();
|
197
196
|
}
|
198
197
|
Ok(LifecycleEvent::Shutdown) => {
|
199
198
|
break;
|
@@ -233,6 +232,11 @@ impl SingleMode {
|
|
233
232
|
|
234
233
|
let (shutdown_sender, _) = watch::channel(RunningPhase::Running);
|
235
234
|
let monitor_thread = self.clone().start_monitors(thread_workers.clone());
|
235
|
+
if monitor_thread.is_none() {
|
236
|
+
error!("Failed to start monitor thread");
|
237
|
+
return Err(ItsiError::new("Failed to start monitor thread"));
|
238
|
+
}
|
239
|
+
let monitor_thread = monitor_thread.unwrap();
|
236
240
|
if SHUTDOWN_REQUESTED.load(Ordering::SeqCst) {
|
237
241
|
return Ok(());
|
238
242
|
}
|
@@ -290,6 +294,7 @@ impl SingleMode {
|
|
290
294
|
}
|
291
295
|
lifecycle_event = lifecycle_rx.recv() => match lifecycle_event{
|
292
296
|
Ok(LifecycleEvent::Shutdown) => {
|
297
|
+
debug!("Received lifecycle event: {:?}", lifecycle_event);
|
293
298
|
shutdown_sender.send(RunningPhase::ShutdownPending).unwrap();
|
294
299
|
tokio::time::sleep(Duration::from_millis(25)).await;
|
295
300
|
for _i in 0..workers_clone.len() {
|
@@ -303,11 +308,23 @@ impl SingleMode {
|
|
303
308
|
}
|
304
309
|
}
|
305
310
|
}
|
306
|
-
|
311
|
+
|
312
|
+
let deadline = Instant::now()
|
313
|
+
+ Duration::from_secs_f64(self_ref.server_config.server_params.read().shutdown_timeout);
|
314
|
+
tokio::select! {
|
315
|
+
_ = async {
|
316
|
+
while let Some(_res) = acceptor_task_set.join_next().await {}
|
317
|
+
} => {},
|
318
|
+
_ = tokio::time::sleep_until(tokio::time::Instant::from_std(deadline)) => {},
|
319
|
+
}
|
307
320
|
});
|
308
321
|
|
309
322
|
}
|
310
323
|
|
324
|
+
if self.is_single_mode() {
|
325
|
+
self.invoke_hook("after_start");
|
326
|
+
}
|
327
|
+
|
311
328
|
while let Some(_res) = listener_task_set.join_next().await {}
|
312
329
|
|
313
330
|
// Explicitly drop all listeners to ensure file descriptors are released
|
@@ -346,15 +363,24 @@ impl SingleMode {
|
|
346
363
|
sleep(Duration::from_millis(50));
|
347
364
|
}
|
348
365
|
|
366
|
+
if self.is_single_mode() {
|
367
|
+
self.invoke_hook("before_shutdown");
|
368
|
+
}
|
369
|
+
|
349
370
|
if self.restart_requested.load(Ordering::SeqCst) {
|
350
371
|
self.restart_requested.store(false, Ordering::SeqCst);
|
351
372
|
info!("Worker restarting");
|
352
373
|
self.run()?;
|
353
374
|
}
|
375
|
+
|
354
376
|
debug!("Runtime has shut down");
|
355
377
|
result
|
356
378
|
}
|
357
379
|
|
380
|
+
pub fn is_single_mode(&self) -> bool {
|
381
|
+
self.server_config.server_params.read().workers == 1
|
382
|
+
}
|
383
|
+
|
358
384
|
pub(crate) async fn serve_connection(
|
359
385
|
&self,
|
360
386
|
stream: IoStream,
|
@@ -417,12 +443,15 @@ impl SingleMode {
|
|
417
443
|
/// Attempts to reload the config "live"
|
418
444
|
/// Not that when running in single mode this will not unload
|
419
445
|
/// old code. If you need a clean restart, use the `restart` (SIGHUP) method instead
|
420
|
-
pub fn reload(&self) -> Result<()> {
|
421
|
-
if !self.server_config.check_config() {
|
446
|
+
pub async fn reload(&self) -> Result<()> {
|
447
|
+
if !self.server_config.check_config().await {
|
422
448
|
return Ok(());
|
423
449
|
}
|
424
450
|
let should_reexec = self.server_config.clone().reload(false)?;
|
425
451
|
if should_reexec {
|
452
|
+
if self.is_single_mode() {
|
453
|
+
self.invoke_hook("before_restart");
|
454
|
+
}
|
426
455
|
self.server_config.dup_fds()?;
|
427
456
|
self.server_config.reload_exec()?;
|
428
457
|
}
|
@@ -432,11 +461,19 @@ impl SingleMode {
|
|
432
461
|
Ok(())
|
433
462
|
}
|
434
463
|
|
464
|
+
pub fn invoke_hook(&self, hook_name: &str) {
|
465
|
+
if let Some(hook) = self.server_config.server_params.read().hooks.get(hook_name) {
|
466
|
+
call_with_gvl(|_| hook.call::<_, Value>(()).ok());
|
467
|
+
}
|
468
|
+
}
|
435
469
|
/// Restart the server while keeping connections open.
|
436
|
-
pub fn restart(&self) -> Result<()> {
|
437
|
-
if !self.server_config.check_config() {
|
470
|
+
pub async fn restart(&self) -> Result<()> {
|
471
|
+
if !self.server_config.check_config().await {
|
438
472
|
return Ok(());
|
439
473
|
}
|
474
|
+
if self.is_single_mode() {
|
475
|
+
self.invoke_hook("before_restart");
|
476
|
+
}
|
440
477
|
self.server_config.dup_fds()?;
|
441
478
|
self.server_config.reload_exec()?;
|
442
479
|
Ok(())
|
@@ -49,6 +49,7 @@ fn receive_signal(signum: i32, _: sighandler_t) {
|
|
49
49
|
pub fn reset_signal_handlers() -> bool {
|
50
50
|
SIGINT_COUNT.store(0, std::sync::atomic::Ordering::SeqCst);
|
51
51
|
SHUTDOWN_REQUESTED.store(false, std::sync::atomic::Ordering::SeqCst);
|
52
|
+
|
52
53
|
unsafe {
|
53
54
|
libc::signal(libc::SIGTERM, receive_signal as usize);
|
54
55
|
libc::signal(libc::SIGINT, receive_signal as usize);
|
@@ -10,6 +10,7 @@ use std::sync::atomic::AtomicUsize;
|
|
10
10
|
use std::sync::atomic::Ordering;
|
11
11
|
use std::task::Context;
|
12
12
|
use std::task::Poll;
|
13
|
+
use tracing::debug;
|
13
14
|
|
14
15
|
/// Custom error to indicate that the maximum body size was exceeded.
|
15
16
|
#[derive(Debug)]
|
@@ -71,6 +72,11 @@ where
|
|
71
72
|
Ok(data) => {
|
72
73
|
let len = data.remaining();
|
73
74
|
self.current += len;
|
75
|
+
debug!(
|
76
|
+
target: "option::max_body",
|
77
|
+
"current: {}, limit: {}",
|
78
|
+
self.current, self.limit.load(Ordering::Relaxed)
|
79
|
+
);
|
74
80
|
if self.current > self.limit.load(Ordering::Relaxed) {
|
75
81
|
Poll::Ready(Some(Err(Box::new(MaxBodySizeReached))))
|
76
82
|
} else {
|
@@ -1,4 +1,5 @@
|
|
1
1
|
use async_channel::Sender;
|
2
|
+
use itsi_error::ItsiError;
|
2
3
|
use itsi_rb_helpers::{
|
3
4
|
call_with_gvl, call_without_gvl, create_ruby_thread, kill_threads, HeapValue,
|
4
5
|
};
|
@@ -159,7 +160,7 @@ impl ThreadWorker {
|
|
159
160
|
pub fn poll_shutdown(&self, deadline: Instant) -> bool {
|
160
161
|
if let Some(thread) = self.thread.read().deref() {
|
161
162
|
if Instant::now() > deadline {
|
162
|
-
|
163
|
+
debug!("Worker shutdown timed out. Killing thread {:?}", thread);
|
163
164
|
self.terminated.store(true, Ordering::SeqCst);
|
164
165
|
kill_threads(vec![thread.as_value()]);
|
165
166
|
}
|
@@ -198,6 +199,9 @@ impl ThreadWorker {
|
|
198
199
|
self_ref.accept_loop(params, name, receiver, terminated);
|
199
200
|
}
|
200
201
|
})
|
202
|
+
.ok_or_else(|| {
|
203
|
+
ItsiError::InternalServerError("Failed to create Ruby thread".to_owned())
|
204
|
+
})?
|
201
205
|
.into(),
|
202
206
|
);
|
203
207
|
Ok::<(), magnus::Error>(())
|
@@ -1,5 +1,5 @@
|
|
1
1
|
use crate::default_responses::{NOT_FOUND_RESPONSE, TIMEOUT_RESPONSE};
|
2
|
-
use crate::ruby_types::itsi_server::itsi_server_config::ServerParams;
|
2
|
+
use crate::ruby_types::itsi_server::itsi_server_config::{ItsiServerTokenPreference, ServerParams};
|
3
3
|
use crate::server::binds::listener::ListenerInfo;
|
4
4
|
use crate::server::http_message_types::{ConversionExt, HttpResponse, RequestExt, ResponseFormat};
|
5
5
|
use crate::server::lifecycle_event::LifecycleEvent;
|
@@ -18,6 +18,7 @@ use itsi_error::ItsiError;
|
|
18
18
|
use regex::Regex;
|
19
19
|
use std::sync::atomic::{AtomicBool, Ordering};
|
20
20
|
use std::sync::OnceLock;
|
21
|
+
use tracing::error;
|
21
22
|
|
22
23
|
use std::{future::Future, ops::Deref, pin::Pin, sync::Arc};
|
23
24
|
use tokio::sync::watch::{self};
|
@@ -153,6 +154,10 @@ impl HttpRequestContext {
|
|
153
154
|
}
|
154
155
|
}
|
155
156
|
|
157
|
+
const SERVER_TOKEN_VERSION: HeaderValue =
|
158
|
+
HeaderValue::from_static(concat!("Itsi/", env!("CARGO_PKG_VERSION")));
|
159
|
+
const SERVER_TOKEN_NAME: HeaderValue = HeaderValue::from_static("Itsi");
|
160
|
+
|
156
161
|
impl Service<Request<Incoming>> for ItsiHttpService {
|
157
162
|
type Response = HttpResponse;
|
158
163
|
type Error = ItsiError;
|
@@ -195,7 +200,10 @@ impl Service<Request<Incoming>> for ItsiHttpService {
|
|
195
200
|
depth = index;
|
196
201
|
break;
|
197
202
|
}
|
198
|
-
Err(e) =>
|
203
|
+
Err(e) => {
|
204
|
+
error!("Middleware error: {}", e);
|
205
|
+
break;
|
206
|
+
}
|
199
207
|
}
|
200
208
|
}
|
201
209
|
|
@@ -208,6 +216,16 @@ impl Service<Request<Incoming>> for ItsiHttpService {
|
|
208
216
|
resp = elm.after(resp, &mut context).await;
|
209
217
|
}
|
210
218
|
|
219
|
+
match params.itsi_server_token_preference {
|
220
|
+
ItsiServerTokenPreference::Version => {
|
221
|
+
resp.headers_mut().insert("Server", SERVER_TOKEN_VERSION);
|
222
|
+
}
|
223
|
+
ItsiServerTokenPreference::Name => {
|
224
|
+
resp.headers_mut().insert("Server", SERVER_TOKEN_NAME);
|
225
|
+
}
|
226
|
+
ItsiServerTokenPreference::None => {}
|
227
|
+
}
|
228
|
+
|
211
229
|
Ok(resp)
|
212
230
|
};
|
213
231
|
|
@@ -5,10 +5,12 @@ use redis::{Client, RedisError, Script};
|
|
5
5
|
use serde::Deserialize;
|
6
6
|
use std::any::Any;
|
7
7
|
use std::collections::{HashMap, HashSet};
|
8
|
+
use std::result::Result;
|
8
9
|
use std::sync::{Arc, LazyLock, Mutex};
|
9
10
|
use std::time::{Duration, Instant};
|
10
11
|
use tokio::sync::{Mutex as AsyncMutex, RwLock};
|
11
12
|
use tokio::time::timeout;
|
13
|
+
use tracing::warn;
|
12
14
|
use url::Url;
|
13
15
|
|
14
16
|
#[derive(Debug)]
|
@@ -109,10 +111,19 @@ impl RedisRateLimiter {
|
|
109
111
|
// Create the Lua script once when initializing the rate limiter
|
110
112
|
let increment_script = Script::new(
|
111
113
|
r#"
|
114
|
+
-- Increment the counter
|
112
115
|
local current = redis.call('INCR', KEYS[1])
|
113
|
-
|
114
|
-
|
116
|
+
|
117
|
+
-- Fetch existing TTL
|
118
|
+
local ttl = redis.call('TTL', KEYS[1])
|
119
|
+
if ttl < 0 then
|
120
|
+
-- If no TTL is set, apply the window interval
|
121
|
+
local window = tonumber(ARGV[1])
|
122
|
+
redis.call('EXPIRE', KEYS[1], window)
|
123
|
+
ttl = window
|
115
124
|
end
|
125
|
+
|
126
|
+
-- Return both the current count and remaining TTL
|
116
127
|
return { current, ttl }
|
117
128
|
"#,
|
118
129
|
);
|
@@ -135,14 +146,14 @@ impl RedisRateLimiter {
|
|
135
146
|
let mut connection = (*self.connection).clone();
|
136
147
|
|
137
148
|
// Set the ban with the reason as the value
|
138
|
-
let _: () = redis::cmd("SET")
|
149
|
+
let _: Result<(), RedisError> = redis::cmd("SET")
|
139
150
|
.arg(&ban_key)
|
140
151
|
.arg(reason)
|
141
152
|
.arg("EX")
|
142
153
|
.arg(timeout_secs)
|
143
154
|
.query_async(&mut connection)
|
144
155
|
.await
|
145
|
-
.
|
156
|
+
.inspect_err(|e| warn!("Exception banning IP {:?}", e));
|
146
157
|
|
147
158
|
Ok(())
|
148
159
|
}
|
@@ -1,9 +1,10 @@
|
|
1
1
|
use crate::{
|
2
|
-
default_responses::
|
2
|
+
default_responses::NOT_FOUND_RESPONSE,
|
3
3
|
prelude::*,
|
4
4
|
server::{
|
5
5
|
http_message_types::{HttpRequest, HttpResponse, RequestExt, ResponseFormat},
|
6
6
|
middleware_stack::ErrorResponse,
|
7
|
+
redirect_type::RedirectType,
|
7
8
|
},
|
8
9
|
};
|
9
10
|
use base64::{engine::general_purpose, Engine};
|
@@ -46,6 +47,7 @@ pub static ROOT_STATIC_FILE_SERVER: LazyLock<StaticFileServer> = LazyLock::new(|
|
|
46
47
|
recheck_interval: Duration::from_secs(1),
|
47
48
|
try_html_extension: true,
|
48
49
|
auto_index: true,
|
50
|
+
headers: None,
|
49
51
|
not_found_behavior: NotFoundBehavior::Error(ErrorResponse::not_found()),
|
50
52
|
serve_hidden_files: false,
|
51
53
|
allowed_extensions: vec!["html".to_string(), "css".to_string(), "js".to_string()],
|
@@ -56,6 +58,7 @@ pub static ROOT_STATIC_FILE_SERVER: LazyLock<StaticFileServer> = LazyLock::new(|
|
|
56
58
|
#[derive(Debug, Clone, Deserialize)]
|
57
59
|
pub struct Redirect {
|
58
60
|
pub to: String,
|
61
|
+
pub r#type: RedirectType,
|
59
62
|
}
|
60
63
|
|
61
64
|
#[derive(Debug, Clone, Deserialize)]
|
@@ -68,8 +71,6 @@ pub enum NotFoundBehavior {
|
|
68
71
|
IndexFile(PathBuf),
|
69
72
|
#[serde(rename = "redirect")]
|
70
73
|
Redirect(Redirect),
|
71
|
-
#[serde(rename = "internal_server_error")]
|
72
|
-
InternalServerError,
|
73
74
|
}
|
74
75
|
|
75
76
|
#[derive(Debug, Clone)]
|
@@ -81,6 +82,7 @@ pub struct StaticFileServerConfig {
|
|
81
82
|
pub try_html_extension: bool,
|
82
83
|
pub auto_index: bool,
|
83
84
|
pub not_found_behavior: NotFoundBehavior,
|
85
|
+
pub headers: Option<HashMap<String, String>>,
|
84
86
|
pub serve_hidden_files: bool,
|
85
87
|
pub allowed_extensions: Vec<String>,
|
86
88
|
}
|
@@ -113,23 +115,26 @@ struct CacheEntry {
|
|
113
115
|
}
|
114
116
|
|
115
117
|
impl CacheEntry {
|
116
|
-
pub fn suggest_content_for(
|
118
|
+
pub fn suggest_content_for(
|
119
|
+
&self,
|
120
|
+
supported_encodings: &[HeaderValue],
|
121
|
+
) -> (Arc<Bytes>, Option<&str>) {
|
117
122
|
for encoding_header in supported_encodings {
|
118
123
|
if let Ok(header_value) = encoding_header.to_str() {
|
119
124
|
for header_value in header_value.split(",").map(|hv| hv.trim()) {
|
120
125
|
for algo in header_value.split(";").map(|hv| hv.trim()) {
|
121
126
|
match algo {
|
122
127
|
"zstd" if self.zstd_encoded.is_some() => {
|
123
|
-
return (self.zstd_encoded.clone().unwrap(), "zstd")
|
128
|
+
return (self.zstd_encoded.clone().unwrap(), Some("zstd"))
|
124
129
|
}
|
125
130
|
"gzip" if self.gzip_encoded.is_some() => {
|
126
|
-
return (self.gzip_encoded.clone().unwrap(), "gzip")
|
131
|
+
return (self.gzip_encoded.clone().unwrap(), Some("gzip"))
|
127
132
|
}
|
128
133
|
"br" if self.br_encoded.is_some() => {
|
129
|
-
return (self.br_encoded.clone().unwrap(), "br")
|
134
|
+
return (self.br_encoded.clone().unwrap(), Some("br"))
|
130
135
|
}
|
131
136
|
"deflate" if self.deflate_encoded.is_some() => {
|
132
|
-
return (self.deflate_encoded.clone().unwrap(), "deflate")
|
137
|
+
return (self.deflate_encoded.clone().unwrap(), Some("deflate"))
|
133
138
|
}
|
134
139
|
_ => {}
|
135
140
|
}
|
@@ -137,7 +142,7 @@ impl CacheEntry {
|
|
137
142
|
}
|
138
143
|
}
|
139
144
|
}
|
140
|
-
(self.content.clone(),
|
145
|
+
(self.content.clone(), None)
|
141
146
|
}
|
142
147
|
}
|
143
148
|
|
@@ -304,15 +309,10 @@ impl StaticFileServer {
|
|
304
309
|
.await
|
305
310
|
}
|
306
311
|
NotFoundBehavior::Redirect(redirect) => Response::builder()
|
307
|
-
.status(
|
312
|
+
.status(redirect.r#type.status_code())
|
308
313
|
.header(header::LOCATION, redirect.to)
|
309
314
|
.body(BoxBody::new(Full::new(Bytes::new())))
|
310
315
|
.unwrap(),
|
311
|
-
NotFoundBehavior::InternalServerError => {
|
312
|
-
INTERNAL_SERVER_ERROR_RESPONSE
|
313
|
-
.to_http_response(request.accept().into())
|
314
|
-
.await
|
315
|
-
}
|
316
316
|
},
|
317
317
|
})
|
318
318
|
}
|
@@ -434,8 +434,8 @@ impl StaticFileServer {
|
|
434
434
|
|
435
435
|
// No valid cached entry, resolve the key to a file path
|
436
436
|
let decoded_key = percent_decode_str(key).decode_utf8_lossy();
|
437
|
-
let normalized_path =
|
438
|
-
|
437
|
+
let normalized_path = normalize_path(decoded_key)
|
438
|
+
.ok_or(NotFoundBehavior::Error(NOT_FOUND_RESPONSE.clone()))?;
|
439
439
|
|
440
440
|
if !self.config.serve_hidden_files
|
441
441
|
&& normalized_path
|
@@ -449,6 +449,7 @@ impl StaticFileServer {
|
|
449
449
|
|
450
450
|
let mut full_path = self.config.root_dir.clone();
|
451
451
|
full_path.push(normalized_path);
|
452
|
+
debug!("Resolving path {:?}", full_path);
|
452
453
|
// Check if path exists and is a file
|
453
454
|
match tokio::fs::metadata(&full_path).await {
|
454
455
|
Ok(metadata) => {
|
@@ -492,7 +493,9 @@ impl StaticFileServer {
|
|
492
493
|
// Check for case insensitive index.html
|
493
494
|
let entries = match tokio::fs::read_dir(&full_path).await {
|
494
495
|
Ok(entries) => entries,
|
495
|
-
Err(_) =>
|
496
|
+
Err(_) => {
|
497
|
+
return Err(NotFoundBehavior::Error(NOT_FOUND_RESPONSE.clone()))
|
498
|
+
}
|
496
499
|
};
|
497
500
|
|
498
501
|
tokio::pin!(entries);
|
@@ -558,6 +561,7 @@ impl StaticFileServer {
|
|
558
561
|
}
|
559
562
|
Err(_) => {
|
560
563
|
// Path doesn't exist, try with .html extension if configured
|
564
|
+
debug!("Path doesn't exist");
|
561
565
|
if self.config.try_html_extension {
|
562
566
|
let mut html_path = full_path.clone();
|
563
567
|
html_path.set_extension("html");
|
@@ -754,6 +758,7 @@ impl StaticFileServer {
|
|
754
758
|
(end_idx - start) as usize,
|
755
759
|
last_modified,
|
756
760
|
content_range,
|
761
|
+
&self.headers,
|
757
762
|
self.stream_file_range(file, start, end_idx).await.unwrap(),
|
758
763
|
)
|
759
764
|
} else {
|
@@ -765,6 +770,7 @@ impl StaticFileServer {
|
|
765
770
|
content_length as usize,
|
766
771
|
last_modified,
|
767
772
|
content_range,
|
773
|
+
&self.headers,
|
768
774
|
self.stream_file(file).await.unwrap(),
|
769
775
|
)
|
770
776
|
}
|
@@ -863,6 +869,7 @@ impl StaticFileServer {
|
|
863
869
|
range_bytes.len(),
|
864
870
|
cache_entry.last_modified,
|
865
871
|
content_range,
|
872
|
+
&self.headers,
|
866
873
|
BoxBody::new(Full::new(range_bytes)),
|
867
874
|
)
|
868
875
|
} else {
|
@@ -871,12 +878,13 @@ impl StaticFileServer {
|
|
871
878
|
let body = build_ok_body(content);
|
872
879
|
build_file_response(
|
873
880
|
status,
|
874
|
-
|
881
|
+
encoding,
|
875
882
|
Some(&cache_entry.etag),
|
876
883
|
get_mime_type(path),
|
877
884
|
content_length as usize,
|
878
885
|
cache_entry.last_modified,
|
879
886
|
content_range,
|
887
|
+
&self.headers,
|
880
888
|
body,
|
881
889
|
)
|
882
890
|
}
|
@@ -947,6 +955,7 @@ fn build_file_response(
|
|
947
955
|
content_length: usize,
|
948
956
|
last_modified: SystemTime,
|
949
957
|
range_header: Option<String>,
|
958
|
+
headers: &Option<HashMap<String, String>>,
|
950
959
|
body: BoxBody<Bytes, Infallible>,
|
951
960
|
) -> http::Response<BoxBody<Bytes, Infallible>> {
|
952
961
|
let mut builder = Response::builder()
|
@@ -966,6 +975,11 @@ fn build_file_response(
|
|
966
975
|
if let Some(range) = range_header {
|
967
976
|
builder = builder.header(CONTENT_RANGE, range);
|
968
977
|
}
|
978
|
+
if let Some(headers) = headers {
|
979
|
+
for (key, value) in headers {
|
980
|
+
builder = builder.header(key, value);
|
981
|
+
}
|
982
|
+
}
|
969
983
|
|
970
984
|
builder.body(body).unwrap()
|
971
985
|
}
|