itsi-server 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +7 -0
  4. data/Cargo.lock +1536 -45
  5. data/README.md +4 -0
  6. data/_index.md +6 -0
  7. data/exe/itsi +33 -74
  8. data/ext/itsi_error/src/lib.rs +9 -0
  9. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  10. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  11. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  12. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  13. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  14. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  15. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  16. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  17. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  18. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  19. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  20. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  21. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  22. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  23. data/ext/itsi_rb_helpers/Cargo.toml +1 -0
  24. data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
  25. data/ext/itsi_rb_helpers/src/lib.rs +34 -7
  26. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  27. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  28. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  29. 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
  30. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  31. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  32. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  33. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  34. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  35. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  36. data/ext/itsi_server/Cargo.toml +69 -30
  37. data/ext/itsi_server/src/lib.rs +79 -147
  38. data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  39. data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +22 -3
  40. data/ext/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
  41. data/ext/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
  42. data/ext/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
  43. data/ext/itsi_server/src/{request/itsi_request.rs → ruby_types/itsi_http_request.rs} +101 -117
  44. data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +72 -41
  45. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
  46. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
  47. data/ext/itsi_server/src/ruby_types/itsi_server.rs +82 -0
  48. data/ext/itsi_server/src/ruby_types/mod.rs +55 -0
  49. data/ext/itsi_server/src/server/bind.rs +13 -5
  50. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  51. data/ext/itsi_server/src/server/cache_store.rs +74 -0
  52. data/ext/itsi_server/src/server/itsi_service.rs +172 -0
  53. data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
  54. data/ext/itsi_server/src/server/listener.rs +102 -2
  55. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
  56. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
  57. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
  58. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
  59. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +321 -0
  60. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
  61. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
  62. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
  63. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
  64. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
  65. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
  66. data/ext/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
  67. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
  68. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
  69. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  70. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
  80. data/ext/itsi_server/src/server/middleware_stack/mod.rs +315 -0
  81. data/ext/itsi_server/src/server/mod.rs +8 -1
  82. data/ext/itsi_server/src/server/process_worker.rs +38 -12
  83. data/ext/itsi_server/src/server/rate_limiter.rs +565 -0
  84. data/ext/itsi_server/src/server/request_job.rs +11 -0
  85. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +119 -42
  86. data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  87. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +256 -111
  88. data/ext/itsi_server/src/server/signal.rs +19 -0
  89. data/ext/itsi_server/src/server/static_file_server.rs +984 -0
  90. data/ext/itsi_server/src/server/thread_worker.rs +139 -94
  91. data/ext/itsi_server/src/server/types.rs +43 -0
  92. data/ext/itsi_tracing/Cargo.toml +1 -0
  93. data/ext/itsi_tracing/src/lib.rs +216 -45
  94. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  95. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  96. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  97. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  98. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  99. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  100. data/lib/itsi/{request.rb → http_request.rb} +29 -5
  101. data/lib/itsi/http_response.rb +39 -0
  102. data/lib/itsi/server/Itsi.rb +11 -19
  103. data/lib/itsi/server/config/dsl.rb +506 -0
  104. data/lib/itsi/server/config.rb +103 -8
  105. data/lib/itsi/server/default_app/default_app.rb +38 -0
  106. data/lib/itsi/server/grpc_interface.rb +213 -0
  107. data/lib/itsi/server/rack/handler/itsi.rb +8 -17
  108. data/lib/itsi/server/rack_interface.rb +23 -4
  109. data/lib/itsi/server/scheduler_interface.rb +1 -1
  110. data/lib/itsi/server/scheduler_mode.rb +4 -0
  111. data/lib/itsi/server/signal_trap.rb +7 -1
  112. data/lib/itsi/server/version.rb +1 -1
  113. data/lib/itsi/server.rb +74 -63
  114. data/lib/itsi/standard_headers.rb +86 -0
  115. metadata +84 -15
  116. data/ext/itsi_scheduler/extconf.rb +0 -6
  117. data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  118. data/ext/itsi_server/src/request/mod.rs +0 -1
  119. data/ext/itsi_server/src/response/mod.rs +0 -1
  120. data/ext/itsi_server/src/server/itsi_server.rs +0 -288
  121. data/lib/itsi/server/options_dsl.rb +0 -401
  122. data/lib/itsi/stream_io.rb +0 -38
  123. /data/lib/itsi/{index.html → server/default_app/index.html} +0 -0
@@ -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
@@ -4,23 +4,14 @@ module Rack
4
4
  module Handler
5
5
  module Itsi
6
6
  def self.run(app, options = {})
7
- ::Itsi::Server.start(
8
- **Itsi::Server::Config.load(
9
- {
10
- app: app,
11
- binds: [
12
- "http://#{
13
- options.fetch(
14
- :host,
15
- "127.0.0.1"
16
- )}:#{
17
- options.fetch(
18
- :Port,
19
- 3001
20
- )}"
21
- ]
22
- }
23
- )
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
24
15
  )
25
16
  end
26
17
  end
@@ -1,6 +1,22 @@
1
1
  module Itsi
2
2
  class Server
3
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
+
4
20
  # Interface to Rack applications.
5
21
  # Here we build the env, and invoke the Rack app's call method.
6
22
  # We then turn the Rack response into something Itsi server understands.
@@ -25,10 +41,13 @@ module Itsi
25
41
  # 2. Set Headers
26
42
  body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack")
27
43
  headers.each do |key, value|
28
- next response.add_header(key, value) unless value.is_a?(Array)
44
+ unless value.is_a?(Array)
45
+ response[key] = value
46
+ next
47
+ end
29
48
 
30
49
  value.each do |v|
31
- response.add_header(key, v)
50
+ response[key] = v
32
51
  end
33
52
  end
34
53
 
@@ -40,7 +59,7 @@ module Itsi
40
59
  # stream this response.
41
60
 
42
61
  if body_streamer
43
- body_streamer.call(StreamIO.new(response))
62
+ body_streamer.call(response)
44
63
 
45
64
  # If we're enumerable with more than one chunk
46
65
  # also stream, otherwise write in a single chunk
@@ -53,7 +72,7 @@ module Itsi
53
72
  # to optimize for the case where there's only one chunk.
54
73
  buffer = nil
55
74
  body.each do |part|
56
- response.send_frame(buffer.to_s) if buffer
75
+ response << buffer.to_s if buffer
57
76
  buffer = part
58
77
  end
59
78
 
@@ -13,7 +13,7 @@ module Itsi
13
13
  # each request is wrapped in a Fiber.
14
14
  def schedule(app, request)
15
15
  Fiber.schedule do
16
- call(app, request)
16
+ app.call(request)
17
17
  end
18
18
  end
19
19
  end
@@ -1,3 +1,7 @@
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.
1
5
  if defined?(ActiveSupport::IsolatedExecutionState) && !ENV["ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL"]
2
6
  Itsi.log_info \
3
7
  "ActiveSupport Isolated Execution state detected. Automatically switching to :fiber mode. "\
@@ -1,6 +1,11 @@
1
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).
2
8
  module SignalTrap
3
-
4
9
  DEFAULT_SIGNALS = ["DEFAULT", "", nil].freeze
5
10
  INTERCEPTED_SIGNALS = ["INT"].freeze
6
11
 
@@ -8,6 +13,7 @@ module Itsi
8
13
  unless INTERCEPTED_SIGNALS.include?(signal.to_s) && block.nil? && Itsi::Server.running?
9
14
  return super(signal, *args, &block)
10
15
  end
16
+
11
17
  Itsi::Server.reset_signal_handlers
12
18
  nil
13
19
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Server
5
- VERSION = "0.1.11"
5
+ VERSION = "0.1.12"
6
6
  end
7
7
  end
data/lib/itsi/server.rb CHANGED
@@ -3,48 +3,14 @@
3
3
  require_relative "server/version"
4
4
  require_relative "server/itsi_server"
5
5
  require_relative "server/rack_interface"
6
- require_relative "server/signal_trap"
6
+ require_relative "server/grpc_interface"
7
7
  require_relative "server/scheduler_interface"
8
+ require_relative "server/signal_trap"
8
9
  require_relative "server/rack/handler/itsi"
9
10
  require_relative "server/config"
10
- require_relative "request"
11
- require_relative "stream_io"
12
-
13
- # When you Run Itsi without a Rack app,
14
- # we start a tiny little echo server, just so you can see it in action.
15
- DEFAULT_INDEX = IO.read("#{__dir__}/index.html").freeze
16
- DEFAULT_BINDS = ["http://0.0.0.0:3000"].freeze
17
- DEFAULT_APP = lambda {
18
- require "json"
19
- require "itsi/scheduler"
20
- Itsi.log_warn "No config.ru or Itsi.rb app detected. Running default app."
21
- lambda do |env|
22
- headers, body = \
23
- if env["itsi.response"].json?
24
- [
25
- { "Content-Type" => "application/json" },
26
- [{ "message" => "You're running on Itsi!", "rack_env" => env,
27
- "version" => Itsi::Server::VERSION }.to_json]
28
- ]
29
- else
30
- [
31
- { "Content-Type" => "text/html" },
32
- [
33
- format(
34
- DEFAULT_INDEX,
35
- REQUEST_METHOD: env["REQUEST_METHOD"],
36
- PATH_INFO: env["PATH_INFO"],
37
- SERVER_NAME: env["SERVER_NAME"],
38
- SERVER_PORT: env["SERVER_PORT"],
39
- REMOTE_ADDR: env["REMOTE_ADDR"],
40
- HTTP_USER_AGENT: env["HTTP_USER_AGENT"]
41
- )
42
- ]
43
- ]
44
- end
45
- [200, headers, body]
46
- end
47
- }
11
+ require_relative "standard_headers"
12
+ require_relative "http_request"
13
+ require_relative "http_response"
48
14
 
49
15
  module Itsi
50
16
  class Server
@@ -56,35 +22,80 @@ module Itsi
56
22
  !!@running
57
23
  end
58
24
 
59
- def build(
60
- app: DEFAULT_APP[],
61
- loader: nil,
62
- binds: DEFAULT_BINDS,
63
- **opts
64
- )
65
- new(app: loader || -> { app }, binds: binds, **opts)
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
66
46
  end
67
47
 
68
- def start_in_background_thread(silence: true, **opts)
69
- start(background: true, silence: silence, **opts)
48
+ def write_pid
49
+ File.write(Itsi::Server::Config.pid_file_path, Process.pid)
70
50
  end
71
51
 
72
- def start(background: false, silence: false, **opts)
73
- build(**opts).tap do |server|
74
- previous_handler = Signal.trap("INT", "DEFAULT")
75
- @running = true
76
- if background
77
- Thread.new do
78
- server.start
79
- @running = false
80
- Signal.trap("INT", previous_handler)
81
- end
82
- else
83
- server.start
84
- @running = false
85
- Signal.trap("INT", previous_handler)
86
- end
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
87
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
97
+
98
+ Process.kill(:INFO, pid)
88
99
  end
89
100
  end
90
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