itsi-server 0.1.1 → 0.1.13
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.
Potentially problematic release.
This version of itsi-server might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +7 -0
- data/Cargo.lock +4417 -0
- data/Cargo.toml +7 -0
- data/README.md +4 -0
- data/Rakefile +8 -1
- data/_index.md +6 -0
- data/exe/itsi +94 -45
- data/ext/itsi_error/Cargo.toml +2 -0
- data/ext/itsi_error/src/from.rs +68 -0
- data/ext/itsi_error/src/lib.rs +18 -34
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
- data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
- data/ext/itsi_instrument_entry/Cargo.toml +15 -0
- data/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/ext/itsi_rb_helpers/Cargo.toml +3 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
- data/ext/itsi_rb_helpers/src/lib.rs +140 -10
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
- data/ext/itsi_rb_helpers/target/debug/build/rb-sys-eb9ed4ff3a60f995/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
- data/ext/itsi_scheduler/Cargo.toml +24 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/ext/itsi_scheduler/src/lib.rs +38 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +73 -13
- data/ext/itsi_server/extconf.rb +1 -1
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +100 -40
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +109 -0
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +141 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +282 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +388 -0
- data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +82 -0
- data/ext/itsi_server/src/ruby_types/mod.rs +55 -0
- data/ext/itsi_server/src/server/bind.rs +75 -31
- data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
- data/ext/itsi_server/src/server/byte_frame.rs +32 -0
- data/ext/itsi_server/src/server/cache_store.rs +74 -0
- data/ext/itsi_server/src/server/io_stream.rs +104 -0
- data/ext/itsi_server/src/server/itsi_service.rs +172 -0
- data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
- data/ext/itsi_server/src/server/listener.rs +332 -132
- data/ext/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +321 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
- data/ext/itsi_server/src/server/middleware_stack/mod.rs +315 -0
- data/ext/itsi_server/src/server/mod.rs +15 -2
- data/ext/itsi_server/src/server/process_worker.rs +229 -0
- data/ext/itsi_server/src/server/rate_limiter.rs +565 -0
- data/ext/itsi_server/src/server/request_job.rs +11 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +337 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +30 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +421 -0
- data/ext/itsi_server/src/server/signal.rs +93 -0
- data/ext/itsi_server/src/server/static_file_server.rs +984 -0
- data/ext/itsi_server/src/server/thread_worker.rs +444 -0
- data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +132 -0
- data/ext/itsi_server/src/server/tls.rs +187 -60
- data/ext/itsi_server/src/server/types.rs +43 -0
- data/ext/itsi_tracing/Cargo.toml +5 -0
- data/ext/itsi_tracing/src/lib.rs +225 -7
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
- data/lib/itsi/http_request.rb +87 -0
- data/lib/itsi/http_response.rb +39 -0
- data/lib/itsi/server/Itsi.rb +119 -0
- data/lib/itsi/server/config/dsl.rb +506 -0
- data/lib/itsi/server/config.rb +131 -0
- data/lib/itsi/server/default_app/default_app.rb +38 -0
- data/lib/itsi/server/default_app/index.html +91 -0
- data/lib/itsi/server/grpc_interface.rb +213 -0
- data/lib/itsi/server/rack/handler/itsi.rb +27 -0
- data/lib/itsi/server/rack_interface.rb +94 -0
- data/lib/itsi/server/scheduler_interface.rb +21 -0
- data/lib/itsi/server/scheduler_mode.rb +10 -0
- data/lib/itsi/server/signal_trap.rb +29 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +90 -9
- data/lib/itsi/standard_headers.rb +86 -0
- metadata +122 -31
- data/ext/itsi_server/src/request/itsi_request.rs +0 -143
- data/ext/itsi_server/src/request/mod.rs +0 -1
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -32
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -52
- data/ext/itsi_server/src/server/itsi_server.rs +0 -182
- data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
- data/lib/itsi/request.rb +0 -39
| @@ -0,0 +1,91 @@ | |
| 1 | 
            +
            <!DOCTYPE html>
         | 
| 2 | 
            +
            <html lang="en">
         | 
| 3 | 
            +
            <head>
         | 
| 4 | 
            +
              <meta charset="UTF-8" />
         | 
| 5 | 
            +
              <title>Itsi - Default</title>
         | 
| 6 | 
            +
              <style>
         | 
| 7 | 
            +
                * {
         | 
| 8 | 
            +
                  box-sizing: border-box;
         | 
| 9 | 
            +
                  margin: 0;
         | 
| 10 | 
            +
                  padding: 0;
         | 
| 11 | 
            +
                }
         | 
| 12 | 
            +
                body {
         | 
| 13 | 
            +
                  font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
         | 
| 14 | 
            +
                  background-color: #f4f4f4;
         | 
| 15 | 
            +
                  color: #333;
         | 
| 16 | 
            +
                  line-height: 1.6;
         | 
| 17 | 
            +
                }
         | 
| 18 | 
            +
                .container {
         | 
| 19 | 
            +
                  max-width: 700px;
         | 
| 20 | 
            +
                  margin: 3rem auto;
         | 
| 21 | 
            +
                  background: #fff;
         | 
| 22 | 
            +
                  border-radius: 8px;
         | 
| 23 | 
            +
                  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
         | 
| 24 | 
            +
                  padding: 2rem;
         | 
| 25 | 
            +
                }
         | 
| 26 | 
            +
                h1 {
         | 
| 27 | 
            +
                  font-size: 1.8rem;
         | 
| 28 | 
            +
                  margin-bottom: 1rem;
         | 
| 29 | 
            +
                  text-align: center;
         | 
| 30 | 
            +
                  color: #444;
         | 
| 31 | 
            +
                }
         | 
| 32 | 
            +
                p {
         | 
| 33 | 
            +
                  margin-bottom: 1rem;
         | 
| 34 | 
            +
                  text-align: center;
         | 
| 35 | 
            +
                  color: #666;
         | 
| 36 | 
            +
                }
         | 
| 37 | 
            +
                ul.fields {
         | 
| 38 | 
            +
                  list-style: none;
         | 
| 39 | 
            +
                  margin-top: 1.5rem;
         | 
| 40 | 
            +
                  padding: 0;
         | 
| 41 | 
            +
                }
         | 
| 42 | 
            +
                ul.fields li {
         | 
| 43 | 
            +
                  background: #fafafa;
         | 
| 44 | 
            +
                  border: 1px solid #eee;
         | 
| 45 | 
            +
                  border-radius: 5px;
         | 
| 46 | 
            +
                  padding: 0.75rem;
         | 
| 47 | 
            +
                  margin-bottom: 0.75rem;
         | 
| 48 | 
            +
                  display: flex;
         | 
| 49 | 
            +
                  justify-content: space-between;
         | 
| 50 | 
            +
                  align-items: center;
         | 
| 51 | 
            +
                }
         | 
| 52 | 
            +
                .label {
         | 
| 53 | 
            +
                  font-weight: bold;
         | 
| 54 | 
            +
                  margin-right: 1rem;
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
              </style>
         | 
| 57 | 
            +
            </head>
         | 
| 58 | 
            +
            <body>
         | 
| 59 | 
            +
              <div class="container">
         | 
| 60 | 
            +
                <h1>You're running on Itsi!</h1>
         | 
| 61 | 
            +
                <p>RACK environment:</p>
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                <ul class="fields">
         | 
| 64 | 
            +
                  <li>
         | 
| 65 | 
            +
                    <span class="label">REQUEST_METHOD:</span>
         | 
| 66 | 
            +
                    <span>%{REQUEST_METHOD}</span>
         | 
| 67 | 
            +
                  </li>
         | 
| 68 | 
            +
                  <li>
         | 
| 69 | 
            +
                    <span class="label">PATH_INFO:</span>
         | 
| 70 | 
            +
                    <span>%{PATH_INFO}</span>
         | 
| 71 | 
            +
                  </li>
         | 
| 72 | 
            +
                  <li>
         | 
| 73 | 
            +
                    <span class="label">SERVER_NAME:</span>
         | 
| 74 | 
            +
                    <span>%{SERVER_NAME}</span>
         | 
| 75 | 
            +
                  </li>
         | 
| 76 | 
            +
                  <li>
         | 
| 77 | 
            +
                    <span class="label">SERVER_PORT:</span>
         | 
| 78 | 
            +
                    <span>%{SERVER_PORT}</span>
         | 
| 79 | 
            +
                  </li>
         | 
| 80 | 
            +
                  <li>
         | 
| 81 | 
            +
                    <span class="label">REMOTE_ADDR:</span>
         | 
| 82 | 
            +
                    <span>%{REMOTE_ADDR}</span>
         | 
| 83 | 
            +
                  </li>
         | 
| 84 | 
            +
                  <li>
         | 
| 85 | 
            +
                    <span class="label">HTTP_USER_AGENT:</span>
         | 
| 86 | 
            +
                    <span>%{HTTP_USER_AGENT}</span>
         | 
| 87 | 
            +
                  </li>
         | 
| 88 | 
            +
                </ul>
         | 
| 89 | 
            +
              </div>
         | 
| 90 | 
            +
            </body>
         | 
| 91 | 
            +
            </html>
         | 
| @@ -0,0 +1,213 @@ | |
| 1 | 
            +
            module Itsi
         | 
| 2 | 
            +
              class Server
         | 
| 3 | 
            +
                class GrpcActiveCall
         | 
| 4 | 
            +
                  attr_reader :stream, :method_rpc, :output_metadata, :method_name
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(stream, method_rpc, service_class, method_name)
         | 
| 7 | 
            +
                    @stream = stream
         | 
| 8 | 
            +
                    @method_rpc = method_rpc
         | 
| 9 | 
            +
                    @service_class = service_class
         | 
| 10 | 
            +
                    @output_metadata = {}
         | 
| 11 | 
            +
                    @method_name = method_name
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                class GrpcInterface
         | 
| 16 | 
            +
                  def self.for(services)
         | 
| 17 | 
            +
                    interface = new(services)
         | 
| 18 | 
            +
                    lambda do |request|
         | 
| 19 | 
            +
                      interface.handle_request(request)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def initialize(services = [])
         | 
| 24 | 
            +
                    @services = {}
         | 
| 25 | 
            +
                    register_services(services)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def register_services(services)
         | 
| 29 | 
            +
                    services.each do |service|
         | 
| 30 | 
            +
                      service_class = service.class
         | 
| 31 | 
            +
                      @services[service_class.service_name] = {
         | 
| 32 | 
            +
                        implementation: service,
         | 
| 33 | 
            +
                        class: service_class
         | 
| 34 | 
            +
                      }
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def handle_request(request)
         | 
| 39 | 
            +
                    service_info = @services[request.service_name]
         | 
| 40 | 
            +
                    raise "Unknown service: #{request.service_name}" unless service_info
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    service_impl = service_info[:implementation]
         | 
| 43 | 
            +
                    service_class = service_info[:class]
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    method_rpc = RpcDescWrapper.new(service_class, request.content_type, request.method_name)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    active_call = GrpcActiveCall.new(request.stream, method_rpc, service_class, request.method_name)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    if method_rpc.bidi_streamer?
         | 
| 50 | 
            +
                      puts "Calling bidirectional streaming handler"
         | 
| 51 | 
            +
                      handle_bidi_streaming(active_call, service_impl)
         | 
| 52 | 
            +
                    elsif method_rpc.client_streamer?
         | 
| 53 | 
            +
                      puts "Calling client streaming handler"
         | 
| 54 | 
            +
                      handle_client_streaming(active_call, service_impl)
         | 
| 55 | 
            +
                    elsif method_rpc.server_streamer?
         | 
| 56 | 
            +
                      puts "Calling server streaming handler"
         | 
| 57 | 
            +
                      handle_server_streaming(active_call, service_impl)
         | 
| 58 | 
            +
                    elsif method_rpc.request_response?
         | 
| 59 | 
            +
                      handle_unary(active_call, service_impl)
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
                    active_call.stream.send_trailers({ "grpc-status" => "0" })
         | 
| 62 | 
            +
                  rescue StandardError => e
         | 
| 63 | 
            +
                    active_call.stream.send_trailers({ "grpc-status" => "13", "grpc-message" => e.message })
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  private
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  # Read a framed message from the stream
         | 
| 69 | 
            +
                  def read_message(stream)
         | 
| 70 | 
            +
                    # Read the gRPC frame header (5 bytes)
         | 
| 71 | 
            +
                    header = stream.read(5)
         | 
| 72 | 
            +
                    return nil if header.nil? || header.bytesize < 5
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    compressed = header.bytes[0] == 1
         | 
| 75 | 
            +
                    length = header[1..4].unpack1("N")
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    # Read the message body
         | 
| 78 | 
            +
                    stream.read(length)
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  # Send a response
         | 
| 82 | 
            +
                  def send_response(active_call, response)
         | 
| 83 | 
            +
                    response_data = active_call.method_rpc.marshal_response(response)
         | 
| 84 | 
            +
                    send_framed_message(active_call.stream, response_data)
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  # Send a framed message
         | 
| 88 | 
            +
                  def send_framed_message(stream, message_data, compressed = false)
         | 
| 89 | 
            +
                    compressed_flag = compressed ? 1 : 0
         | 
| 90 | 
            +
                    header = [compressed_flag, message_data.bytesize].pack("CN")
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    stream.write(header)
         | 
| 93 | 
            +
                    stream.write(message_data)
         | 
| 94 | 
            +
                    stream.flush
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  # Create an enumerator for client streaming requests
         | 
| 98 | 
            +
                  def create_request_enum(active_call)
         | 
| 99 | 
            +
                    Enumerator.new do |yielder|
         | 
| 100 | 
            +
                      loop do
         | 
| 101 | 
            +
                        message_data = read_message(active_call.stream)
         | 
| 102 | 
            +
                        break if message_data.nil?
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                        request = active_call.method_rpc.unmarshal_request(message_data)
         | 
| 105 | 
            +
                        yielder << request
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  # Handlers for different RPC types
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  def handle_unary(active_call, service)
         | 
| 113 | 
            +
                    # Read the request message
         | 
| 114 | 
            +
                    message_data = read_message(active_call.stream)
         | 
| 115 | 
            +
                    request = active_call.method_rpc.unmarshal_request(message_data)
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    # Call the service implementation
         | 
| 118 | 
            +
                    underscore_method = GRPC::GenericService.underscore(active_call.method_name)
         | 
| 119 | 
            +
                    response = service.send(underscore_method, request, active_call)
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    # Send response
         | 
| 122 | 
            +
                    send_response(active_call, response)
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                  def handle_client_streaming(active_call, service)
         | 
| 126 | 
            +
                    # Create an enumerable to read the incoming stream
         | 
| 127 | 
            +
                    request_enum = create_request_enum(active_call)
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    # Call the service implementation
         | 
| 130 | 
            +
                    underscore_method = GRPC::GenericService.underscore(active_call.method_name)
         | 
| 131 | 
            +
                    response = service.send(underscore_method, request_enum, active_call)
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    # Send response
         | 
| 134 | 
            +
                    send_response(active_call, response)
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  def handle_server_streaming(active_call, service)
         | 
| 138 | 
            +
                    # Read the request message
         | 
| 139 | 
            +
                    message_data = read_message(active_call.stream)
         | 
| 140 | 
            +
                    request = active_call.method_rpc.unmarshal_request(message_data)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    # Call the service implementation with a block to handle streaming
         | 
| 143 | 
            +
                    underscore_method = GRPC::GenericService.underscore(active_call.method_name)
         | 
| 144 | 
            +
                    service.send(underscore_method, request, active_call) do |response|
         | 
| 145 | 
            +
                      send_response(active_call, response)
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
                  end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                  def handle_bidi_streaming(active_call, service)
         | 
| 150 | 
            +
                    # Create an enumerable to read the incoming stream
         | 
| 151 | 
            +
                    request_enum = create_request_enum(active_call)
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    # Call the service implementation with a block
         | 
| 154 | 
            +
                    underscore_method = GRPC::GenericService.underscore(active_call.method_name)
         | 
| 155 | 
            +
                    service.send(underscore_method, request_enum, active_call) do |response|
         | 
| 156 | 
            +
                      send_response(active_call, response)
         | 
| 157 | 
            +
                    end
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                class RpcDescWrapper
         | 
| 162 | 
            +
                  def initialize(service_class, content_type, method_name)
         | 
| 163 | 
            +
                    rpc_descs = service_class.rpc_descs
         | 
| 164 | 
            +
                    @rpc_desc = rpc_descs[method_name.to_sym]
         | 
| 165 | 
            +
                    raise "Method not found: #{method_name}" unless @rpc_desc
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    @input_type = @rpc_desc.input
         | 
| 168 | 
            +
                    @content_type = content_type
         | 
| 169 | 
            +
                    @input_is_stream = @input_type.is_a?(GRPC::RpcDesc::Stream)
         | 
| 170 | 
            +
                    @input_type = @input_type.type if @input_is_stream
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                    @output_type = @rpc_desc.output
         | 
| 173 | 
            +
                    @output_is_stream = @output_type.is_a?(GRPC::RpcDesc::Stream)
         | 
| 174 | 
            +
                    @output_type = @output_type.type if @output_is_stream
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                    @client_streamer = @input_is_stream
         | 
| 177 | 
            +
                    @server_streamer = @output_is_stream
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  def client_streamer?
         | 
| 181 | 
            +
                    @client_streamer
         | 
| 182 | 
            +
                  end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                  def server_streamer?
         | 
| 185 | 
            +
                    @server_streamer
         | 
| 186 | 
            +
                  end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                  def bidi_streamer?
         | 
| 189 | 
            +
                    @client_streamer && @server_streamer
         | 
| 190 | 
            +
                  end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                  def request_response?
         | 
| 193 | 
            +
                    !@client_streamer && !@server_streamer
         | 
| 194 | 
            +
                  end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                  def unmarshal_request(data)
         | 
| 197 | 
            +
                    if @content_type == "application/json"
         | 
| 198 | 
            +
                      @input_type.decode_json(data)
         | 
| 199 | 
            +
                    else
         | 
| 200 | 
            +
                      @input_type.decode(data)
         | 
| 201 | 
            +
                    end
         | 
| 202 | 
            +
                  end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                  def marshal_response(response)
         | 
| 205 | 
            +
                    if @content_type == "application/json"
         | 
| 206 | 
            +
                      response.to_json
         | 
| 207 | 
            +
                    else
         | 
| 208 | 
            +
                      response.to_proto
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
                  end
         | 
| 211 | 
            +
                end
         | 
| 212 | 
            +
              end
         | 
| 213 | 
            +
            end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            return unless defined?(::Rackup::Handler) || defined?(Rack::Handler)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rack
         | 
| 4 | 
            +
              module Handler
         | 
| 5 | 
            +
                module Itsi
         | 
| 6 | 
            +
                  def self.run(app, options = {})
         | 
| 7 | 
            +
                    host = options.fetch(:host, "127.0.0.1")
         | 
| 8 | 
            +
                    port = options.fetch(:Port, 3001)
         | 
| 9 | 
            +
                    Itsi::Server.start(
         | 
| 10 | 
            +
                      {
         | 
| 11 | 
            +
                        app: app,
         | 
| 12 | 
            +
                        binds: ["http://#{host}:#{port}"]
         | 
| 13 | 
            +
                      },
         | 
| 14 | 
            +
                      Itsi::Server::Config.config_file_path
         | 
| 15 | 
            +
                    )
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            if defined?(Rackup)
         | 
| 22 | 
            +
              ::Rackup::Handler.register("itsi", Rack::Handler::Itsi)
         | 
| 23 | 
            +
              ::Rackup::Handler.register("Itsi", Rack::Handler::Itsi)
         | 
| 24 | 
            +
            elsif defined?(Rack)
         | 
| 25 | 
            +
              ::Rack::Handler.register("itsi", Rack::Handler::Itsi)
         | 
| 26 | 
            +
              ::Rack::Handler.register("Itsi", Rack::Handler::Itsi)
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            module Itsi
         | 
| 2 | 
            +
              class Server
         | 
| 3 | 
            +
                module RackInterface
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  # Builds a handler proc that is compatible with Rack applications.
         | 
| 6 | 
            +
                  def self.for(app)
         | 
| 7 | 
            +
                    require "rack"
         | 
| 8 | 
            +
                    if app.is_a?(String)
         | 
| 9 | 
            +
                      dir = File.expand_path(File.dirname(app))
         | 
| 10 | 
            +
                      Dir.chdir(dir) do
         | 
| 11 | 
            +
                        loaded_app = ::Rack::Builder.parse_file(app)
         | 
| 12 | 
            +
                        app = loaded_app.is_a?(Array) ? loaded_app.first : loaded_app
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                    lambda do |request|
         | 
| 16 | 
            +
                      Server.respond(request, app.call(request.to_rack_env))
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Interface to Rack applications.
         | 
| 21 | 
            +
                  # Here we build the env, and invoke the Rack app's call method.
         | 
| 22 | 
            +
                  # We then turn the Rack response into something Itsi server understands.
         | 
| 23 | 
            +
                  def call(app, request)
         | 
| 24 | 
            +
                    respond request, app.call(request.to_rack_env)
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # Itsi responses are asynchronous and can be streamed.
         | 
| 28 | 
            +
                  # Response chunks are sent using response.send_frame
         | 
| 29 | 
            +
                  # and the response is finished using response.close_write.
         | 
| 30 | 
            +
                  # If only a single chunk is written, you can use the #send_and_close method.
         | 
| 31 | 
            +
                  def respond(request, (status, headers, body))
         | 
| 32 | 
            +
                    response = request.response
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    # Don't try and respond if we've been hijacked.
         | 
| 35 | 
            +
                    # The hijacker is now responsible for this.
         | 
| 36 | 
            +
                    return if request.hijacked
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    # 1. Set Status
         | 
| 39 | 
            +
                    response.status = status
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    # 2. Set Headers
         | 
| 42 | 
            +
                    body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack")
         | 
| 43 | 
            +
                    headers.each do |key, value|
         | 
| 44 | 
            +
                      unless value.is_a?(Array)
         | 
| 45 | 
            +
                        response[key] = value
         | 
| 46 | 
            +
                        next
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                      value.each do |v|
         | 
| 50 | 
            +
                        response[key] = v
         | 
| 51 | 
            +
                      end
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    # 3. Set Body
         | 
| 55 | 
            +
                    # As soon as we start setting the response
         | 
| 56 | 
            +
                    # the server will begin to stream it to the client.
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    # If we're partially hijacked or returned a streaming body,
         | 
| 59 | 
            +
                    # stream this response.
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    if body_streamer
         | 
| 62 | 
            +
                      body_streamer.call(response)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    # If we're enumerable with more than one chunk
         | 
| 65 | 
            +
                    # also stream, otherwise write in a single chunk
         | 
| 66 | 
            +
                    elsif body.respond_to?(:each) || body.respond_to?(:to_ary)
         | 
| 67 | 
            +
                      unless body.respond_to?(:each)
         | 
| 68 | 
            +
                        body = body.to_ary
         | 
| 69 | 
            +
                        raise "Body #to_ary didn't return an array" unless body.is_a?(Array)
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                      # We offset this iteration intentionally,
         | 
| 72 | 
            +
                      # to optimize for the case where there's only one chunk.
         | 
| 73 | 
            +
                      buffer = nil
         | 
| 74 | 
            +
                      body.each do |part|
         | 
| 75 | 
            +
                        response << buffer.to_s if buffer
         | 
| 76 | 
            +
                        buffer = part
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                      response.send_and_close(buffer.to_s)
         | 
| 80 | 
            +
                    else
         | 
| 81 | 
            +
                      response.send_and_close(body.to_s)
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
                  ensure
         | 
| 84 | 
            +
                    response.close_write
         | 
| 85 | 
            +
                    body.close if body.respond_to?(:close)
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  # A streaming body is one that responds to #call and not #each.
         | 
| 89 | 
            +
                  def streaming_body?(body)
         | 
| 90 | 
            +
                    body.respond_to?(:call) && !body.respond_to?(:each)
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            module Itsi
         | 
| 2 | 
            +
              class Server
         | 
| 3 | 
            +
                module SchedulerInterface
         | 
| 4 | 
            +
                  # Simple wrapper to instantiate a scheduler, start it,
         | 
| 5 | 
            +
                  # and immediate have it invoke a scheduler proc
         | 
| 6 | 
            +
                  def start_scheduler_loop(scheduler_class, scheduler_task)
         | 
| 7 | 
            +
                    scheduler = scheduler_class.new
         | 
| 8 | 
            +
                    Fiber.set_scheduler(scheduler)
         | 
| 9 | 
            +
                    [scheduler, Fiber.schedule(&scheduler_task)]
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # When running in scheduler mode,
         | 
| 13 | 
            +
                  # each request is wrapped in a Fiber.
         | 
| 14 | 
            +
                  def schedule(app, request)
         | 
| 15 | 
            +
                    Fiber.schedule do
         | 
| 16 | 
            +
                      app.call(request)
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,10 @@ | |
| 1 | 
            +
            # Running with a Fiber scheduler enabled but with an ActiveSupport isolation_level set to Thread
         | 
| 2 | 
            +
            # can be dangerous. A thread isolation level means that all fibers sharing a thread can content
         | 
| 3 | 
            +
            # for the same resources, which can lead to race conditions.
         | 
| 4 | 
            +
            # This hook should *only* be disabled if you know there are no such shared resources.
         | 
| 5 | 
            +
            if defined?(ActiveSupport::IsolatedExecutionState) && !ENV["ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL"]
         | 
| 6 | 
            +
              Itsi.log_info \
         | 
| 7 | 
            +
                "ActiveSupport Isolated Execution state detected. Automatically switching to :fiber mode. "\
         | 
| 8 | 
            +
                "Set ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL to disable this behavior"
         | 
| 9 | 
            +
              ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
         | 
| 10 | 
            +
            end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            module Itsi
         | 
| 2 | 
            +
              # This trap is necessary for debuggers and similar which intercept certain signals
         | 
| 3 | 
            +
              # then attempt to restore these to the previous signal when finished.
         | 
| 4 | 
            +
              # If the previous signal handler was registered in native code, this restoration doesn't
         | 
| 5 | 
            +
              # work as expected and the native signal handler is lost.
         | 
| 6 | 
            +
              # We intercept restored signals here and reinstate the Itsi server signal handlers
         | 
| 7 | 
            +
              # (if the server is still running).
         | 
| 8 | 
            +
              module SignalTrap
         | 
| 9 | 
            +
                DEFAULT_SIGNALS = ["DEFAULT", "", nil].freeze
         | 
| 10 | 
            +
                INTERCEPTED_SIGNALS = ["INT"].freeze
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def trap(signal, *args, &block)
         | 
| 13 | 
            +
                  unless INTERCEPTED_SIGNALS.include?(signal.to_s) && block.nil? && Itsi::Server.running?
         | 
| 14 | 
            +
                    return super(signal, *args, &block)
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  Itsi::Server.reset_signal_handlers
         | 
| 18 | 
            +
                  nil
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            [Kernel, Signal].each do |receiver|
         | 
| 24 | 
            +
              receiver.singleton_class.prepend(Itsi::SignalTrap)
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            [Object].each do |receiver|
         | 
| 28 | 
            +
              receiver.include(Itsi::SignalTrap)
         | 
| 29 | 
            +
            end
         | 
    
        data/lib/itsi/server/version.rb
    CHANGED
    
    
    
        data/lib/itsi/server.rb
    CHANGED
    
    | @@ -2,19 +2,100 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require_relative "server/version"
         | 
| 4 4 | 
             
            require_relative "server/itsi_server"
         | 
| 5 | 
            +
            require_relative "server/rack_interface"
         | 
| 6 | 
            +
            require_relative "server/grpc_interface"
         | 
| 7 | 
            +
            require_relative "server/scheduler_interface"
         | 
| 8 | 
            +
            require_relative "server/signal_trap"
         | 
| 9 | 
            +
            require_relative "server/rack/handler/itsi"
         | 
| 10 | 
            +
            require_relative "server/config"
         | 
| 11 | 
            +
            require_relative "standard_headers"
         | 
| 12 | 
            +
            require_relative "http_request"
         | 
| 13 | 
            +
            require_relative "http_response"
         | 
| 5 14 |  | 
| 6 15 | 
             
            module Itsi
         | 
| 7 16 | 
             
              class Server
         | 
| 8 | 
            -
                 | 
| 9 | 
            -
                 | 
| 10 | 
            -
             | 
| 11 | 
            -
                 | 
| 17 | 
            +
                extend RackInterface
         | 
| 18 | 
            +
                extend SchedulerInterface
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                class << self
         | 
| 21 | 
            +
                  def running?
         | 
| 22 | 
            +
                    !!@running
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def start_in_background_thread(cli_params={}, itsi_file = Itsi::Server::Config.config_file_path, &blk)
         | 
| 26 | 
            +
                    @background_thread = start(cli_params, itsi_file, background: true, &blk)
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def start(cli_params, itsi_file = Itsi::Server::Config.config_file_path, background: false, &blk)
         | 
| 30 | 
            +
                    server = new(cli_params, itsi_file, blk)
         | 
| 31 | 
            +
                    previous_handler = Signal.trap(:INT, :DEFAULT)
         | 
| 32 | 
            +
                    run = lambda do
         | 
| 33 | 
            +
                      write_pid
         | 
| 34 | 
            +
                      @running = server
         | 
| 35 | 
            +
                      server.start
         | 
| 36 | 
            +
                      Signal.trap(:INT, previous_handler)
         | 
| 37 | 
            +
                      server
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                    background ? Thread.new(&run) : run[]
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def stop_background_thread
         | 
| 43 | 
            +
                    @running&.stop
         | 
| 44 | 
            +
                    @running = nil
         | 
| 45 | 
            +
                    @background_thread&.join
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def write_pid
         | 
| 49 | 
            +
                    File.write(Itsi::Server::Config.pid_file_path, Process.pid)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def get_pid
         | 
| 53 | 
            +
                    pid = File.read(Itsi::Server::Config.pid_file_path).to_i
         | 
| 54 | 
            +
                    if Process.kill(0, pid)
         | 
| 55 | 
            +
                      pid
         | 
| 56 | 
            +
                    else
         | 
| 57 | 
            +
                      nil
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
                  rescue StandardError
         | 
| 60 | 
            +
                    nil
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def reload
         | 
| 64 | 
            +
                    return unless pid = get_pid
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    Process.kill(:USR2, pid)
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  def restart
         | 
| 70 | 
            +
                    return unless pid = get_pid
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    Process.kill(:USR1, pid)
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def down
         | 
| 76 | 
            +
                    puts :down
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def up
         | 
| 80 | 
            +
                    puts :up
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  def add_worker
         | 
| 84 | 
            +
                    return unless pid = get_pid
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    Process.kill(:TTIN, pid)
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  def remove_worker
         | 
| 90 | 
            +
                    return unless pid = get_pid
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    Process.kill(:TTOU, pid)
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def status
         | 
| 96 | 
            +
                    return unless pid = get_pid
         | 
| 12 97 |  | 
| 13 | 
            -
             | 
| 14 | 
            -
                # Each request is wrapped in a Fiber.
         | 
| 15 | 
            -
                def self.schedule(app, request)
         | 
| 16 | 
            -
                  Fiber.schedule do
         | 
| 17 | 
            -
                    call(app, request)
         | 
| 98 | 
            +
                    Process.kill(:INFO, pid)
         | 
| 18 99 | 
             
                  end
         | 
| 19 100 | 
             
                end
         | 
| 20 101 | 
             
              end
         | 
| @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            module Itsi
         | 
| 2 | 
            +
              module StandardHeaders
         | 
| 3 | 
            +
                ALL = [
         | 
| 4 | 
            +
                  ACCEPT = "accept",
         | 
| 5 | 
            +
                  ACCEPT_CHARSET = "accept-charset",
         | 
| 6 | 
            +
                  ACCEPT_ENCODING = "accept-encoding",
         | 
| 7 | 
            +
                  ACCEPT_LANGUAGE = "accept-language",
         | 
| 8 | 
            +
                  ACCEPT_RANGES = "accept-ranges",
         | 
| 9 | 
            +
                  ACCESS_CONTROL_ALLOW_CREDENTIALS = "access-control-allow-credentials",
         | 
| 10 | 
            +
                  ACCESS_CONTROL_ALLOW_HEADERS = "access-control-allow-headers",
         | 
| 11 | 
            +
                  ACCESS_CONTROL_ALLOW_METHODS = "access-control-allow-methods",
         | 
| 12 | 
            +
                  ACCESS_CONTROL_ALLOW_ORIGIN = "access-control-allow-origin",
         | 
| 13 | 
            +
                  ACCESS_CONTROL_EXPOSE_HEADERS = "access-control-expose-headers",
         | 
| 14 | 
            +
                  ACCESS_CONTROL_MAX_AGE = "access-control-max-age",
         | 
| 15 | 
            +
                  ACCESS_CONTROL_REQUEST_HEADERS = "access-control-request-headers",
         | 
| 16 | 
            +
                  ACCESS_CONTROL_REQUEST_METHOD = "access-control-request-method",
         | 
| 17 | 
            +
                  AGE = "age",
         | 
| 18 | 
            +
                  ALLOW = "allow",
         | 
| 19 | 
            +
                  ALT_SVC = "alt-svc",
         | 
| 20 | 
            +
                  AUTHORIZATION = "authorization",
         | 
| 21 | 
            +
                  CACHE_CONTROL = "cache-control",
         | 
| 22 | 
            +
                  CACHE_STATUS = "cache-status",
         | 
| 23 | 
            +
                  CDN_CACHE_CONTROL = "cdn-cache-control",
         | 
| 24 | 
            +
                  CONNECTION = "connection",
         | 
| 25 | 
            +
                  CONTENT_DISPOSITION = "content-disposition",
         | 
| 26 | 
            +
                  CONTENT_ENCODING = "content-encoding",
         | 
| 27 | 
            +
                  CONTENT_LANGUAGE = "content-language",
         | 
| 28 | 
            +
                  CONTENT_LENGTH = "content-length",
         | 
| 29 | 
            +
                  CONTENT_LOCATION = "content-location",
         | 
| 30 | 
            +
                  CONTENT_RANGE = "content-range",
         | 
| 31 | 
            +
                  CONTENT_SECURITY_POLICY_REPORT_ONLY = "content-security-policy-report-only",
         | 
| 32 | 
            +
                  CONTENT_TYPE = "content-type",
         | 
| 33 | 
            +
                  COOKIE = "cookie",
         | 
| 34 | 
            +
                  DNT = "dnt",
         | 
| 35 | 
            +
                  DATE = "date",
         | 
| 36 | 
            +
                  ETAG = "etag",
         | 
| 37 | 
            +
                  EXPECT = "expect",
         | 
| 38 | 
            +
                  EXPIRES = "expires",
         | 
| 39 | 
            +
                  FORWARDED = "forwarded",
         | 
| 40 | 
            +
                  FROM = "from",
         | 
| 41 | 
            +
                  HOST = "host",
         | 
| 42 | 
            +
                  IF_MATCH = "if-match",
         | 
| 43 | 
            +
                  IF_MODIFIED_SINCE = "if-modified-since",
         | 
| 44 | 
            +
                  IF_NONE_MATCH = "if-none-match",
         | 
| 45 | 
            +
                  IF_RANGE = "if-range",
         | 
| 46 | 
            +
                  IF_UNMODIFIED_SINCE = "if-unmodified-since",
         | 
| 47 | 
            +
                  LAST_MODIFIED = "last-modified",
         | 
| 48 | 
            +
                  LINK = "link",
         | 
| 49 | 
            +
                  LOCATION = "location",
         | 
| 50 | 
            +
                  MAX_FORWARDS = "max-forwards",
         | 
| 51 | 
            +
                  ORIGIN = "origin",
         | 
| 52 | 
            +
                  PRAGMA = "pragma",
         | 
| 53 | 
            +
                  PROXY_AUTHENTICATE = "proxy-authenticate",
         | 
| 54 | 
            +
                  PROXY_AUTHORIZATION = "proxy-authorization",
         | 
| 55 | 
            +
                  PUBLIC_KEY_PINS = "public-key-pins",
         | 
| 56 | 
            +
                  PUBLIC_KEY_PINS_REPORT_ONLY = "public-key-pins-report-only",
         | 
| 57 | 
            +
                  RANGE = "range",
         | 
| 58 | 
            +
                  REFERER = "referer",
         | 
| 59 | 
            +
                  REFERRER_POLICY = "referrer-policy",
         | 
| 60 | 
            +
                  REFRESH = "refresh",
         | 
| 61 | 
            +
                  RETRY_AFTER = "retry-after",
         | 
| 62 | 
            +
                  SEC_WEBSOCKET_ACCEPT = "sec-websocket-accept",
         | 
| 63 | 
            +
                  SEC_WEBSOCKET_EXTENSIONS = "sec-websocket-extensions",
         | 
| 64 | 
            +
                  SEC_WEBSOCKET_KEY = "sec-websocket-key",
         | 
| 65 | 
            +
                  SEC_WEBSOCKET_PROTOCOL = "sec-websocket-protocol",
         | 
| 66 | 
            +
                  SEC_WEBSOCKET_VERSION = "sec-websocket-version",
         | 
| 67 | 
            +
                  SERVER = "server",
         | 
| 68 | 
            +
                  SET_COOKIE = "set-cookie",
         | 
| 69 | 
            +
                  STRICT_TRANSPORT_SECURITY = "strict-transport-security",
         | 
| 70 | 
            +
                  TE = "te",
         | 
| 71 | 
            +
                  TRAILER = "trailer",
         | 
| 72 | 
            +
                  TRANSFER_ENCODING = "transfer-encoding",
         | 
| 73 | 
            +
                  USER_AGENT = "user-agent",
         | 
| 74 | 
            +
                  UPGRADE = "upgrade",
         | 
| 75 | 
            +
                  UPGRADE_INSECURE_REQUESTS = "upgrade-insecure-requests",
         | 
| 76 | 
            +
                  VARY = "vary",
         | 
| 77 | 
            +
                  VIA = "via",
         | 
| 78 | 
            +
                  WARNING = "warning",
         | 
| 79 | 
            +
                  WWW_AUTHENTICATE = "www-authenticate",
         | 
| 80 | 
            +
                  X_CONTENT_TYPE_OPTIONS = "x-content-type-options",
         | 
| 81 | 
            +
                  X_DNS_PREFETCH_CONTROL = "x-dns-prefetch-control",
         | 
| 82 | 
            +
                  X_FRAME_OPTIONS = "x-frame-options",
         | 
| 83 | 
            +
                  X_XSS_PROTECTION = "x-xss-protection",
         | 
| 84 | 
            +
                ]
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
            end
         |