raptor 0.7.0 → 0.9.0
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/.buildkite/pipeline.yml +90 -32
- data/.dockerignore +5 -0
- data/CHANGELOG.md +30 -0
- data/Dockerfile +28 -0
- data/README.md +159 -27
- data/Rakefile +19 -1
- data/docs/raptor-vs-puma.md +557 -0
- data/ext/raptor_bpf/reuseport_select.bpf.c +63 -0
- data/ext/raptor_http/extconf.rb +1 -1
- data/ext/raptor_native/extconf.rb +7 -0
- data/ext/raptor_native/raptor_native.c +99 -0
- data/lib/rackup/handler/raptor.rb +12 -2
- data/lib/raptor/binder.rb +139 -31
- data/lib/raptor/cli.rb +83 -22
- data/lib/raptor/cluster.rb +214 -33
- data/lib/raptor/http.rb +116 -0
- data/lib/raptor/{request.rb → http1.rb} +228 -99
- data/lib/raptor/http2.rb +85 -40
- data/lib/raptor/reactor.rb +22 -15
- data/lib/raptor/reuseport_bpf.rb +108 -0
- data/lib/raptor/server.rb +92 -43
- data/lib/raptor/stats.rb +1 -1
- data/lib/raptor/systemd.rb +69 -0
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/binder.rbs +82 -5
- data/sig/generated/raptor/cli.rbs +2 -3
- data/sig/generated/raptor/cluster.rbs +91 -13
- data/sig/generated/raptor/http.rbs +65 -0
- data/sig/generated/raptor/{request.rbs → http1.rbs} +122 -35
- data/sig/generated/raptor/http2.rbs +46 -13
- data/sig/generated/raptor/reactor.rbs +18 -11
- data/sig/generated/raptor/reuseport_bpf.rbs +56 -0
- data/sig/generated/raptor/server.rbs +49 -21
- data/sig/generated/raptor/systemd.rbs +42 -0
- metadata +30 -3
data/lib/raptor/server.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "socket"
|
|
|
5
5
|
|
|
6
6
|
require "atomic-ruby/atomic_boolean"
|
|
7
7
|
|
|
8
|
+
require_relative "reuseport_bpf"
|
|
9
|
+
|
|
8
10
|
module Raptor
|
|
9
11
|
# Accepts client connections and dispatches them into the request
|
|
10
12
|
# pipeline. Skips acceptance when the reactor backlog is high so an
|
|
@@ -19,9 +21,10 @@ module Raptor
|
|
|
19
21
|
#
|
|
20
22
|
# @example
|
|
21
23
|
# binder = Binder.new(["tcp://0.0.0.0:3000"])
|
|
22
|
-
# reactor = Reactor.new(ractor_pool, thread_pool,
|
|
23
|
-
#
|
|
24
|
-
#
|
|
24
|
+
# reactor = Reactor.new(ractor_pool, thread_pool, connection_options: {}, http1_options: {})
|
|
25
|
+
# http1 = Http1.new(app, 3000)
|
|
26
|
+
# http2 = Http2.new(app, 3000)
|
|
27
|
+
# server = Server.new(binder, reactor, thread_pool, http1, http2, connection_options: { first_data_timeout: 30 }, listeners: binder.listeners)
|
|
25
28
|
# server.run
|
|
26
29
|
# # ... later
|
|
27
30
|
# server.shutdown
|
|
@@ -35,13 +38,17 @@ module Raptor
|
|
|
35
38
|
DEFAULT_REMOTE_ADDR = "127.0.0.1"
|
|
36
39
|
DEFAULT_SERVER_NAME = "localhost"
|
|
37
40
|
|
|
38
|
-
MIN_BACKPRESSURE_THRESHOLD =
|
|
41
|
+
MIN_BACKPRESSURE_THRESHOLD = 8
|
|
39
42
|
|
|
40
43
|
# @rbs @binder: Binder
|
|
44
|
+
# @rbs @listeners: Array[TCPServer | UNIXServer | Binder::SslListener]
|
|
41
45
|
# @rbs @reactor: Reactor
|
|
42
46
|
# @rbs @thread_pool: AtomicThreadPool
|
|
43
|
-
# @rbs @
|
|
44
|
-
# @rbs @
|
|
47
|
+
# @rbs @http1: Http1
|
|
48
|
+
# @rbs @http2: Http2
|
|
49
|
+
# @rbs @first_data_timeout: Integer
|
|
50
|
+
# @rbs @drain_accept_queue: bool
|
|
51
|
+
# @rbs @bpf_active: bool
|
|
45
52
|
# @rbs @running: AtomicBoolean
|
|
46
53
|
|
|
47
54
|
# Creates a new Server instance.
|
|
@@ -49,66 +56,109 @@ module Raptor
|
|
|
49
56
|
# @param binder [Binder] the binder managing listening sockets
|
|
50
57
|
# @param reactor [Reactor] the reactor for handling client connections
|
|
51
58
|
# @param thread_pool [AtomicThreadPool] thread pool for application processing
|
|
52
|
-
# @param
|
|
53
|
-
# @param
|
|
59
|
+
# @param http1 [Http1] the HTTP/1.1 handler
|
|
60
|
+
# @param http2 [Http2] the HTTP/2 handler (provides the initial SETTINGS frame)
|
|
61
|
+
# @param connection_options [Hash] per-connection timeout configuration, used to bound TLS handshakes
|
|
62
|
+
# @param listeners [Array] the per-worker listeners this server accepts on
|
|
63
|
+
# @param drain_accept_queue [Boolean] whether to drain the kernel accept queue on shutdown
|
|
64
|
+
# @param worker_index [Integer, nil] the slot index for BPF load reporting
|
|
54
65
|
# @return [void]
|
|
55
66
|
#
|
|
56
|
-
# @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool,
|
|
57
|
-
def initialize(binder, reactor, thread_pool,
|
|
67
|
+
# @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Http1 http1, Http2 http2, connection_options: Hash[Symbol, untyped], listeners: Array[untyped], ?drain_accept_queue: bool, ?worker_index: Integer?) -> void
|
|
68
|
+
def initialize(binder, reactor, thread_pool, http1, http2, connection_options:, listeners:, drain_accept_queue: false, worker_index: nil)
|
|
58
69
|
@binder = binder
|
|
70
|
+
@listeners = listeners
|
|
59
71
|
@reactor = reactor
|
|
60
72
|
@thread_pool = thread_pool
|
|
61
|
-
@
|
|
62
|
-
@
|
|
73
|
+
@http1 = http1
|
|
74
|
+
@http2 = http2
|
|
75
|
+
@first_data_timeout = connection_options[:first_data_timeout]
|
|
76
|
+
@drain_accept_queue = drain_accept_queue
|
|
77
|
+
@bpf_active = !!worker_index
|
|
63
78
|
@running = AtomicBoolean.new(true)
|
|
64
79
|
end
|
|
65
80
|
|
|
66
81
|
# Starts the server's main accept loop in a new thread.
|
|
67
82
|
#
|
|
68
83
|
# The accept loop polls listening sockets for ready connections and accepts
|
|
69
|
-
# them when
|
|
70
|
-
#
|
|
71
|
-
# worker
|
|
84
|
+
# them when the reactor backlog is under the backpressure threshold. On
|
|
85
|
+
# Linux with BPF-directed dispatch active, a companion thread publishes
|
|
86
|
+
# this worker's backlog to the BPF map.
|
|
72
87
|
#
|
|
73
88
|
# @return [Thread] the thread running the accept loop
|
|
74
89
|
#
|
|
75
90
|
# @rbs () -> Thread
|
|
76
91
|
def run
|
|
77
|
-
|
|
92
|
+
spawn_load_reporter if @bpf_active
|
|
93
|
+
|
|
94
|
+
Thread.new do
|
|
78
95
|
Thread.current.name = "Server"
|
|
79
96
|
|
|
80
|
-
|
|
97
|
+
backpressure_threshold = [(@thread_pool.size * 1.2).ceil, MIN_BACKPRESSURE_THRESHOLD].max
|
|
98
|
+
|
|
99
|
+
while @running.true?
|
|
81
100
|
begin
|
|
82
|
-
ready_servers, _, _ = IO.select(
|
|
101
|
+
ready_servers, _, _ = IO.select(@listeners, nil, nil, 1)
|
|
83
102
|
rescue IOError, Errno::EBADF
|
|
84
103
|
break
|
|
85
104
|
end
|
|
86
105
|
|
|
87
106
|
next unless ready_servers
|
|
88
|
-
next if @reactor.backlog >=
|
|
107
|
+
next if @reactor.backlog >= backpressure_threshold
|
|
89
108
|
|
|
90
|
-
ready_servers.each
|
|
91
|
-
accept_connection(listener, reactor)
|
|
92
|
-
end
|
|
109
|
+
ready_servers.each { |listener| accept_connection(listener) }
|
|
93
110
|
end
|
|
94
111
|
end
|
|
95
112
|
end
|
|
96
113
|
|
|
97
114
|
# Gracefully shuts down the server.
|
|
98
115
|
#
|
|
99
|
-
# Stops accepting new connections and closes all listening sockets.
|
|
100
|
-
#
|
|
116
|
+
# Stops accepting new connections and closes all listening sockets. When
|
|
117
|
+
# `drain_accept_queue` is enabled, dispatches every connection already in
|
|
118
|
+
# the kernel accept queue before closing the listeners.
|
|
101
119
|
#
|
|
102
120
|
# @return [void]
|
|
103
121
|
#
|
|
104
122
|
# @rbs () -> void
|
|
105
123
|
def shutdown
|
|
106
124
|
@running.make_false
|
|
107
|
-
@
|
|
125
|
+
drain_accept_queue if @drain_accept_queue
|
|
126
|
+
@listeners.each(&:close)
|
|
108
127
|
end
|
|
109
128
|
|
|
110
129
|
private
|
|
111
130
|
|
|
131
|
+
# Starts a background thread that publishes this worker's reactor
|
|
132
|
+
# backlog to the BPF map for load-aware dispatch.
|
|
133
|
+
#
|
|
134
|
+
# @return [void]
|
|
135
|
+
#
|
|
136
|
+
# @rbs () -> void
|
|
137
|
+
def spawn_load_reporter
|
|
138
|
+
Thread.new do
|
|
139
|
+
Thread.current.name = "Load Reporter"
|
|
140
|
+
|
|
141
|
+
while @running.true?
|
|
142
|
+
ReuseportBPF.update_load(@reactor.backlog)
|
|
143
|
+
sleep 0.01
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Dispatches every connection already in the kernel accept queue for each
|
|
149
|
+
# listener until all are drained.
|
|
150
|
+
#
|
|
151
|
+
# @return [void]
|
|
152
|
+
#
|
|
153
|
+
# @rbs () -> void
|
|
154
|
+
def drain_accept_queue
|
|
155
|
+
loop do
|
|
156
|
+
accepted = false
|
|
157
|
+
@listeners.each { |listener| accepted = true if accept_connection(listener) }
|
|
158
|
+
break unless accepted
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
112
162
|
# Accepts a connection from the given listener and dispatches it.
|
|
113
163
|
#
|
|
114
164
|
# For SSL listeners the TLS handshake is offloaded to the thread pool so
|
|
@@ -118,15 +168,14 @@ module Raptor
|
|
|
118
168
|
# follow the HTTP/1.1 path.
|
|
119
169
|
#
|
|
120
170
|
# @param listener [TCPServer, UNIXServer, Binder::SslListener] the ready listener
|
|
121
|
-
# @
|
|
122
|
-
# @return [void]
|
|
171
|
+
# @return [Boolean] true if a connection was accepted, false if the listener had nothing to dispatch
|
|
123
172
|
#
|
|
124
|
-
# @rbs (TCPServer | UNIXServer | Binder::SslListener listener
|
|
125
|
-
def accept_connection(listener
|
|
173
|
+
# @rbs (TCPServer | UNIXServer | Binder::SslListener listener) -> bool
|
|
174
|
+
def accept_connection(listener)
|
|
126
175
|
tcp_client = begin
|
|
127
176
|
listener.is_a?(Binder::SslListener) ? listener.tcp_server.accept_nonblock : listener.accept_nonblock
|
|
128
177
|
rescue IO::WaitReadable
|
|
129
|
-
return
|
|
178
|
+
return false
|
|
130
179
|
end
|
|
131
180
|
|
|
132
181
|
if tcp_client.is_a?(TCPSocket)
|
|
@@ -138,19 +187,20 @@ module Raptor
|
|
|
138
187
|
|
|
139
188
|
if listener.is_a?(Binder::SslListener)
|
|
140
189
|
@thread_pool << proc do
|
|
141
|
-
dispatch_ssl_connection(listener, tcp_client, remote_addr
|
|
190
|
+
dispatch_ssl_connection(listener, tcp_client, remote_addr)
|
|
142
191
|
end
|
|
143
|
-
return
|
|
192
|
+
return true
|
|
144
193
|
end
|
|
145
194
|
|
|
146
|
-
@
|
|
195
|
+
@http1.eager_accept(
|
|
147
196
|
tcp_client,
|
|
148
197
|
tcp_client.object_id,
|
|
149
|
-
reactor,
|
|
198
|
+
@reactor,
|
|
150
199
|
@thread_pool,
|
|
151
200
|
remote_addr,
|
|
152
201
|
HTTP_SCHEME
|
|
153
202
|
)
|
|
203
|
+
true
|
|
154
204
|
end
|
|
155
205
|
|
|
156
206
|
# Performs the TLS handshake for an accepted SSL connection and dispatches
|
|
@@ -160,35 +210,34 @@ module Raptor
|
|
|
160
210
|
# @param listener [Binder::SslListener] the SSL listener that accepted the connection
|
|
161
211
|
# @param tcp_client [TCPSocket] the accepted TCP socket
|
|
162
212
|
# @param remote_addr [String] the client's IP address
|
|
163
|
-
# @param reactor [Reactor] the reactor to dispatch the connection to
|
|
164
213
|
# @return [void]
|
|
165
214
|
#
|
|
166
|
-
# @rbs (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr
|
|
167
|
-
def dispatch_ssl_connection(listener, tcp_client, remote_addr
|
|
215
|
+
# @rbs (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr) -> void
|
|
216
|
+
def dispatch_ssl_connection(listener, tcp_client, remote_addr)
|
|
168
217
|
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_client, listener.ssl_context)
|
|
169
218
|
ssl_socket.sync_close = true
|
|
170
219
|
return unless perform_ssl_handshake(ssl_socket)
|
|
171
220
|
|
|
172
221
|
if ssl_socket.alpn_protocol == H2_PROTOCOL
|
|
173
|
-
ssl_socket.write(
|
|
222
|
+
ssl_socket.write(@http2.initial_settings_frame) rescue nil
|
|
174
223
|
|
|
175
|
-
reactor.add(
|
|
224
|
+
@reactor.add(
|
|
176
225
|
id: ssl_socket.object_id,
|
|
177
226
|
socket: ssl_socket,
|
|
178
227
|
remote_addr: remote_addr,
|
|
179
228
|
url_scheme: HTTPS_SCHEME,
|
|
180
229
|
protocol: :http2,
|
|
181
|
-
writer:
|
|
230
|
+
writer: @http2.create_writer,
|
|
182
231
|
flow_control: Http2::FlowControl.new
|
|
183
232
|
)
|
|
184
233
|
|
|
185
234
|
return
|
|
186
235
|
end
|
|
187
236
|
|
|
188
|
-
@
|
|
237
|
+
@http1.eager_accept(
|
|
189
238
|
ssl_socket,
|
|
190
239
|
ssl_socket.object_id,
|
|
191
|
-
reactor,
|
|
240
|
+
@reactor,
|
|
192
241
|
@thread_pool,
|
|
193
242
|
remote_addr,
|
|
194
243
|
HTTPS_SCHEME
|
|
@@ -204,7 +253,7 @@ module Raptor
|
|
|
204
253
|
#
|
|
205
254
|
# @rbs (OpenSSL::SSL::SSLSocket ssl_socket) -> bool
|
|
206
255
|
def perform_ssl_handshake(ssl_socket)
|
|
207
|
-
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @
|
|
256
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @first_data_timeout
|
|
208
257
|
|
|
209
258
|
begin
|
|
210
259
|
ssl_socket.accept_nonblock
|
data/lib/raptor/stats.rb
CHANGED
|
@@ -60,7 +60,7 @@ module Raptor
|
|
|
60
60
|
# @rbs (Integer index, pid: Integer, phase: Integer, requests: Integer, backlog: Integer, busy_threads: Integer, thread_capacity: Integer, started_at: Float, last_checkin: Float, booted: bool) -> void
|
|
61
61
|
def write(index, pid:, phase:, requests:, backlog:, busy_threads:, thread_capacity:, started_at:, last_checkin:, booted:)
|
|
62
62
|
data = [pid, index, phase, requests, backlog, busy_threads, thread_capacity, started_at, last_checkin, booted ? 1 : 0].pack(SLOT_FORMAT)
|
|
63
|
-
@mmap
|
|
63
|
+
@mmap[index * SLOT_SIZE, SLOT_SIZE] = data
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# Returns stats for all worker slots.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "socket"
|
|
5
|
+
|
|
6
|
+
module Raptor
|
|
7
|
+
# Integration with systemd's service notification protocol and
|
|
8
|
+
# socket-activation file descriptors.
|
|
9
|
+
#
|
|
10
|
+
module Systemd
|
|
11
|
+
LISTEN_FDS_START = 3
|
|
12
|
+
|
|
13
|
+
LISTEN_FDNAMES_ENV = "LISTEN_FDNAMES"
|
|
14
|
+
LISTEN_FDS_ENV = "LISTEN_FDS"
|
|
15
|
+
LISTEN_PID_ENV = "LISTEN_PID"
|
|
16
|
+
NOTIFY_SOCKET_ENV = "NOTIFY_SOCKET"
|
|
17
|
+
|
|
18
|
+
# Sends `message` to the systemd notification socket, returning true on
|
|
19
|
+
# success and false when the socket is unset or the send fails.
|
|
20
|
+
#
|
|
21
|
+
# @param message [String] notify protocol payload, e.g. "READY=1"
|
|
22
|
+
# @return [Boolean]
|
|
23
|
+
#
|
|
24
|
+
# @rbs (String message) -> bool
|
|
25
|
+
def self.notify(message)
|
|
26
|
+
socket_path = ENV[NOTIFY_SOCKET_ENV]
|
|
27
|
+
return false if socket_path.nil? || socket_path.empty?
|
|
28
|
+
|
|
29
|
+
address = if socket_path.start_with?("@")
|
|
30
|
+
Socket.pack_sockaddr_un("\0#{socket_path[1..]}")
|
|
31
|
+
else
|
|
32
|
+
Socket.pack_sockaddr_un(socket_path)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
|
|
36
|
+
socket.send(message, 0, address)
|
|
37
|
+
true
|
|
38
|
+
rescue SystemCallError, IOError
|
|
39
|
+
false
|
|
40
|
+
ensure
|
|
41
|
+
socket&.close
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the file descriptors passed in via socket activation, or an
|
|
45
|
+
# empty array when systemd has not exported any.
|
|
46
|
+
#
|
|
47
|
+
# @return [Array<Integer>]
|
|
48
|
+
#
|
|
49
|
+
# @rbs () -> Array[Integer]
|
|
50
|
+
def self.listen_fds
|
|
51
|
+
return [] unless ENV[LISTEN_PID_ENV]&.to_i == Process.pid
|
|
52
|
+
|
|
53
|
+
count = ENV[LISTEN_FDS_ENV]&.to_i || 0
|
|
54
|
+
Array.new(count) { |index| LISTEN_FDS_START + index }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Clears the socket-activation environment variables so children don't
|
|
58
|
+
# act on stale values.
|
|
59
|
+
#
|
|
60
|
+
# @return [void]
|
|
61
|
+
#
|
|
62
|
+
# @rbs () -> void
|
|
63
|
+
def self.clear_listen_env
|
|
64
|
+
ENV.delete(LISTEN_FDNAMES_ENV)
|
|
65
|
+
ENV.delete(LISTEN_FDS_ENV)
|
|
66
|
+
ENV.delete(LISTEN_PID_ENV)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/raptor/version.rb
CHANGED
|
@@ -51,10 +51,26 @@ module Raptor
|
|
|
51
51
|
def members: () -> [ :tcp_server, :ssl_context ]
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
@
|
|
54
|
+
@uri_listeners: Hash[String, Array[TCPServer | UNIXServer | SslListener]]
|
|
55
55
|
|
|
56
56
|
@listeners: Array[TCPServer | UNIXServer | SslListener]
|
|
57
57
|
|
|
58
|
+
@inherited_fds: Hash[String, Array[Integer]]
|
|
59
|
+
|
|
60
|
+
@socket_backlog: Integer
|
|
61
|
+
|
|
62
|
+
@bind_uris: Array[String]
|
|
63
|
+
|
|
64
|
+
# Array of bind URIs.
|
|
65
|
+
#
|
|
66
|
+
# @return [Array<String>] the bind URIs
|
|
67
|
+
attr_reader bind_uris: untyped
|
|
68
|
+
|
|
69
|
+
# Kernel listen() queue depth for TCP/SSL listeners.
|
|
70
|
+
#
|
|
71
|
+
# @return [Integer] the socket backlog
|
|
72
|
+
attr_reader socket_backlog: untyped
|
|
73
|
+
|
|
58
74
|
# Array of listening sockets.
|
|
59
75
|
#
|
|
60
76
|
# @return [Array<TCPServer, UNIXServer, SslListener>] the server sockets
|
|
@@ -64,17 +80,21 @@ module Raptor
|
|
|
64
80
|
#
|
|
65
81
|
# Parses the provided bind URIs and creates listening sockets for each one.
|
|
66
82
|
# Supports tcp://, unix://, and ssl:// schemes. Localhost is expanded to
|
|
67
|
-
# all available loopback addresses (both IPv4 and IPv6).
|
|
83
|
+
# all available loopback addresses (both IPv4 and IPv6). When `inherited_fds`
|
|
84
|
+
# supplies file descriptors for a URI, the listener is reconstructed from
|
|
85
|
+
# those FDs instead of binding fresh.
|
|
68
86
|
#
|
|
69
87
|
# @param bind_uris [Array<String>] array of URI strings to bind to
|
|
88
|
+
# @param socket_backlog [Integer] kernel listen() queue depth for TCP/SSL listeners
|
|
89
|
+
# @param inherited_fds [Hash{String => Array<Integer>}] inherited listener FDs keyed by bind URI
|
|
70
90
|
# @return [void]
|
|
71
91
|
# @raise [UnknownBindSchemeError] if a URI has an unsupported scheme
|
|
72
92
|
#
|
|
73
93
|
# @example
|
|
74
94
|
# binder = Binder.new(["tcp://0.0.0.0:3000", "unix:///tmp/raptor.sock"])
|
|
75
95
|
#
|
|
76
|
-
# @rbs (Array[String] bind_uris) -> void
|
|
77
|
-
def initialize: (Array[String] bind_uris) -> void
|
|
96
|
+
# @rbs (Array[String] bind_uris, ?socket_backlog: Integer, ?inherited_fds: Hash[String, Array[Integer]]) -> void
|
|
97
|
+
def initialize: (Array[String] bind_uris, ?socket_backlog: Integer, ?inherited_fds: Hash[String, Array[Integer]]) -> void
|
|
78
98
|
|
|
79
99
|
# Returns the bound addresses as strings.
|
|
80
100
|
#
|
|
@@ -106,9 +126,27 @@ module Raptor
|
|
|
106
126
|
# @rbs () -> void
|
|
107
127
|
def close: () -> void
|
|
108
128
|
|
|
129
|
+
# Returns the file descriptors of every listener, grouped by the bind URI
|
|
130
|
+
# they were created from. The result is the payload to hand to a successor
|
|
131
|
+
# process via the `inherited_fds:` constructor argument.
|
|
132
|
+
#
|
|
133
|
+
# @return [Hash{String => Array<Integer>}]
|
|
134
|
+
#
|
|
135
|
+
# @rbs () -> Hash[String, Array[Integer]]
|
|
136
|
+
def inheritable_fds: () -> Hash[String, Array[Integer]]
|
|
137
|
+
|
|
138
|
+
# Clears the close-on-exec flag on every listener so the file descriptors
|
|
139
|
+
# survive `Kernel.exec`.
|
|
140
|
+
#
|
|
141
|
+
# @return [void]
|
|
142
|
+
#
|
|
143
|
+
# @rbs () -> void
|
|
144
|
+
def clear_close_on_exec: () -> void
|
|
145
|
+
|
|
109
146
|
private
|
|
110
147
|
|
|
111
|
-
# Parses bind URIs and creates listening sockets
|
|
148
|
+
# Parses bind URIs and creates listening sockets, reusing inherited file
|
|
149
|
+
# descriptors for URIs supplied in `@inherited_fds`.
|
|
112
150
|
#
|
|
113
151
|
# @return [void]
|
|
114
152
|
# @raise [UnknownBindSchemeError] if a URI scheme is not supported
|
|
@@ -116,6 +154,26 @@ module Raptor
|
|
|
116
154
|
# @rbs () -> void
|
|
117
155
|
def parse: () -> void
|
|
118
156
|
|
|
157
|
+
# Creates fresh listeners for the given bind URI.
|
|
158
|
+
#
|
|
159
|
+
# @param bind_uri [String] the URI to bind
|
|
160
|
+
# @return [Array<TCPServer, UNIXServer, SslListener>]
|
|
161
|
+
# @raise [UnknownBindSchemeError] if the URI scheme is not supported
|
|
162
|
+
#
|
|
163
|
+
# @rbs (String bind_uri) -> Array[TCPServer | UNIXServer | SslListener]
|
|
164
|
+
def create_listeners: (String bind_uri) -> Array[TCPServer | UNIXServer | SslListener]
|
|
165
|
+
|
|
166
|
+
# Reconstructs listeners for the given bind URI from inherited file
|
|
167
|
+
# descriptors.
|
|
168
|
+
#
|
|
169
|
+
# @param bind_uri [String] the URI the FDs were bound to
|
|
170
|
+
# @param filenos [Array<Integer>] file descriptors to wrap
|
|
171
|
+
# @return [Array<TCPServer, UNIXServer, SslListener>]
|
|
172
|
+
# @raise [UnknownBindSchemeError] if the URI scheme is not supported
|
|
173
|
+
#
|
|
174
|
+
# @rbs (String bind_uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener]
|
|
175
|
+
def restore_listeners: (String bind_uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener]
|
|
176
|
+
|
|
119
177
|
# Creates TCP server sockets for the given host and port.
|
|
120
178
|
#
|
|
121
179
|
# @param host [String, nil] hostname or IP address to bind to
|
|
@@ -138,6 +196,16 @@ module Raptor
|
|
|
138
196
|
# @rbs (String path) -> Array[UNIXServer]
|
|
139
197
|
def create_unix_listeners: (String path) -> Array[UNIXServer]
|
|
140
198
|
|
|
199
|
+
# Registers an `at_exit` hook that removes the Unix socket file on the
|
|
200
|
+
# owning master's clean exit. Each call records the current process so
|
|
201
|
+
# forked workers won't delete a socket their master still owns.
|
|
202
|
+
#
|
|
203
|
+
# @param path [String] filesystem path of the Unix socket
|
|
204
|
+
# @return [void]
|
|
205
|
+
#
|
|
206
|
+
# @rbs (String path) -> void
|
|
207
|
+
def register_unix_socket_cleanup: (String path) -> void
|
|
208
|
+
|
|
141
209
|
# Creates SSL server sockets for the given host, port, and SSL parameters.
|
|
142
210
|
#
|
|
143
211
|
# Wraps each TCP listener with an SSL context to produce SslListener objects.
|
|
@@ -152,6 +220,15 @@ module Raptor
|
|
|
152
220
|
# @rbs (String? host, Integer? port, Hash[String, String] ssl_params) -> Array[SslListener]
|
|
153
221
|
def create_ssl_listeners: (String? host, Integer? port, Hash[String, String] ssl_params) -> Array[SslListener]
|
|
154
222
|
|
|
223
|
+
# Builds a frozen `OpenSSL::SSL::SSLContext` configured for HTTP/2 and
|
|
224
|
+
# HTTP/1.1 ALPN negotiation.
|
|
225
|
+
#
|
|
226
|
+
# @param ssl_params [Hash<String, String>] SSL options ("cert" and "key" paths)
|
|
227
|
+
# @return [OpenSSL::SSL::SSLContext]
|
|
228
|
+
#
|
|
229
|
+
# @rbs (Hash[String, String] ssl_params) -> OpenSSL::SSL::SSLContext
|
|
230
|
+
def build_ssl_context: (Hash[String, String] ssl_params) -> OpenSSL::SSL::SSLContext
|
|
231
|
+
|
|
155
232
|
# Returns all available loopback IP addresses.
|
|
156
233
|
#
|
|
157
234
|
# @return [Array<String>] unique loopback addresses (IPv4 and IPv6)
|
|
@@ -18,6 +18,8 @@ module Raptor
|
|
|
18
18
|
class CLI
|
|
19
19
|
DEFAULT_WORKER_COUNT: untyped
|
|
20
20
|
|
|
21
|
+
NESTED_OPTION_KEYS: untyped
|
|
22
|
+
|
|
21
23
|
DEFAULT_OPTIONS: untyped
|
|
22
24
|
|
|
23
25
|
DEFAULT_CONFIG_PATHS: untyped
|
|
@@ -103,9 +105,6 @@ module Raptor
|
|
|
103
105
|
|
|
104
106
|
# Loads a config file and merges it into `@options` over the defaults.
|
|
105
107
|
#
|
|
106
|
-
# Top-level keys replace defaults; the nested `:client` hash is merged
|
|
107
|
-
# key-by-key so a config file does not need to restate every client option.
|
|
108
|
-
#
|
|
109
108
|
# @param path [String, nil] path to the config file, or nil to no-op
|
|
110
109
|
# @return [void]
|
|
111
110
|
#
|