quicsilver 0.2.0 → 0.3.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/.github/workflows/ci.yml +3 -4
- data/CHANGELOG.md +49 -0
- data/Gemfile.lock +8 -4
- data/README.md +7 -6
- data/Rakefile +29 -2
- data/benchmarks/components.rb +191 -0
- data/benchmarks/concurrent.rb +110 -0
- data/benchmarks/helpers.rb +88 -0
- data/benchmarks/quicsilver_server.rb +1 -1
- data/benchmarks/rails.rb +170 -0
- data/benchmarks/throughput.rb +113 -0
- data/ext/quicsilver/quicsilver.c +529 -181
- data/lib/quicsilver/client/client.rb +250 -0
- data/lib/quicsilver/client/request.rb +98 -0
- data/lib/quicsilver/{http3.rb → protocol/frames.rb} +133 -28
- data/lib/quicsilver/protocol/qpack/decoder.rb +165 -0
- data/lib/quicsilver/protocol/qpack/encoder.rb +189 -0
- data/lib/quicsilver/protocol/qpack/header_block_decoder.rb +125 -0
- data/lib/quicsilver/protocol/qpack/huffman.rb +459 -0
- data/lib/quicsilver/protocol/request_encoder.rb +47 -0
- data/lib/quicsilver/protocol/request_parser.rb +387 -0
- data/lib/quicsilver/protocol/response_encoder.rb +72 -0
- data/lib/quicsilver/protocol/response_parser.rb +249 -0
- data/lib/quicsilver/server/listener_data.rb +14 -0
- data/lib/quicsilver/server/request_handler.rb +86 -0
- data/lib/quicsilver/server/request_registry.rb +50 -0
- data/lib/quicsilver/server/server.rb +336 -0
- data/lib/quicsilver/transport/configuration.rb +132 -0
- data/lib/quicsilver/transport/connection.rb +350 -0
- data/lib/quicsilver/transport/event_loop.rb +38 -0
- data/lib/quicsilver/transport/inbound_stream.rb +33 -0
- data/lib/quicsilver/transport/stream.rb +28 -0
- data/lib/quicsilver/transport/stream_event.rb +26 -0
- data/lib/quicsilver/version.rb +1 -1
- data/lib/quicsilver.rb +31 -13
- data/lib/rackup/handler/quicsilver.rb +1 -2
- data/quicsilver.gemspec +3 -1
- metadata +58 -18
- data/benchmarks/benchmark.rb +0 -68
- data/lib/quicsilver/client.rb +0 -261
- data/lib/quicsilver/connection.rb +0 -42
- data/lib/quicsilver/event_loop.rb +0 -38
- data/lib/quicsilver/http3/request_encoder.rb +0 -133
- data/lib/quicsilver/http3/request_parser.rb +0 -176
- data/lib/quicsilver/http3/response_encoder.rb +0 -186
- data/lib/quicsilver/http3/response_parser.rb +0 -160
- data/lib/quicsilver/listener_data.rb +0 -29
- data/lib/quicsilver/quic_stream.rb +0 -36
- data/lib/quicsilver/request_registry.rb +0 -48
- data/lib/quicsilver/server.rb +0 -355
- data/lib/quicsilver/server_configuration.rb +0 -78
data/lib/quicsilver/server.rb
DELETED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Quicsilver
|
|
4
|
-
class Server
|
|
5
|
-
attr_reader :address, :port, :server_configuration, :running, :connections, :request_registry, :shutting_down
|
|
6
|
-
|
|
7
|
-
STREAM_EVENT_RECEIVE = "RECEIVE"
|
|
8
|
-
STREAM_EVENT_RECEIVE_FIN = "RECEIVE_FIN"
|
|
9
|
-
STREAM_EVENT_CONNECTION_ESTABLISHED = "CONNECTION_ESTABLISHED"
|
|
10
|
-
STREAM_EVENT_SEND_COMPLETE = "SEND_COMPLETE"
|
|
11
|
-
STREAM_EVENT_CONNECTION_CLOSED = "CONNECTION_CLOSED"
|
|
12
|
-
|
|
13
|
-
ServerStopError = Class.new(StandardError)
|
|
14
|
-
|
|
15
|
-
class << self
|
|
16
|
-
attr_accessor :instance
|
|
17
|
-
|
|
18
|
-
# Callback from C extension - delegates to server instance
|
|
19
|
-
def handle_stream(connection_data, stream_id, event, data)
|
|
20
|
-
instance&.handle_stream_event(connection_data, stream_id, event, data)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def initialize(port = 4433, address: "0.0.0.0", app: nil, server_configuration: nil)
|
|
25
|
-
@port = port
|
|
26
|
-
@address = address
|
|
27
|
-
@app = app || default_rack_app
|
|
28
|
-
@server_configuration = server_configuration || ServerConfiguration.new
|
|
29
|
-
@running = false
|
|
30
|
-
@shutting_down = false
|
|
31
|
-
@listener_data = nil
|
|
32
|
-
@connections = {}
|
|
33
|
-
@request_registry = RequestRegistry.new
|
|
34
|
-
|
|
35
|
-
self.class.instance = self
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def start
|
|
39
|
-
raise ServerIsRunningError, "Server is already running" if @running
|
|
40
|
-
|
|
41
|
-
Quicsilver.open_connection
|
|
42
|
-
config = Quicsilver.create_server_configuration(@server_configuration.to_h)
|
|
43
|
-
raise ServerConfigurationError, "Failed to create server configuration" unless config
|
|
44
|
-
|
|
45
|
-
# Create and start the listener
|
|
46
|
-
result = Quicsilver.create_listener(config)
|
|
47
|
-
@listener_data = ListenerData.new(result[0], result[1])
|
|
48
|
-
raise ServerListenerError, "Failed to create listener #{@address}:#{@port}" unless @listener_data
|
|
49
|
-
|
|
50
|
-
unless Quicsilver.start_listener(@listener_data.listener_handle, @address, @port)
|
|
51
|
-
Quicsilver.close_configuration(config)
|
|
52
|
-
cleanup_failed_server
|
|
53
|
-
raise ServerListenerError, "Failed to start listener on #{@address}:#{@port}"
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
@running = true
|
|
57
|
-
|
|
58
|
-
Quicsilver.event_loop.start
|
|
59
|
-
Quicsilver.event_loop.join # Block until shutdown
|
|
60
|
-
rescue ServerConfigurationError, ServerListenerError => e
|
|
61
|
-
cleanup_failed_server
|
|
62
|
-
@running = false
|
|
63
|
-
raise e
|
|
64
|
-
rescue => e
|
|
65
|
-
cleanup_failed_server
|
|
66
|
-
@running = false
|
|
67
|
-
|
|
68
|
-
error_msg = case e.message
|
|
69
|
-
when /0x16/
|
|
70
|
-
"Invalid parameter error - check certificate files and network configuration"
|
|
71
|
-
when /0x30/
|
|
72
|
-
"Address already in use - port #{@port} may be occupied"
|
|
73
|
-
else
|
|
74
|
-
e.message
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
raise ServerError, "Server start failed: #{error_msg}"
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def stop
|
|
81
|
-
return unless @running
|
|
82
|
-
|
|
83
|
-
if @listener_data && @listener_data.listener_handle
|
|
84
|
-
Quicsilver.stop_listener(@listener_data.listener_handle)
|
|
85
|
-
Quicsilver.close_listener([@listener_data.listener_handle, @listener_data.context_handle])
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
Quicsilver.event_loop.stop # Stop event loop so start unblocks
|
|
89
|
-
@running = false
|
|
90
|
-
@listener_data = nil
|
|
91
|
-
rescue => e
|
|
92
|
-
@listener_data = nil
|
|
93
|
-
@running = false
|
|
94
|
-
raise ServerStopError, "Failed to stop server: #{e.message}"
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def running?
|
|
98
|
-
@running
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Graceful shutdown: send GOAWAY, wait for in-flight requests, then stop
|
|
102
|
-
def shutdown(timeout: 30)
|
|
103
|
-
return unless @running
|
|
104
|
-
return if @shutting_down
|
|
105
|
-
|
|
106
|
-
@shutting_down = true
|
|
107
|
-
Quicsilver.logger.info("Initiating graceful shutdown (timeout: #{timeout}s)")
|
|
108
|
-
|
|
109
|
-
# Phase 1: Send GOAWAY with max stream ID to all connections
|
|
110
|
-
# This tells clients to stop sending new requests
|
|
111
|
-
@connections.each_value do |connection|
|
|
112
|
-
send_goaway(connection, HTTP3::MAX_STREAM_ID)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Phase 2: Wait for in-flight requests to drain
|
|
116
|
-
deadline = Time.now + timeout
|
|
117
|
-
until @request_registry.empty? || Time.now > deadline
|
|
118
|
-
sleep 0.1
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Log any requests that didn't complete
|
|
122
|
-
unless @request_registry.empty?
|
|
123
|
-
@request_registry.active_requests.each do |stream_id, req|
|
|
124
|
-
elapsed = Time.now - req[:started_at]
|
|
125
|
-
Quicsilver.logger.warn("Force-closing request: #{req[:method]} #{req[:path]} (stream: #{stream_id}, elapsed: #{elapsed.round(2)}s)")
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Phase 3: Send final GOAWAY with actual last stream ID and shutdown connections
|
|
130
|
-
@connections.each_value do |connection|
|
|
131
|
-
last_stream_id = connection.streams.keys.select { |id| (id & 0x02) == 0 }.max || 0
|
|
132
|
-
send_goaway(connection, last_stream_id)
|
|
133
|
-
|
|
134
|
-
# Graceful QUIC shutdown (sends CONNECTION_CLOSE to peer)
|
|
135
|
-
Quicsilver.connection_shutdown(connection.handle, 0, false)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Give connections a moment to close gracefully
|
|
139
|
-
sleep 0.1
|
|
140
|
-
|
|
141
|
-
# Phase 4: Hard stop
|
|
142
|
-
stop
|
|
143
|
-
@shutting_down = false
|
|
144
|
-
|
|
145
|
-
Quicsilver.logger.info("Graceful shutdown complete")
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def handle_stream_event(connection_data, stream_id, event, data)
|
|
149
|
-
connection_handle = connection_data[0]
|
|
150
|
-
|
|
151
|
-
case event
|
|
152
|
-
when STREAM_EVENT_CONNECTION_ESTABLISHED
|
|
153
|
-
connection = Connection.new(connection_handle, connection_data)
|
|
154
|
-
@connections[connection_handle] = connection
|
|
155
|
-
setup_http3_streams(connection)
|
|
156
|
-
when STREAM_EVENT_CONNECTION_CLOSED
|
|
157
|
-
@connections.delete(connection_handle)&.streams&.clear
|
|
158
|
-
when STREAM_EVENT_SEND_COMPLETE
|
|
159
|
-
# Buffer cleanup handled in C extension
|
|
160
|
-
when STREAM_EVENT_RECEIVE
|
|
161
|
-
return unless connection = @connections[connection_handle]
|
|
162
|
-
|
|
163
|
-
stream = connection.get_stream(stream_id) || QuicStream.new(stream_id)
|
|
164
|
-
connection.add_stream(stream) unless connection.get_stream(stream_id)
|
|
165
|
-
stream.append_data(data)
|
|
166
|
-
when STREAM_EVENT_RECEIVE_FIN
|
|
167
|
-
return unless connection = @connections[connection_handle]
|
|
168
|
-
|
|
169
|
-
# Extract stream handle from data (first 8 bytes)
|
|
170
|
-
stream_handle = data[0, 8].unpack1('Q')
|
|
171
|
-
actual_data = data[8..-1] || ""
|
|
172
|
-
|
|
173
|
-
stream = connection.get_stream(stream_id) || QuicStream.new(stream_id)
|
|
174
|
-
stream.stream_handle = stream_handle
|
|
175
|
-
stream.append_data(actual_data)
|
|
176
|
-
|
|
177
|
-
if stream.bidirectional?
|
|
178
|
-
handle_request(connection, stream)
|
|
179
|
-
else
|
|
180
|
-
handle_unidirectional_stream(connection, stream) # Unidirectional stream (control/QPACK)
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
connection.remove_stream(stream_id)
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
private
|
|
188
|
-
|
|
189
|
-
def default_rack_app
|
|
190
|
-
->(env) {
|
|
191
|
-
[200,
|
|
192
|
-
{'Content-Type' => 'text/plain'},
|
|
193
|
-
["Hello from Quicsilver!\nMethod: #{env['REQUEST_METHOD']}\nPath: #{env['PATH_INFO']}\n"]]
|
|
194
|
-
}
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def cleanup_failed_server
|
|
198
|
-
if @listener_data
|
|
199
|
-
begin
|
|
200
|
-
Quicsilver.stop_listener(@listener_data.listener_handle) if @listener_data.listener_handle
|
|
201
|
-
Quicsilver.close_listener([@listener_data.listener_handle, @listener_data.context_handle]) if @listener_data.listener_handle
|
|
202
|
-
rescue
|
|
203
|
-
# Ignore cleanup errors
|
|
204
|
-
ensure
|
|
205
|
-
@listener_data = nil
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def setup_http3_streams(connection)
|
|
211
|
-
connection_data = connection.data
|
|
212
|
-
|
|
213
|
-
# Send control stream (required) - store handle for GOAWAY
|
|
214
|
-
control_stream = Quicsilver.open_stream(connection_data, true)
|
|
215
|
-
control_data = HTTP3.build_control_stream
|
|
216
|
-
Quicsilver.send_stream(control_stream, control_data, false)
|
|
217
|
-
connection.server_control_stream = control_stream
|
|
218
|
-
|
|
219
|
-
# Open QPACK encoder/decoder streams (required)
|
|
220
|
-
[0x02, 0x03].each do |type|
|
|
221
|
-
stream = Quicsilver.open_stream(connection_data, true)
|
|
222
|
-
Quicsilver.send_stream(stream, [type].pack('C'), false)
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def handle_control_stream(connection, stream)
|
|
228
|
-
return if stream.data.empty?
|
|
229
|
-
|
|
230
|
-
case stream.data[0].ord
|
|
231
|
-
when 0x00 then connection.set_control_stream(stream.stream_id)
|
|
232
|
-
when 0x02 then connection.set_qpack_encoder_stream(stream.stream_id)
|
|
233
|
-
when 0x03 then connection.set_qpack_decoder_stream(stream.stream_id)
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def handle_unidirectional_stream(connection, stream)
|
|
238
|
-
data = stream.data
|
|
239
|
-
return if data.empty?
|
|
240
|
-
|
|
241
|
-
stream_type = data[0].ord
|
|
242
|
-
payload = data[1..-1]
|
|
243
|
-
|
|
244
|
-
case stream_type
|
|
245
|
-
when 0x00 # Control stream
|
|
246
|
-
connection.set_control_stream(stream.stream_id)
|
|
247
|
-
parse_client_control_stream(payload)
|
|
248
|
-
when 0x02 # QPACK encoder stream
|
|
249
|
-
# Store encoder stream for sending dynamic table updates
|
|
250
|
-
connection.set_qpack_encoder_stream(stream.stream_id)
|
|
251
|
-
when 0x03 # QPACK decoder stream
|
|
252
|
-
# Store decoder stream for receiving acknowledgments
|
|
253
|
-
connection.set_qpack_decoder_stream(stream.stream_id)
|
|
254
|
-
else
|
|
255
|
-
raise "⚠️ Ruby: Stream #{stream.stream_id}: Unknown stream type: 0x#{stream_type.to_s(16)}"
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def parse_client_control_stream(data)
|
|
260
|
-
offset = 0
|
|
261
|
-
while offset < data.bytesize
|
|
262
|
-
frame_type, type_len = HTTP3.decode_varint(data.bytes, offset)
|
|
263
|
-
frame_length, length_len = HTTP3.decode_varint(data.bytes, offset + type_len)
|
|
264
|
-
|
|
265
|
-
if frame_type == HTTP3::FRAME_SETTINGS
|
|
266
|
-
# Parse client settings
|
|
267
|
-
settings_payload = data[offset + type_len + length_len, frame_length]
|
|
268
|
-
parse_settings_frame(settings_payload)
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
offset += type_len + length_len + frame_length
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def parse_settings_frame(payload)
|
|
276
|
-
offset = 0
|
|
277
|
-
settings = {}
|
|
278
|
-
|
|
279
|
-
while offset < payload.bytesize
|
|
280
|
-
setting_id, id_len = HTTP3.decode_varint(payload.bytes, offset)
|
|
281
|
-
setting_value, value_len = HTTP3.decode_varint(payload.bytes, offset + id_len)
|
|
282
|
-
settings[setting_id] = setting_value
|
|
283
|
-
offset += id_len + value_len
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
settings
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def handle_request(connection, stream)
|
|
290
|
-
parser = HTTP3::RequestParser.new(stream.data)
|
|
291
|
-
parser.parse
|
|
292
|
-
env = parser.to_rack_env
|
|
293
|
-
|
|
294
|
-
if env && @app
|
|
295
|
-
# Track request
|
|
296
|
-
@request_registry.track(
|
|
297
|
-
stream.stream_id,
|
|
298
|
-
connection.handle,
|
|
299
|
-
path: env["PATH_INFO"] || "/",
|
|
300
|
-
method: env["REQUEST_METHOD"] || "GET"
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
# Call Rack app
|
|
304
|
-
status, headers, body = @app.call(env)
|
|
305
|
-
encoder = HTTP3::ResponseEncoder.new(status, headers, body)
|
|
306
|
-
|
|
307
|
-
raise "Stream handle not found for stream #{stream.stream_id}" unless stream.ready_to_send?
|
|
308
|
-
|
|
309
|
-
# Rack convention: body.to_ary means bufferable, otherwise stream
|
|
310
|
-
if body.respond_to?(:to_ary)
|
|
311
|
-
# Buffer mode - small responses (Arrays), send all at once
|
|
312
|
-
Quicsilver.send_stream(stream.stream_handle, encoder.encode, true)
|
|
313
|
-
else
|
|
314
|
-
# Stream mode - lazy bodies (ActionController::Live, SSE), send incrementally
|
|
315
|
-
encoder.stream_encode do |frame_data, fin|
|
|
316
|
-
Quicsilver.send_stream(stream.stream_handle, frame_data, fin) unless frame_data.empty? && !fin
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
# Mark request complete
|
|
321
|
-
@request_registry.complete(stream.stream_id)
|
|
322
|
-
else
|
|
323
|
-
# failed to parse request
|
|
324
|
-
if stream.ready_to_send?
|
|
325
|
-
error_response = encode_error_response(400, "Bad Request")
|
|
326
|
-
Quicsilver.send_stream(stream.stream_handle, error_response, true)
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
rescue => e
|
|
330
|
-
Quicsilver.logger.error("Error handling request: #{e.class} - #{e.message}")
|
|
331
|
-
Quicsilver.logger.debug(e.backtrace.first(5).join("\n"))
|
|
332
|
-
error_response = encode_error_response(500, "Internal Server Error")
|
|
333
|
-
|
|
334
|
-
Quicsilver.send_stream(stream.stream_handle, error_response, true) if stream.ready_to_send?
|
|
335
|
-
ensure
|
|
336
|
-
# Always complete the request, even on error
|
|
337
|
-
@request_registry.complete(stream.stream_id) if @request_registry.include?(stream.stream_id)
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
def encode_error_response(status, message)
|
|
341
|
-
body = ["#{status} #{message}"]
|
|
342
|
-
encoder = HTTP3::ResponseEncoder.new(status, {"content-type" => "text/plain"}, body)
|
|
343
|
-
encoder.encode
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
def send_goaway(connection, stream_id)
|
|
347
|
-
return unless connection.server_control_stream
|
|
348
|
-
|
|
349
|
-
goaway_frame = HTTP3.build_goaway_frame(stream_id)
|
|
350
|
-
Quicsilver.send_stream(connection.server_control_stream, goaway_frame, false)
|
|
351
|
-
rescue => e
|
|
352
|
-
Quicsilver.logger.error("Failed to send GOAWAY to connection #{connection.handle}: #{e.message}")
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
end
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "localhost"
|
|
4
|
-
|
|
5
|
-
module Quicsilver
|
|
6
|
-
class ServerConfiguration
|
|
7
|
-
attr_reader :cert_file, :key_file, :idle_timeout, :server_resumption_level, :peer_bidi_stream_count,
|
|
8
|
-
:peer_unidi_stream_count, :stream_recv_window, :stream_recv_buffer, :conn_flow_control_window
|
|
9
|
-
|
|
10
|
-
QUIC_SERVER_RESUME_AND_ZERORTT = 1
|
|
11
|
-
QUIC_SERVER_RESUME_ONLY = 2
|
|
12
|
-
QUIC_SERVER_RESUME_AND_REUSE = 3
|
|
13
|
-
QUIC_SERVER_RESUME_AND_REUSE_ZERORTT = 4
|
|
14
|
-
|
|
15
|
-
DEFAULT_CERT_FILE = "certificates/server.crt"
|
|
16
|
-
DEFAULT_KEY_FILE = "certificates/server.key"
|
|
17
|
-
DEFAULT_ALPN = "h3"
|
|
18
|
-
|
|
19
|
-
# Flow control defaults (msquic defaults)
|
|
20
|
-
# See: https://github.com/microsoft/msquic/blob/main/docs/Settings.md
|
|
21
|
-
DEFAULT_STREAM_RECV_WINDOW = 65_536 # 64KB - initial stream receive window
|
|
22
|
-
DEFAULT_STREAM_RECV_BUFFER = 4_096 # 4KB - stream buffer size
|
|
23
|
-
DEFAULT_CONN_FLOW_CONTROL_WINDOW = 16_777_216 # 16MB - connection-wide flow control
|
|
24
|
-
|
|
25
|
-
def initialize(cert_file = nil, key_file = nil, options = {})
|
|
26
|
-
@idle_timeout = options[:idle_timeout].nil? ? 10000 : options[:idle_timeout]
|
|
27
|
-
@server_resumption_level = options[:server_resumption_level].nil? ? QUIC_SERVER_RESUME_AND_ZERORTT : options[:server_resumption_level]
|
|
28
|
-
@peer_bidi_stream_count = options[:peer_bidi_stream_count].nil? ? 10 : options[:peer_bidi_stream_count]
|
|
29
|
-
@peer_unidi_stream_count = options[:peer_unidi_stream_count].nil? ? 10 : options[:peer_unidi_stream_count]
|
|
30
|
-
@alpn = options[:alpn].nil? ? DEFAULT_ALPN : options[:alpn]
|
|
31
|
-
|
|
32
|
-
# Flow control / backpressure settings
|
|
33
|
-
@stream_recv_window = options[:stream_recv_window].nil? ? DEFAULT_STREAM_RECV_WINDOW : options[:stream_recv_window]
|
|
34
|
-
@stream_recv_buffer = options[:stream_recv_buffer].nil? ? DEFAULT_STREAM_RECV_BUFFER : options[:stream_recv_buffer]
|
|
35
|
-
@conn_flow_control_window = options[:conn_flow_control_window].nil? ? DEFAULT_CONN_FLOW_CONTROL_WINDOW : options[:conn_flow_control_window]
|
|
36
|
-
|
|
37
|
-
@cert_file = cert_file.nil? ? DEFAULT_CERT_FILE : cert_file
|
|
38
|
-
@key_file = key_file.nil? ? DEFAULT_KEY_FILE : key_file
|
|
39
|
-
|
|
40
|
-
unless File.exist?(@cert_file)
|
|
41
|
-
raise ServerConfigurationError, "Certificate file not found: #{@cert_file}"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
unless File.exist?(@key_file)
|
|
45
|
-
raise ServerConfigurationError, "Key file not found: #{@key_file}"
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Common HTTP/3 ALPN Values:
|
|
50
|
-
# "h3" - HTTP/3 (most common)
|
|
51
|
-
# "h3-29" - HTTP/3 draft version 29
|
|
52
|
-
# "h3-28" - HTTP/3 draft version 28
|
|
53
|
-
# "h3-27" - HTTP/3 draft version 27
|
|
54
|
-
# Other QUIC ALPN Values:
|
|
55
|
-
# "hq-interop" - HTTP/0.9 over QUIC (testing)
|
|
56
|
-
# "hq-29" - HTTP/0.9 over QUIC draft 29
|
|
57
|
-
# "doq" - DNS over QUIC
|
|
58
|
-
# "doq-i03" - DNS over QUIC draft
|
|
59
|
-
def alpn
|
|
60
|
-
@alpn
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def to_h
|
|
64
|
-
{
|
|
65
|
-
cert_file: @cert_file,
|
|
66
|
-
key_file: @key_file,
|
|
67
|
-
idle_timeout: @idle_timeout,
|
|
68
|
-
server_resumption_level: @server_resumption_level,
|
|
69
|
-
peer_bidi_stream_count: @peer_bidi_stream_count,
|
|
70
|
-
peer_unidi_stream_count: @peer_unidi_stream_count,
|
|
71
|
-
alpn: alpn,
|
|
72
|
-
stream_recv_window: @stream_recv_window,
|
|
73
|
-
stream_recv_buffer: @stream_recv_buffer,
|
|
74
|
-
conn_flow_control_window: @conn_flow_control_window
|
|
75
|
-
}
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|