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
@@ -19,6 +19,7 @@ parking_lot = "0.12.3"
19
19
  rustls-pemfile = "2.2.0"
20
20
  tokio-rustls = "0.26.2"
21
21
  bytes = "1.3"
22
+ tokio-rustls-acme = "0.6.0"
22
23
  rcgen = { version = "0.13.2", features = ["x509-parser", "pem"] }
23
24
  base64 = "0.22.1"
24
25
  http-body-util = "0.1.2"
@@ -39,7 +40,6 @@ httparse = "1.10.1"
39
40
  async-channel = "2.3.1"
40
41
  tempfile = "3.18.0"
41
42
  sysinfo = "0.33.1"
42
- tokio-rustls-acme = "0.6.0"
43
43
  rustls = "0.23.23"
44
44
  fs2 = "0.4.3"
45
45
  ring = "0.17.14"
@@ -71,6 +71,7 @@ fn init(ruby: &Ruby) -> Result<()> {
71
71
  server.define_singleton_method("new", function!(Server::new, -1))?;
72
72
  server.define_singleton_method("reset_signal_handlers", function!(reset_signal_handlers, 0))?;
73
73
  server.define_method("start", method!(Server::start, 0))?;
74
+ server.define_method("stop", method!(Server::stop, 0))?;
74
75
 
75
76
  let request = ruby.get_inner(&ITSI_REQUEST);
76
77
  request.define_method("path", method!(ItsiRequest::path, 0))?;
@@ -86,6 +87,8 @@ fn init(ruby: &Ruby) -> Result<()> {
86
87
  request.define_method("port", method!(ItsiRequest::port, 0))?;
87
88
  request.define_method("body", method!(ItsiRequest::body, 0))?;
88
89
  request.define_method("response", method!(ItsiRequest::response, 0))?;
90
+ request.define_method("json?", method!(ItsiRequest::is_json, 0))?;
91
+ request.define_method("html?", method!(ItsiRequest::is_html, 0))?;
89
92
 
90
93
  let body_proxy = ruby.get_inner(&ITSI_BODY_PROXY);
91
94
  body_proxy.define_method("gets", method!(ItsiBodyProxy::gets, 0))?;
@@ -102,6 +105,8 @@ fn init(ruby: &Ruby) -> Result<()> {
102
105
  response.define_method("close_read", method!(ItsiResponse::close_read, 0))?;
103
106
  response.define_method("close", method!(ItsiResponse::close, 0))?;
104
107
  response.define_method("hijack", method!(ItsiResponse::hijack, 1))?;
108
+ response.define_method("json?", method!(ItsiResponse::is_json, 0))?;
109
+ response.define_method("html?", method!(ItsiResponse::is_html, 0))?;
105
110
 
106
111
  Ok(())
107
112
  }
@@ -6,14 +6,14 @@ use crate::{
6
6
  response::itsi_response::ItsiResponse,
7
7
  server::{
8
8
  itsi_server::{RequestJob, Server},
9
- listener::{SockAddr, TokioListener},
9
+ listener::{ListenerInfo, SockAddr},
10
10
  serve_strategy::single_mode::RunningPhase,
11
11
  },
12
12
  };
13
13
  use bytes::Bytes;
14
14
  use derive_more::Debug;
15
15
  use futures::StreamExt;
16
- use http::{request::Parts, Response, StatusCode};
16
+ use http::{request::Parts, HeaderValue, Response, StatusCode};
17
17
  use http_body_util::{combinators::BoxBody, BodyExt, Empty};
18
18
  use hyper::{body::Incoming, Request};
19
19
  use itsi_error::from::CLIENT_CONNECTION_CLOSED;
@@ -44,11 +44,12 @@ pub struct ItsiRequest {
44
44
  pub remote_addr: String,
45
45
  pub version: String,
46
46
  #[debug(skip)]
47
- pub(crate) listener: Arc<TokioListener>,
47
+ pub(crate) listener: Arc<ListenerInfo>,
48
48
  #[debug(skip)]
49
49
  pub server: Arc<Server>,
50
50
  pub response: ItsiResponse,
51
51
  pub start: Instant,
52
+ pub content_type: String,
52
53
  }
53
54
 
54
55
  impl fmt::Display for ItsiRequest {
@@ -82,6 +83,14 @@ impl ItsiRequest {
82
83
  }
83
84
  }
84
85
 
86
+ pub fn is_json(&self) -> bool {
87
+ self.content_type.eq("application/json")
88
+ }
89
+
90
+ pub fn is_html(&self) -> bool {
91
+ self.content_type.eq("text/html")
92
+ }
93
+
85
94
  pub fn process(
86
95
  self,
87
96
  ruby: &Ruby,
@@ -128,7 +137,7 @@ impl ItsiRequest {
128
137
  hyper_request: Request<Incoming>,
129
138
  sender: async_channel::Sender<RequestJob>,
130
139
  server: Arc<Server>,
131
- listener: Arc<TokioListener>,
140
+ listener: Arc<ListenerInfo>,
132
141
  addr: SockAddr,
133
142
  shutdown_rx: watch::Receiver<RunningPhase>,
134
143
  ) -> itsi_error::Result<Response<BoxBody<Bytes, Infallible>>> {
@@ -153,7 +162,7 @@ impl ItsiRequest {
153
162
  request: Request<Incoming>,
154
163
  sock_addr: SockAddr,
155
164
  server: Arc<Server>,
156
- listener: Arc<TokioListener>,
165
+ listener: Arc<ListenerInfo>,
157
166
  ) -> (ItsiRequest, mpsc::Receiver<Option<Bytes>>) {
158
167
  let (parts, body) = request.into_parts();
159
168
  let body = if server.stream_body.is_some_and(|f| f) {
@@ -175,8 +184,27 @@ impl ItsiRequest {
175
184
  server,
176
185
  listener,
177
186
  version: format!("{:?}", &parts.version),
178
- response: ItsiResponse::new(parts.clone(), response_channel.0),
187
+ response: ItsiResponse::new(
188
+ parts.clone(),
189
+ response_channel.0,
190
+ parts
191
+ .headers
192
+ .get("Accept")
193
+ .unwrap_or(&HeaderValue::from_static("text/html"))
194
+ .to_str()
195
+ .unwrap()
196
+ .to_string(),
197
+ ),
179
198
  start: Instant::now(),
199
+ content_type: parts
200
+ .headers
201
+ .get("Content-Type")
202
+ .unwrap_or(&HeaderValue::from_static(
203
+ "application/x-www-form-urlencoded",
204
+ ))
205
+ .to_str()
206
+ .unwrap()
207
+ .to_string(),
180
208
  parts,
181
209
  },
182
210
  response_channel.1,
@@ -231,7 +259,7 @@ impl ItsiRequest {
231
259
  .uri
232
260
  .host()
233
261
  .map(|host| host.to_string())
234
- .unwrap_or_else(|| self.listener.host()))
262
+ .unwrap_or_else(|| self.listener.host.clone()))
235
263
  }
236
264
 
237
265
  pub(crate) fn scheme(&self) -> MagnusResult<String> {
@@ -240,7 +268,7 @@ impl ItsiRequest {
240
268
  .uri
241
269
  .scheme()
242
270
  .map(|scheme| scheme.to_string())
243
- .unwrap_or_else(|| self.listener.scheme()))
271
+ .unwrap_or_else(|| self.listener.scheme.clone()))
244
272
  }
245
273
 
246
274
  pub(crate) fn headers(&self) -> MagnusResult<Vec<(String, &str)>> {
@@ -264,7 +292,7 @@ impl ItsiRequest {
264
292
  }
265
293
 
266
294
  pub(crate) fn port(&self) -> MagnusResult<u16> {
267
- Ok(self.parts.uri.port_u16().unwrap_or(self.listener.port()))
295
+ Ok(self.parts.uri.port_u16().unwrap_or(self.listener.port))
268
296
  }
269
297
 
270
298
  pub(crate) fn body(&self) -> MagnusResult<Value> {
@@ -37,6 +37,7 @@ use crate::server::serve_strategy::single_mode::RunningPhase;
37
37
  #[derive(Debug, Clone)]
38
38
  pub struct ItsiResponse {
39
39
  pub data: Arc<ResponseData>,
40
+ pub accept: String,
40
41
  }
41
42
 
42
43
  #[derive(Debug)]
@@ -293,11 +294,19 @@ impl ItsiResponse {
293
294
  Ok(true)
294
295
  }
295
296
 
297
+ pub fn is_html(&self) -> bool {
298
+ self.accept.starts_with("text/html")
299
+ }
300
+
301
+ pub fn is_json(&self) -> bool {
302
+ self.accept.starts_with("application/json")
303
+ }
304
+
296
305
  pub fn close_read(&self) -> MagnusResult<bool> {
297
- todo!();
306
+ Ok(true)
298
307
  }
299
308
 
300
- pub fn new(parts: Parts, response_writer: mpsc::Sender<Option<Bytes>>) -> Self {
309
+ pub fn new(parts: Parts, response_writer: mpsc::Sender<Option<Bytes>>, accept: String) -> Self {
301
310
  Self {
302
311
  data: Arc::new(ResponseData {
303
312
  response: RwLock::new(Some(Response::new(BoxBody::new(Empty::new())))),
@@ -306,6 +315,7 @@ impl ItsiResponse {
306
315
  hijacked_socket: RwLock::new(None),
307
316
  parts,
308
317
  }),
318
+ accept,
309
319
  }
310
320
  }
311
321
 
@@ -9,6 +9,7 @@ use std::{
9
9
  path::PathBuf,
10
10
  str::FromStr,
11
11
  };
12
+
12
13
  #[derive(Debug, Clone)]
13
14
  pub enum BindAddress {
14
15
  Ip(IpAddr),
@@ -129,13 +130,13 @@ impl FromStr for Bind {
129
130
  BindProtocol::Unix => None,
130
131
  BindProtocol::Unixs => Some(configure_tls(host, &options)?),
131
132
  };
132
-
133
- Ok(Self {
133
+ let bind = Self {
134
134
  address,
135
135
  port,
136
136
  protocol,
137
137
  tls_config,
138
- })
138
+ };
139
+ Ok(bind)
139
140
  }
140
141
  }
141
142
 
@@ -2,24 +2,26 @@ use super::{
2
2
  bind::Bind,
3
3
  listener::Listener,
4
4
  serve_strategy::{cluster_mode::ClusterMode, single_mode::SingleMode},
5
- signal::{clear_signal_handlers, reset_signal_handlers, SIGNAL_HANDLER_CHANNEL},
5
+ signal::{
6
+ clear_signal_handlers, reset_signal_handlers, send_shutdown_event, SIGNAL_HANDLER_CHANNEL,
7
+ },
6
8
  };
7
9
  use crate::{request::itsi_request::ItsiRequest, server::serve_strategy::ServeStrategy};
8
10
  use derive_more::Debug;
9
- use itsi_rb_helpers::call_without_gvl;
10
- use itsi_tracing::error;
11
+ use itsi_rb_helpers::{call_without_gvl, HeapVal};
12
+ use itsi_tracing::{error, run_silently};
11
13
  use magnus::{
12
14
  block::Proc,
13
15
  error::Result,
14
- scan_args::{get_kwargs, scan_args, Args, KwArgs},
16
+ scan_args::{get_kwargs, scan_args, Args, KwArgs, ScanArgsKw, ScanArgsOpt, ScanArgsRequired},
15
17
  value::{InnerValue, Opaque, ReprValue},
16
- RHash, Ruby, Symbol, Value,
18
+ ArgList, RHash, Ruby, Symbol, Value,
17
19
  };
18
- use parking_lot::Mutex;
20
+ use parking_lot::{Mutex, RwLock};
19
21
  use std::{cmp::max, ops::Deref, sync::Arc};
20
22
  use tracing::{info, instrument};
21
23
 
22
- static DEFAULT_BIND: &str = "localhost:3000";
24
+ static DEFAULT_BIND: &str = "http://localhost:3000";
23
25
 
24
26
  #[magnus::wrap(class = "Itsi::Server", free_immediately, size)]
25
27
  #[derive(Clone)]
@@ -39,7 +41,7 @@ type AfterFork = Mutex<Arc<Option<Box<dyn Fn() + Send + Sync>>>>;
39
41
  #[derive(Debug)]
40
42
  pub struct ServerConfig {
41
43
  #[debug(skip)]
42
- pub app: Opaque<Value>,
44
+ pub app: HeapVal,
43
45
  #[allow(unused)]
44
46
  pub workers: u8,
45
47
  #[allow(unused)]
@@ -55,6 +57,9 @@ pub struct ServerConfig {
55
57
  pub scheduler_class: Option<String>,
56
58
  pub stream_body: Option<bool>,
57
59
  pub worker_memory_limit: Option<u64>,
60
+ #[debug(skip)]
61
+ pub(crate) strategy: RwLock<Option<ServeStrategy>>,
62
+ pub silence: bool,
58
63
  }
59
64
 
60
65
  #[derive(Debug)]
@@ -63,6 +68,35 @@ pub enum RequestJob {
63
68
  Shutdown,
64
69
  }
65
70
 
71
+ // Define your helper function.
72
+ // Here P, A, C correspond to the types for the first tuple, second tuple, and extra parameters respectively.
73
+ fn extract_args<Req, Opt, Splat>(
74
+ scan_args: &Args<(), (), (), (), RHash, ()>,
75
+ primaries: &[&str],
76
+ rest: &[&str],
77
+ ) -> Result<KwArgs<Req, Opt, Splat>>
78
+ where
79
+ Req: ScanArgsRequired,
80
+ Opt: ScanArgsOpt,
81
+ Splat: ScanArgsKw,
82
+ {
83
+ // Combine the primary and rest names into one Vec of Symbols.
84
+ let symbols: Vec<Symbol> = primaries
85
+ .iter()
86
+ .chain(rest.iter())
87
+ .map(|&name| Symbol::new(name))
88
+ .collect();
89
+
90
+ // Call the "slice" function with the combined symbols.
91
+ let hash = scan_args
92
+ .keywords
93
+ .funcall::<_, _, RHash>("slice", symbols.into_arg_list_with(&Ruby::get().unwrap()))
94
+ .unwrap();
95
+
96
+ // Finally, call get_kwargs with the original name slices.
97
+ get_kwargs(hash, primaries, rest)
98
+ }
99
+
66
100
  impl Server {
67
101
  #[instrument(
68
102
  name = "Itsi",
@@ -73,39 +107,44 @@ impl Server {
73
107
  pub fn new(args: &[Value]) -> Result<Self> {
74
108
  let scan_args: Args<(), (), (), (), RHash, ()> = scan_args(args)?;
75
109
 
76
- type ArgSet1 = (
77
- Option<u8>,
78
- Option<u8>,
79
- Option<f64>,
80
- Option<String>,
81
- Option<Vec<String>>,
82
- Option<Proc>,
83
- Option<Proc>,
84
- Option<String>,
85
- Option<bool>,
86
- );
87
-
88
- type ArgSet2 = (Option<u64>,);
89
-
90
- let args1: KwArgs<(Value,), ArgSet1, ()> = get_kwargs(
91
- scan_args
92
- .keywords
93
- .funcall::<_, _, RHash>(
94
- "slice",
95
- (
96
- Symbol::new("app"),
97
- Symbol::new("workers"),
98
- Symbol::new("threads"),
99
- Symbol::new("shutdown_timeout"),
100
- Symbol::new("script_name"),
101
- Symbol::new("binds"),
102
- Symbol::new("before_fork"),
103
- Symbol::new("after_fork"),
104
- Symbol::new("scheduler_class"),
105
- Symbol::new("stream_body"),
106
- ),
107
- )
108
- .unwrap(),
110
+ type Args1 = KwArgs<
111
+ (Value,),
112
+ (
113
+ // Workers
114
+ Option<u8>,
115
+ // Threads
116
+ Option<u8>,
117
+ // Shutdown Timeout
118
+ Option<f64>,
119
+ // Script Name
120
+ Option<String>,
121
+ // Binds
122
+ Option<Vec<String>>,
123
+ // Stream Body
124
+ Option<bool>,
125
+ ),
126
+ (),
127
+ >;
128
+
129
+ type Args2 = KwArgs<
130
+ (),
131
+ (
132
+ // Before Fork
133
+ Option<Proc>,
134
+ // After Fork
135
+ Option<Proc>,
136
+ // Scheduler Class
137
+ Option<String>,
138
+ // Worker Memory Limit
139
+ Option<u64>,
140
+ // Silence
141
+ Option<bool>,
142
+ ),
143
+ (),
144
+ >;
145
+
146
+ let args1: Args1 = extract_args(
147
+ &scan_args,
109
148
  &["app"],
110
149
  &[
111
150
  "workers",
@@ -113,24 +152,24 @@ impl Server {
113
152
  "shutdown_timeout",
114
153
  "script_name",
115
154
  "binds",
116
- "before_fork",
117
- "after_fork",
118
- "scheduler_class",
119
155
  "stream_body",
120
156
  ],
121
157
  )?;
122
158
 
123
- let args2: KwArgs<(), ArgSet2, ()> = get_kwargs(
124
- scan_args
125
- .keywords
126
- .funcall::<_, _, RHash>("slice", (Symbol::new("worker_memory_limit"),))
127
- .unwrap(),
159
+ let args2: Args2 = extract_args(
160
+ &scan_args,
128
161
  &[],
129
- &["worker_memory_limit"],
162
+ &[
163
+ "before_fork",
164
+ "after_fork",
165
+ "scheduler_class",
166
+ "worker_memory_limit",
167
+ "silence",
168
+ ],
130
169
  )?;
131
170
 
132
171
  let config = ServerConfig {
133
- app: Opaque::from(args1.required.0),
172
+ app: HeapVal::from(args1.required.0),
134
173
  workers: max(args1.optional.0.unwrap_or(1), 1),
135
174
  threads: max(args1.optional.1.unwrap_or(1), 1),
136
175
  shutdown_timeout: args1.optional.2.unwrap_or(5.0),
@@ -144,7 +183,8 @@ impl Server {
144
183
  .map(|s| s.parse())
145
184
  .collect::<itsi_error::Result<Vec<Bind>>>()?,
146
185
  ),
147
- before_fork: Mutex::new(args1.optional.5.map(|p| {
186
+ stream_body: args1.optional.5,
187
+ before_fork: Mutex::new(args2.optional.0.map(|p| {
148
188
  let opaque_proc = Opaque::from(p);
149
189
  Box::new(move || {
150
190
  opaque_proc
@@ -153,7 +193,7 @@ impl Server {
153
193
  .unwrap();
154
194
  }) as Box<dyn FnOnce() + Send + Sync>
155
195
  })),
156
- after_fork: Mutex::new(Arc::new(args1.optional.6.map(|p| {
196
+ after_fork: Mutex::new(Arc::new(args2.optional.1.map(|p| {
157
197
  let opaque_proc = Opaque::from(p);
158
198
  Box::new(move || {
159
199
  opaque_proc
@@ -162,15 +202,18 @@ impl Server {
162
202
  .unwrap();
163
203
  }) as Box<dyn Fn() + Send + Sync>
164
204
  }))),
165
- scheduler_class: args1.optional.7.clone(),
166
- stream_body: args1.optional.8,
167
- worker_memory_limit: args2.optional.0,
205
+ scheduler_class: args2.optional.2.clone(),
206
+ worker_memory_limit: args2.optional.3,
207
+ silence: args2.optional.4.is_some_and(|s| s),
208
+ strategy: RwLock::new(None),
168
209
  };
169
210
 
170
- if let Some(scheduler_class) = args1.optional.7 {
171
- info!(scheduler_class, fiber_scheduler = true);
172
- } else {
173
- info!(fiber_scheduler = false);
211
+ if !config.silence {
212
+ if let Some(scheduler_class) = args2.optional.2 {
213
+ info!(scheduler_class, fiber_scheduler = true);
214
+ } else {
215
+ info!(fiber_scheduler = false);
216
+ }
174
217
  }
175
218
 
176
219
  Ok(Server {
@@ -179,7 +222,7 @@ impl Server {
179
222
  }
180
223
 
181
224
  #[instrument(name = "Bind", skip_all, fields(binds=format!("{:?}", self.config.binds.lock())))]
182
- pub(crate) fn listeners(&self) -> Result<Arc<Vec<Arc<Listener>>>> {
225
+ pub(crate) fn build_listeners(&self) -> Result<Arc<Vec<Arc<Listener>>>> {
183
226
  let listeners = self
184
227
  .config
185
228
  .binds
@@ -195,11 +238,9 @@ impl Server {
195
238
  Ok(Arc::new(listeners))
196
239
  }
197
240
 
198
- pub(crate) fn build_strategy(
199
- self,
200
- listeners: Arc<Vec<Arc<Listener>>>,
201
- ) -> Result<ServeStrategy> {
241
+ pub(crate) fn build_strategy(self, listeners: Arc<Vec<Arc<Listener>>>) -> Result<()> {
202
242
  let server = Arc::new(self);
243
+ let server_clone = server.clone();
203
244
 
204
245
  let strategy = if server.config.workers == 1 {
205
246
  ServeStrategy::Single(Arc::new(SingleMode::new(
@@ -214,31 +255,40 @@ impl Server {
214
255
  SIGNAL_HANDLER_CHANNEL.0.clone(),
215
256
  )))
216
257
  };
217
- Ok(strategy)
258
+
259
+ *server_clone.strategy.write() = Some(strategy);
260
+ Ok(())
261
+ }
262
+
263
+ pub fn stop(&self) -> Result<()> {
264
+ send_shutdown_event();
265
+ Ok(())
218
266
  }
219
267
 
220
268
  pub fn start(&self) -> Result<()> {
269
+ if self.silence {
270
+ run_silently(|| self.build_and_run_strategy())
271
+ } else {
272
+ self.build_and_run_strategy()
273
+ }
274
+ }
275
+
276
+ fn build_and_run_strategy(&self) -> Result<()> {
221
277
  reset_signal_handlers();
222
278
  let rself = self.clone();
223
- let listeners = self.listeners()?;
279
+ let listeners = self.build_listeners()?;
224
280
  let listeners_clone = listeners.clone();
225
281
  call_without_gvl(move || -> Result<()> {
226
- let strategy = rself.build_strategy(listeners_clone)?;
227
- if let Err(e) = strategy.run() {
282
+ rself.clone().build_strategy(listeners_clone)?;
283
+ if let Err(e) = rself.clone().strategy.read().as_ref().unwrap().run() {
228
284
  error!("Error running server: {}", e);
229
- strategy.stop()?;
285
+ rself.strategy.read().as_ref().unwrap().stop()?;
230
286
  }
231
- drop(strategy);
232
287
  Ok(())
233
288
  })?;
234
- if let Ok(listeners) = Arc::try_unwrap(listeners) {
235
- listeners.into_iter().for_each(|listener| {
236
- if let Ok(listener) = Arc::try_unwrap(listener) {
237
- listener.unbind()
238
- };
239
- });
240
- }
241
289
  clear_signal_handlers();
290
+ self.strategy.write().take();
291
+ info!("Server stopped");
242
292
  Ok(())
243
293
  }
244
294
  }