itsi 0.1.8 → 0.1.9

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -0
  3. data/crates/itsi_server/src/lib.rs +5 -0
  4. data/crates/itsi_server/src/request/itsi_request.rs +30 -2
  5. data/crates/itsi_server/src/response/itsi_response.rs +12 -2
  6. data/crates/itsi_server/src/server/itsi_server.rs +127 -70
  7. data/crates/itsi_server/src/server/listener.rs +1 -1
  8. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +18 -12
  9. data/crates/itsi_server/src/server/signal.rs +7 -0
  10. data/crates/itsi_server/src/server/thread_worker.rs +3 -4
  11. data/crates/itsi_server/src/server/tls.rs +11 -8
  12. data/crates/itsi_tracing/src/lib.rs +18 -1
  13. data/gems/scheduler/Cargo.lock +12 -12
  14. data/gems/scheduler/ext/itsi_server/src/lib.rs +5 -0
  15. data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +30 -2
  16. data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +12 -2
  17. data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +127 -70
  18. data/gems/scheduler/ext/itsi_server/src/server/listener.rs +1 -1
  19. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +18 -12
  20. data/gems/scheduler/ext/itsi_server/src/server/signal.rs +7 -0
  21. data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +3 -4
  22. data/gems/scheduler/ext/itsi_server/src/server/tls.rs +11 -8
  23. data/gems/scheduler/ext/itsi_tracing/src/lib.rs +18 -1
  24. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  25. data/gems/scheduler/test/test_address_resolve.rb +0 -1
  26. data/gems/scheduler/test/test_file_io.rb +0 -1
  27. data/gems/scheduler/test/test_kernel_sleep.rb +3 -4
  28. data/gems/server/Rakefile +8 -1
  29. data/gems/server/ext/itsi_server/src/lib.rs +5 -0
  30. data/gems/server/ext/itsi_server/src/request/itsi_request.rs +30 -2
  31. data/gems/server/ext/itsi_server/src/response/itsi_response.rs +12 -2
  32. data/gems/server/ext/itsi_server/src/server/itsi_server.rs +127 -70
  33. data/gems/server/ext/itsi_server/src/server/listener.rs +1 -1
  34. data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +18 -12
  35. data/gems/server/ext/itsi_server/src/server/signal.rs +7 -0
  36. data/gems/server/ext/itsi_server/src/server/thread_worker.rs +3 -4
  37. data/gems/server/ext/itsi_server/src/server/tls.rs +11 -8
  38. data/gems/server/ext/itsi_tracing/src/lib.rs +18 -1
  39. data/gems/server/lib/itsi/request.rb +29 -21
  40. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -4
  41. data/gems/server/lib/itsi/server/rack_interface.rb +79 -0
  42. data/gems/server/lib/itsi/server/scheduler_interface.rb +21 -0
  43. data/gems/server/lib/itsi/server/signal_trap.rb +24 -0
  44. data/gems/server/lib/itsi/server/version.rb +1 -1
  45. data/gems/server/lib/itsi/server.rb +67 -101
  46. data/gems/server/test/helpers/test_helper.rb +28 -0
  47. data/gems/server/test/test_itsi_server.rb +275 -3
  48. data/lib/itsi/version.rb +1 -1
  49. data/sandbox/deploy/main.tf +1 -0
  50. data/sandbox/itsi_sandbox_rack/Gemfile.lock +2 -2
  51. data/tasks.txt +0 -6
  52. metadata +13 -11
  53. data/gems/server/lib/itsi/signals.rb +0 -23
  54. data/gems/server/test/test_helper.rb +0 -7
  55. /data/gems/server/lib/itsi/{index.html.erb → index.html} +0 -0
@@ -1,9 +1,281 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "test_helper"
1
+ require "socket"
2
+ require "net/http"
3
+ require "minitest/autorun"
4
4
 
5
5
  class TestItsiServer < Minitest::Test
6
6
  def test_that_it_has_a_version_number
7
7
  refute_nil ::Itsi::Server::VERSION
8
8
  end
9
+
10
+ def test_hello_world
11
+ run_app(lambda do |env|
12
+ [200, { "Content-Type" => "text/plain" }, ["Hello, World!"]]
13
+ end) do |uri|
14
+ assert_equal "Hello, World!", Net::HTTP.get(uri)
15
+ end
16
+ end
17
+
18
+ def test_post
19
+ run_app(lambda do |env|
20
+ assert_equal env["REQUEST_METHOD"], "POST"
21
+ assert_equal "data", env["rack.input"].read
22
+ [200, { "Content-Type" => "text/plain" }, ["Hello, World!"]]
23
+ end) do |uri|
24
+ assert_equal "Hello, World!", Net::HTTP.post(uri, "data").body
25
+ end
26
+ end
27
+
28
+ def test_full_hijack
29
+ run_app(lambda do |env|
30
+ io = env["rack.hijack"].call
31
+ io.write("HTTP/1.1 200 Ok\r\n")
32
+ io.write("Content-Type: text/plain\r\n")
33
+ io.write("Transfer-Encoding: chunked\r\n")
34
+ io.write("\r\n")
35
+ io.write("7\r\n")
36
+ io.write("Hello, \r\n")
37
+ io.write("6\r\n")
38
+ io.write("World!\r\n")
39
+ io.write("0\r\n\r\n")
40
+ io.close
41
+ end) do |uri|
42
+ assert_equal "Hello, World!", Net::HTTP.get(uri)
43
+ end
44
+ end
45
+
46
+ def test_streaming_body
47
+ run_app(lambda do |env|
48
+ [200, { "Content-Type" => "text/plain" }, lambda { |stream|
49
+ stream.write("Hello")
50
+ stream.write(", World!")
51
+ stream.close
52
+ }]
53
+ end) do |uri|
54
+ assert_equal "Hello, World!", Net::HTTP.get(uri)
55
+ end
56
+ end
57
+
58
+ def test_partial_hijack
59
+ run_app(lambda do |env|
60
+ [200, { "Content-Type" => "text/plain", "rack.hijack" => lambda { |stream|
61
+ stream.write("Hello")
62
+ stream.write(", World!")
63
+ stream.close
64
+ } }, []]
65
+ end) do |uri|
66
+ assert_equal "Hello, World!", Net::HTTP.get(uri)
67
+ end
68
+ end
69
+
70
+ def test_enumerable_body
71
+ run_app(lambda do |env|
72
+ [200, { "Content-Type" => "application/json" },
73
+ %W[one\n two\n three\n]]
74
+ end) do |uri|
75
+ assert_equal "one\ntwo\nthree\n", Net::HTTP.get(uri)
76
+ end
77
+ end
78
+
79
+ def test_scheduler_non_blocking
80
+ run_app(
81
+ lambda do |env|
82
+ sleep 0.25
83
+ [200, { "Content-Type" => "text/plain" }, "Hello, World!"]
84
+ end,
85
+ scheduler_class: "Itsi::Scheduler"
86
+ ) do |uri|
87
+ start_time = Time.now
88
+ 20.times.map do
89
+ Thread.new do
90
+ assert_equal "Hello, World!", Net::HTTP.get(uri)
91
+ end
92
+ end.each(&:join)
93
+ assert_in_delta 0.25, Time.now - start_time, 0.5
94
+ end
95
+ end
96
+
97
+ def test_query_params
98
+ run_app(lambda do |env|
99
+ [200, { "Content-Type" => "text/plain" }, [env["QUERY_STRING"]]]
100
+ end) do |uri|
101
+ uri.query = "foo=bar&baz=qux"
102
+ assert_equal "foo=bar&baz=qux", Net::HTTP.get(uri)
103
+ end
104
+ end
105
+
106
+ def test_put_request
107
+ run_app(lambda do |env|
108
+ body = env["rack.input"].read
109
+ [200, { "Content-Type" => "text/plain" }, [body]]
110
+ end) do |uri|
111
+ uri_obj = URI(uri)
112
+ req = Net::HTTP::Put.new(uri_obj)
113
+ req.body = "put data"
114
+ response = Net::HTTP.start(uri_obj.hostname, uri_obj.port) { |http| http.request(req) }
115
+ assert_equal "put data", response.body
116
+ end
117
+ end
118
+
119
+ def test_custom_headers
120
+ run_app(lambda do |env|
121
+ header = env["HTTP_X_CUSTOM"] || ""
122
+ [200, { "Content-Type" => "text/plain" }, [header]]
123
+ end) do |uri|
124
+ uri_obj = URI(uri)
125
+ req = Net::HTTP::Get.new(uri_obj)
126
+ req["X-Custom"] = "custom-value"
127
+ response = Net::HTTP.start(uri_obj.hostname, uri_obj.port) { |http| http.request(req) }
128
+ assert_equal "custom-value", response.body
129
+ end
130
+ end
131
+
132
+ def test_error_response
133
+ response = nil
134
+ capture_subprocess_io do
135
+ run_app(lambda do |env|
136
+ raise "Intentional error for testing"
137
+ end) do |uri|
138
+ response = Net::HTTP.get_response(uri)
139
+ end
140
+ end
141
+ assert_equal "500", response.code
142
+ end
143
+
144
+ def test_redirect
145
+ run_app(lambda do |env|
146
+ [302, { "Location" => "http://example.com" }, []]
147
+ end) do |uri|
148
+ response = Net::HTTP.get_response(uri)
149
+ assert_equal "302", response.code
150
+ assert_equal "http://example.com", response["location"]
151
+ end
152
+ end
153
+
154
+ def test_not_found
155
+ run_app(lambda do |env|
156
+ if env["PATH_INFO"] == "/"
157
+ [200, { "Content-Type" => "text/plain" }, ["Home"]]
158
+ else
159
+ [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
160
+ end
161
+ end) do |uri|
162
+ uri.path = "/nonexistent"
163
+ response = Net::HTTP.get_response(uri)
164
+ assert_equal "404", response.code
165
+ assert_equal "Not Found", response.body
166
+ end
167
+ end
168
+
169
+ def test_head_request
170
+ run_app(lambda do |env|
171
+ [200, { "Content-Type" => "text/plain", "Content-Length" => "13" }, ["Hello, World!"]]
172
+ end) do |uri|
173
+ uri_obj = URI(uri)
174
+ response = Net::HTTP.start(uri_obj.hostname, uri_obj.port) do |http|
175
+ http.head("/")
176
+ end
177
+ assert_equal "200", response.code
178
+ assert_empty response.body.to_s
179
+ assert_equal "13", response["content-length"]
180
+ end
181
+ end
182
+
183
+ def test_options_request
184
+ run_app(lambda do |env|
185
+ [200, { "Allow" => "GET,POST,OPTIONS", "Content-Type" => "text/plain" }, ["Options Response"]]
186
+ end) do |uri|
187
+ uri_obj = URI(uri)
188
+ req = Net::HTTP::Options.new(uri_obj)
189
+ response = Net::HTTP.start(uri_obj.hostname, uri_obj.port) { |http| http.request(req) }
190
+ assert_equal "200", response.code
191
+ assert_equal "GET,POST,OPTIONS", response["allow"]
192
+ assert_equal "Options Response", response.body
193
+ end
194
+ end
195
+
196
+ def test_cookie_handling
197
+ run_app(lambda do |env|
198
+ [200, { "Content-Type" => "text/plain", "Set-Cookie" => "session=abc123; Path=/" }, ["Cookie Test"]]
199
+ end) do |uri|
200
+ response = Net::HTTP.get_response(uri)
201
+ assert_equal "200", response.code
202
+ assert_match(/session=abc123/, response["set-cookie"])
203
+ assert_equal "Cookie Test", response.body
204
+ end
205
+ end
206
+
207
+ def test_multiple_headers
208
+ run_app(lambda do |env|
209
+ [200, { "Content-Type" => "text/plain", "X-Example" => "one, two, three" }, ["Multiple Headers"]]
210
+ end) do |uri|
211
+ response = Net::HTTP.get_response(uri)
212
+ assert_equal "200", response.code
213
+ assert_equal "one, two, three", response["x-example"]
214
+ assert_equal "Multiple Headers", response.body
215
+ end
216
+ end
217
+
218
+ def test_large_body
219
+ large_text = "A" * 10_000
220
+ run_app(lambda do |env|
221
+ [200, { "Content-Type" => "text/plain", "Content-Length" => large_text.bytesize.to_s }, [large_text]]
222
+ end) do |uri|
223
+ response = Net::HTTP.get_response(uri)
224
+ assert_equal "200", response.code
225
+ assert_equal large_text, response.body
226
+ end
227
+ end
228
+
229
+ def test_custom_status_code
230
+ run_app(lambda do |env|
231
+ [201, { "Content-Type" => "text/plain" }, ["Created"]]
232
+ end) do |uri|
233
+ response = Net::HTTP.get_response(uri)
234
+ assert_equal "201", response.code
235
+ assert_equal "Created", response.body
236
+ end
237
+ end
238
+
239
+ def test_empty_body
240
+ run_app(lambda do |env|
241
+ [204, { "Content-Type" => "text/plain" }, []]
242
+ end) do |uri|
243
+ response = Net::HTTP.get_response(uri)
244
+ assert_equal "204", response.code
245
+ assert_nil response.body
246
+ end
247
+ end
248
+
249
+ def test_utf8_response
250
+ utf8_text = "こんにちは世界"
251
+ run_app(lambda do |env|
252
+ [200, { "Content-Type" => "text/plain; charset=utf-8" }, [utf8_text]]
253
+ end) do |uri|
254
+ response = Net::HTTP.get_response(uri)
255
+ assert_equal "200", response.code
256
+ assert_equal utf8_text, response.body.force_encoding("UTF-8")
257
+ end
258
+ end
259
+
260
+ def test_custom_request_header
261
+ run_app(lambda do |env|
262
+ header_value = env["HTTP_X_MY_HEADER"] || ""
263
+ [200, { "Content-Type" => "text/plain" }, [header_value]]
264
+ end) do |uri|
265
+ uri_obj = URI(uri)
266
+ req = Net::HTTP::Get.new(uri_obj)
267
+ req["X-My-Header"] = "test-header"
268
+ response = Net::HTTP.start(uri_obj.hostname, uri_obj.port) { |http| http.request(req) }
269
+ assert_equal "test-header", response.body
270
+ end
271
+ end
272
+
273
+ def test_url_encoded_query_params
274
+ run_app(lambda do |env|
275
+ [200, { "Content-Type" => "text/plain" }, [env["QUERY_STRING"]]]
276
+ end) do |uri|
277
+ uri.query = "param=%C3%A9" # %C3%A9 represents 'é'
278
+ assert_equal "param=%C3%A9", Net::HTTP.get(uri)
279
+ end
280
+ end
9
281
  end
data/lib/itsi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Itsi
2
- VERSION = "0.1.8"
2
+ VERSION = '0.1.9'
3
3
  end
@@ -160,6 +160,7 @@ apt-get install zlib1g-dev libyaml-dev libssl-dev libffi-dev libgmp3-dev libclan
160
160
  apt-get clean && rm -rf /var/lib/apt/lists/* && apt-get autoremove -y
161
161
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
162
162
  curl https://mise.run | sh
163
+ export HOME=/root
163
164
  echo "eval \"\$(/root/.local/bin/mise activate bash)\"" >> ~/.bashrc
164
165
  eval "$(/root/.local/bin/mise activate bash)"
165
166
  mise use ruby@3.4.2
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: ../../gems/scheduler
3
3
  specs:
4
- itsi-scheduler (0.1.7)
4
+ itsi-scheduler (0.1.8)
5
5
  rb_sys (~> 0.9.91)
6
6
 
7
7
  PATH
8
8
  remote: ../../gems/server
9
9
  specs:
10
- itsi-server (0.1.7)
10
+ itsi-server (0.1.8)
11
11
  rack (>= 1.6)
12
12
  rb_sys (~> 0.9.91)
13
13
 
data/tasks.txt CHANGED
@@ -1,9 +1,3 @@
1
- - Fix dependency lock issue
2
- - Check Iodine PostgreSQL fork support
3
-
4
-
5
- - Support adding let's encrypt email to cert string
6
- - Support single domain (not domain(s)).
7
1
  - Test on MOD
8
2
  - Filters (Medium)
9
3
  - Static File Serve (Medium).error pages
metadata CHANGED
@@ -1,42 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itsi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-15 00:00:00.000000000 Z
10
+ date: 2025-03-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: itsi-server
13
+ name: itsi-scheduler
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.1.8
18
+ version: 0.1.9
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.1.8
25
+ version: 0.1.9
26
26
  - !ruby/object:Gem::Dependency
27
- name: itsi-scheduler
27
+ name: itsi-server
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.1.8
32
+ version: 0.1.9
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.1.8
39
+ version: 0.1.9
40
40
  description: Wrapper Gem for both the Itsi server and it's Fiber scheduler
41
41
  email:
42
42
  - wc@pico.net.nz
@@ -227,16 +227,18 @@ files:
227
227
  - gems/server/ext/itsi_tracing/Cargo.toml
228
228
  - gems/server/ext/itsi_tracing/src/lib.rs
229
229
  - gems/server/itsi-server.gemspec
230
- - gems/server/lib/itsi/index.html.erb
230
+ - gems/server/lib/itsi/index.html
231
231
  - gems/server/lib/itsi/request.rb
232
232
  - gems/server/lib/itsi/server.rb
233
233
  - gems/server/lib/itsi/server/rack/handler/itsi.rb
234
+ - gems/server/lib/itsi/server/rack_interface.rb
235
+ - gems/server/lib/itsi/server/scheduler_interface.rb
234
236
  - gems/server/lib/itsi/server/scheduler_mode.rb
237
+ - gems/server/lib/itsi/server/signal_trap.rb
235
238
  - gems/server/lib/itsi/server/version.rb
236
- - gems/server/lib/itsi/signals.rb
237
239
  - gems/server/lib/itsi/stream_io.rb
238
240
  - gems/server/sig/itsi_server.rbs
239
- - gems/server/test/test_helper.rb
241
+ - gems/server/test/helpers/test_helper.rb
240
242
  - gems/server/test/test_itsi_server.rb
241
243
  - lib/itsi.rb
242
244
  - lib/itsi/version.rb
@@ -1,23 +0,0 @@
1
- module Itsi
2
- module Signals
3
- DEFAULT_SIGNALS = ["DEFAULT", ""].freeze
4
- module SignalTrap
5
- def self.trap(signal, *args, &block)
6
- if DEFAULT_SIGNALS.include?(command.to_s) && block.nil?
7
- Itsi::Server.reset_signal_handlers
8
- nil
9
- else
10
- super(signal, *args, &block)
11
- end
12
- end
13
- end
14
- end
15
- end
16
-
17
- [Kernel, Signal].each do |receiver|
18
- receiver.singleton_class.prepend(Itsi::Signals::SignalTrap)
19
- end
20
-
21
- [Object].each do |receiver|
22
- receiver.include(Itsi::Signals::SignalTrap)
23
- end
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
- $LOAD_PATH.unshift __dir__
5
- require "itsi/server"
6
-
7
- require "minitest/autorun"