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.
Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +21 -0
  3. data/crates/itsi_error/src/from.rs +26 -29
  4. data/crates/itsi_server/Cargo.lock +2956 -0
  5. data/crates/itsi_server/Cargo.toml +1 -1
  6. data/crates/itsi_server/src/lib.rs +5 -0
  7. data/crates/itsi_server/src/request/itsi_request.rs +37 -9
  8. data/crates/itsi_server/src/response/itsi_response.rs +12 -2
  9. data/crates/itsi_server/src/server/bind.rs +4 -3
  10. data/crates/itsi_server/src/server/itsi_server.rs +128 -78
  11. data/crates/itsi_server/src/server/listener.rs +98 -107
  12. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +40 -24
  13. data/crates/itsi_server/src/server/signal.rs +7 -0
  14. data/crates/itsi_server/src/server/thread_worker.rs +3 -4
  15. data/crates/itsi_server/src/server/tls.rs +20 -13
  16. data/crates/itsi_tracing/src/lib.rs +18 -1
  17. data/gems/scheduler/Cargo.lock +12 -12
  18. data/gems/scheduler/ext/itsi_error/src/from.rs +26 -29
  19. data/gems/scheduler/ext/itsi_server/Cargo.lock +2956 -0
  20. data/gems/scheduler/ext/itsi_server/Cargo.toml +1 -1
  21. data/gems/scheduler/ext/itsi_server/src/lib.rs +5 -0
  22. data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +37 -9
  23. data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +12 -2
  24. data/gems/scheduler/ext/itsi_server/src/server/bind.rs +4 -3
  25. data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +128 -78
  26. data/gems/scheduler/ext/itsi_server/src/server/listener.rs +98 -107
  27. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +40 -24
  28. data/gems/scheduler/ext/itsi_server/src/server/signal.rs +7 -0
  29. data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +3 -4
  30. data/gems/scheduler/ext/itsi_server/src/server/tls.rs +20 -13
  31. data/gems/scheduler/ext/itsi_tracing/src/lib.rs +18 -1
  32. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  33. data/gems/scheduler/test/test_address_resolve.rb +0 -1
  34. data/gems/scheduler/test/test_file_io.rb +0 -1
  35. data/gems/scheduler/test/test_kernel_sleep.rb +3 -4
  36. data/gems/server/Cargo.lock +2917 -0
  37. data/gems/server/Cargo.toml +7 -0
  38. data/gems/server/Rakefile +8 -1
  39. data/gems/server/ext/itsi_error/src/from.rs +26 -29
  40. data/gems/server/ext/itsi_server/Cargo.lock +2956 -0
  41. data/gems/server/ext/itsi_server/Cargo.toml +1 -1
  42. data/gems/server/ext/itsi_server/src/lib.rs +5 -0
  43. data/gems/server/ext/itsi_server/src/request/itsi_request.rs +37 -9
  44. data/gems/server/ext/itsi_server/src/response/itsi_response.rs +12 -2
  45. data/gems/server/ext/itsi_server/src/server/bind.rs +4 -3
  46. data/gems/server/ext/itsi_server/src/server/itsi_server.rs +128 -78
  47. data/gems/server/ext/itsi_server/src/server/listener.rs +98 -107
  48. data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +40 -24
  49. data/gems/server/ext/itsi_server/src/server/signal.rs +7 -0
  50. data/gems/server/ext/itsi_server/src/server/thread_worker.rs +3 -4
  51. data/gems/server/ext/itsi_server/src/server/tls.rs +20 -13
  52. data/gems/server/ext/itsi_tracing/src/lib.rs +18 -1
  53. data/gems/server/lib/itsi/index.html +91 -0
  54. data/gems/server/lib/itsi/request.rb +29 -21
  55. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -4
  56. data/gems/server/lib/itsi/server/rack_interface.rb +79 -0
  57. data/gems/server/lib/itsi/server/scheduler_interface.rb +21 -0
  58. data/gems/server/lib/itsi/server/scheduler_mode.rb +1 -1
  59. data/gems/server/lib/itsi/server/signal_trap.rb +24 -0
  60. data/gems/server/lib/itsi/server/version.rb +1 -1
  61. data/gems/server/lib/itsi/server.rb +68 -82
  62. data/gems/server/test/helpers/test_helper.rb +28 -0
  63. data/gems/server/test/test_itsi_server.rb +275 -3
  64. data/lib/itsi/version.rb +1 -1
  65. data/sandbox/deploy/main.tf +238 -0
  66. data/sandbox/deploy/outputs.tf +4 -0
  67. data/sandbox/deploy/vars.tf +11 -0
  68. data/sandbox/falcon_benchmark/Gemfile +10 -0
  69. data/sandbox/falcon_benchmark/Gemfile.lock +140 -0
  70. data/sandbox/falcon_benchmark/config.ru +54 -0
  71. data/sandbox/itsi_sandbox_async/Gemfile +10 -0
  72. data/sandbox/itsi_sandbox_async/Gemfile.lock +69 -0
  73. data/sandbox/itsi_sandbox_async/config.ru +10 -0
  74. data/sandbox/itsi_sandbox_hanami/.env +2 -0
  75. data/sandbox/itsi_sandbox_hanami/.gitignore +6 -0
  76. data/sandbox/itsi_sandbox_hanami/.rspec +1 -0
  77. data/sandbox/itsi_sandbox_hanami/Gemfile +49 -0
  78. data/sandbox/itsi_sandbox_hanami/Gemfile.lock +440 -0
  79. data/sandbox/itsi_sandbox_hanami/Guardfile +9 -0
  80. data/sandbox/itsi_sandbox_hanami/Procfile.dev +2 -0
  81. data/sandbox/itsi_sandbox_hanami/README.md +1 -0
  82. data/sandbox/itsi_sandbox_hanami/Rakefile +3 -0
  83. data/sandbox/itsi_sandbox_hanami/app/action.rb +12 -0
  84. data/sandbox/itsi_sandbox_hanami/app/actions/.keep +0 -0
  85. data/sandbox/itsi_sandbox_hanami/app/assets/css/app.css +5 -0
  86. data/sandbox/itsi_sandbox_hanami/app/assets/images/favicon.ico +0 -0
  87. data/sandbox/itsi_sandbox_hanami/app/assets/js/app.js +1 -0
  88. data/sandbox/itsi_sandbox_hanami/app/db/relation.rb +10 -0
  89. data/sandbox/itsi_sandbox_hanami/app/db/repo.rb +10 -0
  90. data/sandbox/itsi_sandbox_hanami/app/db/struct.rb +10 -0
  91. data/sandbox/itsi_sandbox_hanami/app/operation.rb +9 -0
  92. data/sandbox/itsi_sandbox_hanami/app/relations/.keep +0 -0
  93. data/sandbox/itsi_sandbox_hanami/app/repos/.keep +0 -0
  94. data/sandbox/itsi_sandbox_hanami/app/structs/.keep +0 -0
  95. data/sandbox/itsi_sandbox_hanami/app/templates/layouts/app.html.erb +14 -0
  96. data/sandbox/itsi_sandbox_hanami/app/view.rb +9 -0
  97. data/sandbox/itsi_sandbox_hanami/app/views/helpers.rb +10 -0
  98. data/sandbox/itsi_sandbox_hanami/bin/dev +8 -0
  99. data/sandbox/itsi_sandbox_hanami/config/app.rb +8 -0
  100. data/sandbox/itsi_sandbox_hanami/config/assets.js +16 -0
  101. data/sandbox/itsi_sandbox_hanami/config/db/migrate/.keep +0 -0
  102. data/sandbox/itsi_sandbox_hanami/config/db/seeds.rb +15 -0
  103. data/sandbox/itsi_sandbox_hanami/config/puma.rb +47 -0
  104. data/sandbox/itsi_sandbox_hanami/config/routes.rb +7 -0
  105. data/sandbox/itsi_sandbox_hanami/config/settings.rb +9 -0
  106. data/sandbox/itsi_sandbox_hanami/config.ru +5 -0
  107. data/sandbox/itsi_sandbox_hanami/db/.keep +0 -0
  108. data/sandbox/itsi_sandbox_hanami/lib/itsi_hanami/types.rb +11 -0
  109. data/sandbox/itsi_sandbox_hanami/lib/tasks/.keep +0 -0
  110. data/sandbox/itsi_sandbox_hanami/package-lock.json +946 -0
  111. data/sandbox/itsi_sandbox_hanami/package.json +8 -0
  112. data/sandbox/itsi_sandbox_hanami/spec/requests/root_spec.rb +11 -0
  113. data/sandbox/itsi_sandbox_hanami/spec/spec_helper.rb +9 -0
  114. data/sandbox/itsi_sandbox_hanami/spec/support/db/cleaning.rb +42 -0
  115. data/sandbox/itsi_sandbox_hanami/spec/support/db.rb +10 -0
  116. data/sandbox/itsi_sandbox_hanami/spec/support/features.rb +5 -0
  117. data/sandbox/itsi_sandbox_hanami/spec/support/operations.rb +8 -0
  118. data/sandbox/itsi_sandbox_hanami/spec/support/requests.rb +13 -0
  119. data/sandbox/itsi_sandbox_hanami/spec/support/rspec.rb +61 -0
  120. data/sandbox/itsi_sandbox_rack/Gemfile +17 -0
  121. data/sandbox/itsi_sandbox_rack/Gemfile.lock +153 -0
  122. data/sandbox/itsi_sandbox_rack/config.ru +18 -0
  123. data/sandbox/itsi_sandbox_rack_lint/Gemfile +7 -0
  124. data/sandbox/itsi_sandbox_rack_lint/Gemfile.lock +27 -0
  125. data/sandbox/itsi_sandbox_rack_lint/config.ru +3 -0
  126. data/sandbox/itsi_sandbox_rails/.dockerignore +51 -0
  127. data/sandbox/itsi_sandbox_rails/.gitattributes +9 -0
  128. data/sandbox/itsi_sandbox_rails/.github/dependabot.yml +12 -0
  129. data/sandbox/itsi_sandbox_rails/.github/workflows/ci.yml +90 -0
  130. data/sandbox/itsi_sandbox_rails/.gitignore +34 -0
  131. data/sandbox/itsi_sandbox_rails/.kamal/hooks/docker-setup.sample +3 -0
  132. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-app-boot.sample +3 -0
  133. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-deploy.sample +14 -0
  134. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-proxy-reboot.sample +3 -0
  135. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-app-boot.sample +3 -0
  136. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-build.sample +51 -0
  137. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-connect.sample +47 -0
  138. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-deploy.sample +109 -0
  139. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  140. data/sandbox/itsi_sandbox_rails/.kamal/secrets +17 -0
  141. data/sandbox/itsi_sandbox_rails/.rubocop.yml +8 -0
  142. data/sandbox/itsi_sandbox_rails/.ruby-version +1 -0
  143. data/sandbox/itsi_sandbox_rails/Dockerfile +72 -0
  144. data/sandbox/itsi_sandbox_rails/Gemfile +72 -0
  145. data/sandbox/itsi_sandbox_rails/Gemfile.lock +480 -0
  146. data/sandbox/itsi_sandbox_rails/README.md +24 -0
  147. data/sandbox/itsi_sandbox_rails/Rakefile +6 -0
  148. data/sandbox/itsi_sandbox_rails/app/assets/images/.keep +0 -0
  149. data/sandbox/itsi_sandbox_rails/app/assets/stylesheets/application.css +10 -0
  150. data/sandbox/itsi_sandbox_rails/app/controllers/application_controller.rb +4 -0
  151. data/sandbox/itsi_sandbox_rails/app/controllers/concerns/.keep +0 -0
  152. data/sandbox/itsi_sandbox_rails/app/controllers/home_controller.rb +51 -0
  153. data/sandbox/itsi_sandbox_rails/app/controllers/live_controller.rb +41 -0
  154. data/sandbox/itsi_sandbox_rails/app/controllers/uploads_controller.rb +32 -0
  155. data/sandbox/itsi_sandbox_rails/app/helpers/application_helper.rb +2 -0
  156. data/sandbox/itsi_sandbox_rails/app/javascript/application.js +3 -0
  157. data/sandbox/itsi_sandbox_rails/app/javascript/controllers/application.js +9 -0
  158. data/sandbox/itsi_sandbox_rails/app/javascript/controllers/hello_controller.js +7 -0
  159. data/sandbox/itsi_sandbox_rails/app/javascript/controllers/index.js +4 -0
  160. data/sandbox/itsi_sandbox_rails/app/jobs/application_job.rb +7 -0
  161. data/sandbox/itsi_sandbox_rails/app/mailers/application_mailer.rb +4 -0
  162. data/sandbox/itsi_sandbox_rails/app/models/application_record.rb +3 -0
  163. data/sandbox/itsi_sandbox_rails/app/models/concerns/.keep +0 -0
  164. data/sandbox/itsi_sandbox_rails/app/models/post.rb +2 -0
  165. data/sandbox/itsi_sandbox_rails/app/views/layouts/application.html.erb +28 -0
  166. data/sandbox/itsi_sandbox_rails/app/views/layouts/mailer.html.erb +13 -0
  167. data/sandbox/itsi_sandbox_rails/app/views/layouts/mailer.text.erb +1 -0
  168. data/sandbox/itsi_sandbox_rails/app/views/pwa/manifest.json.erb +22 -0
  169. data/sandbox/itsi_sandbox_rails/app/views/pwa/service-worker.js +26 -0
  170. data/sandbox/itsi_sandbox_rails/bin/brakeman +7 -0
  171. data/sandbox/itsi_sandbox_rails/bin/bundle +109 -0
  172. data/sandbox/itsi_sandbox_rails/bin/dev +2 -0
  173. data/sandbox/itsi_sandbox_rails/bin/docker-entrypoint +14 -0
  174. data/sandbox/itsi_sandbox_rails/bin/importmap +4 -0
  175. data/sandbox/itsi_sandbox_rails/bin/jobs +6 -0
  176. data/sandbox/itsi_sandbox_rails/bin/kamal +27 -0
  177. data/sandbox/itsi_sandbox_rails/bin/rails +4 -0
  178. data/sandbox/itsi_sandbox_rails/bin/rake +4 -0
  179. data/sandbox/itsi_sandbox_rails/bin/rubocop +8 -0
  180. data/sandbox/itsi_sandbox_rails/bin/setup +34 -0
  181. data/sandbox/itsi_sandbox_rails/bin/thrust +5 -0
  182. data/sandbox/itsi_sandbox_rails/config/application.rb +61 -0
  183. data/sandbox/itsi_sandbox_rails/config/boot.rb +4 -0
  184. data/sandbox/itsi_sandbox_rails/config/cable.yml +17 -0
  185. data/sandbox/itsi_sandbox_rails/config/cache.yml +16 -0
  186. data/sandbox/itsi_sandbox_rails/config/credentials.yml.enc +1 -0
  187. data/sandbox/itsi_sandbox_rails/config/database.yml +40 -0
  188. data/sandbox/itsi_sandbox_rails/config/deploy.yml +116 -0
  189. data/sandbox/itsi_sandbox_rails/config/environment.rb +5 -0
  190. data/sandbox/itsi_sandbox_rails/config/environments/development.rb +72 -0
  191. data/sandbox/itsi_sandbox_rails/config/environments/production.rb +90 -0
  192. data/sandbox/itsi_sandbox_rails/config/environments/test.rb +53 -0
  193. data/sandbox/itsi_sandbox_rails/config/importmap.rb +7 -0
  194. data/sandbox/itsi_sandbox_rails/config/initializers/assets.rb +7 -0
  195. data/sandbox/itsi_sandbox_rails/config/initializers/content_security_policy.rb +25 -0
  196. data/sandbox/itsi_sandbox_rails/config/initializers/filter_parameter_logging.rb +8 -0
  197. data/sandbox/itsi_sandbox_rails/config/initializers/inflections.rb +16 -0
  198. data/sandbox/itsi_sandbox_rails/config/locales/en.yml +31 -0
  199. data/sandbox/itsi_sandbox_rails/config/puma.rb +41 -0
  200. data/sandbox/itsi_sandbox_rails/config/queue.yml +18 -0
  201. data/sandbox/itsi_sandbox_rails/config/recurring.yml +10 -0
  202. data/sandbox/itsi_sandbox_rails/config/routes.rb +21 -0
  203. data/sandbox/itsi_sandbox_rails/config/storage.yml +34 -0
  204. data/sandbox/itsi_sandbox_rails/config.ru +7 -0
  205. data/sandbox/itsi_sandbox_rails/db/cable_schema.rb +11 -0
  206. data/sandbox/itsi_sandbox_rails/db/cache_schema.rb +14 -0
  207. data/sandbox/itsi_sandbox_rails/db/migrate/20250301041554_create_posts.rb +10 -0
  208. data/sandbox/itsi_sandbox_rails/db/queue_schema.rb +129 -0
  209. data/sandbox/itsi_sandbox_rails/db/schema.rb +23 -0
  210. data/sandbox/itsi_sandbox_rails/db/seeds.rb +9 -0
  211. data/sandbox/itsi_sandbox_rails/lib/tasks/.keep +0 -0
  212. data/sandbox/itsi_sandbox_rails/log/.keep +0 -0
  213. data/sandbox/itsi_sandbox_rails/public/400.html +114 -0
  214. data/sandbox/itsi_sandbox_rails/public/404.html +114 -0
  215. data/sandbox/itsi_sandbox_rails/public/406-unsupported-browser.html +114 -0
  216. data/sandbox/itsi_sandbox_rails/public/422.html +114 -0
  217. data/sandbox/itsi_sandbox_rails/public/500.html +114 -0
  218. data/sandbox/itsi_sandbox_rails/public/icon.png +0 -0
  219. data/sandbox/itsi_sandbox_rails/public/icon.svg +3 -0
  220. data/sandbox/itsi_sandbox_rails/public/robots.txt +1 -0
  221. data/sandbox/itsi_sandbox_rails/script/.keep +0 -0
  222. data/sandbox/itsi_sandbox_rails/storage/.keep +0 -0
  223. data/sandbox/itsi_sandbox_rails/test/application_system_test_case.rb +5 -0
  224. data/sandbox/itsi_sandbox_rails/test/controllers/.keep +0 -0
  225. data/sandbox/itsi_sandbox_rails/test/fixtures/files/.keep +0 -0
  226. data/sandbox/itsi_sandbox_rails/test/fixtures/posts.yml +9 -0
  227. data/sandbox/itsi_sandbox_rails/test/helpers/.keep +0 -0
  228. data/sandbox/itsi_sandbox_rails/test/integration/.keep +0 -0
  229. data/sandbox/itsi_sandbox_rails/test/mailers/.keep +0 -0
  230. data/sandbox/itsi_sandbox_rails/test/models/.keep +0 -0
  231. data/sandbox/itsi_sandbox_rails/test/models/post_test.rb +7 -0
  232. data/sandbox/itsi_sandbox_rails/test/system/.keep +0 -0
  233. data/sandbox/itsi_sandbox_rails/test/test_helper.rb +15 -0
  234. data/sandbox/itsi_sandbox_rails/tmp/.keep +0 -0
  235. data/sandbox/itsi_sandbox_rails/tmp/pids/.keep +0 -0
  236. data/sandbox/itsi_sandbox_rails/tmp/storage/.keep +0 -0
  237. data/sandbox/itsi_sandbox_rails/vendor/.keep +0 -0
  238. data/sandbox/itsi_sandbox_rails/vendor/javascript/.keep +0 -0
  239. data/sandbox/itsi_sandbox_roda/Gemfile +5 -0
  240. data/sandbox/itsi_sandbox_roda/Gemfile.lock +44 -0
  241. data/sandbox/itsi_sandbox_roda/config.ru +39 -0
  242. data/sandbox/itsi_sinatra/Gemfile +9 -0
  243. data/sandbox/itsi_sinatra/Gemfile.lock +81 -0
  244. data/sandbox/itsi_sinatra/app.rb +9 -0
  245. data/sandbox/pebble/docker-compose.yml +11 -0
  246. data/tasks.txt +4 -4
  247. metadata +199 -10
  248. data/gems/server/lib/itsi/signals.rb +0 -23
  249. 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 to_env
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" => lambda do
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: ["#{options.fetch(:host, "127.0.0.1")}:#{options.fetch(:Port, 3001)}"],
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
- "Use ENV['ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL'] to disable this behavior"
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,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Server
5
- VERSION = "0.1.7"
5
+ VERSION = "0.1.9"
6
6
  end
7
7
  end
@@ -2,99 +2,85 @@
2
2
 
3
3
  require_relative "server/version"
4
4
  require_relative "server/itsi_server"
5
- require_relative "signals"
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
- require_relative "server/rack/handler/itsi"
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
- def self.running?
14
- @running ||= false
15
- end
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
- # 3. Set Body
54
- # As soon as we start setting the response
55
- # the server will begin to stream it to the client.
56
-
57
- # If we're partially hijacked or returned a streaming body,
58
- # stream this response.
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
- def self.start_scheduler_loop(scheduler_class, scheduler_task)
88
- scheduler = scheduler_class.new
89
- Fiber.set_scheduler(scheduler)
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
- # If scheduler is enabled
94
- # Each request is wrapped in a Fiber.
95
- def self.schedule(app, request)
96
- Fiber.schedule do
97
- call(app, request)
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