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.
- checksums.yaml +4 -4
- data/Rakefile +1 -0
- data/crates/itsi_server/src/lib.rs +5 -0
- data/crates/itsi_server/src/request/itsi_request.rs +30 -2
- data/crates/itsi_server/src/response/itsi_response.rs +12 -2
- data/crates/itsi_server/src/server/itsi_server.rs +127 -70
- data/crates/itsi_server/src/server/listener.rs +1 -1
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +18 -12
- data/crates/itsi_server/src/server/signal.rs +7 -0
- data/crates/itsi_server/src/server/thread_worker.rs +3 -4
- data/crates/itsi_server/src/server/tls.rs +11 -8
- data/crates/itsi_tracing/src/lib.rs +18 -1
- data/gems/scheduler/Cargo.lock +12 -12
- data/gems/scheduler/ext/itsi_server/src/lib.rs +5 -0
- data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +30 -2
- data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +12 -2
- data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +127 -70
- data/gems/scheduler/ext/itsi_server/src/server/listener.rs +1 -1
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +18 -12
- data/gems/scheduler/ext/itsi_server/src/server/signal.rs +7 -0
- data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +3 -4
- data/gems/scheduler/ext/itsi_server/src/server/tls.rs +11 -8
- data/gems/scheduler/ext/itsi_tracing/src/lib.rs +18 -1
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/test/test_address_resolve.rb +0 -1
- data/gems/scheduler/test/test_file_io.rb +0 -1
- data/gems/scheduler/test/test_kernel_sleep.rb +3 -4
- data/gems/server/Rakefile +8 -1
- data/gems/server/ext/itsi_server/src/lib.rs +5 -0
- data/gems/server/ext/itsi_server/src/request/itsi_request.rs +30 -2
- data/gems/server/ext/itsi_server/src/response/itsi_response.rs +12 -2
- data/gems/server/ext/itsi_server/src/server/itsi_server.rs +127 -70
- data/gems/server/ext/itsi_server/src/server/listener.rs +1 -1
- data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +18 -12
- data/gems/server/ext/itsi_server/src/server/signal.rs +7 -0
- data/gems/server/ext/itsi_server/src/server/thread_worker.rs +3 -4
- data/gems/server/ext/itsi_server/src/server/tls.rs +11 -8
- data/gems/server/ext/itsi_tracing/src/lib.rs +18 -1
- data/gems/server/lib/itsi/request.rb +29 -21
- data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -4
- data/gems/server/lib/itsi/server/rack_interface.rb +79 -0
- data/gems/server/lib/itsi/server/scheduler_interface.rb +21 -0
- data/gems/server/lib/itsi/server/signal_trap.rb +24 -0
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +67 -101
- data/gems/server/test/helpers/test_helper.rb +28 -0
- data/gems/server/test/test_itsi_server.rb +275 -3
- data/lib/itsi/version.rb +1 -1
- data/sandbox/deploy/main.tf +1 -0
- data/sandbox/itsi_sandbox_rack/Gemfile.lock +2 -2
- data/tasks.txt +0 -6
- metadata +13 -11
- data/gems/server/lib/itsi/signals.rb +0 -23
- data/gems/server/test/test_helper.rb +0 -7
- /data/gems/server/lib/itsi/{index.html.erb → index.html} +0 -0
@@ -1,9 +1,281 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
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
data/sandbox/deploy/main.tf
CHANGED
@@ -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.
|
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.
|
10
|
+
itsi-server (0.1.8)
|
11
11
|
rack (>= 1.6)
|
12
12
|
rb_sys (~> 0.9.91)
|
13
13
|
|
data/tasks.txt
CHANGED
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.
|
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-
|
10
|
+
date: 2025-03-16 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
|
-
name: itsi-
|
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.
|
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.
|
25
|
+
version: 0.1.9
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
|
-
name: itsi-
|
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.
|
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.
|
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
|
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
|
File without changes
|