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
@@ -0,0 +1,131 @@
|
|
1
|
+
# test_deny_list.rb
|
2
|
+
require_relative "../helpers/test_helper"
|
3
|
+
|
4
|
+
class TestDenyList < Minitest::Test
|
5
|
+
# 1. Single‐pattern: localhost is denied
|
6
|
+
def test_single_pattern_denies_and_allows
|
7
|
+
server(
|
8
|
+
itsi_rb: lambda do
|
9
|
+
deny_list denied_patterns: ["^127\\.0\\.0\\.1$"]
|
10
|
+
get("/foo") { |r| r.ok "ok" }
|
11
|
+
end
|
12
|
+
) do
|
13
|
+
# Client from 127.0.0.1 is denied
|
14
|
+
res1 = get_resp("/foo")
|
15
|
+
assert_equal "403", res1.code
|
16
|
+
|
17
|
+
# If we change the pattern to something else, localhost is allowed
|
18
|
+
server(
|
19
|
+
itsi_rb: lambda do
|
20
|
+
deny_list denied_patterns: ["^10\\."]
|
21
|
+
get("/foo") { |r| r.ok "allowed" }
|
22
|
+
end
|
23
|
+
) do
|
24
|
+
res2 = get_resp("/foo")
|
25
|
+
assert_equal "200", res2.code
|
26
|
+
assert_equal "allowed", res2.body
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# 2. Multiple‐pattern: deny localhost or 172.16.x.x
|
32
|
+
def test_multiple_patterns
|
33
|
+
server(
|
34
|
+
itsi_rb: lambda do
|
35
|
+
deny_list denied_patterns: ["^127\\.0\\.0\\.1$", "^172\\.16\\."]
|
36
|
+
get("/h") { |r| r.ok "h" }
|
37
|
+
end
|
38
|
+
) do
|
39
|
+
# localhost matches first pattern → denied
|
40
|
+
res1 = get_resp("/h")
|
41
|
+
assert_equal "403", res1.code
|
42
|
+
|
43
|
+
# If we restrict only to 172.16.*, localhost becomes allowed
|
44
|
+
server(
|
45
|
+
itsi_rb: lambda do
|
46
|
+
deny_list denied_patterns: ["^172\\.16\\."]
|
47
|
+
get("/h") { |r| r.ok "ok" }
|
48
|
+
end
|
49
|
+
) do
|
50
|
+
res2 = get_resp("/h")
|
51
|
+
assert_equal "200", res2.code
|
52
|
+
assert_equal "ok", res2.body
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# 3. Custom error_response
|
58
|
+
def test_custom_error_response
|
59
|
+
server(
|
60
|
+
itsi_rb: lambda do
|
61
|
+
deny_list \
|
62
|
+
denied_patterns: ["^127\\.0\\.0\\.1$"],
|
63
|
+
error_response: {
|
64
|
+
code: 403,
|
65
|
+
plaintext: { inline: "Blocked by IP" },
|
66
|
+
default: "plaintext"
|
67
|
+
}
|
68
|
+
get("/z") { |r| r.ok "never" }
|
69
|
+
end
|
70
|
+
) do
|
71
|
+
res = get_resp("/z")
|
72
|
+
assert_equal "403", res.code
|
73
|
+
assert_equal "Blocked by IP", res.body
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# 4. Trusted proxies: extract client IP from header if proxy is trusted
|
78
|
+
def test_trusted_proxy_denies_based_on_header
|
79
|
+
server(
|
80
|
+
itsi_rb: lambda do
|
81
|
+
deny_list \
|
82
|
+
denied_patterns: ["^198\\.51\\.100\\.9$"],
|
83
|
+
trusted_proxies: {
|
84
|
+
"127.0.0.1" => { header: { name: "X-Forwarded-For" } }
|
85
|
+
}
|
86
|
+
get("/deny") { |r| r.ok "shouldn't see" }
|
87
|
+
end
|
88
|
+
) do
|
89
|
+
# Header says user is 198.51.100.9, which is denied
|
90
|
+
res = get_resp("/deny", { "X-Forwarded-For" => "198.51.100.9" })
|
91
|
+
assert_equal "403", res.code
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_trusted_proxy_allows_if_forwarded_ip_not_denied
|
96
|
+
server(
|
97
|
+
itsi_rb: lambda do
|
98
|
+
deny_list \
|
99
|
+
denied_patterns: ["^198\\.51\\.100\\.9$"], # deny this IP
|
100
|
+
trusted_proxies: {
|
101
|
+
"127.0.0.1" => { header: { name: "X-Forwarded-For" } }
|
102
|
+
}
|
103
|
+
get("/deny") { |r| r.ok "welcome" }
|
104
|
+
end
|
105
|
+
) do
|
106
|
+
# Header says user is 198.51.100.10, which is not denied
|
107
|
+
res = get_resp("/deny", { "X-Forwarded-For" => "198.51.100.10" })
|
108
|
+
assert_equal "200", res.code
|
109
|
+
assert_equal "welcome", res.body
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_untrusted_proxy_ignores_forwarded_ip
|
114
|
+
server(
|
115
|
+
itsi_rb: lambda do
|
116
|
+
deny_list \
|
117
|
+
denied_patterns: ["^198\\.51\\.100\\.9$"],
|
118
|
+
trusted_proxies: {
|
119
|
+
"10.0.0.1" => { header: { name: "X-Forwarded-For" } } # current sender not trusted
|
120
|
+
}
|
121
|
+
get("/deny") { |r| r.ok "should be denied by socket IP" }
|
122
|
+
end
|
123
|
+
) do
|
124
|
+
# Because 127.0.0.1 is not trusted, header is ignored and socket IP (which matches ^127) is not denied
|
125
|
+
# But 127.0.0.1 isn't in the denied_patterns, so it's allowed
|
126
|
+
res = get_resp("/deny", { "X-Forwarded-For" => "198.51.100.9" })
|
127
|
+
assert_equal "200", res.code
|
128
|
+
assert_equal "should be denied by socket IP", res.body
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require_relative "../helpers/test_helper"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
MySchema = { _required: %i[name], name: String }
|
5
|
+
PingSchema = { _required: %i[status], status: String }
|
6
|
+
|
7
|
+
|
8
|
+
class TestEndpoint < Minitest::Test
|
9
|
+
# 1. Basic inline GET endpoint
|
10
|
+
def test_basic_get
|
11
|
+
server(itsi_rb: lambda do
|
12
|
+
endpoint "/hello" do |req|
|
13
|
+
req.ok "world"
|
14
|
+
end
|
15
|
+
end) do
|
16
|
+
res = get_resp("/hello")
|
17
|
+
assert_equal "200", res.code
|
18
|
+
assert_equal "world", res.body
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# 2. GET-only wrapper rejects POST
|
23
|
+
def test_get_wrapper_rejects_post
|
24
|
+
server(itsi_rb: lambda do
|
25
|
+
get "/onlyget" do |req|
|
26
|
+
req.ok "got"
|
27
|
+
end
|
28
|
+
end) do
|
29
|
+
assert_equal "got", get("/onlyget")
|
30
|
+
post_res = post("/onlyget", "")
|
31
|
+
refute_equal post_res.body, "got"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# 3. Trailing slash handling: both /path and /path/ map
|
36
|
+
def test_trailing_slash_equivalence
|
37
|
+
server(itsi_rb: lambda do
|
38
|
+
endpoint "/users" do |req|
|
39
|
+
req.ok req.path
|
40
|
+
end
|
41
|
+
end) do
|
42
|
+
res1 = get_resp("/users")
|
43
|
+
assert_equal "200", res1.code
|
44
|
+
assert_equal "/users", res1.body
|
45
|
+
|
46
|
+
res2 = get_resp("/users/")
|
47
|
+
assert_equal "200", res2.code
|
48
|
+
assert_equal "/users/", res2.body
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# 4. Empty‑path endpoint: wildcard match all paths
|
53
|
+
def test_empty_path_wildcard
|
54
|
+
server(itsi_rb: lambda do
|
55
|
+
endpoint do |req|
|
56
|
+
req.ok req.path
|
57
|
+
end
|
58
|
+
end) do
|
59
|
+
%w[/ /foo /foo/bar].each do |path|
|
60
|
+
res = get_resp(path)
|
61
|
+
assert_equal "200", res.code
|
62
|
+
assert_equal path, res.body
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# 5. JSON body parsing into params
|
68
|
+
def test_json_body_parsing
|
69
|
+
server(itsi_rb: lambda do
|
70
|
+
endpoint "/echo_json" do |req, params|
|
71
|
+
req.ok JSON.generate(params)
|
72
|
+
end
|
73
|
+
end) do
|
74
|
+
payload = { "a" => 1, "b" => 2 }
|
75
|
+
res = post(
|
76
|
+
"/echo_json",
|
77
|
+
JSON.generate(payload),
|
78
|
+
"Content-Type" => "application/json"
|
79
|
+
)
|
80
|
+
assert_equal "200", res.code
|
81
|
+
assert_equal payload, JSON.parse(res.body)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# 6. Form‑encoded body parsing
|
86
|
+
def test_form_urlencoded_parsing
|
87
|
+
server(itsi_rb: lambda do
|
88
|
+
endpoint "/echo_form" do |req, params|
|
89
|
+
req.ok "#{params['x']}-#{params['y']}"
|
90
|
+
end
|
91
|
+
end) do
|
92
|
+
res = post(
|
93
|
+
"/echo_form",
|
94
|
+
"x=foo&y=bar",
|
95
|
+
"Content-Type" => "application/x-www-form-urlencoded"
|
96
|
+
)
|
97
|
+
assert_equal "200", res.code
|
98
|
+
assert_equal "foo-bar", res.body
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# 7. Streaming via low‑level response object
|
103
|
+
def test_streaming_response
|
104
|
+
server(itsi_rb: lambda do
|
105
|
+
endpoint "/stream" do |req|
|
106
|
+
stream = req.response
|
107
|
+
stream << "A"
|
108
|
+
stream << "B"
|
109
|
+
stream << "C"
|
110
|
+
stream.close
|
111
|
+
end
|
112
|
+
end) do
|
113
|
+
res = get_resp("/stream")
|
114
|
+
assert_equal "200", res.code
|
115
|
+
assert_equal "ABC", res.body
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# 8. Exceptions inside handler produce 500
|
120
|
+
def test_internal_error_returns_500
|
121
|
+
server(itsi_rb: lambda do
|
122
|
+
endpoint "/boom" do |_req|
|
123
|
+
raise "test crash"
|
124
|
+
end
|
125
|
+
end) do
|
126
|
+
res = get_resp("/boom")
|
127
|
+
assert_equal "500", res.code
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# 9. Controller method dispatch via symbol
|
132
|
+
def test_controller_symbol_dispatch
|
133
|
+
server(itsi_rb: lambda do
|
134
|
+
# define handler method in top‑level scope
|
135
|
+
def foo_handler(req)
|
136
|
+
req.ok "from_symbol"
|
137
|
+
end
|
138
|
+
endpoint "/sym", :foo_handler
|
139
|
+
end) do
|
140
|
+
res = get_resp("/sym")
|
141
|
+
assert_equal "200", res.code
|
142
|
+
assert_equal "from_symbol", res.body
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# 10. Schema enforcement rejects bad JSON
|
147
|
+
def test_schema_validation_failure
|
148
|
+
server(itsi_rb: lambda do
|
149
|
+
# declare a schema and use typed params
|
150
|
+
endpoint "/typed" do |req, params: MySchema|
|
151
|
+
req.ok params["name"]
|
152
|
+
end
|
153
|
+
end) do
|
154
|
+
# missing "name"
|
155
|
+
res = post(
|
156
|
+
"/typed",
|
157
|
+
"{}",
|
158
|
+
"Content-Type" => "application/json"
|
159
|
+
)
|
160
|
+
assert_equal "400", res.code
|
161
|
+
assert_match /Validation failed/, res.body
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# 11. Response‑format schema enforcement
|
166
|
+
def test_response_schema_enforcement
|
167
|
+
server(itsi_rb: lambda do
|
168
|
+
endpoint "/ping" do |req, _params, response_format: PingSchema|
|
169
|
+
req.ok json: { "wrong" => 123 }, as: response_format
|
170
|
+
end
|
171
|
+
end) do
|
172
|
+
res = get_resp("/ping")
|
173
|
+
assert_equal "400", res.code
|
174
|
+
assert_match /Validation failed/, res.body
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# 12. Unrestricted HTTP method on endpoint (no wrapper)
|
179
|
+
def test_unrestricted_endpoint_all_methods
|
180
|
+
server(itsi_rb: lambda do
|
181
|
+
endpoint "/all" do |req|
|
182
|
+
req.ok req.request_method
|
183
|
+
end
|
184
|
+
end) do
|
185
|
+
%w[POST PUT PATCH DELETE].each do |m|
|
186
|
+
resp = send(m.downcase, "/all")
|
187
|
+
assert_equal "200", resp.code
|
188
|
+
assert_equal m, resp.body
|
189
|
+
end
|
190
|
+
resp = get_resp("/all")
|
191
|
+
assert_equal "200", resp.code
|
192
|
+
assert_equal "GET", resp.body
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_head_on_get_wrapper
|
197
|
+
server(itsi_rb: lambda do
|
198
|
+
get "/resource" do |req|
|
199
|
+
req.respond body: "payload", status: 200, headers: { "X-Foo" => "bar" }
|
200
|
+
end
|
201
|
+
end) do
|
202
|
+
res = head("/resource")
|
203
|
+
assert_equal "200", res.code
|
204
|
+
assert_equal "bar", res["X-Foo"]
|
205
|
+
assert_empty res.body.to_s
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# 14. POST wrapper accepts POST only
|
210
|
+
def test_post_wrapper
|
211
|
+
server(itsi_rb: lambda do
|
212
|
+
post "/submit" do |req|
|
213
|
+
req.ok "posted"
|
214
|
+
end
|
215
|
+
endpoint do |req|
|
216
|
+
req.not_found ""
|
217
|
+
end
|
218
|
+
end) do
|
219
|
+
assert_equal "posted", post("/submit", "").body
|
220
|
+
assert_equal "404", get_resp("/submit").code
|
221
|
+
assert_equal "404", head("/submit").code
|
222
|
+
assert_equal "404", options("/submit").code
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# 15. PUT, PATCH, DELETE wrappers
|
227
|
+
%w[put patch delete].each do |verb|
|
228
|
+
define_method("test_#{verb}_wrapper") do
|
229
|
+
server(itsi_rb: lambda do
|
230
|
+
send(verb, "/thing") do |req|
|
231
|
+
req.ok req.request_method
|
232
|
+
end
|
233
|
+
endpoint do |req|
|
234
|
+
req.not_found ""
|
235
|
+
end
|
236
|
+
end) do
|
237
|
+
# correct method
|
238
|
+
send("#{verb}", "/thing")
|
239
|
+
# mismatched methods
|
240
|
+
%w[POST OPTIONS HEAD].each do |bad|
|
241
|
+
res = send("#{bad.downcase}", "/thing")
|
242
|
+
assert_equal "404", res.code, "#{verb.upcase} wrapper should 404 on #{bad}"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# 16. OPTIONS on unrestricted endpoint yields 200
|
249
|
+
def test_options_on_unrestricted
|
250
|
+
server(itsi_rb: lambda do
|
251
|
+
endpoint "/open" do |req|
|
252
|
+
req.ok "ok"
|
253
|
+
end
|
254
|
+
end) do
|
255
|
+
res = options("/open")
|
256
|
+
assert_equal "200", res.code
|
257
|
+
assert_equal "ok", res.body
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# 17. HEAD on streaming endpoint
|
262
|
+
def test_head_on_streaming_endpoint
|
263
|
+
server(itsi_rb: lambda do
|
264
|
+
endpoint "/stream" do |req|
|
265
|
+
s = req.response
|
266
|
+
s << "XYZ"
|
267
|
+
s.close
|
268
|
+
end
|
269
|
+
end) do
|
270
|
+
res = head("/stream")
|
271
|
+
assert_equal "200", res.code
|
272
|
+
assert_empty res.body.to_s
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# 18. Wildcard segment matching
|
277
|
+
def test_wildcard_endpoint
|
278
|
+
server(itsi_rb: lambda do
|
279
|
+
endpoint "/api/*" do |req|
|
280
|
+
req.ok req.path
|
281
|
+
end
|
282
|
+
end) do
|
283
|
+
["/api/foo", "/api/foo/bar", "/api/"].each do |path|
|
284
|
+
res = get_resp(path)
|
285
|
+
assert_equal path, res.body
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# 19. Multiple endpoints same path, different verbs
|
291
|
+
def test_same_path_different_verb
|
292
|
+
server(itsi_rb: lambda do
|
293
|
+
get "/duo" do |req| req.ok "g" end
|
294
|
+
post "/duo" do |req| req.ok "p" end
|
295
|
+
end) do
|
296
|
+
assert_equal "g", get("/duo")
|
297
|
+
assert_equal "p", post("/duo", "").body
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative "../helpers/test_helper"
|
2
|
+
|
3
|
+
class TestETag < Minitest::Test
|
4
|
+
def test_strong_etag_added
|
5
|
+
server(
|
6
|
+
itsi_rb: lambda do
|
7
|
+
etag type: "strong", algorithm: "sha256", min_body_size: 0, handle_if_none_match: false
|
8
|
+
get("/foo") { |r| r.ok "etag-body" }
|
9
|
+
end
|
10
|
+
) do
|
11
|
+
res = get_resp("/foo")
|
12
|
+
assert res["ETag"].start_with?("\"")
|
13
|
+
refute res["ETag"].start_with?("W/")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_weak_etag_added_with_md5
|
18
|
+
server(
|
19
|
+
itsi_rb: lambda do
|
20
|
+
etag type: "weak", algorithm: "md5", min_body_size: 0, handle_if_none_match: false
|
21
|
+
get("/foo") { |r| r.ok "etag-weak-md5" }
|
22
|
+
end
|
23
|
+
) do
|
24
|
+
res = get_resp("/foo")
|
25
|
+
assert res["ETag"].start_with?("W/\"")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_etag_not_added_below_min_body_size
|
30
|
+
server(
|
31
|
+
itsi_rb: lambda do
|
32
|
+
etag min_body_size: 50
|
33
|
+
get("/foo") { |r| r.ok "short" }
|
34
|
+
end
|
35
|
+
) do
|
36
|
+
res = get_resp("/foo")
|
37
|
+
refute res.key?("ETag")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_etag_responds_with_304_if_none_match
|
42
|
+
body = "etag-content"
|
43
|
+
server(
|
44
|
+
itsi_rb: lambda do
|
45
|
+
etag type: "strong", min_body_size: 0, handle_if_none_match: true
|
46
|
+
get("/foo") { |r| r.ok body }
|
47
|
+
end
|
48
|
+
) do
|
49
|
+
first = get_resp("/foo")
|
50
|
+
etag = first["ETag"]
|
51
|
+
assert etag
|
52
|
+
|
53
|
+
second = get_resp("/foo", { "If-None-Match" => etag })
|
54
|
+
assert_equal "304", second.code
|
55
|
+
refute second.body && second.body.size > 0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_etag_ignored_for_streaming_body
|
60
|
+
server(
|
61
|
+
itsi_rb: lambda do
|
62
|
+
etag min_body_size: 0
|
63
|
+
get("/stream") do |req|
|
64
|
+
r = req.response
|
65
|
+
r << "streaming-1"
|
66
|
+
r << "streaming-2"
|
67
|
+
r.close
|
68
|
+
end
|
69
|
+
end
|
70
|
+
) do
|
71
|
+
res = get_resp("/stream")
|
72
|
+
refute res.key?("ETag")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require_relative "../../helpers/test_helper"
|
2
|
+
require_relative "test_service_impl"
|
3
|
+
|
4
|
+
class TestGrpc < Minitest::Test
|
5
|
+
Stub = Test::TestService::Stub
|
6
|
+
|
7
|
+
def new_stub(uri)
|
8
|
+
address = uri.to_s[/\/\/(.*)/,1] # e.g. "127.0.0.1:12345"
|
9
|
+
channel = GRPC::Core::Channel.new(address,
|
10
|
+
{}, # channel_args hash
|
11
|
+
:this_channel_is_insecure,
|
12
|
+
)
|
13
|
+
stub = Stub.new(nil,
|
14
|
+
nil,
|
15
|
+
channel_override: channel)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_unary_echo
|
19
|
+
server(itsi_rb: lambda do
|
20
|
+
grpc TestServiceImpl.new
|
21
|
+
end) do
|
22
|
+
stub = new_stub(@uri)
|
23
|
+
req = Test::EchoRequest.new(message: "hello")
|
24
|
+
res = stub.unary_echo(req)
|
25
|
+
assert_equal "hello", res.message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_client_stream
|
30
|
+
server(itsi_rb: lambda do
|
31
|
+
grpc TestServiceImpl.new
|
32
|
+
end) do
|
33
|
+
stub = new_stub(@uri)
|
34
|
+
# send a few messages in one stream
|
35
|
+
requests = %w[a b c].map { |m| Test::StreamRequest.new(message: m) }
|
36
|
+
res = stub.client_stream(requests.each)
|
37
|
+
assert_equal %w[a b c], res.messages
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_server_stream
|
42
|
+
server(itsi_rb: lambda do
|
43
|
+
grpc TestServiceImpl.new
|
44
|
+
end) do
|
45
|
+
stub = new_stub(@uri)
|
46
|
+
req = Test::EchoRequest.new(message: "xy")
|
47
|
+
# expect two StreamResponse frames, one per character
|
48
|
+
chars = stub.server_stream(req).map(&:messages).flatten
|
49
|
+
assert_equal ["x", "y"], chars
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_bidi_stream
|
54
|
+
server(itsi_rb: lambda do
|
55
|
+
grpc TestServiceImpl.new
|
56
|
+
end) do
|
57
|
+
stub = new_stub(@uri)
|
58
|
+
inputs = %w[test1 test2].map { |m| Test::EchoRequest.new(message: m) }
|
59
|
+
# each response uppercases its incoming message
|
60
|
+
results = stub.bidi_stream(inputs.each).map(&:message)
|
61
|
+
assert_equal ["TEST1", "TEST2"], results
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_empty_streams
|
66
|
+
server(itsi_rb: lambda do
|
67
|
+
grpc TestServiceImpl.new
|
68
|
+
end) do
|
69
|
+
stub = new_stub(@uri)
|
70
|
+
|
71
|
+
# empty client‐streaming
|
72
|
+
res = stub.client_stream([].each)
|
73
|
+
assert_equal [], res.messages
|
74
|
+
|
75
|
+
# empty server‐streaming
|
76
|
+
empty_server = stub.server_stream(Test::EchoRequest.new(message: "")).to_a
|
77
|
+
assert empty_server.empty?
|
78
|
+
|
79
|
+
# empty bidi‐streaming
|
80
|
+
empty_bidi = stub.bidi_stream([].each).to_a
|
81
|
+
assert empty_bidi.empty?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def test_unary_echo_json
|
87
|
+
server(itsi_rb: lambda do
|
88
|
+
grpc TestServiceImpl.new
|
89
|
+
end) do
|
90
|
+
payload = { "message" => "hello" }.to_json
|
91
|
+
res = post("test.TestService/UnaryEcho", payload, { "Content-Type" => "application/json" })
|
92
|
+
json = JSON.parse(res.body)
|
93
|
+
assert_equal 200, res.code.to_i
|
94
|
+
assert_equal "hello", json["message"]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_client_stream_json
|
99
|
+
server(itsi_rb: lambda do
|
100
|
+
grpc TestServiceImpl.new
|
101
|
+
end) do
|
102
|
+
input = [{"message" => "a"}, {"message" => "b"}, {"message" => "c"}]
|
103
|
+
res = post("test.TestService/ClientStream", input.to_json, { "Content-Type" => "application/json" })
|
104
|
+
json = JSON.parse(res.body)
|
105
|
+
assert_equal 200, res.code.to_i
|
106
|
+
assert_equal %w[a b c], json["messages"]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_server_stream_json
|
111
|
+
server(itsi_rb: lambda do
|
112
|
+
grpc TestServiceImpl.new
|
113
|
+
end) do
|
114
|
+
payload = { "message" => "xy" }.to_json
|
115
|
+
res = post("test.TestService/ServerStream", payload, { "Content-Type" => "application/json" })
|
116
|
+
arr = JSON.parse(res.body)
|
117
|
+
# service streams back one JSON object per character
|
118
|
+
messages = arr.flat_map { |frame| frame["messages"] }
|
119
|
+
assert_equal 200, res.code.to_i
|
120
|
+
assert_equal ["x","y"], messages
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_bidi_stream_json
|
125
|
+
server(itsi_rb: lambda do
|
126
|
+
grpc TestServiceImpl.new
|
127
|
+
end) do
|
128
|
+
inputs = [{"message":"foo"}, {"message":"Bar"}]
|
129
|
+
res = post("test.TestService/BidiStream", inputs.to_json, { "Content-Type" => "application/json" })
|
130
|
+
arr = JSON.parse(res.body)
|
131
|
+
# bidi uppercases each incoming message
|
132
|
+
results = arr.map { |frame| frame["message"] }
|
133
|
+
assert_equal 200, res.code.to_i
|
134
|
+
assert_equal ["FOO","BAR"], results
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_empty_streams_json
|
139
|
+
server(itsi_rb: lambda do
|
140
|
+
grpc TestServiceImpl.new
|
141
|
+
end) do
|
142
|
+
# empty client-stream → still JSON object with empty messages
|
143
|
+
res1 = post("test.TestService/ClientStream", "[]", { "Content-Type" => "application/json" })
|
144
|
+
json1 = JSON.parse(res1.body)
|
145
|
+
assert_nil json1["messages"]
|
146
|
+
|
147
|
+
# empty server-stream
|
148
|
+
res2 = post("test.TestService/ServerStream", { "message" => "" }.to_json, { "Content-Type" => "application/json" })
|
149
|
+
arr2 = JSON.parse(res2.body)
|
150
|
+
assert arr2.empty?
|
151
|
+
|
152
|
+
# empty bidi-stream
|
153
|
+
res3 = post("test.TestService/BidiStream", "[]", { "Content-Type" => "application/json" })
|
154
|
+
arr3 = JSON.parse(res3.body)
|
155
|
+
assert arr3.empty?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
syntax = "proto3";
|
2
|
+
|
3
|
+
package test;
|
4
|
+
|
5
|
+
// A service with one of each RPC type [oai_citation_attribution:0‡Википедия — свободная энциклопедия](https://ru.wikipedia.org/wiki/GRPC?utm_source=chatgpt.com)
|
6
|
+
service TestService {
|
7
|
+
// Unary RPC
|
8
|
+
rpc UnaryEcho (EchoRequest) returns (EchoResponse) {}
|
9
|
+
|
10
|
+
// Client‑streaming RPC
|
11
|
+
rpc ClientStream (stream StreamRequest) returns (StreamResponse) {}
|
12
|
+
|
13
|
+
// Server‑streaming RPC
|
14
|
+
rpc ServerStream (EchoRequest) returns (stream StreamResponse) {}
|
15
|
+
|
16
|
+
// Bidirectional streaming RPC
|
17
|
+
rpc BidiStream (stream EchoRequest) returns (stream EchoResponse) {}
|
18
|
+
}
|
19
|
+
|
20
|
+
message EchoRequest {
|
21
|
+
string message = 1;
|
22
|
+
}
|
23
|
+
message EchoResponse {
|
24
|
+
string message = 1;
|
25
|
+
}
|
26
|
+
|
27
|
+
message StreamRequest {
|
28
|
+
string message = 1;
|
29
|
+
}
|
30
|
+
message StreamResponse {
|
31
|
+
repeated string messages = 1;
|
32
|
+
}
|