hyperion-rb 1.6.1 → 2.10.1

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4570 -0
  3. data/README.md +212 -13
  4. data/ext/hyperion_h2_codec/Cargo.lock +7 -0
  5. data/ext/hyperion_h2_codec/Cargo.toml +33 -0
  6. data/ext/hyperion_h2_codec/extconf.rb +73 -0
  7. data/ext/hyperion_h2_codec/src/frames.rs +140 -0
  8. data/ext/hyperion_h2_codec/src/hpack/huffman.rs +161 -0
  9. data/ext/hyperion_h2_codec/src/hpack.rs +457 -0
  10. data/ext/hyperion_h2_codec/src/lib.rs +296 -0
  11. data/ext/hyperion_http/extconf.rb +28 -0
  12. data/ext/hyperion_http/h2_codec_glue.c +408 -0
  13. data/ext/hyperion_http/page_cache.c +1125 -0
  14. data/ext/hyperion_http/parser.c +473 -38
  15. data/ext/hyperion_http/sendfile.c +982 -0
  16. data/ext/hyperion_http/websocket.c +493 -0
  17. data/ext/hyperion_io_uring/Cargo.lock +33 -0
  18. data/ext/hyperion_io_uring/Cargo.toml +34 -0
  19. data/ext/hyperion_io_uring/extconf.rb +74 -0
  20. data/ext/hyperion_io_uring/src/lib.rs +316 -0
  21. data/lib/hyperion/adapter/rack.rb +370 -42
  22. data/lib/hyperion/admin_listener.rb +207 -0
  23. data/lib/hyperion/admin_middleware.rb +36 -7
  24. data/lib/hyperion/cli.rb +310 -11
  25. data/lib/hyperion/config.rb +440 -14
  26. data/lib/hyperion/connection.rb +679 -22
  27. data/lib/hyperion/deprecations.rb +81 -0
  28. data/lib/hyperion/dispatch_mode.rb +165 -0
  29. data/lib/hyperion/fiber_local.rb +75 -13
  30. data/lib/hyperion/h2_admission.rb +77 -0
  31. data/lib/hyperion/h2_codec.rb +452 -0
  32. data/lib/hyperion/http/page_cache.rb +122 -0
  33. data/lib/hyperion/http/sendfile.rb +696 -0
  34. data/lib/hyperion/http2/native_hpack_adapter.rb +70 -0
  35. data/lib/hyperion/http2_handler.rb +368 -9
  36. data/lib/hyperion/io_uring.rb +317 -0
  37. data/lib/hyperion/lint_wrapper_pool.rb +126 -0
  38. data/lib/hyperion/master.rb +96 -9
  39. data/lib/hyperion/metrics/path_templater.rb +68 -0
  40. data/lib/hyperion/metrics.rb +256 -0
  41. data/lib/hyperion/prometheus_exporter.rb +150 -0
  42. data/lib/hyperion/request.rb +13 -0
  43. data/lib/hyperion/response_writer.rb +477 -16
  44. data/lib/hyperion/runtime.rb +195 -0
  45. data/lib/hyperion/server/route_table.rb +179 -0
  46. data/lib/hyperion/server.rb +519 -55
  47. data/lib/hyperion/static_preload.rb +133 -0
  48. data/lib/hyperion/thread_pool.rb +61 -7
  49. data/lib/hyperion/tls.rb +343 -1
  50. data/lib/hyperion/version.rb +1 -1
  51. data/lib/hyperion/websocket/close_codes.rb +71 -0
  52. data/lib/hyperion/websocket/connection.rb +876 -0
  53. data/lib/hyperion/websocket/frame.rb +356 -0
  54. data/lib/hyperion/websocket/handshake.rb +525 -0
  55. data/lib/hyperion/worker.rb +111 -9
  56. data/lib/hyperion.rb +137 -3
  57. metadata +50 -1
data/lib/hyperion.rb CHANGED
@@ -3,22 +3,48 @@
3
3
  require_relative 'hyperion/version'
4
4
  require_relative 'hyperion/logger'
5
5
  require_relative 'hyperion/metrics'
6
+ require_relative 'hyperion/runtime'
7
+ require_relative 'hyperion/deprecations'
8
+ require_relative 'hyperion/dispatch_mode'
6
9
  require_relative 'hyperion/config'
10
+ require_relative 'hyperion/h2_admission'
7
11
 
8
12
  module Hyperion
9
13
  class Error < StandardError; end
10
14
  class ParseError < Error; end
11
15
  class UnsupportedError < Error; end
12
16
 
17
+ # Probe table for fiber-cooperative I/O libraries. 1.7.0 expanded
18
+ # the validation surface (RFC A9): `async_io: true` now requires at
19
+ # least one of these to be loaded, otherwise CLI bootstrap raises
20
+ # at `Hyperion.validate_async_io_loaded_libs!`. The CLI's pre-fork
21
+ # `warn_orphan_async_io` (1.6.1) still emits a soft warn for the
22
+ # nil-default case so existing operators see the same advisory log.
23
+ FIBER_IO_PROBES = {
24
+ 'hyperion-async-pg' => -> { defined?(::Hyperion::AsyncPg) },
25
+ 'async-redis' => -> { defined?(::Async::Redis) },
26
+ 'async-http' => -> { defined?(::Async::HTTP) }
27
+ }.freeze
28
+
13
29
  class << self
30
+ # 2.0.0: legacy module-level `Hyperion.metrics =` / `Hyperion.logger =`
31
+ # SETTERS are removed. The getters stay as Runtime.default delegators —
32
+ # they're the canonical REPL convenience — and assignment now happens
33
+ # via the Runtime API:
34
+ #
35
+ # Hyperion::Runtime.default.metrics = MyAdapter.new # mutate default
36
+ # server = Hyperion::Server.new(app:, runtime: Hyperion::Runtime.new(metrics: …))
37
+ #
38
+ # The 1.8.0 deprecation warns called this out for one full release;
39
+ # in-tree spec rewrites flipped to `Runtime.default.metrics =` already.
14
40
  def logger
15
- @logger ||= Logger.new
41
+ Runtime.default.logger
16
42
  end
17
43
 
18
- attr_writer :logger, :log_requests
44
+ attr_writer :log_requests
19
45
 
20
46
  def metrics
21
- @metrics ||= Metrics.new
47
+ Runtime.default.metrics
22
48
  end
23
49
 
24
50
  def stats
@@ -75,6 +101,95 @@ module Hyperion
75
101
  # with a warn log: warmup is an optimization, not a correctness gate.
76
102
  # If, for instance, OpenSSL can't be required in some odd environment,
77
103
  # we'd rather start cold than refuse to boot.
104
+ # PID of the Hyperion master process. Writable so the master records its
105
+ # own PID at boot; readable everywhere so AdminMiddleware (and other
106
+ # would-be signallers) can target the master regardless of context.
107
+ #
108
+ # Why not `Process.ppid`? Two reasons:
109
+ #
110
+ # 1. In single-worker mode, the "master" and "worker" are the same
111
+ # process; `Process.ppid` points to the shell / init that launched
112
+ # hyperion, NOT to ourselves.
113
+ # 2. When the master runs as PID 1 inside containerd / Docker (the
114
+ # default for `hyperion` as a container CMD), `Process.ppid` from a
115
+ # worker is `1` — but the worker IS a child of PID 1, so `kill`ing
116
+ # ppid signals the master correctly only by accident, and the
117
+ # pre-1.6.3 fallback `ppid > 1 ? ppid : Process.pid` would
118
+ # MIS-target the worker itself. (Repro: `docker run -e
119
+ # HYPERION_ADMIN_TOKEN=… hyperion` then `curl -X POST /-/quit` —
120
+ # response says draining, nothing happens.)
121
+ #
122
+ # The master sets this at boot (cluster: Master#run, single: CLI.run_single)
123
+ # AND exports `HYPERION_MASTER_PID` into ENV so forked workers read the
124
+ # correct value via copy-on-write. The reader prefers the in-process
125
+ # ivar (faster) and falls back to ENV (cross-fork) and finally to
126
+ # `Process.pid` (last-resort: someone constructed AdminMiddleware before
127
+ # the master booted, or in a non-Hyperion test context).
128
+ def master_pid
129
+ return @master_pid if @master_pid
130
+
131
+ env = ENV['HYPERION_MASTER_PID']
132
+ env_pid = env && env =~ /\A\d+\z/ ? env.to_i : nil
133
+ env_pid&.positive? ? env_pid : Process.pid
134
+ end
135
+
136
+ # Record the master PID and export it for forked workers. Called once
137
+ # by the master at boot. Workers inherit ENV via fork; the worker's own
138
+ # `master_pid` ivar stays nil and its reader falls back to ENV.
139
+ def master_pid!(pid = Process.pid)
140
+ @master_pid = pid
141
+ ENV['HYPERION_MASTER_PID'] = pid.to_s
142
+ pid
143
+ end
144
+
145
+ # Returns the list of currently-loaded fiber-cooperative I/O
146
+ # libraries. Reads `Hyperion::FIBER_IO_PROBES` via `const_get` so
147
+ # `stub_const('Hyperion::FIBER_IO_PROBES', ...)` works for the
148
+ # strict-validation specs without needing a method-injection seam.
149
+ def fiber_io_libs_loaded
150
+ probes = Hyperion.const_get(:FIBER_IO_PROBES)
151
+ probes.select { |_name, probe| probe.call }.keys
152
+ end
153
+
154
+ # Strict tri-state validation of `async_io` at warmup time (RFC A9).
155
+ # Run after `Hyperion.warmup!`'s eager-load section so any library
156
+ # that monkey-patches in fiber-cooperative I/O during boot has had
157
+ # the chance to install itself.
158
+ #
159
+ # - `true` → MUST have at least one fiber-IO library loaded; raise
160
+ # ArgumentError otherwise. The error message lists the
161
+ # checked libraries so operators can pick one.
162
+ # - `false` → No fiber-IO library should be loaded; if one is, emit
163
+ # a warn (the operator may still want this for some
164
+ # edge case, so we don't raise).
165
+ # - `nil` → Default. The CLI's existing soft-warn path covers
166
+ # this; warmup is a no-op.
167
+ def validate_async_io_loaded_libs!(setting)
168
+ probes = Hyperion.const_get(:FIBER_IO_PROBES)
169
+ case setting
170
+ when true
171
+ loaded = fiber_io_libs_loaded
172
+ if loaded.empty?
173
+ raise ArgumentError,
174
+ 'async_io: true requires a fiber-cooperative I/O library to be loaded ' \
175
+ "(checked: #{probes.keys.join(', ')}); none detected. " \
176
+ 'See https://github.com/andrew-woblavobla/hyperion#operator-guidance'
177
+ end
178
+ when false
179
+ loaded = fiber_io_libs_loaded
180
+ unless loaded.empty?
181
+ Hyperion.logger.warn do
182
+ {
183
+ message: 'async_io: false but fiber-cooperative I/O library is loaded',
184
+ loaded: loaded,
185
+ impact: 'the library will not yield to a scheduler under async_io: false; verify this is intentional'
186
+ }
187
+ end
188
+ end
189
+ end
190
+ nil
191
+ end
192
+
78
193
  def warmup!
79
194
  return if @warmed
80
195
 
@@ -97,10 +212,22 @@ module Hyperion
97
212
  # Subsequent calls hit the per-thread `cached_date` slot in response_writer.
98
213
  Time.now.httpdate
99
214
  nil
215
+ rescue ArgumentError
216
+ # Strict-validation error from `validate_async_io_loaded_libs!` —
217
+ # propagate so operators see the boot-time abort, not a warn-and-
218
+ # continue.
219
+ raise
100
220
  rescue StandardError => e
101
221
  Hyperion.logger.warn { { message: 'warmup failed (non-fatal)', error: e.message } }
102
222
  nil
103
223
  end
224
+
225
+ # Test seam: clear the warmup flag so a fresh `warmup!` call can
226
+ # re-run. Used by the async_io strict-validation specs that need to
227
+ # exercise the raise/warn paths multiple times in one process.
228
+ def reset_warmup!
229
+ @warmed = false
230
+ end
104
231
  end
105
232
  end
106
233
 
@@ -126,9 +253,16 @@ require_relative 'hyperion/fiber_local'
126
253
  require_relative 'hyperion/request'
127
254
  require_relative 'hyperion/parser'
128
255
  require_relative 'hyperion/c_parser'
256
+ require_relative 'hyperion/http/sendfile'
257
+ require_relative 'hyperion/http/page_cache'
258
+ require_relative 'hyperion/static_preload'
129
259
  require_relative 'hyperion/adapter/rack'
260
+ require_relative 'hyperion/lint_wrapper_pool'
130
261
  require_relative 'hyperion/prometheus_exporter'
131
262
  require_relative 'hyperion/admin_middleware'
263
+ require_relative 'hyperion/admin_listener'
264
+ require_relative 'hyperion/h2_codec'
265
+ require_relative 'hyperion/io_uring'
132
266
  require_relative 'hyperion/response_writer'
133
267
  require_relative 'hyperion/thread_pool'
134
268
  require_relative 'hyperion/connection'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperion-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 2.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Lobanov
@@ -83,6 +83,20 @@ dependencies:
83
83
  - - "<"
84
84
  - !ruby/object:Gem::Version
85
85
  version: '4.0'
86
+ - !ruby/object:Gem::Dependency
87
+ name: base64
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: '0.2'
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '0.2'
86
100
  - !ruby/object:Gem::Dependency
87
101
  name: rspec
88
102
  requirement: !ruby/object:Gem::Requirement
@@ -132,41 +146,76 @@ email:
132
146
  executables:
133
147
  - hyperion
134
148
  extensions:
149
+ - ext/hyperion_h2_codec/extconf.rb
135
150
  - ext/hyperion_http/extconf.rb
151
+ - ext/hyperion_io_uring/extconf.rb
136
152
  extra_rdoc_files: []
137
153
  files:
138
154
  - CHANGELOG.md
139
155
  - LICENSE
140
156
  - README.md
141
157
  - bin/hyperion
158
+ - ext/hyperion_h2_codec/Cargo.lock
159
+ - ext/hyperion_h2_codec/Cargo.toml
160
+ - ext/hyperion_h2_codec/extconf.rb
161
+ - ext/hyperion_h2_codec/src/frames.rs
162
+ - ext/hyperion_h2_codec/src/hpack.rs
163
+ - ext/hyperion_h2_codec/src/hpack/huffman.rs
164
+ - ext/hyperion_h2_codec/src/lib.rs
142
165
  - ext/hyperion_http/extconf.rb
166
+ - ext/hyperion_http/h2_codec_glue.c
143
167
  - ext/hyperion_http/llhttp/api.c
144
168
  - ext/hyperion_http/llhttp/http.c
145
169
  - ext/hyperion_http/llhttp/llhttp.c
146
170
  - ext/hyperion_http/llhttp/llhttp.h
171
+ - ext/hyperion_http/page_cache.c
147
172
  - ext/hyperion_http/parser.c
173
+ - ext/hyperion_http/sendfile.c
174
+ - ext/hyperion_http/websocket.c
175
+ - ext/hyperion_io_uring/Cargo.lock
176
+ - ext/hyperion_io_uring/Cargo.toml
177
+ - ext/hyperion_io_uring/extconf.rb
178
+ - ext/hyperion_io_uring/src/lib.rs
148
179
  - lib/hyperion-rb.rb
149
180
  - lib/hyperion.rb
150
181
  - lib/hyperion/adapter/rack.rb
182
+ - lib/hyperion/admin_listener.rb
151
183
  - lib/hyperion/admin_middleware.rb
152
184
  - lib/hyperion/c_parser.rb
153
185
  - lib/hyperion/cli.rb
154
186
  - lib/hyperion/config.rb
155
187
  - lib/hyperion/connection.rb
188
+ - lib/hyperion/deprecations.rb
189
+ - lib/hyperion/dispatch_mode.rb
156
190
  - lib/hyperion/fiber_local.rb
191
+ - lib/hyperion/h2_admission.rb
192
+ - lib/hyperion/h2_codec.rb
193
+ - lib/hyperion/http/page_cache.rb
194
+ - lib/hyperion/http/sendfile.rb
195
+ - lib/hyperion/http2/native_hpack_adapter.rb
157
196
  - lib/hyperion/http2_handler.rb
197
+ - lib/hyperion/io_uring.rb
198
+ - lib/hyperion/lint_wrapper_pool.rb
158
199
  - lib/hyperion/logger.rb
159
200
  - lib/hyperion/master.rb
160
201
  - lib/hyperion/metrics.rb
202
+ - lib/hyperion/metrics/path_templater.rb
161
203
  - lib/hyperion/parser.rb
162
204
  - lib/hyperion/pool.rb
163
205
  - lib/hyperion/prometheus_exporter.rb
164
206
  - lib/hyperion/request.rb
165
207
  - lib/hyperion/response_writer.rb
208
+ - lib/hyperion/runtime.rb
166
209
  - lib/hyperion/server.rb
210
+ - lib/hyperion/server/route_table.rb
211
+ - lib/hyperion/static_preload.rb
167
212
  - lib/hyperion/thread_pool.rb
168
213
  - lib/hyperion/tls.rb
169
214
  - lib/hyperion/version.rb
215
+ - lib/hyperion/websocket/close_codes.rb
216
+ - lib/hyperion/websocket/connection.rb
217
+ - lib/hyperion/websocket/frame.rb
218
+ - lib/hyperion/websocket/handshake.rb
170
219
  - lib/hyperion/worker.rb
171
220
  - lib/hyperion/worker_health.rb
172
221
  homepage: https://github.com/andrew-woblavobla/hyperion