itsi 0.2.26 → 0.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Cargo.lock +7 -3
- data/README.md +4 -1
- data/Rakefile +24 -3
- data/crates/itsi_acme/Cargo.toml +2 -1
- data/crates/itsi_acme/src/acceptor.rs +1 -1
- data/crates/itsi_acme/src/acme.rs +31 -3
- data/crates/itsi_acme/src/http_challenge.rs +81 -0
- data/crates/itsi_acme/src/https_helper.rs +3 -1
- data/crates/itsi_acme/src/jose.rs +6 -2
- data/crates/itsi_acme/src/lib.rs +2 -0
- data/crates/itsi_acme/src/resolver.rs +27 -4
- data/crates/itsi_acme/src/state.rs +183 -22
- data/crates/itsi_scheduler/Cargo.toml +1 -1
- data/crates/itsi_scheduler/src/itsi_scheduler.rs +115 -64
- data/crates/itsi_scheduler/src/lib.rs +2 -1
- data/crates/itsi_server/Cargo.toml +2 -1
- data/crates/itsi_server/src/lib.rs +15 -0
- data/crates/itsi_server/src/ruby_types/itsi_grpc_call.rs +0 -1
- data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +9 -0
- data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +114 -4
- data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +22 -1
- data/crates/itsi_server/src/ruby_types/itsi_server.rs +100 -0
- data/crates/itsi_server/src/server/binds/listener.rs +9 -24
- data/crates/itsi_server/src/server/binds/tls.rs +372 -67
- data/crates/itsi_server/src/server/signal.rs +14 -9
- data/crates/itsi_server/src/services/itsi_http_service.rs +46 -2
- data/docs/content/features/_index.md +3 -0
- data/docs/content/getting_started/_index.md +6 -2
- data/docs/content/getting_started/local_development.md +13 -0
- data/docs/content/itsi_scheduler/_index.md +3 -0
- data/gems/scheduler/Cargo.lock +4011 -527
- data/gems/scheduler/Gemfile +8 -2
- data/gems/scheduler/Gemfile.lock +107 -0
- data/gems/scheduler/Rakefile +33 -9
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/lib/itsi/scheduler.rb +121 -6
- data/gems/scheduler/test/helpers/test_helper.rb +2 -0
- data/gems/scheduler/test/test_address_resolve.rb +8 -2
- data/gems/scheduler/test/test_itsi_scheduler.rb +80 -0
- data/gems/scheduler/test/test_timeout_after.rb +102 -0
- data/gems/server/Cargo.lock +30 -1
- data/gems/server/Gemfile +3 -0
- data/gems/server/Gemfile.lock +195 -0
- data/gems/server/Rakefile +18 -5
- data/gems/server/lib/itsi/http_request.rb +10 -0
- data/gems/server/lib/itsi/server/config/options/certificates.md +37 -9
- data/gems/server/lib/itsi/server/config/options/fiber_scheduler.md +2 -0
- data/gems/server/lib/itsi/server/default_config/Itsi.rb +4 -0
- data/gems/server/lib/itsi/server/rack_interface.rb +47 -3
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +24 -0
- data/gems/server/test/acme/local_acme_challenges.rb +190 -0
- data/gems/server/test/helpers/local_acme.rb +218 -0
- data/gems/server/test/helpers/test_helper.rb +7 -9
- data/gems/server/test/middleware/endpoint.rb +9 -6
- data/gems/server/test/rack/test_rack_server.rb +144 -0
- data/lib/itsi/version.rb +1 -1
- metadata +12 -6
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
require "net/http"
|
|
6
|
+
require "openssl"
|
|
7
|
+
require "rbconfig"
|
|
8
|
+
require "shellwords"
|
|
9
|
+
require "timeout"
|
|
10
|
+
require "tmpdir"
|
|
11
|
+
|
|
12
|
+
class LocalAcmeAuthority
|
|
13
|
+
PEBBLE_VERSION = "v2.10.1"
|
|
14
|
+
|
|
15
|
+
attr_reader :directory_url, :cache_dir, :http_port, :tls_port
|
|
16
|
+
|
|
17
|
+
def self.available?
|
|
18
|
+
system("go version > /dev/null 2>&1")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@workspace = Dir.mktmpdir("itsi-local-acme-")
|
|
23
|
+
@bin_dir = File.join(@workspace, "bin")
|
|
24
|
+
@cache_dir = File.join(@workspace, "cache")
|
|
25
|
+
FileUtils.mkdir_p(@bin_dir)
|
|
26
|
+
FileUtils.mkdir_p(@cache_dir)
|
|
27
|
+
@pids = []
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def start
|
|
31
|
+
install_binaries
|
|
32
|
+
resolve_module_paths
|
|
33
|
+
|
|
34
|
+
@acme_port = free_port
|
|
35
|
+
@management_port = free_port
|
|
36
|
+
@dns_port = free_port
|
|
37
|
+
@dns_management_port = free_port
|
|
38
|
+
@http_port = free_port
|
|
39
|
+
@tls_port = free_port
|
|
40
|
+
|
|
41
|
+
write_pebble_config
|
|
42
|
+
start_challtestsrv
|
|
43
|
+
start_pebble
|
|
44
|
+
|
|
45
|
+
@directory_url = "https://localhost:#{@acme_port}/dir"
|
|
46
|
+
wait_for_https(@directory_url, @pebble_api_ca_path)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def stop
|
|
50
|
+
@pids.reverse_each do |pid|
|
|
51
|
+
begin
|
|
52
|
+
Process.kill("TERM", pid)
|
|
53
|
+
rescue Errno::ESRCH
|
|
54
|
+
next
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
Timeout.timeout(5) { Process.wait(pid) }
|
|
59
|
+
rescue Timeout::Error
|
|
60
|
+
begin
|
|
61
|
+
Process.kill("KILL", pid)
|
|
62
|
+
rescue Errno::ESRCH
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
begin
|
|
66
|
+
Process.wait(pid)
|
|
67
|
+
rescue Errno::ECHILD
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
rescue Errno::ECHILD
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
FileUtils.remove_entry(@workspace) if File.exist?(@workspace)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def env
|
|
78
|
+
{
|
|
79
|
+
"ITSI_ACME_DIRECTORY_URL" => @directory_url,
|
|
80
|
+
"ITSI_ACME_CA_PEM_PATH" => @pebble_api_ca_path,
|
|
81
|
+
"ITSI_ACME_CACHE_DIR" => @cache_dir
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def install_binaries
|
|
88
|
+
install_go_binary("github.com/letsencrypt/pebble/v2/cmd/pebble", "pebble")
|
|
89
|
+
install_go_binary("github.com/letsencrypt/pebble/v2/cmd/pebble-challtestsrv", "pebble-challtestsrv")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def install_go_binary(package_name, binary_name)
|
|
93
|
+
destination = File.join(@bin_dir, binary_name)
|
|
94
|
+
return if File.exist?(destination)
|
|
95
|
+
|
|
96
|
+
system(
|
|
97
|
+
{
|
|
98
|
+
"GOBIN" => @bin_dir
|
|
99
|
+
},
|
|
100
|
+
"go", "install", "#{package_name}@#{PEBBLE_VERSION}",
|
|
101
|
+
exception: true
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def resolve_module_paths
|
|
106
|
+
module_json = JSON.parse(`go mod download -json github.com/letsencrypt/pebble/v2@#{PEBBLE_VERSION}`)
|
|
107
|
+
@pebble_module_dir = module_json.fetch("Dir")
|
|
108
|
+
@pebble_api_ca_path = File.join(@pebble_module_dir, "test/certs/pebble.minica.pem")
|
|
109
|
+
@pebble_tls_cert_path = File.join(@pebble_module_dir, "test/certs/localhost/cert.pem")
|
|
110
|
+
@pebble_tls_key_path = File.join(@pebble_module_dir, "test/certs/localhost/key.pem")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def write_pebble_config
|
|
114
|
+
@pebble_config_path = File.join(@workspace, "pebble-config.json")
|
|
115
|
+
File.write(
|
|
116
|
+
@pebble_config_path,
|
|
117
|
+
JSON.pretty_generate(
|
|
118
|
+
{
|
|
119
|
+
pebble: {
|
|
120
|
+
listenAddress: "0.0.0.0:#{@acme_port}",
|
|
121
|
+
managementListenAddress: "0.0.0.0:#{@management_port}",
|
|
122
|
+
certificate: @pebble_tls_cert_path,
|
|
123
|
+
privateKey: @pebble_tls_key_path,
|
|
124
|
+
httpPort: @http_port,
|
|
125
|
+
tlsPort: @tls_port,
|
|
126
|
+
ocspResponderURL: "",
|
|
127
|
+
externalAccountBindingRequired: false,
|
|
128
|
+
domainBlocklist: [],
|
|
129
|
+
retryAfter: {
|
|
130
|
+
authz: 1,
|
|
131
|
+
order: 1
|
|
132
|
+
},
|
|
133
|
+
keyAlgorithm: "ecdsa"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def start_challtestsrv
|
|
141
|
+
spawn_process(
|
|
142
|
+
[
|
|
143
|
+
File.join(@bin_dir, "pebble-challtestsrv"),
|
|
144
|
+
"-dnsserver", "127.0.0.1:#{@dns_port}",
|
|
145
|
+
"-management", "127.0.0.1:#{@dns_management_port}",
|
|
146
|
+
"-http01", "",
|
|
147
|
+
"-https01", "",
|
|
148
|
+
"-tlsalpn01", "",
|
|
149
|
+
"-defaultIPv4", "127.0.0.1",
|
|
150
|
+
"-defaultIPv6", ""
|
|
151
|
+
],
|
|
152
|
+
"challtestsrv"
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def start_pebble
|
|
157
|
+
spawn_process(
|
|
158
|
+
[
|
|
159
|
+
File.join(@bin_dir, "pebble"),
|
|
160
|
+
"-config", @pebble_config_path,
|
|
161
|
+
"-dnsserver", "127.0.0.1:#{@dns_port}",
|
|
162
|
+
"-strict=false"
|
|
163
|
+
],
|
|
164
|
+
"pebble",
|
|
165
|
+
{
|
|
166
|
+
"PEBBLE_VA_NOSLEEP" => "1",
|
|
167
|
+
"PEBBLE_WFE_NONCEREJECT" => "0",
|
|
168
|
+
"PEBBLE_AUTHZREUSE" => "0"
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def spawn_process(command, name, env = {})
|
|
174
|
+
log_path = File.join(@workspace, "#{name}.log")
|
|
175
|
+
log = File.open(log_path, "w")
|
|
176
|
+
pid = Process.spawn(
|
|
177
|
+
env,
|
|
178
|
+
*command,
|
|
179
|
+
out: log,
|
|
180
|
+
err: log
|
|
181
|
+
)
|
|
182
|
+
@pids << pid
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def free_port
|
|
186
|
+
server = TCPServer.new("127.0.0.1", 0)
|
|
187
|
+
port = server.addr[1]
|
|
188
|
+
server.close
|
|
189
|
+
port
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def wait_for_https(url, ca_file)
|
|
193
|
+
uri = URI(url)
|
|
194
|
+
store = OpenSSL::X509::Store.new
|
|
195
|
+
store.add_file(ca_file)
|
|
196
|
+
|
|
197
|
+
Timeout.timeout(20) do
|
|
198
|
+
loop do
|
|
199
|
+
begin
|
|
200
|
+
Net::HTTP.start(
|
|
201
|
+
uri.host,
|
|
202
|
+
uri.port,
|
|
203
|
+
use_ssl: true,
|
|
204
|
+
cert_store: store,
|
|
205
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER,
|
|
206
|
+
open_timeout: 1,
|
|
207
|
+
read_timeout: 1
|
|
208
|
+
) do |http|
|
|
209
|
+
response = http.get(uri.request_uri)
|
|
210
|
+
return if response.code.to_i < 500
|
|
211
|
+
end
|
|
212
|
+
rescue StandardError
|
|
213
|
+
sleep 0.1
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
ENV["ITSI_LOG"] = "off"
|
|
4
4
|
|
|
5
|
+
Warning[:experimental] = false if Warning.respond_to?(:[]=)
|
|
6
|
+
|
|
5
7
|
require "minitest/reporters"
|
|
6
8
|
require "rackup"
|
|
7
9
|
require "itsi/server"
|
|
@@ -26,12 +28,12 @@ def free_bind(protocol = "http", unix_socket: false)
|
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def server(
|
|
29
|
-
app: nil, app_with_lint: nil, protocol: "http", bind: free_bind(protocol), itsi_rb: nil, cleanup: true,
|
|
31
|
+
app: nil, app_with_lint: nil, protocol: "http", bind: free_bind(protocol), binds: nil, itsi_rb: nil, cleanup: true,
|
|
30
32
|
&blk)
|
|
31
33
|
app ||= Rack::Lint.new(app_with_lint) if app_with_lint
|
|
32
34
|
|
|
33
35
|
cli_params = {}
|
|
34
|
-
cli_params[:binds] = [bind] if bind
|
|
36
|
+
cli_params[:binds] = binds || [bind] if binds || bind
|
|
35
37
|
|
|
36
38
|
sync = Queue.new
|
|
37
39
|
cli_params[:hooks] ||= {}
|
|
@@ -40,10 +42,10 @@ def server(
|
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
Itsi::Server.start_in_background_thread(cli_params) do
|
|
43
|
-
bind bind
|
|
45
|
+
bind bind if bind && binds.nil?
|
|
44
46
|
workers 1
|
|
45
47
|
threads 1
|
|
46
|
-
log_level :
|
|
48
|
+
log_level :error
|
|
47
49
|
run app if app
|
|
48
50
|
instance_exec(&itsi_rb) if itsi_rb
|
|
49
51
|
end
|
|
@@ -51,11 +53,6 @@ def server(
|
|
|
51
53
|
sync.pop
|
|
52
54
|
uri = URI(bind)
|
|
53
55
|
RequestContext.new(uri, self).instance_exec(uri, &blk)
|
|
54
|
-
rescue StandardError => e
|
|
55
|
-
puts e
|
|
56
|
-
# puts e.message
|
|
57
|
-
# puts e.backtrace.join("\n")
|
|
58
|
-
raise
|
|
59
56
|
ensure
|
|
60
57
|
Itsi::Server.stop_background_threads if cleanup
|
|
61
58
|
end
|
|
@@ -157,6 +154,7 @@ class RequestContext
|
|
|
157
154
|
@uri.host,
|
|
158
155
|
@uri.port,
|
|
159
156
|
use_ssl: @uri.scheme == "https",
|
|
157
|
+
verify_mode: (@uri.scheme == "https" ? OpenSSL::SSL::VERIFY_NONE : nil),
|
|
160
158
|
**opts
|
|
161
159
|
)
|
|
162
160
|
end
|
|
@@ -118,14 +118,17 @@ class TestEndpoint < Minitest::Test
|
|
|
118
118
|
|
|
119
119
|
# 8. Exceptions inside handler produce 500
|
|
120
120
|
def test_internal_error_returns_500
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
response = nil
|
|
122
|
+
capture_subprocess_io do
|
|
123
|
+
server(itsi_rb: lambda do
|
|
124
|
+
endpoint "/boom" do |_req|
|
|
125
|
+
raise "test crash"
|
|
126
|
+
end
|
|
127
|
+
end) do
|
|
128
|
+
response = get_resp("/boom")
|
|
124
129
|
end
|
|
125
|
-
end) do
|
|
126
|
-
res = get_resp("/boom")
|
|
127
|
-
assert_equal "500", res.code
|
|
128
130
|
end
|
|
131
|
+
assert_equal "500", response.code
|
|
129
132
|
end
|
|
130
133
|
|
|
131
134
|
# 9. Controller method dispatch via symbol
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
require_relative "../helpers/test_helper"
|
|
2
|
+
require "async/websocket/adapters/rack"
|
|
3
|
+
require "protocol/websocket/connection"
|
|
4
|
+
require "base64"
|
|
2
5
|
|
|
3
6
|
class TestRackServer < Minitest::Test
|
|
4
7
|
def test_that_it_has_a_version_number
|
|
@@ -66,6 +69,115 @@ class TestRackServer < Minitest::Test
|
|
|
66
69
|
end
|
|
67
70
|
end
|
|
68
71
|
|
|
72
|
+
def test_partial_hijack_yields_writable_stream
|
|
73
|
+
stream_class = nil
|
|
74
|
+
stream_closed = nil
|
|
75
|
+
required_methods_ok = nil
|
|
76
|
+
|
|
77
|
+
server(app_with_lint: lambda do |_env|
|
|
78
|
+
[200, { "content-type" => "text/plain", "rack.hijack" => lambda { |stream|
|
|
79
|
+
stream_class = stream.class
|
|
80
|
+
refute stream.closed?
|
|
81
|
+
required_methods_ok = %i[read write << flush close close_read close_write closed?]
|
|
82
|
+
.all? { |method_name| stream.respond_to?(method_name) }
|
|
83
|
+
stream.write("Hello")
|
|
84
|
+
stream.write(", World!")
|
|
85
|
+
stream.close
|
|
86
|
+
stream_closed = stream.closed?
|
|
87
|
+
} }, []]
|
|
88
|
+
end) do
|
|
89
|
+
assert_equal "Hello, World!", get("/")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
assert_equal Rack::Lint::Wrapper::StreamWrapper, stream_class
|
|
93
|
+
assert_equal true, required_methods_ok
|
|
94
|
+
assert_equal true, stream_closed
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_partial_hijack_upgrade_supports_bidirectional_io
|
|
98
|
+
callback_error = Queue.new
|
|
99
|
+
stream_details = Queue.new
|
|
100
|
+
|
|
101
|
+
server(app: lambda do |_env|
|
|
102
|
+
[101, {
|
|
103
|
+
"connection" => "Upgrade",
|
|
104
|
+
"upgrade" => "test",
|
|
105
|
+
"rack.hijack" => lambda { |stream|
|
|
106
|
+
stream_details << [stream.class, stream.is_a?(IO), stream.method(:read).arity]
|
|
107
|
+
Thread.new do
|
|
108
|
+
begin
|
|
109
|
+
payload = stream.read(2)
|
|
110
|
+
stream.write("echo:#{payload}")
|
|
111
|
+
rescue => e
|
|
112
|
+
callback_error << e
|
|
113
|
+
ensure
|
|
114
|
+
stream.close rescue nil
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
}
|
|
118
|
+
}, []]
|
|
119
|
+
end) do |uri|
|
|
120
|
+
socket = TCPSocket.new(uri.host, uri.port)
|
|
121
|
+
socket.write(
|
|
122
|
+
"GET / HTTP/1.1\r\n" \
|
|
123
|
+
"Host: #{uri.host}:#{uri.port}\r\n" \
|
|
124
|
+
"Connection: Upgrade\r\n" \
|
|
125
|
+
"Upgrade: test\r\n" \
|
|
126
|
+
"\r\n"
|
|
127
|
+
)
|
|
128
|
+
socket.flush
|
|
129
|
+
|
|
130
|
+
response = +""
|
|
131
|
+
until response.include?("\r\n\r\n")
|
|
132
|
+
response << socket.readpartial(1024)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
assert_includes response, "101"
|
|
136
|
+
socket.write("hi")
|
|
137
|
+
socket.flush
|
|
138
|
+
echoed = socket.readpartial(7)
|
|
139
|
+
assert_equal "echo:hi", echoed
|
|
140
|
+
ensure
|
|
141
|
+
socket&.close
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
raise callback_error.pop unless callback_error.empty?
|
|
145
|
+
stream_class, stream_is_io, read_arity = stream_details.pop
|
|
146
|
+
assert_equal UNIXSocket, stream_class
|
|
147
|
+
assert_equal true, stream_is_io
|
|
148
|
+
assert_operator read_arity, :!=, 0
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_async_websocket_rack_adapter_can_upgrade_and_echo_messages
|
|
152
|
+
callback_error = Queue.new
|
|
153
|
+
|
|
154
|
+
server(app_with_lint: lambda do |env|
|
|
155
|
+
Async::WebSocket::Adapters::Rack.open(env) do |connection|
|
|
156
|
+
begin
|
|
157
|
+
message = connection.read
|
|
158
|
+
connection.write(message)
|
|
159
|
+
connection.flush
|
|
160
|
+
rescue => e
|
|
161
|
+
callback_error << e
|
|
162
|
+
end
|
|
163
|
+
end || [200, { "content-type" => "text/plain" }, ["ok"]]
|
|
164
|
+
end) do |uri|
|
|
165
|
+
socket = websocket_upgrade_socket(uri)
|
|
166
|
+
connection = websocket_client_connection(socket)
|
|
167
|
+
|
|
168
|
+
connection.write("hello")
|
|
169
|
+
connection.flush
|
|
170
|
+
|
|
171
|
+
message = connection.read
|
|
172
|
+
assert_equal "hello", message.to_str
|
|
173
|
+
ensure
|
|
174
|
+
connection&.close rescue nil
|
|
175
|
+
socket&.close
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
raise callback_error.pop unless callback_error.empty?
|
|
179
|
+
end
|
|
180
|
+
|
|
69
181
|
def test_enumerable_body
|
|
70
182
|
server(app_with_lint: lambda do |env|
|
|
71
183
|
[200, { "content-type" => "application/json" },
|
|
@@ -424,4 +536,36 @@ class TestRackServer < Minitest::Test
|
|
|
424
536
|
assert_equal %w[a=b c=d], resp.get_fields("set-cookie")
|
|
425
537
|
end
|
|
426
538
|
end
|
|
539
|
+
|
|
540
|
+
private
|
|
541
|
+
|
|
542
|
+
def websocket_upgrade_socket(uri)
|
|
543
|
+
socket = TCPSocket.new(uri.host, uri.port)
|
|
544
|
+
socket.write(
|
|
545
|
+
"GET / HTTP/1.1\r\n" \
|
|
546
|
+
"Host: #{uri.host}:#{uri.port}\r\n" \
|
|
547
|
+
"Connection: Upgrade\r\n" \
|
|
548
|
+
"Upgrade: websocket\r\n" \
|
|
549
|
+
"Sec-WebSocket-Version: 13\r\n" \
|
|
550
|
+
"Sec-WebSocket-Key: #{Base64.strict_encode64(SecureRandom.random_bytes(16))}\r\n" \
|
|
551
|
+
"\r\n"
|
|
552
|
+
)
|
|
553
|
+
socket.flush
|
|
554
|
+
|
|
555
|
+
response = +""
|
|
556
|
+
until response.include?("\r\n\r\n")
|
|
557
|
+
chunk = socket.read(1)
|
|
558
|
+
raise EOFError, "Unexpected EOF during websocket handshake" unless chunk
|
|
559
|
+
|
|
560
|
+
response << chunk
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
assert_includes response, "101"
|
|
564
|
+
socket
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def websocket_client_connection(socket)
|
|
568
|
+
framer = Protocol::WebSocket::Framer.new(socket)
|
|
569
|
+
Protocol::WebSocket::Connection.new(framer, mask: "\x01\x02\x03\x04")
|
|
570
|
+
end
|
|
427
571
|
end
|
data/lib/itsi/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: itsi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.27
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Wouter Coppieters
|
|
@@ -15,28 +15,28 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.2.
|
|
18
|
+
version: 0.2.27
|
|
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.2.
|
|
25
|
+
version: 0.2.27
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
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.2.
|
|
32
|
+
version: 0.2.27
|
|
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.2.
|
|
39
|
+
version: 0.2.27
|
|
40
40
|
description: Wrapper Gem for both the Itsi server and the Itsi Fiber scheduler
|
|
41
41
|
email:
|
|
42
42
|
- wc@pico.net.nz
|
|
@@ -74,6 +74,7 @@ files:
|
|
|
74
74
|
- crates/itsi_acme/src/caches/no.rs
|
|
75
75
|
- crates/itsi_acme/src/caches/test.rs
|
|
76
76
|
- crates/itsi_acme/src/config.rs
|
|
77
|
+
- crates/itsi_acme/src/http_challenge.rs
|
|
77
78
|
- crates/itsi_acme/src/https_helper.rs
|
|
78
79
|
- crates/itsi_acme/src/incoming.rs
|
|
79
80
|
- crates/itsi_acme/src/jose.rs
|
|
@@ -768,6 +769,7 @@ files:
|
|
|
768
769
|
- gems/scheduler/Cargo.lock
|
|
769
770
|
- gems/scheduler/Cargo.toml
|
|
770
771
|
- gems/scheduler/Gemfile
|
|
772
|
+
- gems/scheduler/Gemfile.lock
|
|
771
773
|
- gems/scheduler/Rakefile
|
|
772
774
|
- gems/scheduler/bin/console
|
|
773
775
|
- gems/scheduler/bin/setup
|
|
@@ -787,11 +789,13 @@ files:
|
|
|
787
789
|
- gems/scheduler/test/test_nested_fibers.rb
|
|
788
790
|
- gems/scheduler/test/test_network_io.rb
|
|
789
791
|
- gems/scheduler/test/test_process_wait.rb
|
|
792
|
+
- gems/scheduler/test/test_timeout_after.rb
|
|
790
793
|
- gems/server/.gitignore
|
|
791
794
|
- gems/server/.rubocop.yml
|
|
792
795
|
- gems/server/Cargo.lock
|
|
793
796
|
- gems/server/Cargo.toml
|
|
794
797
|
- gems/server/Gemfile
|
|
798
|
+
- gems/server/Gemfile.lock
|
|
795
799
|
- gems/server/Rakefile
|
|
796
800
|
- gems/server/bin/console
|
|
797
801
|
- gems/server/bin/setup
|
|
@@ -1084,6 +1088,8 @@ files:
|
|
|
1084
1088
|
- gems/server/lib/itsi/standard_headers.rb
|
|
1085
1089
|
- gems/server/lib/ruby_lsp/itsi/addon.rb
|
|
1086
1090
|
- gems/server/lib/shell_completions/completions.rb
|
|
1091
|
+
- gems/server/test/acme/local_acme_challenges.rb
|
|
1092
|
+
- gems/server/test/helpers/local_acme.rb
|
|
1087
1093
|
- gems/server/test/helpers/test_helper.rb
|
|
1088
1094
|
- gems/server/test/middleware/allow_list.rb
|
|
1089
1095
|
- gems/server/test/middleware/auth_api_key.rb
|
|
@@ -1155,7 +1161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
1155
1161
|
- !ruby/object:Gem::Version
|
|
1156
1162
|
version: '3.2'
|
|
1157
1163
|
requirements: []
|
|
1158
|
-
rubygems_version:
|
|
1164
|
+
rubygems_version: 3.6.9
|
|
1159
1165
|
specification_version: 4
|
|
1160
1166
|
summary: Wrapper Gem for both the Itsi server and the Itsi Fiber scheduler
|
|
1161
1167
|
test_files: []
|