itsi 0.1.7 → 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 +21 -0
- data/crates/itsi_error/src/from.rs +26 -29
- data/crates/itsi_server/Cargo.lock +2956 -0
- data/crates/itsi_server/Cargo.toml +1 -1
- data/crates/itsi_server/src/lib.rs +5 -0
- data/crates/itsi_server/src/request/itsi_request.rs +37 -9
- data/crates/itsi_server/src/response/itsi_response.rs +12 -2
- data/crates/itsi_server/src/server/bind.rs +4 -3
- data/crates/itsi_server/src/server/itsi_server.rs +128 -78
- data/crates/itsi_server/src/server/listener.rs +98 -107
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +40 -24
- 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 +20 -13
- data/crates/itsi_tracing/src/lib.rs +18 -1
- data/gems/scheduler/Cargo.lock +12 -12
- data/gems/scheduler/ext/itsi_error/src/from.rs +26 -29
- data/gems/scheduler/ext/itsi_server/Cargo.lock +2956 -0
- data/gems/scheduler/ext/itsi_server/Cargo.toml +1 -1
- data/gems/scheduler/ext/itsi_server/src/lib.rs +5 -0
- data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +37 -9
- data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +12 -2
- data/gems/scheduler/ext/itsi_server/src/server/bind.rs +4 -3
- data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +128 -78
- data/gems/scheduler/ext/itsi_server/src/server/listener.rs +98 -107
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +40 -24
- 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 +20 -13
- 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/Cargo.lock +2917 -0
- data/gems/server/Cargo.toml +7 -0
- data/gems/server/Rakefile +8 -1
- data/gems/server/ext/itsi_error/src/from.rs +26 -29
- data/gems/server/ext/itsi_server/Cargo.lock +2956 -0
- data/gems/server/ext/itsi_server/Cargo.toml +1 -1
- data/gems/server/ext/itsi_server/src/lib.rs +5 -0
- data/gems/server/ext/itsi_server/src/request/itsi_request.rs +37 -9
- data/gems/server/ext/itsi_server/src/response/itsi_response.rs +12 -2
- data/gems/server/ext/itsi_server/src/server/bind.rs +4 -3
- data/gems/server/ext/itsi_server/src/server/itsi_server.rs +128 -78
- data/gems/server/ext/itsi_server/src/server/listener.rs +98 -107
- data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +40 -24
- 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 +20 -13
- data/gems/server/ext/itsi_tracing/src/lib.rs +18 -1
- data/gems/server/lib/itsi/index.html +91 -0
- 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/scheduler_mode.rb +1 -1
- 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 +68 -82
- 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 +238 -0
- data/sandbox/deploy/outputs.tf +4 -0
- data/sandbox/deploy/vars.tf +11 -0
- data/sandbox/falcon_benchmark/Gemfile +10 -0
- data/sandbox/falcon_benchmark/Gemfile.lock +140 -0
- data/sandbox/falcon_benchmark/config.ru +54 -0
- data/sandbox/itsi_sandbox_async/Gemfile +10 -0
- data/sandbox/itsi_sandbox_async/Gemfile.lock +69 -0
- data/sandbox/itsi_sandbox_async/config.ru +10 -0
- data/sandbox/itsi_sandbox_hanami/.env +2 -0
- data/sandbox/itsi_sandbox_hanami/.gitignore +6 -0
- data/sandbox/itsi_sandbox_hanami/.rspec +1 -0
- data/sandbox/itsi_sandbox_hanami/Gemfile +49 -0
- data/sandbox/itsi_sandbox_hanami/Gemfile.lock +440 -0
- data/sandbox/itsi_sandbox_hanami/Guardfile +9 -0
- data/sandbox/itsi_sandbox_hanami/Procfile.dev +2 -0
- data/sandbox/itsi_sandbox_hanami/README.md +1 -0
- data/sandbox/itsi_sandbox_hanami/Rakefile +3 -0
- data/sandbox/itsi_sandbox_hanami/app/action.rb +12 -0
- data/sandbox/itsi_sandbox_hanami/app/actions/.keep +0 -0
- data/sandbox/itsi_sandbox_hanami/app/assets/css/app.css +5 -0
- data/sandbox/itsi_sandbox_hanami/app/assets/images/favicon.ico +0 -0
- data/sandbox/itsi_sandbox_hanami/app/assets/js/app.js +1 -0
- data/sandbox/itsi_sandbox_hanami/app/db/relation.rb +10 -0
- data/sandbox/itsi_sandbox_hanami/app/db/repo.rb +10 -0
- data/sandbox/itsi_sandbox_hanami/app/db/struct.rb +10 -0
- data/sandbox/itsi_sandbox_hanami/app/operation.rb +9 -0
- data/sandbox/itsi_sandbox_hanami/app/relations/.keep +0 -0
- data/sandbox/itsi_sandbox_hanami/app/repos/.keep +0 -0
- data/sandbox/itsi_sandbox_hanami/app/structs/.keep +0 -0
- data/sandbox/itsi_sandbox_hanami/app/templates/layouts/app.html.erb +14 -0
- data/sandbox/itsi_sandbox_hanami/app/view.rb +9 -0
- data/sandbox/itsi_sandbox_hanami/app/views/helpers.rb +10 -0
- data/sandbox/itsi_sandbox_hanami/bin/dev +8 -0
- data/sandbox/itsi_sandbox_hanami/config/app.rb +8 -0
- data/sandbox/itsi_sandbox_hanami/config/assets.js +16 -0
- data/sandbox/itsi_sandbox_hanami/config/db/migrate/.keep +0 -0
- data/sandbox/itsi_sandbox_hanami/config/db/seeds.rb +15 -0
- data/sandbox/itsi_sandbox_hanami/config/puma.rb +47 -0
- data/sandbox/itsi_sandbox_hanami/config/routes.rb +7 -0
- data/sandbox/itsi_sandbox_hanami/config/settings.rb +9 -0
- data/sandbox/itsi_sandbox_hanami/config.ru +5 -0
- data/sandbox/itsi_sandbox_hanami/db/.keep +0 -0
- data/sandbox/itsi_sandbox_hanami/lib/itsi_hanami/types.rb +11 -0
- data/sandbox/itsi_sandbox_hanami/lib/tasks/.keep +0 -0
- data/sandbox/itsi_sandbox_hanami/package-lock.json +946 -0
- data/sandbox/itsi_sandbox_hanami/package.json +8 -0
- data/sandbox/itsi_sandbox_hanami/spec/requests/root_spec.rb +11 -0
- data/sandbox/itsi_sandbox_hanami/spec/spec_helper.rb +9 -0
- data/sandbox/itsi_sandbox_hanami/spec/support/db/cleaning.rb +42 -0
- data/sandbox/itsi_sandbox_hanami/spec/support/db.rb +10 -0
- data/sandbox/itsi_sandbox_hanami/spec/support/features.rb +5 -0
- data/sandbox/itsi_sandbox_hanami/spec/support/operations.rb +8 -0
- data/sandbox/itsi_sandbox_hanami/spec/support/requests.rb +13 -0
- data/sandbox/itsi_sandbox_hanami/spec/support/rspec.rb +61 -0
- data/sandbox/itsi_sandbox_rack/Gemfile +17 -0
- data/sandbox/itsi_sandbox_rack/Gemfile.lock +153 -0
- data/sandbox/itsi_sandbox_rack/config.ru +18 -0
- data/sandbox/itsi_sandbox_rack_lint/Gemfile +7 -0
- data/sandbox/itsi_sandbox_rack_lint/Gemfile.lock +27 -0
- data/sandbox/itsi_sandbox_rack_lint/config.ru +3 -0
- data/sandbox/itsi_sandbox_rails/.dockerignore +51 -0
- data/sandbox/itsi_sandbox_rails/.gitattributes +9 -0
- data/sandbox/itsi_sandbox_rails/.github/dependabot.yml +12 -0
- data/sandbox/itsi_sandbox_rails/.github/workflows/ci.yml +90 -0
- data/sandbox/itsi_sandbox_rails/.gitignore +34 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/docker-setup.sample +3 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-app-boot.sample +3 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-deploy.sample +14 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-proxy-reboot.sample +3 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-app-boot.sample +3 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-build.sample +51 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-connect.sample +47 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-deploy.sample +109 -0
- data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-proxy-reboot.sample +3 -0
- data/sandbox/itsi_sandbox_rails/.kamal/secrets +17 -0
- data/sandbox/itsi_sandbox_rails/.rubocop.yml +8 -0
- data/sandbox/itsi_sandbox_rails/.ruby-version +1 -0
- data/sandbox/itsi_sandbox_rails/Dockerfile +72 -0
- data/sandbox/itsi_sandbox_rails/Gemfile +72 -0
- data/sandbox/itsi_sandbox_rails/Gemfile.lock +480 -0
- data/sandbox/itsi_sandbox_rails/README.md +24 -0
- data/sandbox/itsi_sandbox_rails/Rakefile +6 -0
- data/sandbox/itsi_sandbox_rails/app/assets/images/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/app/assets/stylesheets/application.css +10 -0
- data/sandbox/itsi_sandbox_rails/app/controllers/application_controller.rb +4 -0
- data/sandbox/itsi_sandbox_rails/app/controllers/concerns/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/app/controllers/home_controller.rb +51 -0
- data/sandbox/itsi_sandbox_rails/app/controllers/live_controller.rb +41 -0
- data/sandbox/itsi_sandbox_rails/app/controllers/uploads_controller.rb +32 -0
- data/sandbox/itsi_sandbox_rails/app/helpers/application_helper.rb +2 -0
- data/sandbox/itsi_sandbox_rails/app/javascript/application.js +3 -0
- data/sandbox/itsi_sandbox_rails/app/javascript/controllers/application.js +9 -0
- data/sandbox/itsi_sandbox_rails/app/javascript/controllers/hello_controller.js +7 -0
- data/sandbox/itsi_sandbox_rails/app/javascript/controllers/index.js +4 -0
- data/sandbox/itsi_sandbox_rails/app/jobs/application_job.rb +7 -0
- data/sandbox/itsi_sandbox_rails/app/mailers/application_mailer.rb +4 -0
- data/sandbox/itsi_sandbox_rails/app/models/application_record.rb +3 -0
- data/sandbox/itsi_sandbox_rails/app/models/concerns/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/app/models/post.rb +2 -0
- data/sandbox/itsi_sandbox_rails/app/views/layouts/application.html.erb +28 -0
- data/sandbox/itsi_sandbox_rails/app/views/layouts/mailer.html.erb +13 -0
- data/sandbox/itsi_sandbox_rails/app/views/layouts/mailer.text.erb +1 -0
- data/sandbox/itsi_sandbox_rails/app/views/pwa/manifest.json.erb +22 -0
- data/sandbox/itsi_sandbox_rails/app/views/pwa/service-worker.js +26 -0
- data/sandbox/itsi_sandbox_rails/bin/brakeman +7 -0
- data/sandbox/itsi_sandbox_rails/bin/bundle +109 -0
- data/sandbox/itsi_sandbox_rails/bin/dev +2 -0
- data/sandbox/itsi_sandbox_rails/bin/docker-entrypoint +14 -0
- data/sandbox/itsi_sandbox_rails/bin/importmap +4 -0
- data/sandbox/itsi_sandbox_rails/bin/jobs +6 -0
- data/sandbox/itsi_sandbox_rails/bin/kamal +27 -0
- data/sandbox/itsi_sandbox_rails/bin/rails +4 -0
- data/sandbox/itsi_sandbox_rails/bin/rake +4 -0
- data/sandbox/itsi_sandbox_rails/bin/rubocop +8 -0
- data/sandbox/itsi_sandbox_rails/bin/setup +34 -0
- data/sandbox/itsi_sandbox_rails/bin/thrust +5 -0
- data/sandbox/itsi_sandbox_rails/config/application.rb +61 -0
- data/sandbox/itsi_sandbox_rails/config/boot.rb +4 -0
- data/sandbox/itsi_sandbox_rails/config/cable.yml +17 -0
- data/sandbox/itsi_sandbox_rails/config/cache.yml +16 -0
- data/sandbox/itsi_sandbox_rails/config/credentials.yml.enc +1 -0
- data/sandbox/itsi_sandbox_rails/config/database.yml +40 -0
- data/sandbox/itsi_sandbox_rails/config/deploy.yml +116 -0
- data/sandbox/itsi_sandbox_rails/config/environment.rb +5 -0
- data/sandbox/itsi_sandbox_rails/config/environments/development.rb +72 -0
- data/sandbox/itsi_sandbox_rails/config/environments/production.rb +90 -0
- data/sandbox/itsi_sandbox_rails/config/environments/test.rb +53 -0
- data/sandbox/itsi_sandbox_rails/config/importmap.rb +7 -0
- data/sandbox/itsi_sandbox_rails/config/initializers/assets.rb +7 -0
- data/sandbox/itsi_sandbox_rails/config/initializers/content_security_policy.rb +25 -0
- data/sandbox/itsi_sandbox_rails/config/initializers/filter_parameter_logging.rb +8 -0
- data/sandbox/itsi_sandbox_rails/config/initializers/inflections.rb +16 -0
- data/sandbox/itsi_sandbox_rails/config/locales/en.yml +31 -0
- data/sandbox/itsi_sandbox_rails/config/puma.rb +41 -0
- data/sandbox/itsi_sandbox_rails/config/queue.yml +18 -0
- data/sandbox/itsi_sandbox_rails/config/recurring.yml +10 -0
- data/sandbox/itsi_sandbox_rails/config/routes.rb +21 -0
- data/sandbox/itsi_sandbox_rails/config/storage.yml +34 -0
- data/sandbox/itsi_sandbox_rails/config.ru +7 -0
- data/sandbox/itsi_sandbox_rails/db/cable_schema.rb +11 -0
- data/sandbox/itsi_sandbox_rails/db/cache_schema.rb +14 -0
- data/sandbox/itsi_sandbox_rails/db/migrate/20250301041554_create_posts.rb +10 -0
- data/sandbox/itsi_sandbox_rails/db/queue_schema.rb +129 -0
- data/sandbox/itsi_sandbox_rails/db/schema.rb +23 -0
- data/sandbox/itsi_sandbox_rails/db/seeds.rb +9 -0
- data/sandbox/itsi_sandbox_rails/lib/tasks/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/log/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/public/400.html +114 -0
- data/sandbox/itsi_sandbox_rails/public/404.html +114 -0
- data/sandbox/itsi_sandbox_rails/public/406-unsupported-browser.html +114 -0
- data/sandbox/itsi_sandbox_rails/public/422.html +114 -0
- data/sandbox/itsi_sandbox_rails/public/500.html +114 -0
- data/sandbox/itsi_sandbox_rails/public/icon.png +0 -0
- data/sandbox/itsi_sandbox_rails/public/icon.svg +3 -0
- data/sandbox/itsi_sandbox_rails/public/robots.txt +1 -0
- data/sandbox/itsi_sandbox_rails/script/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/storage/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/application_system_test_case.rb +5 -0
- data/sandbox/itsi_sandbox_rails/test/controllers/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/fixtures/files/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/fixtures/posts.yml +9 -0
- data/sandbox/itsi_sandbox_rails/test/helpers/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/integration/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/mailers/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/models/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/models/post_test.rb +7 -0
- data/sandbox/itsi_sandbox_rails/test/system/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/test/test_helper.rb +15 -0
- data/sandbox/itsi_sandbox_rails/tmp/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/tmp/pids/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/tmp/storage/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/vendor/.keep +0 -0
- data/sandbox/itsi_sandbox_rails/vendor/javascript/.keep +0 -0
- data/sandbox/itsi_sandbox_roda/Gemfile +5 -0
- data/sandbox/itsi_sandbox_roda/Gemfile.lock +44 -0
- data/sandbox/itsi_sandbox_roda/config.ru +39 -0
- data/sandbox/itsi_sinatra/Gemfile +9 -0
- data/sandbox/itsi_sinatra/Gemfile.lock +81 -0
- data/sandbox/itsi_sinatra/app.rb +9 -0
- data/sandbox/pebble/docker-compose.yml +11 -0
- data/tasks.txt +4 -4
- metadata +199 -10
- data/gems/server/lib/itsi/signals.rb +0 -23
- data/gems/server/test/test_helper.rb +0 -7
@@ -1,17 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "stringio"
|
4
|
+
require "socket"
|
5
|
+
|
3
6
|
module Itsi
|
4
7
|
class Request
|
5
|
-
require "stringio"
|
6
|
-
require "socket"
|
7
|
-
|
8
8
|
attr_accessor :hijacked
|
9
9
|
|
10
|
-
def
|
10
|
+
def to_rack_env
|
11
11
|
path = self.path
|
12
12
|
host = self.host
|
13
13
|
version = self.version
|
14
|
-
body = self.body
|
15
14
|
{
|
16
15
|
"SERVER_SOFTWARE" => "Itsi",
|
17
16
|
"SCRIPT_NAME" => script_name,
|
@@ -25,31 +24,40 @@ module Itsi
|
|
25
24
|
"HTTP_HOST" => host,
|
26
25
|
"SERVER_PROTOCOL" => version,
|
27
26
|
"HTTP_VERSION" => version,
|
27
|
+
"itsi.request" => self,
|
28
|
+
"itsi.response" => response,
|
28
29
|
"rack.version" => [version],
|
29
30
|
"rack.url_scheme" => scheme,
|
30
|
-
"rack.input" =>
|
31
|
-
case body
|
32
|
-
when Array then File.open(body.first, "rb")
|
33
|
-
when String then StringIO.new(body)
|
34
|
-
else body
|
35
|
-
end,
|
31
|
+
"rack.input" => build_input_io,
|
36
32
|
"rack.errors" => $stderr,
|
37
33
|
"rack.multithread" => true,
|
38
34
|
"rack.multiprocess" => true,
|
39
35
|
"rack.run_once" => false,
|
40
36
|
"rack.hijack?" => true,
|
41
37
|
"rack.multipart.buffer_size" => 16_384,
|
42
|
-
"rack.hijack" =>
|
43
|
-
self.hijacked = true
|
44
|
-
UNIXSocket.pair.yield_self do |(server_sock, app_sock)|
|
45
|
-
response.hijack(server_sock.fileno)
|
46
|
-
server_sock.sync = true
|
47
|
-
app_sock.sync = true
|
48
|
-
app_sock.instance_variable_set("@server_sock", server_sock)
|
49
|
-
app_sock
|
50
|
-
end
|
51
|
-
end
|
38
|
+
"rack.hijack" => build_hijack_proc
|
52
39
|
}.tap { |r| headers.each { |(k, v)| r[k] = v } }
|
53
40
|
end
|
41
|
+
|
42
|
+
def build_hijack_proc
|
43
|
+
lambda do
|
44
|
+
self.hijacked = true
|
45
|
+
UNIXSocket.pair.yield_self do |(server_sock, app_sock)|
|
46
|
+
response.hijack(server_sock.fileno)
|
47
|
+
server_sock.sync = true
|
48
|
+
app_sock.sync = true
|
49
|
+
app_sock.instance_variable_set("@server_sock", server_sock)
|
50
|
+
app_sock
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_input_io
|
56
|
+
case body
|
57
|
+
when Array then File.open(body.first, "rb")
|
58
|
+
when String then StringIO.new(body)
|
59
|
+
else body
|
60
|
+
end
|
61
|
+
end
|
54
62
|
end
|
55
63
|
end
|
@@ -3,13 +3,12 @@ return unless defined?(::Rackup::Handler) || defined?(Rack::Handler)
|
|
3
3
|
module Rack
|
4
4
|
module Handler
|
5
5
|
module Itsi
|
6
|
-
|
7
6
|
def self.run(app, options = {})
|
8
7
|
::Itsi::Server.new(
|
9
|
-
app: ->{ app },
|
10
|
-
binds: ["
|
8
|
+
app: -> { app },
|
9
|
+
binds: ["http://#{options.fetch(:host, "127.0.0.1")}:#{options.fetch(:Port, 3001)}"],
|
11
10
|
workers: options.fetch(:workers, 1),
|
12
|
-
threads: options.fetch(:threads, 1)
|
11
|
+
threads: options.fetch(:threads, 1)
|
13
12
|
).start
|
14
13
|
end
|
15
14
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Itsi
|
2
|
+
class Server
|
3
|
+
module RackInterface
|
4
|
+
# Interface to Rack applications.
|
5
|
+
# Here we build the env, and invoke the Rack app's call method.
|
6
|
+
# We then turn the Rack response into something Itsi server understands.
|
7
|
+
def call(app, request)
|
8
|
+
respond request, app.call(request.to_rack_env)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Itsi responses are asynchronous and can be streamed.
|
12
|
+
# Response chunks are sent using response.send_frame
|
13
|
+
# and the response is finished using response.close_write.
|
14
|
+
# If only a single chunk is written, you can use the #send_and_close method.
|
15
|
+
def respond(request, (status, headers, body))
|
16
|
+
response = request.response
|
17
|
+
|
18
|
+
# Don't try and respond if we've been hijacked.
|
19
|
+
# The hijacker is now responsible for this.
|
20
|
+
return if request.hijacked
|
21
|
+
|
22
|
+
# 1. Set Status
|
23
|
+
response.status = status
|
24
|
+
|
25
|
+
# 2. Set Headers
|
26
|
+
body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack")
|
27
|
+
headers.each do |key, value|
|
28
|
+
next response.add_header(key, value) unless value.is_a?(Array)
|
29
|
+
|
30
|
+
value.each do |v|
|
31
|
+
response.add_header(key, v)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# 3. Set Body
|
36
|
+
# As soon as we start setting the response
|
37
|
+
# the server will begin to stream it to the client.
|
38
|
+
|
39
|
+
# If we're partially hijacked or returned a streaming body,
|
40
|
+
# stream this response.
|
41
|
+
|
42
|
+
if body_streamer
|
43
|
+
body_streamer.call(StreamIO.new(response))
|
44
|
+
|
45
|
+
# If we're enumerable with more than one chunk
|
46
|
+
# also stream, otherwise write in a single chunk
|
47
|
+
elsif body.respond_to?(:each) || body.respond_to?(:to_ary)
|
48
|
+
unless body.respond_to?(:each)
|
49
|
+
body = body.to_ary
|
50
|
+
raise "Body #to_ary didn't return an array" unless body.is_a?(Array)
|
51
|
+
end
|
52
|
+
# We offset this iteration intentionally,
|
53
|
+
# to optimize for the case where there's only one chunk.
|
54
|
+
buffer = nil
|
55
|
+
body.each do |part|
|
56
|
+
response.send_frame(buffer.to_s) if buffer
|
57
|
+
buffer = part
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
response.send_and_close(buffer.to_s)
|
62
|
+
rescue StandardError
|
63
|
+
binding.b
|
64
|
+
end
|
65
|
+
else
|
66
|
+
response.send_and_close(body.to_s)
|
67
|
+
end
|
68
|
+
ensure
|
69
|
+
response.close_write
|
70
|
+
body.close if body.respond_to?(:close)
|
71
|
+
end
|
72
|
+
|
73
|
+
# A streaming body is one that responds to #call and not #each.
|
74
|
+
def streaming_body?(body)
|
75
|
+
body.respond_to?(:call) && !body.respond_to?(:each)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
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
|
+
call(app, request)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
if defined?(ActiveSupport::IsolatedExecutionState) && !ENV["ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL"]
|
2
2
|
Itsi.log_info \
|
3
3
|
"ActiveSupport Isolated Execution state detected. Automatically switching to :fiber mode. "\
|
4
|
-
"
|
4
|
+
"Set ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL to disable this behavior"
|
5
5
|
ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
|
6
6
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Itsi
|
2
|
+
module SignalTrap
|
3
|
+
|
4
|
+
DEFAULT_SIGNALS = ["DEFAULT", "", nil].freeze
|
5
|
+
INTERCEPTED_SIGNALS = ["INT"].freeze
|
6
|
+
|
7
|
+
def trap(signal, *args, &block)
|
8
|
+
unless INTERCEPTED_SIGNALS.include?(signal.to_s) && block.nil? && Itsi::Server.running?
|
9
|
+
return super(signal, *args, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
Itsi::Server.reset_signal_handlers
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
[Kernel, Signal].each do |receiver|
|
19
|
+
receiver.singleton_class.prepend(Itsi::SignalTrap)
|
20
|
+
end
|
21
|
+
|
22
|
+
[Object].each do |receiver|
|
23
|
+
receiver.include(Itsi::SignalTrap)
|
24
|
+
end
|
@@ -2,99 +2,85 @@
|
|
2
2
|
|
3
3
|
require_relative "server/version"
|
4
4
|
require_relative "server/itsi_server"
|
5
|
-
require_relative "
|
5
|
+
require_relative "server/rack_interface"
|
6
|
+
require_relative "server/signal_trap"
|
7
|
+
require_relative "server/scheduler_interface"
|
8
|
+
require_relative "server/rack/handler/itsi"
|
6
9
|
require_relative "request"
|
7
10
|
require_relative "stream_io"
|
8
|
-
|
11
|
+
|
12
|
+
# When you Run Itsi without a Rack app,
|
13
|
+
# we start a tiny
|
14
|
+
DEFAULT_INDEX = IO.read("#{__dir__}/index.html").freeze
|
15
|
+
DEFAULT_BINDS = ["http://0.0.0.0:3000"].freeze
|
16
|
+
DEFAULT_APP = lambda {
|
17
|
+
require "json"
|
18
|
+
lambda do |env|
|
19
|
+
headers, body = \
|
20
|
+
if env["itsi.response"].json?
|
21
|
+
[
|
22
|
+
{ "Content-Type" => "application/json" },
|
23
|
+
[{ "message" => "You're running on Itsi!", "rack_env" => env,
|
24
|
+
"version" => Itsi::Server::VERSION }.to_json]
|
25
|
+
]
|
26
|
+
else
|
27
|
+
[
|
28
|
+
{ "Content-Type" => "text/html" },
|
29
|
+
[
|
30
|
+
format(
|
31
|
+
DEFAULT_INDEX,
|
32
|
+
REQUEST_METHOD: env["REQUEST_METHOD"],
|
33
|
+
PATH_INFO: env["PATH_INFO"],
|
34
|
+
SERVER_NAME: env["SERVER_NAME"],
|
35
|
+
SERVER_PORT: env["SERVER_PORT"],
|
36
|
+
REMOTE_ADDR: env["REMOTE_ADDR"],
|
37
|
+
HTTP_USER_AGENT: env["HTTP_USER_AGENT"]
|
38
|
+
)
|
39
|
+
]
|
40
|
+
]
|
41
|
+
end
|
42
|
+
[200, headers, body]
|
43
|
+
end
|
44
|
+
}
|
9
45
|
|
10
46
|
module Itsi
|
11
47
|
class Server
|
48
|
+
extend RackInterface
|
49
|
+
extend SchedulerInterface
|
12
50
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def self.start(app:, **opts)
|
18
|
-
server = new(app: ->{app}, **opts)
|
19
|
-
@running = true
|
20
|
-
Signal.trap('INT', 'DEFAULT')
|
21
|
-
server.start
|
22
|
-
ensure
|
23
|
-
@running = false
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.call(app, request)
|
27
|
-
respond request, app.call(request.to_env)
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.streaming_body?(body)
|
31
|
-
body.respond_to?(:call) && !body.respond_to?(:each)
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.respond(request, (status, headers, body))
|
35
|
-
response = request.response
|
36
|
-
|
37
|
-
# Don't try and respond if we've been hijacked.
|
38
|
-
# The hijacker is now responsible for this.
|
39
|
-
return if request.hijacked
|
40
|
-
|
41
|
-
# 1. Set Status
|
42
|
-
response.status = status
|
43
|
-
|
44
|
-
# 2. Set Headers
|
45
|
-
headers.each do |key, value|
|
46
|
-
next response.add_header(key, value) unless value.is_a?(Array)
|
47
|
-
|
48
|
-
value.each do |v|
|
49
|
-
response.add_header(key, v)
|
50
|
-
end
|
51
|
+
class << self
|
52
|
+
def running?
|
53
|
+
!!@running
|
51
54
|
end
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
if (body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack"))
|
61
|
-
body_streamer.call(StreamIO.new(response))
|
62
|
-
|
63
|
-
# If we're enumerable with more than one chunk
|
64
|
-
# also stream, otherwise write in a single chunk
|
65
|
-
elsif body.respond_to?(:each) || body.respond_to?(:to_ary)
|
66
|
-
unless body.respond_to?(:each)
|
67
|
-
body = body.to_ary
|
68
|
-
raise "Body #to_ary didn't return an array" unless body.is_a?(Array)
|
69
|
-
end
|
70
|
-
# We offset this iteration intentionally,
|
71
|
-
# to optimize for the case where there's only one chunk.
|
72
|
-
buffer = nil
|
73
|
-
body.each do |part|
|
74
|
-
response.send_frame(buffer.to_s) if buffer
|
75
|
-
buffer = part
|
76
|
-
end
|
77
|
-
|
78
|
-
response.send_and_close(buffer.to_s)
|
79
|
-
else
|
80
|
-
response.send_and_close(body.to_s)
|
56
|
+
def build(
|
57
|
+
app: DEFAULT_APP[],
|
58
|
+
binds: DEFAULT_BINDS,
|
59
|
+
**opts
|
60
|
+
)
|
61
|
+
new(app: -> { app }, binds: binds, **opts)
|
81
62
|
end
|
82
|
-
ensure
|
83
|
-
response.close_write
|
84
|
-
body.close if body.respond_to?(:close)
|
85
|
-
end
|
86
63
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
[scheduler, Fiber.schedule(&scheduler_task)]
|
91
|
-
end
|
64
|
+
def start_in_background_thread(silence: true, **opts)
|
65
|
+
start(background: true, silence: silence, **opts)
|
66
|
+
end
|
92
67
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
68
|
+
def start(background: false, **opts)
|
69
|
+
build(**opts).tap do |server|
|
70
|
+
previous_handler = Signal.trap("INT", "DEFAULT")
|
71
|
+
@running = true
|
72
|
+
if background
|
73
|
+
Thread.new do
|
74
|
+
server.start
|
75
|
+
@running = false
|
76
|
+
Signal.trap("INT", previous_handler)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
server.start
|
80
|
+
@running = false
|
81
|
+
Signal.trap("INT", previous_handler)
|
82
|
+
end
|
83
|
+
end
|
98
84
|
end
|
99
85
|
end
|
100
86
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest/reporters"
|
4
|
+
require "itsi/server"
|
5
|
+
require "itsi/scheduler"
|
6
|
+
|
7
|
+
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
|
8
|
+
|
9
|
+
def free_bind
|
10
|
+
server = TCPServer.new("0.0.0.0", 0)
|
11
|
+
port = server.addr[1]
|
12
|
+
server.close
|
13
|
+
"http://0.0.0.0:#{port}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def run_app(app, **opts)
|
17
|
+
bind = free_bind
|
18
|
+
server = Itsi::Server.start_in_background_thread(
|
19
|
+
app: app,
|
20
|
+
binds: [bind],
|
21
|
+
**opts
|
22
|
+
)
|
23
|
+
|
24
|
+
sleep 0.1
|
25
|
+
yield URI(bind), server
|
26
|
+
ensure
|
27
|
+
server&.stop
|
28
|
+
end
|