hyperion-rb 2.16.3 → 2.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +75 -0
- data/ext/hyperion_http/extconf.rb +9 -0
- data/ext/hyperion_http/parser.c +93 -21
- data/ext/hyperion_http/response_writer.c +604 -0
- data/ext/hyperion_http/response_writer.h +28 -0
- data/ext/hyperion_io_uring/Cargo.lock +1 -1
- data/ext/hyperion_io_uring/Cargo.toml +1 -1
- data/ext/hyperion_io_uring/src/buffer_ring.rs +319 -0
- data/ext/hyperion_io_uring/src/hotpath.rs +645 -0
- data/ext/hyperion_io_uring/src/lib.rs +5 -1
- data/lib/hyperion/cli.rb +23 -0
- data/lib/hyperion/config.rb +9 -0
- data/lib/hyperion/connection.rb +209 -1
- data/lib/hyperion/http/response_writer.rb +46 -0
- data/lib/hyperion/io_uring.rb +270 -5
- data/lib/hyperion/response_writer.rb +91 -1
- data/lib/hyperion/server.rb +200 -4
- data/lib/hyperion/version.rb +1 -1
- data/lib/hyperion.rb +1 -0
- metadata +6 -1
|
@@ -103,6 +103,47 @@ module Hyperion
|
|
|
103
103
|
private
|
|
104
104
|
|
|
105
105
|
def write_buffered(io, status, headers, body, keep_alive:)
|
|
106
|
+
if c_path_eligible?(io)
|
|
107
|
+
date_str = cached_date
|
|
108
|
+
|
|
109
|
+
# Plan #2 — io_uring hotpath write-side: when the current
|
|
110
|
+
# accept fiber owns a HotpathRing AND the C ext exposes the
|
|
111
|
+
# via-ring entrypoint, route the response through a send SQE
|
|
112
|
+
# instead of issuing sendmsg(2) directly. The hotpath ring is
|
|
113
|
+
# set as a fiber-local by Server#run_accept_fiber_io_uring_hotpath
|
|
114
|
+
# (Task 2.3.3) and inherited by the dispatch fiber. Ring is
|
|
115
|
+
# nil for non-hotpath connections (TLS / accept-only ring /
|
|
116
|
+
# epoll path) — we fall through to direct-syscall write.
|
|
117
|
+
ring = Fiber[:hyperion_hotpath_ring]
|
|
118
|
+
if ring && !ring.closed? &&
|
|
119
|
+
::Hyperion::Http::ResponseWriter.respond_to?(:c_write_buffered_via_ring)
|
|
120
|
+
bytes_out = ::Hyperion::Http::ResponseWriter.c_write_buffered_via_ring(
|
|
121
|
+
io, status, headers, Array(body), keep_alive, date_str, ring.ptr
|
|
122
|
+
)
|
|
123
|
+
Hyperion.metrics.increment(:bytes_written, bytes_out)
|
|
124
|
+
Hyperion.metrics.increment(:hotpath_writes_via_ring)
|
|
125
|
+
body.close if body.respond_to?(:close)
|
|
126
|
+
return
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
bytes_out = ::Hyperion::Http::ResponseWriter.c_write_buffered(
|
|
130
|
+
io, status, headers, Array(body), keep_alive, date_str
|
|
131
|
+
)
|
|
132
|
+
if bytes_out == ::Hyperion::Http::ResponseWriter::WOULDBLOCK
|
|
133
|
+
# EAGAIN on the C path — fall back to the Ruby writer, which
|
|
134
|
+
# yields the fiber under Async / blocks the thread under
|
|
135
|
+
# threadpool correctly.
|
|
136
|
+
return write_buffered_ruby(io, status, headers, body, keep_alive: keep_alive)
|
|
137
|
+
end
|
|
138
|
+
Hyperion.metrics.increment(:bytes_written, bytes_out)
|
|
139
|
+
body.close if body.respond_to?(:close)
|
|
140
|
+
return
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
write_buffered_ruby(io, status, headers, body, keep_alive: keep_alive)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def write_buffered_ruby(io, status, headers, body, keep_alive:)
|
|
106
147
|
# Phase 1 buffers the full body so Content-Length is exact.
|
|
107
148
|
# Phase 2 introduces chunked transfer-encoding for streaming bodies;
|
|
108
149
|
# Phase 5 batches via IO::Buffer to avoid this intermediate String.
|
|
@@ -386,6 +427,55 @@ module Hyperion
|
|
|
386
427
|
false
|
|
387
428
|
end
|
|
388
429
|
|
|
430
|
+
# Plan #1 (perf roadmap) — predicate for the C-side direct-syscall
|
|
431
|
+
# write path. True when:
|
|
432
|
+
# (a) The Hyperion::Http::ResponseWriter C ext loaded.
|
|
433
|
+
# (b) `io` exposes a real kernel fd (real_fd_io? handles the
|
|
434
|
+
# SSLSocket / StringIO / IO-like-but-no-fileno cases).
|
|
435
|
+
# (c) The class-level operator switch hasn't been flipped off.
|
|
436
|
+
#
|
|
437
|
+
# Operators flip the switch off via:
|
|
438
|
+
# Hyperion::Http::ResponseWriter.c_writer_available = false
|
|
439
|
+
# (mirrors the Hyperion::ResponseWriter.page_cache_available pattern).
|
|
440
|
+
def c_path_eligible?(io)
|
|
441
|
+
return false unless defined?(::Hyperion::Http::ResponseWriter)
|
|
442
|
+
return false unless ::Hyperion::Http::ResponseWriter.c_writer_available?
|
|
443
|
+
return false unless real_fd_io?(io)
|
|
444
|
+
|
|
445
|
+
true
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def write_chunked(io, status, headers, body, keep_alive:)
|
|
449
|
+
if c_path_eligible?(io)
|
|
450
|
+
date_str = cached_date
|
|
451
|
+
begin
|
|
452
|
+
bytes_out = ::Hyperion::Http::ResponseWriter.c_write_chunked(
|
|
453
|
+
io, status, headers, body, keep_alive, date_str
|
|
454
|
+
)
|
|
455
|
+
rescue Errno::EAGAIN
|
|
456
|
+
# Mid-body backpressure on the chunked path leaves the wire
|
|
457
|
+
# in a half-written state; we cannot recover by falling back
|
|
458
|
+
# to the Ruby writer because the chunked head has already
|
|
459
|
+
# been emitted. Re-raise so Connection#serve closes the
|
|
460
|
+
# connection cleanly. This is the documented contract from
|
|
461
|
+
# response_writer.c hyp_chunked_drain.
|
|
462
|
+
raise
|
|
463
|
+
end
|
|
464
|
+
if bytes_out == ::Hyperion::Http::ResponseWriter::WOULDBLOCK
|
|
465
|
+
# Pre-body WOULDBLOCK — only the head failed to ship; nothing
|
|
466
|
+
# is on the wire yet. Fall back to the Ruby chunked writer,
|
|
467
|
+
# which yields under Async / blocks under threadpool correctly.
|
|
468
|
+
return write_chunked_ruby(io, status, headers, body, keep_alive: keep_alive)
|
|
469
|
+
end
|
|
470
|
+
Hyperion.metrics.increment(:bytes_written, bytes_out)
|
|
471
|
+
Hyperion.metrics.increment(:chunked_responses)
|
|
472
|
+
body.close if body.respond_to?(:close)
|
|
473
|
+
return
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
write_chunked_ruby(io, status, headers, body, keep_alive: keep_alive)
|
|
477
|
+
end
|
|
478
|
+
|
|
389
479
|
# Phase 5 — streaming chunked writer with per-response coalescing.
|
|
390
480
|
#
|
|
391
481
|
# Wire format per RFC 7230 §4.1:
|
|
@@ -405,7 +495,7 @@ module Hyperion
|
|
|
405
495
|
# past per-event coalescing latency.
|
|
406
496
|
# * body.close (or end-of-each) drains the buffer and appends the
|
|
407
497
|
# 0\r\n\r\n terminator in a single syscall (atomic w.r.t. the wire).
|
|
408
|
-
def
|
|
498
|
+
def write_chunked_ruby(io, status, headers, body, keep_alive:)
|
|
409
499
|
reason = REASONS[status] || 'Unknown'
|
|
410
500
|
date_str = cached_date
|
|
411
501
|
head = build_head_chunked(status, reason, headers, keep_alive, date_str)
|
data/lib/hyperion/server.rb
CHANGED
|
@@ -195,6 +195,7 @@ module Hyperion
|
|
|
195
195
|
tls_session_cache_size: TLS::DEFAULT_SESSION_CACHE_SIZE,
|
|
196
196
|
tls_ktls: :auto,
|
|
197
197
|
io_uring: :off,
|
|
198
|
+
io_uring_hotpath: :off,
|
|
198
199
|
max_in_flight_per_conn: nil,
|
|
199
200
|
tls_handshake_rate_limit: :unlimited,
|
|
200
201
|
route_table: nil,
|
|
@@ -243,6 +244,17 @@ module Hyperion
|
|
|
243
244
|
@io_uring_policy = io_uring
|
|
244
245
|
@io_uring_active = io_uring != :off && Hyperion::IOUring.resolve_policy!(io_uring)
|
|
245
246
|
log_io_uring_state_once
|
|
247
|
+
# Plan #2 — hotpath gate. Independent of @io_uring_active: the
|
|
248
|
+
# hotpath owns multishot accept + multishot recv + send SQEs on a
|
|
249
|
+
# single unified ring, while the accept-only path uses a simpler
|
|
250
|
+
# ring that only drives accept SQEs. The two paths are mutually
|
|
251
|
+
# exclusive at runtime — HotpathRing takes priority when active.
|
|
252
|
+
# Workers don't share hotpath rings across fork; each child opens
|
|
253
|
+
# its own ring lazily on first use inside `run_accept_fiber`.
|
|
254
|
+
@io_uring_hotpath_policy = io_uring_hotpath
|
|
255
|
+
@io_uring_hotpath_active = io_uring_hotpath != :off &&
|
|
256
|
+
Hyperion::IOUring.resolve_hotpath_policy!(io_uring_hotpath)
|
|
257
|
+
log_io_uring_hotpath_state_once
|
|
246
258
|
# 2.3-B: per-conn fairness cap (validated/finalized upstream by
|
|
247
259
|
# `Config#finalize!`; constructor accepts the resolved value, not
|
|
248
260
|
# a sentinel). nil = no cap (default). The cap propagates to
|
|
@@ -880,8 +892,15 @@ module Hyperion
|
|
|
880
892
|
# epoll branch — io_uring accept is wired only for the plain TCP
|
|
881
893
|
# listener; the SSL handshake still wants the userspace
|
|
882
894
|
# `accept` + `SSL_accept` dance.
|
|
895
|
+
#
|
|
896
|
+
# Plan #2: when `io_uring_hotpath: :auto/:on` resolves to active, the
|
|
897
|
+
# hotpath ring takes priority — it owns multishot accept + multishot recv
|
|
898
|
+
# + send SQEs. The accept-only ring and the epoll path are mutually
|
|
899
|
+
# exclusive with it. TLS still uses the epoll branch regardless.
|
|
883
900
|
def run_accept_fiber(task)
|
|
884
|
-
if @
|
|
901
|
+
if @io_uring_hotpath_active && !@tls
|
|
902
|
+
run_accept_fiber_io_uring_hotpath(task)
|
|
903
|
+
elsif @io_uring_active && !@tls
|
|
885
904
|
run_accept_fiber_io_uring(task)
|
|
886
905
|
else
|
|
887
906
|
run_accept_fiber_epoll(task)
|
|
@@ -905,7 +924,7 @@ module Hyperion
|
|
|
905
924
|
# working off a `::Socket` object identical to what
|
|
906
925
|
# `accept_nonblock` would have returned.
|
|
907
926
|
def run_accept_fiber_io_uring(task)
|
|
908
|
-
ring = Fiber
|
|
927
|
+
ring = Fiber[:hyperion_io_uring] ||= Hyperion::IOUring::Ring.new(queue_depth: 256)
|
|
909
928
|
listener_fd = listening_io.fileno
|
|
910
929
|
until @stopped
|
|
911
930
|
client_fd = ring.accept(listener_fd)
|
|
@@ -925,13 +944,168 @@ module Hyperion
|
|
|
925
944
|
end
|
|
926
945
|
run_accept_fiber_epoll(task)
|
|
927
946
|
ensure
|
|
928
|
-
ring = Fiber
|
|
947
|
+
ring = Fiber[:hyperion_io_uring]
|
|
929
948
|
if ring && !ring.closed?
|
|
930
949
|
ring.close
|
|
931
|
-
Fiber
|
|
950
|
+
Fiber[:hyperion_io_uring] = nil
|
|
932
951
|
end
|
|
933
952
|
end
|
|
934
953
|
|
|
954
|
+
# Plan #2 — io_uring hotpath accept loop. Opens a per-fiber
|
|
955
|
+
# HotpathRing on first use (multishot accept + multishot recv +
|
|
956
|
+
# send SQEs on one unified ring). For this task the loop only
|
|
957
|
+
# submits the multishot accept SQE and drains accept completions;
|
|
958
|
+
# the per-connection recv wiring is added in Task 2.3.4.
|
|
959
|
+
#
|
|
960
|
+
# On failure it closes the hotpath ring and falls back to the epoll
|
|
961
|
+
# path, matching the accept-only ring's fallback contract.
|
|
962
|
+
def run_accept_fiber_io_uring_hotpath(task)
|
|
963
|
+
ring = Fiber[:hyperion_hotpath_ring] ||=
|
|
964
|
+
Hyperion::IOUring::HotpathRing.new
|
|
965
|
+
listener_fd = listening_io.fileno
|
|
966
|
+
ring.submit_accept_multishot(listener_fd)
|
|
967
|
+
# Plan #2 Task 2.3.4 — per-connection state map.
|
|
968
|
+
# Maps client_fd (Integer) → Connection. Populated on OP_ACCEPT
|
|
969
|
+
# and cleared on OP_RECV result <= 0 (EOF / error). Single-fiber
|
|
970
|
+
# — no mutex needed.
|
|
971
|
+
hotpath_connections = {}
|
|
972
|
+
until @stopped
|
|
973
|
+
ring.each_completion(min_complete: 1, timeout_ms: 100) do |c|
|
|
974
|
+
case c[:op_kind]
|
|
975
|
+
when Hyperion::IOUring::HotpathRing::OP_ACCEPT
|
|
976
|
+
next if c[:result].negative?
|
|
977
|
+
|
|
978
|
+
client_fd = c[:result].to_i
|
|
979
|
+
socket = ::Socket.for_fd(client_fd)
|
|
980
|
+
socket.autoclose = true
|
|
981
|
+
apply_timeout(socket)
|
|
982
|
+
# Build a Connection for the accepted fd. io_uring_owned: true
|
|
983
|
+
# arms the guard in read_chunk (should never be called for
|
|
984
|
+
# these connections) and signals close_for_eof callers.
|
|
985
|
+
conn = Connection.new(runtime: @explicit_runtime ? @runtime : nil,
|
|
986
|
+
max_in_flight_per_conn: @max_in_flight_per_conn,
|
|
987
|
+
route_table: @route_table,
|
|
988
|
+
io_uring_owned: true,
|
|
989
|
+
app: @app)
|
|
990
|
+
conn.instance_variable_set(:@socket, socket)
|
|
991
|
+
# Server has no @metrics ivar — sibling accept paths bump
|
|
992
|
+
# these counters via runtime_metrics (record_dispatch et al.)
|
|
993
|
+
# or via Connection#serve, which we bypass here. Using @metrics
|
|
994
|
+
# here raised NoMethodError on the first ACCEPT completion and
|
|
995
|
+
# took down the --async-io + hotpath=on + 1w boot before
|
|
996
|
+
# wait_for_bind could land its first probe (row-19 BOOT-FAIL).
|
|
997
|
+
runtime_metrics.increment(:connections_accepted)
|
|
998
|
+
runtime_metrics.increment(:connections_active)
|
|
999
|
+
hotpath_connections[client_fd] = conn
|
|
1000
|
+
# Post the first multishot-recv SQE for this fd. From here
|
|
1001
|
+
# on, the kernel delivers recv CQEs for every batch of bytes
|
|
1002
|
+
# that arrives on this socket until EOF or cancel.
|
|
1003
|
+
ring.submit_recv_multishot(client_fd)
|
|
1004
|
+
|
|
1005
|
+
when Hyperion::IOUring::HotpathRing::OP_RECV
|
|
1006
|
+
fd = c[:fd]
|
|
1007
|
+
result = c[:result]
|
|
1008
|
+
buf_id = c[:buf_id]
|
|
1009
|
+
|
|
1010
|
+
conn = hotpath_connections[fd]
|
|
1011
|
+
next unless conn
|
|
1012
|
+
|
|
1013
|
+
if result <= 0
|
|
1014
|
+
# 0 = peer EOF; negative = error. Clean up and discard.
|
|
1015
|
+
hotpath_connections.delete(fd)
|
|
1016
|
+
conn.close_for_eof
|
|
1017
|
+
begin
|
|
1018
|
+
conn.socket&.close unless conn.socket&.closed?
|
|
1019
|
+
rescue StandardError
|
|
1020
|
+
nil
|
|
1021
|
+
end
|
|
1022
|
+
else
|
|
1023
|
+
# Copy `result` bytes from the kernel buffer slot into a
|
|
1024
|
+
# Ruby String (one allocation), then release the slot so
|
|
1025
|
+
# the kernel can reuse it for the next recv.
|
|
1026
|
+
bytes = ring.copy_buffer(buf_id, result)
|
|
1027
|
+
ring.release_buffer(buf_id)
|
|
1028
|
+
conn.feed_read_bytes(bytes)
|
|
1029
|
+
end
|
|
1030
|
+
end
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
# Plan #2 Task 2.5.2 — per-worker fallback-engaged detection.
|
|
1034
|
+
# After each completion drain, check whether the ring went
|
|
1035
|
+
# unhealthy (sustained SQE submit failures / repeated EBADR
|
|
1036
|
+
# from the Rust side set a dirty flag). When detected:
|
|
1037
|
+
# 1. Increment the observable metric so operators can alert.
|
|
1038
|
+
# 2. Emit a single warn-level log line (actionable, not spammy).
|
|
1039
|
+
# 3. Flip @io_uring_hotpath_active to false so subsequent
|
|
1040
|
+
# accept-fiber dispatch (run_accept_fiber's top-level branch)
|
|
1041
|
+
# uses the epoll path for newly-spawned accept fibers on
|
|
1042
|
+
# restart — the current fiber exits the loop and falls
|
|
1043
|
+
# through to run_accept_fiber_epoll below.
|
|
1044
|
+
unless ring.healthy?
|
|
1045
|
+
runtime_metrics.increment(:io_uring_hotpath_fallback_engaged)
|
|
1046
|
+
runtime_logger.warn do
|
|
1047
|
+
{ message: 'io_uring hotpath ring unhealthy; engaging accept4 fallback per-worker',
|
|
1048
|
+
worker_pid: Process.pid }
|
|
1049
|
+
end
|
|
1050
|
+
@io_uring_hotpath_active = false
|
|
1051
|
+
begin
|
|
1052
|
+
ring.close
|
|
1053
|
+
rescue StandardError
|
|
1054
|
+
nil
|
|
1055
|
+
end
|
|
1056
|
+
Fiber[:hyperion_hotpath_ring] = nil
|
|
1057
|
+
break
|
|
1058
|
+
end
|
|
1059
|
+
end
|
|
1060
|
+
# If we broke out due to an unhealthy ring (not a clean stop), fall
|
|
1061
|
+
# through to the epoll path so existing + new connections keep being
|
|
1062
|
+
# served. @stopped is still false in that case — the server is alive.
|
|
1063
|
+
run_accept_fiber_epoll(task) unless @stopped
|
|
1064
|
+
rescue IOError, Errno::EBADF
|
|
1065
|
+
@stopped = true
|
|
1066
|
+
rescue Hyperion::IOUring::Unsupported => e
|
|
1067
|
+
runtime_logger.warn do
|
|
1068
|
+
{ message: 'io_uring hotpath unsupported at fiber open; falling back to epoll',
|
|
1069
|
+
error: e.message }
|
|
1070
|
+
end
|
|
1071
|
+
close_hotpath_ring_for_fallback
|
|
1072
|
+
run_accept_fiber_epoll(task)
|
|
1073
|
+
rescue StandardError => e
|
|
1074
|
+
runtime_logger.warn do
|
|
1075
|
+
{ message: 'io_uring hotpath accept fiber error; falling back to epoll',
|
|
1076
|
+
error: e.message, error_class: e.class.name }
|
|
1077
|
+
end
|
|
1078
|
+
close_hotpath_ring_for_fallback
|
|
1079
|
+
run_accept_fiber_epoll(task)
|
|
1080
|
+
ensure
|
|
1081
|
+
close_hotpath_ring_for_fallback
|
|
1082
|
+
end
|
|
1083
|
+
|
|
1084
|
+
# Cancel the multishot-accept SQE armed on @listener_fd by closing
|
|
1085
|
+
# the hotpath ring before the epoll fallback takes over. Without this,
|
|
1086
|
+
# `accept_or_nil` in run_accept_fiber_epoll competes with a still-armed
|
|
1087
|
+
# kernel-side multishot-accept consumer and inbound connections can be
|
|
1088
|
+
# delivered to a dead CQ. Idempotent.
|
|
1089
|
+
def close_hotpath_ring_for_fallback
|
|
1090
|
+
ring = Fiber[:hyperion_hotpath_ring]
|
|
1091
|
+
return unless ring && !ring.closed?
|
|
1092
|
+
|
|
1093
|
+
ring.close
|
|
1094
|
+
Fiber[:hyperion_hotpath_ring] = nil
|
|
1095
|
+
rescue StandardError
|
|
1096
|
+
nil
|
|
1097
|
+
end
|
|
1098
|
+
private :close_hotpath_ring_for_fallback
|
|
1099
|
+
|
|
1100
|
+
# Plan #2 — test seam: returns the active HotpathRing on the current
|
|
1101
|
+
# accept fiber, or nil if none. Used by io_uring_hotpath_fallback_engaged_spec
|
|
1102
|
+
# to inject force_unhealthy! without exposing the ring through the
|
|
1103
|
+
# public Server surface.
|
|
1104
|
+
def hotpath_ring_for_test
|
|
1105
|
+
Fiber[:hyperion_hotpath_ring]
|
|
1106
|
+
end
|
|
1107
|
+
private :hotpath_ring_for_test
|
|
1108
|
+
|
|
935
1109
|
# Boot-time log line per worker capturing the resolved io_uring
|
|
936
1110
|
# state. Mirrors the `log_ktls_state_once` pattern from 2.2.0.
|
|
937
1111
|
# Single-shot via the class-level ivar so multi-worker boots
|
|
@@ -953,6 +1127,28 @@ module Hyperion
|
|
|
953
1127
|
nil
|
|
954
1128
|
end
|
|
955
1129
|
|
|
1130
|
+
# Plan #2 — boot-time log for the hotpath gate. Single-shot via the
|
|
1131
|
+
# class-level ivar so multi-worker boots don't fan into N identical
|
|
1132
|
+
# lines. Mirrors the log_io_uring_state_once pattern.
|
|
1133
|
+
def log_io_uring_hotpath_state_once
|
|
1134
|
+
return if Hyperion::Server.instance_variable_get(:@io_uring_hotpath_state_logged)
|
|
1135
|
+
return if @io_uring_hotpath_policy == :off
|
|
1136
|
+
|
|
1137
|
+
Hyperion::Server.instance_variable_set(:@io_uring_hotpath_state_logged, true)
|
|
1138
|
+
runtime_logger.info do
|
|
1139
|
+
{
|
|
1140
|
+
message: 'io_uring hotpath state',
|
|
1141
|
+
policy: @io_uring_hotpath_policy,
|
|
1142
|
+
active: @io_uring_hotpath_active,
|
|
1143
|
+
kernel_ok: Hyperion::IOUring.kernel_supports_io_uring?,
|
|
1144
|
+
hotpath_supported: Hyperion::IOUring.respond_to?(:hotpath_supported?) &&
|
|
1145
|
+
Hyperion::IOUring.hotpath_supported?
|
|
1146
|
+
}
|
|
1147
|
+
end
|
|
1148
|
+
rescue StandardError
|
|
1149
|
+
nil
|
|
1150
|
+
end
|
|
1151
|
+
|
|
956
1152
|
def dispatch(socket)
|
|
957
1153
|
alpn = socket.is_a?(::OpenSSL::SSL::SSLSocket) ? socket.alpn_protocol : nil
|
|
958
1154
|
mode = DispatchMode.resolve(tls: !@tls.nil?, async_io: @async_io,
|
data/lib/hyperion/version.rb
CHANGED
data/lib/hyperion.rb
CHANGED
|
@@ -263,6 +263,7 @@ require_relative 'hyperion/parser'
|
|
|
263
263
|
require_relative 'hyperion/c_parser'
|
|
264
264
|
require_relative 'hyperion/http/sendfile'
|
|
265
265
|
require_relative 'hyperion/http/page_cache'
|
|
266
|
+
require_relative 'hyperion/http/response_writer'
|
|
266
267
|
require_relative 'hyperion/static_preload'
|
|
267
268
|
require_relative 'hyperion/adapter/rack'
|
|
268
269
|
require_relative 'hyperion/lint_wrapper_pool'
|
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: 2.16.
|
|
4
|
+
version: 2.16.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrey Lobanov
|
|
@@ -173,11 +173,15 @@ files:
|
|
|
173
173
|
- ext/hyperion_http/page_cache.c
|
|
174
174
|
- ext/hyperion_http/page_cache_internal.h
|
|
175
175
|
- ext/hyperion_http/parser.c
|
|
176
|
+
- ext/hyperion_http/response_writer.c
|
|
177
|
+
- ext/hyperion_http/response_writer.h
|
|
176
178
|
- ext/hyperion_http/sendfile.c
|
|
177
179
|
- ext/hyperion_http/websocket.c
|
|
178
180
|
- ext/hyperion_io_uring/Cargo.lock
|
|
179
181
|
- ext/hyperion_io_uring/Cargo.toml
|
|
180
182
|
- ext/hyperion_io_uring/extconf.rb
|
|
183
|
+
- ext/hyperion_io_uring/src/buffer_ring.rs
|
|
184
|
+
- ext/hyperion_io_uring/src/hotpath.rs
|
|
181
185
|
- ext/hyperion_io_uring/src/lib.rs
|
|
182
186
|
- lib/hyperion-rb.rb
|
|
183
187
|
- lib/hyperion.rb
|
|
@@ -194,6 +198,7 @@ files:
|
|
|
194
198
|
- lib/hyperion/h2_admission.rb
|
|
195
199
|
- lib/hyperion/h2_codec.rb
|
|
196
200
|
- lib/hyperion/http/page_cache.rb
|
|
201
|
+
- lib/hyperion/http/response_writer.rb
|
|
197
202
|
- lib/hyperion/http/sendfile.rb
|
|
198
203
|
- lib/hyperion/http2/native_hpack_adapter.rb
|
|
199
204
|
- lib/hyperion/http2_handler.rb
|