raioquic 0.1.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.containerignore +4 -0
  3. data/.rubocop.yml +93 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Containerfile +6 -0
  7. data/Gemfile +24 -0
  8. data/Gemfile.lock +113 -0
  9. data/LICENSE +28 -0
  10. data/README.md +48 -0
  11. data/Rakefile +16 -0
  12. data/Steepfile +8 -0
  13. data/example/curlcatcher.rb +18 -0
  14. data/example/interoperability/README.md +9 -0
  15. data/example/interoperability/aioquic/aioquic_client.py +47 -0
  16. data/example/interoperability/aioquic/aioquic_server.py +34 -0
  17. data/example/interoperability/key.pem +28 -0
  18. data/example/interoperability/localhost-unasuke-dev.crt +21 -0
  19. data/example/interoperability/quic-go/sample_server.go +61 -0
  20. data/example/interoperability/raioquic_client.rb +42 -0
  21. data/example/interoperability/raioquic_server.rb +43 -0
  22. data/example/parse_curl_example.rb +108 -0
  23. data/lib/raioquic/buffer.rb +202 -0
  24. data/lib/raioquic/core_ext.rb +54 -0
  25. data/lib/raioquic/crypto/README.md +5 -0
  26. data/lib/raioquic/crypto/aesgcm.rb +52 -0
  27. data/lib/raioquic/crypto/backend/aead.rb +52 -0
  28. data/lib/raioquic/crypto/backend.rb +12 -0
  29. data/lib/raioquic/crypto.rb +10 -0
  30. data/lib/raioquic/quic/configuration.rb +81 -0
  31. data/lib/raioquic/quic/connection.rb +2776 -0
  32. data/lib/raioquic/quic/crypto.rb +317 -0
  33. data/lib/raioquic/quic/event.rb +69 -0
  34. data/lib/raioquic/quic/logger.rb +272 -0
  35. data/lib/raioquic/quic/packet.rb +471 -0
  36. data/lib/raioquic/quic/packet_builder.rb +301 -0
  37. data/lib/raioquic/quic/rangeset.rb +113 -0
  38. data/lib/raioquic/quic/recovery.rb +528 -0
  39. data/lib/raioquic/quic/stream.rb +343 -0
  40. data/lib/raioquic/quic.rb +20 -0
  41. data/lib/raioquic/tls.rb +1659 -0
  42. data/lib/raioquic/version.rb +5 -0
  43. data/lib/raioquic.rb +12 -0
  44. data/misc/export_x25519.py +43 -0
  45. data/misc/gen_rfc8448_keypair.rb +90 -0
  46. data/raioquic.gemspec +37 -0
  47. data/sig/raioquic/buffer.rbs +37 -0
  48. data/sig/raioquic/core_ext.rbs +7 -0
  49. data/sig/raioquic/crypto/aesgcm.rbs +20 -0
  50. data/sig/raioquic/crypto/backend/aead.rbs +11 -0
  51. data/sig/raioquic/quic/configuration.rbs +34 -0
  52. data/sig/raioquic/quic/connection.rbs +277 -0
  53. data/sig/raioquic/quic/crypto.rbs +88 -0
  54. data/sig/raioquic/quic/event.rbs +51 -0
  55. data/sig/raioquic/quic/logger.rbs +57 -0
  56. data/sig/raioquic/quic/packet.rbs +157 -0
  57. data/sig/raioquic/quic/packet_builder.rbs +76 -0
  58. data/sig/raioquic/quic/rangeset.rbs +17 -0
  59. data/sig/raioquic/quic/recovery.rbs +142 -0
  60. data/sig/raioquic/quic/stream.rbs +87 -0
  61. data/sig/raioquic/tls.rbs +444 -0
  62. data/sig/raioquic.rbs +9 -0
  63. metadata +121 -0
@@ -0,0 +1,2776 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Raioquic
6
+ module Quic
7
+ module Connection
8
+ CRYPTO_BUFFER_SIZE = 16384
9
+ EPOCH_SHORTCUTS = {
10
+ "I" => TLS::Epoch::INITIAL,
11
+ "H" => TLS::Epoch::HANDSHAKE,
12
+ "0" => TLS::Epoch::ZERO_RTT,
13
+ "1" => TLS::Epoch::ONE_RTT,
14
+ }
15
+ MAX_EARLY_DATA = 0xffffffff
16
+ SECRETS_LABELS = [
17
+ [
18
+ nil,
19
+ "CLIENT_EARLY_TRAFFIC_SECRET",
20
+ "CLIENT_HANDSHAKE_TRAFFIC_SECRET",
21
+ "CLIENT_TRAFFIC_SECRET_0",
22
+ ],
23
+ [
24
+ nil,
25
+ nil,
26
+ "SERVER_HANDSHAKE_TRAFFIC_SECRET",
27
+ "SERVER_TRAFFIC_SECRET_0",
28
+ ]
29
+ ]
30
+ STREAM_FLAGS = 0x07
31
+ STREAM_COUNT_MAX = 0x1000000000000000
32
+ UDP_HEADER_SIZE = 8
33
+
34
+ # frame sizes
35
+ ACK_FRAME_CAPACITY = 64 # FIXME: this is arbitrary!
36
+ APPLICATION_CLOSE_FRAME_CAPACITY = 1 + (2 * Buffer::UINT_VAR_MAX_SIZE) # + reason length
37
+ CONNECTION_LIMIT_FRAME_CAPACITY = 1 + Buffer::UINT_VAR_MAX_SIZE
38
+ HANDSHAKE_DONE_FRAME_CAPACITY = 1
39
+ MAX_STREAM_DATA_FRAME_CAPACITY = 1 + (2 * Buffer::UINT_VAR_MAX_SIZE)
40
+ NEW_CONNECTION_ID_FRAME_CAPACITY = (
41
+ 1 + (2 * Buffer::UINT_VAR_MAX_SIZE) + 1 + Packet::CONNECTION_ID_MAX_SIZE + Packet::STATELESS_RESET_TOKEN_SIZE
42
+ )
43
+ PATH_CHALLENGE_FRAME_CAPACITY = 1 + 8
44
+ PATH_RESPONSE_FRAME_CAPACITY = 1 + 8
45
+ PING_FRAME_CAPACITY = 1
46
+ RESET_STREAM_FRAME_CAPACITY = 1 + (3 * Buffer::UINT_VAR_MAX_SIZE)
47
+ RETIRE_CONNECTION_ID_CAPACITY = 1 + Buffer::UINT_VAR_MAX_SIZE
48
+ STOP_SENDING_FRAME_CAPACITY = 1 + (2 * Buffer::UINT_VAR_MAX_SIZE)
49
+ STREAMS_BLOCKED_CAPACITY = 1 + Buffer::UINT_VAR_MAX_SIZE
50
+ TRANSPORT_CLOSE_FLAME_CAPACITY = 1 + (3 * Buffer::UINT_VAR_MAX_SIZE) # + reason length
51
+
52
+ def self.epochs(shortcut)
53
+ shortcut.each_char.map do |s|
54
+ EPOCH_SHORTCUTS[s]
55
+ end.sort
56
+ end
57
+
58
+ def self.dump_cid(cid)
59
+ cid.unpack1("H*")
60
+ end
61
+
62
+ def self.get_epoch(packet_type)
63
+ case packet_type
64
+ when Quic::Packet::PACKET_TYPE_INITIAL
65
+ TLS::Epoch::INITIAL
66
+ when Quic::Packet::PACKET_TYPE_ZERO_RTT
67
+ TLS::Epoch::ZERO_RTT
68
+ when Quic::Packet::PACKET_TYPE_HANDSHAKE
69
+ TLS::Epoch::HANDSHAKE
70
+ else
71
+ TLS::Epoch::ONE_RTT
72
+ end
73
+ end
74
+
75
+ def self.get_transport_parameters_extension(version)
76
+ if Quic::Packet.is_draft_version(version)
77
+ TLS::ExtensionType::QUIC_TRANSPORT_PARAMETERS_DRAFT
78
+ else
79
+ TLS::ExtensionType::QUIC_TRANSPORT_PARAMETERS
80
+ end
81
+ end
82
+
83
+ # Returns true if the stream is client initiated.
84
+ def self.stream_is_client_initiated(stream_id)
85
+ stream_id & 1 == 0
86
+ end
87
+
88
+ # Returns true if the stream is unidirectional.
89
+ def self.stream_is_unidirectional(stream_id)
90
+ stream_id & 2 != 0
91
+ end
92
+
93
+ class Limit
94
+ attr_reader :frame_type
95
+ attr_reader :name
96
+ attr_accessor :used
97
+ attr_accessor :sent
98
+ attr_accessor :value
99
+
100
+ def initialize(frame_type:, name:, value:)
101
+ @frame_type = frame_type
102
+ @name = name
103
+ @sent = value
104
+ @used = 0
105
+ @value = value
106
+ end
107
+ end
108
+
109
+ class QuicConnectionError < StandardError # TODO: Raioquic::Error
110
+ attr_accessor :error_code
111
+ attr_accessor :frame_type
112
+ attr_accessor :reason_phrase
113
+ end
114
+
115
+ # TODO: Use logger with formatter
116
+ # class QuicConnectionAdapter
117
+ # def process(msd:, kwargs:)
118
+ # end
119
+ # end
120
+
121
+ QuicConnectionId = _ = Struct.new(
122
+ :cid,
123
+ :sequence_number,
124
+ :stateless_reset_token,
125
+ :was_sent,
126
+ )
127
+
128
+ class QuicConnectionState
129
+ FIRSTFLIGHT = 0
130
+ CONNECTED = 1
131
+ CLOSING = 2
132
+ DRAINING = 3
133
+ TERMINATED = 4
134
+ end
135
+
136
+ QuicNetworkPath = _ = Struct.new(
137
+ :addr,
138
+ :bytes_received,
139
+ :bytes_sent,
140
+ :is_validated,
141
+ :local_challenge,
142
+ :remote_challenge,
143
+ ) do
144
+ def initialize(**)
145
+ super
146
+ self.bytes_sent ||= 0
147
+ self.bytes_received ||= 0
148
+ end
149
+
150
+ def can_send(size)
151
+ self.is_validated || (self.bytes_sent + size) <= 3 * self.bytes_received
152
+ end
153
+ end
154
+
155
+ QuicReceiveContext = _ = Struct.new(
156
+ :epoch,
157
+ :host_cid,
158
+ :network_path,
159
+ :quic_logger_frames,
160
+ :time,
161
+ )
162
+
163
+ END_STATES = [
164
+ QuicConnectionState::CLOSING,
165
+ QuicConnectionState::DRAINING,
166
+ QuicConnectionState::TERMINATED,
167
+ ].freeze
168
+
169
+ # A QUIC connection.
170
+ #
171
+ # The state machine is driven by three kinds of sources:
172
+ #
173
+ # - the API user requesting data to be send out (see `connect`,
174
+ # `reset_stream`, `send_ping`, `send_datagram_frame` and `send_stream_data`)
175
+ # - data being received from the network (see `receive_datagram`)
176
+ # - a timer firing (see `handle_timer`)
177
+ #
178
+ # param configuration: The QUIC configuration to use.
179
+ class QuicConnection
180
+ attr_reader :configuration
181
+ attr_reader :loss
182
+ attr_reader :peer_cid_available
183
+ attr_reader :host_cid
184
+ attr_reader :host_cids
185
+ attr_reader :version
186
+ attr_reader :network_paths
187
+ attr_reader :peer_cid
188
+ attr_reader :local_max_stream_data_uni
189
+ attr_reader :remote_max_streams_uni
190
+ attr_reader :remote_max_streams_bidi
191
+ attr_reader :local_max_data
192
+ attr_reader :remote_max_data
193
+ attr_reader :streams
194
+ attr_reader :cryptos
195
+ attr_reader :close_event
196
+ attr_reader :local_max_stream_data_bidi_remote
197
+ attr_reader :local_max_stream_data_bidi_local
198
+ attr_reader :local_max_streams_bidi
199
+ attr_reader :tls
200
+ attr_reader :events
201
+ attr_reader :quic_logger
202
+ attr_reader :handshake_done_pending
203
+ attr_accessor :original_destination_connection_id
204
+ attr_accessor :ack_delay
205
+ attr_accessor :is_client
206
+ attr_accessor :initial_source_connection_id # TODO: did not used?
207
+
208
+ attr_reader :events # TODO: remove
209
+
210
+ def initialize(
211
+ configuration:,
212
+ original_destination_connection_id: nil,
213
+ retry_source_connection_id: nil,
214
+ session_ticket_fetcher: nil,
215
+ session_ticket_handler: nil
216
+ )
217
+ # binding.irb
218
+ if configuration.is_client
219
+ raise ArgumentError, "Cannot set original_destination_connection_id for a client" if original_destination_connection_id
220
+ raise ArgumentError, "Cannot set retry_source_connection_id for a client" if retry_source_connection_id
221
+ else
222
+ raise ArgumentError, "SSL certificate is required for a server" unless configuration.certificate
223
+ raise ArgumentError, "SSL private key is required for a server" unless configuration.private_key
224
+ end
225
+
226
+ # configuration
227
+ @configuration = configuration
228
+ @is_client = configuration.is_client
229
+
230
+ @ack_delay = Recovery::K_GRANULARITY
231
+ @close_at = nil
232
+ @close_event = nil
233
+ @connect_called = false
234
+ @cryptos = {}
235
+ @crypto_buffers = {}
236
+ @crypto_retransmitted = false
237
+ @crypto_streams = {}
238
+ @events = []
239
+ @handshake_complete = false
240
+ @handshake_confirmed = false
241
+ @host_cids = [
242
+ QuicConnectionId.new.tap do |conn|
243
+ conn.cid = Random.urandom(@configuration.connection_id_length)
244
+ conn.sequence_number = 0
245
+ conn.stateless_reset_token = @is_client ? nil : Random.urandom(16)
246
+ conn.was_sent = true
247
+ end
248
+ ]
249
+ @host_cid = @host_cids[0].cid
250
+ @host_cid_seq = 1
251
+ @local_ack_delay_exponent = 3
252
+ @local_active_connection_id_limit = 8
253
+ @local_initial_source_connection_id = @host_cids[0].cid
254
+ @local_max_data = Limit.new(
255
+ frame_type: Packet::QuicFrameType::MAX_DATA,
256
+ name: "max_data",
257
+ value: configuration.max_data,
258
+ )
259
+ @local_max_stream_data_bidi_local = @configuration.max_stream_data
260
+ @local_max_stream_data_bidi_remote = @configuration.max_stream_data
261
+ @local_max_stream_data_uni = @configuration.max_stream_data
262
+ @local_max_streams_bidi = Limit.new(
263
+ frame_type: Packet::QuicFrameType::MAX_STREAMS_BIDI,
264
+ name: "max_streams_bidi",
265
+ value: 128,
266
+ )
267
+ @local_max_streams_uni = Limit.new(
268
+ frame_type: Packet::QuicFrameType::MAX_STREAMS_UNI,
269
+ name: "max_streams_uni",
270
+ value: 128,
271
+ )
272
+ @loss_at = nil
273
+ @network_paths = []
274
+ @pacing_at = nil
275
+ @packet_number = 0
276
+ @parameters_received = false
277
+ @peer_cid = QuicConnectionId.new.tap do |cid|
278
+ cid.cid = Random.urandom(@configuration.connection_id_length)
279
+ cid.sequence_number = nil
280
+ end
281
+ @peer_cid_available = []
282
+ @peer_cid_sequence_numbers = Set.new([0])
283
+ @peer_token = ""
284
+ @quic_logger = nil
285
+ @remote_ack_delay_exponent = 3
286
+ @remote_active_connection_id_limit = 2
287
+ @remote_initial_source_connection_id = nil
288
+ @remote_max_idle_timeout = 0.0 # seconds
289
+ @remote_max_data = 0
290
+ @remote_max_data_used = 0
291
+ @remote_max_datagram_frame_size = nil
292
+ @remote_max_stream_data_bidi_local = 0
293
+ @remote_max_stream_data_bidi_remote = 0
294
+ @remote_max_stream_data_uni = 0
295
+ @remote_max_streams_bidi = 0
296
+ @remote_max_streams_uni = 0
297
+ @retry_count = 0
298
+ @retry_source_connection_id = retry_source_connection_id
299
+ @spaces = {}
300
+ @spin_bit = false
301
+ @spin_highest_pn = 0
302
+ @state = QuicConnectionState::FIRSTFLIGHT
303
+ @streams = {}
304
+ @streams_blocked_bidi = []
305
+ @streams_blocked_uni = []
306
+ @streams_finished = Set.new
307
+ @version = nil
308
+ @version_negotiation_count = 0
309
+
310
+ @original_destination_connection_id = \
311
+ @is_client ? @peer_cid.cid : original_destination_connection_id
312
+
313
+ @logger = nil # TODO: logging
314
+ if @configuration.quic_logger
315
+ @quic_logger = @configuration.quic_logger.start_trace(
316
+ is_client: @configuration.is_client,
317
+ odcid: @original_destination_connection_id,
318
+ )
319
+ end
320
+
321
+ # loss recovery
322
+ @loss = Quic::Recovery::QuicPacketRecovery.new(
323
+ initial_rtt: @configuration.initial_rtt,
324
+ peer_completed_address_validation: !@is_client,
325
+ quic_logger: @quic_logger,
326
+ send_probe: method(:send_probe),
327
+ logger: nil,
328
+ )
329
+
330
+ # things to send
331
+ @close_pending = false
332
+ @datagrams_pending = [] # TODO: deque
333
+ @handshake_done_pending = false
334
+ @ping_pending = []
335
+ @probe_pending = false
336
+ @retire_connection_ids = []
337
+ @streams_blocked_pending = false
338
+
339
+ # callbacks
340
+ @session_ticket_fetcher = session_ticket_fetcher
341
+ @session_ticket_handler = session_ticket_handler
342
+
343
+ # frame handlers
344
+ @frame_handlers = {
345
+ 0x00 => [:handle_padding_frame, Connection.epochs("IH01")],
346
+ 0x01 => [:handle_ping_frame, Connection.epochs("IH01")],
347
+ 0x02 => [:handle_ack_frame, Connection.epochs("IH1")],
348
+ 0x03 => [:handle_ack_frame, Connection.epochs("IH1")],
349
+ 0x04 => [:handle_reset_stream_frame, Connection.epochs("01")],
350
+ 0x05 => [:handle_stop_sending_frame, Connection.epochs("01")],
351
+ 0x06 => [:handle_crypto_frame, Connection.epochs("IH1")],
352
+ 0x07 => [:handle_new_token_frame, Connection.epochs("1")],
353
+ 0x08 => [:handle_stream_frame, Connection.epochs("01")],
354
+ 0x09 => [:handle_stream_frame, Connection.epochs("01")],
355
+ 0x0a => [:handle_stream_frame, Connection.epochs("01")],
356
+ 0x0b => [:handle_stream_frame, Connection.epochs("01")],
357
+ 0x0c => [:handle_stream_frame, Connection.epochs("01")],
358
+ 0x0d => [:handle_stream_frame, Connection.epochs("01")],
359
+ 0x0e => [:handle_stream_frame, Connection.epochs("01")],
360
+ 0x0f => [:handle_stream_frame, Connection.epochs("01")],
361
+ 0x10 => [:handle_max_data_frame, Connection.epochs("01")],
362
+ 0x11 => [:handle_max_stream_data_frame, Connection.epochs("01")],
363
+ 0x12 => [:handle_max_streams_bidi_frame, Connection.epochs("01")],
364
+ 0x13 => [:handle_max_streams_uni_frame, Connection.epochs("01")],
365
+ 0x14 => [:handle_data_blocked_frame, Connection.epochs("01")],
366
+ 0x15 => [:handle_stream_data_blocked_frame, Connection.epochs("01")],
367
+ 0x16 => [:handle_streams_blocked_frame, Connection.epochs("01")],
368
+ 0x17 => [:handle_streams_blocked_frame, Connection.epochs("01")],
369
+ 0x18 => [:handle_new_connection_id_frame, Connection.epochs("01")],
370
+ 0x19 => [:handle_retire_connection_id_frame, Connection.epochs("01")],
371
+ 0x1a => [:handle_path_challenge_frame, Connection.epochs("01")],
372
+ 0x1b => [:handle_path_response_frame, Connection.epochs("01")],
373
+ 0x1c => [:handle_connection_close_frame, Connection.epochs("IH01")],
374
+ 0x1d => [:handle_connection_close_frame, Connection.epochs("01")],
375
+ 0x1e => [:handle_handshake_done_frame, Connection.epochs("1")],
376
+ 0x30 => [:handle_datagram_frame, Connection.epochs("01")],
377
+ 0x31 => [:handle_datagram_frame, Connection.epochs("01")],
378
+ }
379
+ end
380
+
381
+ # Switch to the next available connection ID ans retire the previous one.
382
+ #
383
+ # After calling this method call `datagrams_to_send` to retrieve data which needs to be sent.
384
+ def change_connection_id
385
+ if @peer_cid_available
386
+ # retire precious CID
387
+ retire_peer_cid(@peer_cid)
388
+
389
+ # assign new CID
390
+ consume_peer_cid
391
+ end
392
+ end
393
+
394
+ # Close the connection.
395
+ #
396
+ # param error_code: An error code indication why the connection is being closed.
397
+ # param reason_phrase: A human-readable explanation of why the connection is being closed.
398
+ def close(error_code: Quic::Packet::QuicErrorCode::NO_ERROR, frame_type: nil, reason_phrase: "")
399
+ if @close_event.nil? && !END_STATES.include?(@state)
400
+ @close_event = Quic::Event::ConnectionTerminated.new.tap do |error|
401
+ error.error_code = error_code
402
+ error.frame_type = frame_type
403
+ error.reason_phrase = reason_phrase
404
+ end
405
+ @close_pending = true
406
+ end
407
+ end
408
+
409
+ # Initiate the TLS handshake.
410
+ #
411
+ # This method can only be called for clients and a single time.
412
+ #
413
+ # After calling this method call `datagrams_to_send` to retrieve data which needs to be sent.
414
+ #
415
+ # param addr: The network address of the remote peer
416
+ # param now: The current time.
417
+ def connect(addr:, now:)
418
+ raise "`connect` can only be called for clients and a single time" if !@is_client && @connect_called
419
+
420
+ @connect_called = true
421
+ # np = QuicNetworkPath.new.tap { |n| n.addr = addr; n.is_validated = true; }
422
+ # @network_paths = [np]
423
+ @network_paths = [QuicNetworkPath.new.tap { |n| n.addr = addr; n.is_validated = true; }]
424
+ @version = @configuration.supported_versions[0]
425
+ _connect(now: now)
426
+ end
427
+
428
+ # Return a list of `[data, addr]` array of datagrams which need to be sent, and the network address to which they need to be sent.
429
+ #
430
+ # After calling this method call `get_timer` to know when the next timer needs to be set.
431
+ #
432
+ # param now: The current time.
433
+ def datagrams_to_send(now:)
434
+ network_path = @network_paths[0]
435
+
436
+ return [] if END_STATES.include?(@state)
437
+
438
+ # build datagrams
439
+ builder = Quic::PacketBuilder::QuicPacketBuilder.new(
440
+ host_cid: @host_cid,
441
+ is_client: @is_client,
442
+ packet_number: @packet_number,
443
+ peer_cid: @peer_cid.cid,
444
+ peer_token: @peer_token,
445
+ quic_logger: @quic_logger,
446
+ spin_bit: @spin_bit,
447
+ version: @version,
448
+ )
449
+ if @close_pending
450
+ epoch_packet_types = []
451
+ unless @handshake_confirmed
452
+ epoch_packet_types += [
453
+ [TLS::Epoch::INITIAL, Quic::Packet::PACKET_TYPE_INITIAL],
454
+ [TLS::Epoch::HANDSHAKE, Quic::Packet::PACKET_TYPE_HANDSHAKE]
455
+ ]
456
+ end
457
+ epoch_packet_types += [[TLS::Epoch::ONE_RTT, Quic::Packet::PACKET_TYPE_ONE_RTT]]
458
+ epoch_packet_types.each do |epoch, packet_type|
459
+ crypto = @cryptos[epoch]
460
+ # binding.irb
461
+ next unless crypto&.send&.is_valid
462
+
463
+ # binding.irb unless @is_client
464
+ builder.start_packet(packet_type: packet_type, crypto: crypto)
465
+ # binding.irb
466
+ write_connection_close_frame(
467
+ builder: builder,
468
+ epoch: epoch,
469
+ error_code: @close_event.error_code,
470
+ frame_type: @close_event.frame_type,
471
+ reason_phrase: @close_event.reason_phrase,
472
+ )
473
+ end
474
+ # TODO: logger "Connection close sent"
475
+ @close_pending = true
476
+ close_begin(is_initiator: true, now: now)
477
+ else
478
+ # congestion control
479
+ builder.max_flight_bytes = @loss.congestion_window - @loss.bytes_in_flight
480
+ if @probe_pending && builder.max_flight_bytes < Quic::PacketBuilder::PACKET_MAX_SIZE
481
+ builder.max_flight_bytes = Quic::PacketBuilder::PACKET_MAX_SIZE
482
+ end
483
+
484
+ # limit data on un-validated network paths
485
+ builder.max_total_bytes = (network_path.bytes_received * 3) - network_path.bytes_sent unless network_path.is_validated
486
+
487
+ begin
488
+ unless @handshake_confirmed
489
+ [TLS::Epoch::INITIAL, TLS::Epoch::HANDSHAKE].each do |epoch|
490
+ write_handshake(builder: builder, epoch: epoch, now: now)
491
+ end
492
+ end
493
+ write_application(builder: builder, network_path: network_path, now: now)
494
+ rescue Quic::PacketBuilder::QuicPacketBuilderStop
495
+ # pass
496
+ end
497
+ end
498
+
499
+ datagrams, packets = builder.flush
500
+
501
+ if datagrams
502
+ @packet_number = builder.packet_number
503
+
504
+ # register packets
505
+ sent_handshake = false
506
+ packets.each do |packet|
507
+ packet.sent_time = now
508
+ @loss.on_packet_sent(packet: packet, space: @spaces[packet.epoch])
509
+ sent_handshake = true if packet.epoch == TLS::Epoch::HANDSHAKE
510
+
511
+ # log packet
512
+ if @quic_logger
513
+ @quic_logger.log_event(
514
+ category: "transport",
515
+ event: "packet_sent",
516
+ data: {
517
+ frames: packet.quic_logger_frames,
518
+ header: {
519
+ packet_number: packet.packet_number,
520
+ packet_type: @quic_logger.packet_type(packet.packet_type),
521
+ scid: ( Quic::Packet.is_long_header(packet.packet_type) ? Connection.dump_cid(@host_cid) : ""),
522
+ dcid: Connection.dump_cid(@peer_cid.cid),
523
+ },
524
+ raw: { length: packet.sent_bytes },
525
+ },
526
+ )
527
+ end
528
+ end
529
+
530
+ # check if we can discard initial keys
531
+ if sent_handshake && @is_client
532
+ discard_epoch(TLS::Epoch::INITIAL)
533
+ end
534
+ end
535
+
536
+ # return datagrams to send and the destination network address
537
+ ret = []
538
+ datagrams.each do |datagram|
539
+ payload_length = datagram.bytesize
540
+ network_path.bytes_sent += payload_length
541
+ ret << [datagram, network_path.addr]
542
+
543
+ if @quic_logger
544
+ @quic_logger.log_event(
545
+ category: "transport",
546
+ event: "datagrams_sent",
547
+ data: {
548
+ count: 1,
549
+ raw: [{ length: UDP_HEADER_SIZE + payload_length, payload_length: payload_length }],
550
+ },
551
+ )
552
+ end
553
+ end
554
+
555
+ return ret
556
+ end
557
+
558
+ # Return the stream ID for the next stream created by this endpoint.
559
+ def get_next_available_stream_id(is_unidirectional: false)
560
+ uni_int = is_unidirectional ? 1 : 0
561
+ client_int = @is_client ? 0 : 1
562
+ stream_id = (uni_int << 1) | client_int
563
+ while @streams.keys.include?(stream_id) || @streams_finished.include?(stream_id)
564
+ stream_id += 4
565
+ end
566
+ return stream_id
567
+ end
568
+
569
+ # Return the time at which the timer should fire or nil if no timer is needed.
570
+ def get_timer
571
+ timer_at = @close_at
572
+ unless END_STATES.include?(@state)
573
+ # ack timer
574
+ @loss.spaces.each do |space|
575
+ timer_at = space.ack_at if space.ack_at && space.ack_at < timer_at
576
+ end
577
+
578
+ # loss detection timer
579
+ @loss_at = @loss.get_loss_detection_time
580
+ timer_at = @loss_at if @loss_at && @loss_at < timer_at
581
+
582
+ # pacing timer
583
+ timer_at = @pacing_at if @pacing_at && @pacing_at < timer_at
584
+ end
585
+
586
+ return timer_at
587
+ end
588
+
589
+ # Handle the timer.
590
+ #
591
+ # After calling this method call `datagram_to_send` to retrieve data which needs to be sent.
592
+ #
593
+ # param now: The current timer.
594
+ def handle_timer(now:)
595
+ # binding.b
596
+ # enf of closing period or idle timeout
597
+ if now >= @close_at
598
+ @close_event ||= Quic::Event::ConnectionTerminated.new.tap do |event|
599
+ event.error_code = Quic::Packet::QuicErrorCode::INTERNAL_ERROR
600
+ event.frame_type = Quic::Packet::QuicFrameType::PADDING
601
+ event.reason_phrase = "Ide timeout"
602
+ end
603
+ close_end
604
+ return
605
+ end
606
+
607
+ # loss detection timeout
608
+ if @loss_at && now >= @loss_at
609
+ # TODO: logger "Loss detection triggered"
610
+ @loss.on_loss_detection_timeout(now: now)
611
+ end
612
+ end
613
+
614
+ # Retrieve the next event from the event buffer.
615
+ #
616
+ # Returns `nil` if there are no buffered events.
617
+ def next_event
618
+ # binding.irb unless @is_client
619
+ @events.shift
620
+ rescue StandardError
621
+ return nil
622
+ end
623
+
624
+ # Handle an incoming datagram.
625
+ #
626
+ # After calling this method call `datagrams_to_send` to retrieve data which needs to be sent.
627
+ #
628
+ # param data: The datagram which was received.
629
+ # param addr: The network address from which the datagram was received.
630
+ # param now: The current time.
631
+ def receive_datagram(data:, addr:, now:)
632
+ # stop handling packets when closing
633
+ return if END_STATES.include?(@state)
634
+
635
+ # log datagram
636
+ if @quic_logger
637
+ @quic_logger.log_event(
638
+ category: "transport",
639
+ event: "datagrams_received",
640
+ data: {
641
+ count: 1,
642
+ raw: [{ length: UDP_HEADER_SIZE + data.bytesize, payload_length: data.bytesize }],
643
+ },
644
+ )
645
+ end
646
+
647
+ # for servers. arm the idle timeout on the first datagram
648
+ @close_at ||= now + @configuration.idle_timeout
649
+
650
+ buf = Buffer.new(data: data)
651
+ header = nil
652
+ until buf.eof
653
+ start_off = buf.tell
654
+ begin
655
+ header = Quic::Packet.pull_quic_header(buf: buf, host_cid_length: @configuration.connection_id_length)
656
+ rescue ValueError
657
+ if @quic_logger
658
+ @quic_logger.log_event(
659
+ category: "transport",
660
+ event: "packet_dropped",
661
+ data: {
662
+ trigger: "header_parse_error",
663
+ raw: { length: buf.capacity - start_off },
664
+ },
665
+ )
666
+ end
667
+ return
668
+ end
669
+
670
+ # check destination CID matches
671
+ destination_cid_seq = nil
672
+ @host_cids.each do |connection_id|
673
+ if header.destination_cid == connection_id.cid
674
+ destination_cid_seq = connection_id.sequence_number
675
+ break
676
+ end
677
+ end
678
+
679
+ if @is_client && destination_cid_seq.nil?
680
+ if @quic_logger
681
+ @quic_logger.log_event(
682
+ category: "transport",
683
+ event: "packet_dropped",
684
+ data: { trigger: "unknown_connection_id" },
685
+ )
686
+ end
687
+ return
688
+ end
689
+
690
+ # check protocol version
691
+ if @is_client &&
692
+ @state == QuicConnectionState::FIRSTFLIGHT &&
693
+ header.version == Quic::Packet::QuicProtocolVersion::NEGOTIATION &&
694
+ @version_negotiation_count == 0
695
+ # version negotiation
696
+ versions = []
697
+
698
+ versions << buf.pull_uint32 until buf.eof
699
+
700
+ if @quic_logger
701
+ @quic_logger.log_event(
702
+ category: "transport",
703
+ event: "packet_received",
704
+ data: {
705
+ frames: [],
706
+ header: {
707
+ packet_type: "version_negotiation",
708
+ scid: Connection.dump_cid(header.source_cid),
709
+ dcid: Connection.dump_cid(header.destination_cid),
710
+ },
711
+ raw: { length: buf.tell - start_off },
712
+ },
713
+ )
714
+ end
715
+
716
+ if versions.include?(@version)
717
+ # TODO: logging
718
+ return
719
+ end
720
+
721
+ common = @configuration.supported_versions & versions # TODO: correct?
722
+ if common.empty?
723
+ # TODO: logging
724
+ @close_event = Quic::Event::ConnectionTerminated.new.tap do |event|
725
+ event.error_code = Quic::Packet::QuicErrorCode::INTERNAL_ERROR
726
+ event.frame_type = Quic::Packet::QuicFrameType::PADDING
727
+ event.reason_phrase = "Could not find a common protocol version"
728
+ end
729
+ close_end
730
+ return
731
+ end
732
+
733
+ @packet_number = 0
734
+ @version = Quic::Packet::QuicProtocolVersion::VERSION_1 # TODO: check version
735
+ @version_negotiation_count += 1
736
+ _connect(now: now)
737
+ return
738
+ elsif header.version && !@configuration.supported_versions.include?(header.version)
739
+ # unsupported version
740
+ if @quic_logger
741
+ @quic_logger.log_event(
742
+ category: "transport",
743
+ event: "packet_dropped",
744
+ data: { trigger: "unsupported_version" },
745
+ )
746
+ end
747
+ return
748
+ end
749
+
750
+ # handle retry packet
751
+ # binding.irb
752
+ if header.packet_type == Quic::Packet::PACKET_TYPE_RETRY
753
+ tag = Quic::Packet.get_retry_integrity_tag(
754
+ packet_without_tag: buf.data_slice(
755
+ start: start_off,
756
+ ends: buf.tell - Quic::Packet::RETRY_INTEGRITY_TAG_SIZE,
757
+ ),
758
+ original_destination_cid: @peer_cid.cid,
759
+ )
760
+ if @is_client && @retry_count.zero? && header.destination_cid == @host_cid && header.integrity_tag == tag
761
+ if @quic_logger
762
+ @quic_logger.log_event(
763
+ category: "transport",
764
+ event: "packet_received",
765
+ data: {
766
+ frames: [],
767
+ header: {
768
+ packet_type: "retry",
769
+ scid: Connection.dump_cid(header.source_cid),
770
+ dcid: Connection.dump_cid(header.destination_cid),
771
+ },
772
+ raw: { length: buf.tell - start_off },
773
+ },
774
+ )
775
+ end
776
+ @peer_cid.cid = header.source_cid
777
+ @peer_token = header.token
778
+ @retry_count += 1
779
+ @retry_source_connection_id = header.source_cid
780
+ _connect(now: now)
781
+ else
782
+ # unexpected or invalid retry packet
783
+ if @quic_logger
784
+ @quic_logger.log_event(
785
+ category: "transport",
786
+ event: "packet_dropped",
787
+ data: { trigger: "unexpected_packet" },
788
+ )
789
+ end
790
+ return
791
+ end
792
+ end
793
+
794
+ network_path = find_network_path(addr)
795
+
796
+ # server initialization
797
+ if !@is_client && @state == QuicConnectionState::FIRSTFLIGHT
798
+ raise "first packet must be initial" unless header.packet_type == Quic::Packet::PACKET_TYPE_INITIAL
799
+
800
+ @network_paths = [network_path]
801
+ @version = Quic::Packet::QuicProtocolVersion::VERSION_1 # TODO: version
802
+ initialize_connection(header.destination_cid)
803
+ end
804
+
805
+ # determine crypto and packet space
806
+ epoch = Connection.get_epoch(header.packet_type)
807
+ crypto = @cryptos[epoch]
808
+ space = if epoch == TLS::Epoch::ZERO_RTT
809
+ @spaces[TLS::Epoch::ONE_RTT]
810
+ else
811
+ @spaces[epoch]
812
+ end
813
+
814
+ # decrypt packet
815
+ encrypted_off = buf.tell - start_off
816
+ end_off = buf.tell + header.rest_length
817
+ buf.seek(end_off)
818
+
819
+ begin
820
+ plain_header, plain_payload, packet_number = crypto.decrypt_packet(
821
+ packet: data[start_off...end_off], encrypted_offset: encrypted_off, expected_packet_number: space.expected_packet_number,
822
+ )
823
+ rescue ArgumentError # TODO: KeyUnavailableError
824
+ @quic_logger&.log_event(category: "transport", event: "packet_dropped", data: { trigger: "key_unavailable" })
825
+
826
+ # if a client receives HANDSHAKE or 1-RTT packets before it has handshake keys, it can assume that the server's INITIAL was lost
827
+ if @is_client && [TLS::Epoch::HANDSHAKE, TLS::Epoch::ONE_RTT].include?(epoch) && !@crypto_retransmitted
828
+ @loss.reschedule_data(now: now)
829
+ @crypto_retransmitted = true
830
+ end
831
+ next
832
+ rescue Quic::Crypto::CryptoError, OpenSSL::Cipher::CipherError # TODO: raise CryptoError from crypto module
833
+ @quic_logger&.log_event(category: "transport", event: "packet_dropped", data: { trigger: "payload_decrypt_error" })
834
+ next
835
+ end
836
+
837
+ # check reserved bits
838
+ reserved_mask = header.is_long_header ? 0x0c : 0x18
839
+ if plain_header[0].unpack1("C*") & reserved_mask != 0
840
+ close(
841
+ error_code: Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION,
842
+ frame_type: Quic::Packet::QuicFrameType::PADDING,
843
+ reason_phrase: "Reserved bits must be zero",
844
+ )
845
+ end
846
+
847
+ # log packet
848
+ quic_logger_frames = []
849
+ if @quic_logger
850
+ @quic_logger.log_event(
851
+ category: "transport",
852
+ event: "packet_received",
853
+ data: {
854
+ frames: quic_logger_frames,
855
+ header: {
856
+ packet_number: packet_number,
857
+ packet_type: @quic_logger.packet_type(header.packet_type),
858
+ dcid: Connection.dump_cid(header.destination_cid),
859
+ scid: Connection.dump_cid(header.source_cid),
860
+ },
861
+ raw: { length: end_off - start_off },
862
+ },
863
+ )
864
+ end
865
+
866
+ # raise expected packet number
867
+ space.expected_packet_number = packet_number + 1 if packet_number > space.expected_packet_number
868
+
869
+ # discard initial keys and packet space
870
+ discard_epoch(TLS::Epoch::INITIAL) if !@is_client && epoch == TLS::Epoch::HANDSHAKE
871
+
872
+ # update status
873
+ if @peer_cid.sequence_number.nil?
874
+ @peer_cid.cid = header.source_cid
875
+ @peer_cid.sequence_number = 0
876
+ end
877
+
878
+ if @state == Quic::Connection::QuicConnectionState::FIRSTFLIGHT
879
+ @remote_initial_source_connection_id = header.source_cid
880
+ set_state(Quic::Connection::QuicConnectionState::CONNECTED)
881
+ end
882
+
883
+ # update spin bit
884
+ if !header.is_long_header && packet_number > @spin_highest_pn
885
+ spin_bit = Quic::Packet.get_spin_bit(plain_header[0])
886
+ @spin_bit = if @is_client
887
+ !spin_bit
888
+ else
889
+ spin_bit
890
+ end
891
+ @spin_highest_pn = packet_number
892
+ @quic_logger&.log_event(category: "connectivity", event: "spin_bit_updated", data: { state: @spin_bit })
893
+ end
894
+
895
+ # handle payload
896
+ context = Quic::Connection::QuicReceiveContext.new.tap do |ctx|
897
+ ctx.epoch = epoch
898
+ ctx.host_cid = header.destination_cid
899
+ ctx.network_path = network_path
900
+ ctx.quic_logger_frames = quic_logger_frames
901
+ ctx.time = now
902
+ end
903
+
904
+ begin
905
+ is_ack_eliciting, is_probing = payload_received(context: context, plain: plain_payload)
906
+ rescue QuicConnectionError => err
907
+ # TODO: logging
908
+ close(error_code: err.error_code, frame_type: err.frame_type, reason_phrase: err.reason_phrase)
909
+ end
910
+ return if END_STATES.include?(@state) || @close_pending
911
+
912
+ # update idle timeout
913
+ @close_at = now + @configuration.idle_timeout
914
+
915
+ # handle migration
916
+ if !@is_client && context.host_cid != @host_cid && epoch == TLS::Epoch::ONE_RTT
917
+ # TODO: logging
918
+ # raise RuntimeError
919
+ @host_cid = context.host_cid
920
+ change_connection_id
921
+ end
922
+
923
+ # update network path
924
+ if !network_path.is_validated && epoch == TLS::Epoch::HANDSHAKE
925
+ # TODO: logging
926
+ network_path.is_validated = true
927
+ end
928
+ network_path.bytes_received += end_off - start_off
929
+ @network_paths << network_path unless @network_paths.include?(network_path)
930
+ idx = @network_paths.index(network_path)
931
+ if idx && idx > 0 && !is_probing && packet_number > space.largest_received_packet
932
+ # TODO: logging
933
+ @network_paths.delete_at(idx)
934
+ @network_paths.insert(0, network_path)
935
+ end
936
+
937
+ # record packet as received
938
+ unless space.discarded
939
+ if packet_number > space.largest_received_packet
940
+ space.largest_received_packet = packet_number
941
+ space.largest_received_time = now
942
+ end
943
+ space.ack_queue.add(packet_number)
944
+ space.ack_at = now + @ack_delay if is_ack_eliciting && space.ack_at.nil?
945
+ end
946
+ end
947
+ end
948
+
949
+ # Request an update of the encryption kyes.
950
+ def request_key_update
951
+ raise "cannot change key before handshake compeletes" unless @handshake_complete
952
+ @cryptos[TLS::Epoch::ONE_RTT].update_key
953
+ end
954
+
955
+ # Abruptly terminate the sending part of a stream.
956
+ #
957
+ # param stream_id: The stream's ID.
958
+ # param error_code: An error code inidicating why the stream is being reset.
959
+ def reset_stream(stream_id:, error_code:)
960
+ stream = get_or_create_stream_for_send(stream_id)
961
+ stream.sender.reset(error_code: error_code)
962
+ end
963
+
964
+ # Send a PING frame to the peer.
965
+ #
966
+ # param: uid: A unique ID for this PING.
967
+ def send_ping(uid)
968
+ @ping_pending.append(uid)
969
+ end
970
+
971
+ # Send a DATAGRAM frame.
972
+ #
973
+ # param data: The data to be sent.
974
+ def send_datagram_frame(data)
975
+ @datagrams_pending.append(data)
976
+ end
977
+
978
+ # Send data on the specific stream.
979
+ #
980
+ # param stream_id: The stream's ID.
981
+ # param data: The data to be sent.
982
+ # param end_stream: If set to `true`, the FIN bit will be set.
983
+ def send_stream_data(stream_id:, data:, end_stream: false)
984
+ stream = get_or_create_stream_for_send(stream_id)
985
+ stream.sender.write(data: data, end_stream: end_stream)
986
+ end
987
+
988
+ # Request termination of the receiveing part of a stream.
989
+ #
990
+ # param stream_id: The stream's ID.
991
+ # param error_code: An error code indicatig why the stream is being stopped.
992
+ def stop_stream(stream_id:, error_code:)
993
+ raise ValueError, "Cannot stop receiving on a local-initiated unidirectional stream" unless stream_can_receive(stream_id)
994
+
995
+ stream = @streams[stream_id]
996
+ raise ValueError, "Cannot stop receiving on an unknown stream" unless stream
997
+
998
+ stream.receiver.stop(error_code: error_code)
999
+ end
1000
+
1001
+ # Callback which is invoked by the TLS engine when ALPN negotiation completes.
1002
+ private def alpn_handler(alpn_protocol)
1003
+ # binding.irb
1004
+ @events.append(Event::ProtocolNegotiated.new.tap { |e| e.alpn_protocol = alpn_protocol })
1005
+ end
1006
+
1007
+ # Check the specified strem can receive data or raises a QuicConnectionError.
1008
+ private def assert_stream_can_receive(frame_type:, stream_id:)
1009
+ unless stream_can_receive(stream_id)
1010
+ err = QuicConnectionError.new.tap do |e|
1011
+ e.error_code = Quic::Packet::QuicErrorCode::STREAM_STATE_ERROR
1012
+ e.frame_type = frame_type
1013
+ e.reason_phrase = "Stream is send-only"
1014
+ end
1015
+ raise err
1016
+ end
1017
+ end
1018
+
1019
+ # Check the specified strem can send data or raises a QuicConnectionError.
1020
+ private def assert_stream_can_send(frame_type:, stream_id:)
1021
+ unless stream_can_send(stream_id)
1022
+ err = QuicConnectionError.new.tap do |e|
1023
+ e.error_code = Quic::Packet::QuicErrorCode::STREAM_STATE_ERROR
1024
+ e.frame_type = frame_type
1025
+ e.reason_phrase = "Stream is receive-only"
1026
+ end
1027
+ raise err
1028
+ end
1029
+ end
1030
+
1031
+ # Update the destination connection ID by taking the next available connection ID provided by the peer.
1032
+ private def consume_peer_cid
1033
+ @peer_cid = @peer_cid_available.delete_at(0)
1034
+ # TODO: logging
1035
+ end
1036
+
1037
+ # Begin the close procedure.
1038
+ private def close_begin(is_initiator:, now:)
1039
+ @close_at = now + (3 * @loss.get_probe_timeout)
1040
+ if is_initiator
1041
+ set_state(QuicConnectionState::CLOSING)
1042
+ else
1043
+ set_state(QuicConnectionState::DRAINING)
1044
+ end
1045
+ end
1046
+
1047
+ # End the close procedure.
1048
+ private def close_end
1049
+ @close_at = nil
1050
+ @spaces.keys.each do |epoch|
1051
+ discard_epoch(epoch)
1052
+ end
1053
+ @events << @close_event
1054
+ set_state(QuicConnectionState::TERMINATED)
1055
+ # "signal log end"
1056
+ if @quic_logger
1057
+ @configuration.quic_logger.end_trace(@quic_logger)
1058
+ @quic_logger = nil
1059
+ end
1060
+ end
1061
+
1062
+ # Start the client handshake.
1063
+ private def _connect(now:)
1064
+ raise unless @is_client
1065
+
1066
+ @close_at = now + @configuration.idle_timeout
1067
+ initialize_connection(@peer_cid.cid)
1068
+
1069
+ @tls.handle_message(input_data: "", output_buf: @crypto_buffers)
1070
+ push_crypto_data
1071
+ end
1072
+
1073
+ private def discard_epoch(epoch)
1074
+ unless @spaces[epoch].discarded
1075
+ # TODO: logger
1076
+ @cryptos[epoch].teardown
1077
+ @loss.discard_space(space: @spaces[epoch])
1078
+ @spaces[epoch].discarded = true
1079
+ end
1080
+ end
1081
+
1082
+ private def find_network_path(addr)
1083
+ # check existing network paths
1084
+ @network_paths.each do |network_path|
1085
+ return network_path if network_path.addr == addr
1086
+ end
1087
+
1088
+ # new network path
1089
+ network_path = QuicNetworkPath.new.tap { |n| n.addr = addr; n.bytes_received = 0; }
1090
+ # TODO: logging
1091
+ return network_path
1092
+ end
1093
+
1094
+ # Get or create a stream in response to a received frame.
1095
+ private def get_or_create_stream(frame_type:, stream_id:)
1096
+ raise Quic::Stream::StreamFinishedError if @streams_finished.include?(stream_id)
1097
+
1098
+ stream = @streams[stream_id]
1099
+ unless stream
1100
+ # check initiator
1101
+ if Connection.stream_is_client_initiated(stream_id || 0) == @is_client
1102
+ # binding.irb
1103
+ err = QuicConnectionError.new.tap do |e|
1104
+ e.error_code = Quic::Packet::QuicErrorCode::STREAM_STATE_ERROR
1105
+ e.frame_type = frame_type
1106
+ e.reason_phrase = "Wrong stream initiator"
1107
+ end
1108
+ raise err
1109
+ end
1110
+
1111
+ # determine limits
1112
+ if Connection.stream_is_unidirectional(stream_id)
1113
+ max_stream_data_local = @local_max_stream_data_uni
1114
+ max_stream_data_remote = 0
1115
+ max_streams = @local_max_streams_uni
1116
+ else
1117
+ max_stream_data_local = @local_max_stream_data_bidi_local
1118
+ max_stream_data_remote = @remote_max_stream_data_bidi_local
1119
+ max_streams = @local_max_streams_bidi
1120
+ end
1121
+
1122
+ # check max streams
1123
+ stream_count = (stream_id.floor(4)) + 1
1124
+ if stream_count > max_streams.value
1125
+ err = QuicConnectionError.new.tap do |e|
1126
+ e.error_code = Quic::Packet::QuicErrorCode::STREAM_LIMIT_ERROR
1127
+ e.frame_type = frame_type
1128
+ e.reason_phrase = "Too many streams open"
1129
+ end
1130
+ raise err
1131
+ elsif stream_count > max_streams.used
1132
+ max_streams.used = stream_count
1133
+ end
1134
+
1135
+ # create stream
1136
+ # TODO: logger
1137
+ stream = @streams[stream_id] = Stream::QuicStream.new(
1138
+ stream_id: stream_id,
1139
+ max_stream_data_local: max_stream_data_local,
1140
+ max_stream_data_remote: max_stream_data_remote,
1141
+ writable: !Connection.stream_is_unidirectional(stream_id),
1142
+ )
1143
+ end
1144
+ return stream
1145
+ end
1146
+
1147
+ # Get or create a QUIC stream in order to send datra to the peer.
1148
+ #
1149
+ # This always occurs as a result of an API call.
1150
+ private def get_or_create_stream_for_send(stream_id)
1151
+ raise ValueError, "Cannot send data on peer-initiated unidirectional stream" unless stream_can_send(stream_id)
1152
+
1153
+ stream = @streams[stream_id]
1154
+ unless stream
1155
+ # check initiator
1156
+ raise ValueError, "Cannot send data on unknown peer-initiated stream" if Connection.stream_is_client_initiated(stream_id) != @is_client
1157
+
1158
+ # determine limits
1159
+ if Connection.stream_is_unidirectional(stream_id)
1160
+ max_stream_data_local = 0
1161
+ max_stream_data_remote = @remote_max_stream_data_uni
1162
+ max_streams = @remote_max_streams_uni
1163
+ streams_blocked = @streams_blocked_uni
1164
+ else
1165
+ max_stream_data_local = @local_max_stream_data_bidi_local
1166
+ max_stream_data_remote = @remote_max_stream_data_bidi_remote
1167
+ max_streams = @remote_max_streams_bidi
1168
+ streams_blocked = @streams_blocked_bidi
1169
+ end
1170
+
1171
+ # create stream
1172
+ stream = @streams[stream_id] = Stream::QuicStream.new(
1173
+ stream_id: stream_id,
1174
+ max_stream_data_local: max_stream_data_local,
1175
+ max_stream_data_remote: max_stream_data_remote,
1176
+ readable: !Connection.stream_is_unidirectional(stream_id),
1177
+ )
1178
+
1179
+ # mark stream as blocked if needed
1180
+ if stream_id.floor(4) >= max_streams
1181
+ stream.is_blocked = true
1182
+ streams_blocked << stream
1183
+ @streams_blocked_pending = true
1184
+ end
1185
+ end
1186
+ return stream
1187
+ end
1188
+
1189
+ private def handle_session_ticket(session_ticket)
1190
+ if session_ticket.max_early_data_size && session_ticket.max_early_data_size != MAX_EARLY_DATA
1191
+ err = QuicConnectionError.new.tap do |e|
1192
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1193
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
1194
+ e.reason_phrase = "Invalid max_early_data value #{session_ticket.max_early_data_size}"
1195
+ end
1196
+ raise err
1197
+ end
1198
+ @session_ticket_handler&.call(session_ticket)
1199
+ end
1200
+
1201
+ private def initialize_connection(peer_cid)
1202
+ # TLS
1203
+ @tls = TLS::Context.new(
1204
+ alpn_protocols: @configuration.alpn_protocols,
1205
+ cadata: @configuration.cadata,
1206
+ cafile: @configuration.cafile,
1207
+ capath: @configuration.capath,
1208
+ cipher_suites: @configuration.cipher_suites,
1209
+ is_client: @is_client,
1210
+ logger: @logger,
1211
+ max_early_data: (@is_client ? nil : MAX_EARLY_DATA),
1212
+ server_name: @configuration.server_name,
1213
+ verify_mode: @configuration.verify_mode,
1214
+ )
1215
+ @tls.certificate = @configuration.certificate
1216
+ @tls.certificate_chain = @configuration.certificate_chain
1217
+ @tls.certificate_private_key = @configuration.private_key
1218
+ @tls.handshake_extensions = [[Connection.get_transport_parameters_extension(@version), serialize_transport_parameters]]
1219
+
1220
+ # TLS session resumption
1221
+ session_ticket = @configuration.session_ticket
1222
+ if @is_client && session_ticket && session_ticket.is_valid && session_ticket.server_name == @configuration.server_name
1223
+ @tls.session_ticket = @configuration.session_ticket
1224
+
1225
+ # parse saved QUIC transport parameters - for 0-RTT
1226
+ if session_ticket.max_early_data_size == MAX_EARLY_DATA
1227
+ session_ticket.other_extensions.each do |ext_type, ext_data|
1228
+ if ext_type == Connection.get_transport_parameters_extension(@version)
1229
+ parse_transport_parameters(data: ext_data, from_session_ticket: true)
1230
+ break
1231
+ end
1232
+ end
1233
+ end
1234
+ end
1235
+
1236
+ # TLS callbacks
1237
+ @tls.alpn_cb = method(:alpn_handler)
1238
+ @tls.get_session_ticket_cb = @session_ticket_fetcher if @session_ticket_fetcher
1239
+ @tls.new_session_ticket_cb = method(:handle_session_ticket) if @session_ticket_handler
1240
+ @tls.update_traffic_key_cb = method(:update_traffic_key)
1241
+ # binding.irb
1242
+
1243
+ # packet spaces
1244
+ create_crypto_pair = lambda do |epoch|
1245
+ eopch_name = %w(initial 0rtt handshake 1rtt)[epoch]
1246
+ secret_names = ["server_#{eopch_name}_secret", "client_#{eopch_name}_secret"]
1247
+ recv_secret_name = secret_names[@is_client ? 0 : 1]
1248
+ send_secret_name = secret_names[@is_client ? 1 : 0]
1249
+ # TODO: keylog
1250
+ return Crypto::CryptoPair.new(
1251
+ recv_setup_cb: ->(trigger) { log_key_updated(key_type: recv_secret_name, trigger: trigger) },
1252
+ recv_teardown_cb: ->(trigger) { log_key_retired(key_type: recv_secret_name, trigger: trigger) },
1253
+ send_setup_cb: ->(trigger) { log_key_updated(key_type: send_secret_name, trigger: trigger) },
1254
+ send_teardown_cb: ->(trigger) { log_key_retired(key_type: send_secret_name, trigger: trigger) },
1255
+ )
1256
+ end
1257
+
1258
+ @cryptos = [TLS::Epoch::INITIAL, TLS::Epoch::ZERO_RTT, TLS::Epoch::HANDSHAKE, TLS::Epoch::ONE_RTT].inject({}) do |hash, epoch|
1259
+ hash[epoch] = create_crypto_pair.call(epoch)
1260
+ hash
1261
+ end
1262
+ @crypto_buffers = {
1263
+ TLS::Epoch::INITIAL => Buffer.new(capacity: CRYPTO_BUFFER_SIZE),
1264
+ TLS::Epoch::HANDSHAKE => Buffer.new(capacity: CRYPTO_BUFFER_SIZE),
1265
+ TLS::Epoch::ONE_RTT => Buffer.new(capacity: CRYPTO_BUFFER_SIZE),
1266
+ }
1267
+ @crypto_streams = {
1268
+ TLS::Epoch::INITIAL => Stream::QuicStream.new,
1269
+ TLS::Epoch::HANDSHAKE => Stream::QuicStream.new,
1270
+ TLS::Epoch::ONE_RTT => Stream::QuicStream.new,
1271
+ }
1272
+ @spaces = {
1273
+ TLS::Epoch::INITIAL => Recovery::QuicPacketSpace.new,
1274
+ TLS::Epoch::HANDSHAKE => Recovery::QuicPacketSpace.new,
1275
+ TLS::Epoch::ONE_RTT => Recovery::QuicPacketSpace.new,
1276
+ }
1277
+
1278
+ @cryptos[TLS::Epoch::INITIAL].setup_initial(cid: peer_cid, is_client: @is_client, version: @version)
1279
+ @loss.spaces = @spaces.values
1280
+ end
1281
+
1282
+ # Handle an ACK frame.
1283
+ private def handle_ack_frame(context:, frame_type:, buf:)
1284
+ ack_rangeset, ack_delay_encoded = Quic::Packet.pull_ack_frame(buf)
1285
+ if frame_type == Quic::Packet::QuicFrameType::ACK_ECN
1286
+ 3.times { buf.pull_uint_var }
1287
+ end
1288
+ ack_delay = (ack_delay_encoded << @remote_ack_delay_exponent) / 1000000
1289
+
1290
+ if @quic_logger
1291
+ context.quic_logger_frames << @quic_logger.encode_ack_frame(ranges: ack_rangeset, delay: ack_delay)
1292
+ end
1293
+
1294
+ # check whether per completed address validation
1295
+ if !@loss.peer_completed_address_validation && [TLS::Epoch::HANDSHAKE, TLS::Epoch::ONE_RTT].include?(context.epoch)
1296
+ @loss.peer_completed_address_validation = true
1297
+ end
1298
+
1299
+ @loss.on_ack_received(space: @spaces[context.epoch], ack_rangeset: ack_rangeset, ack_delay: ack_delay, now: context.time)
1300
+ end
1301
+
1302
+ # Handle a CONNECTION_CLOSE frame.
1303
+ private def handle_connection_close_frame(context:, frame_type:, buf:)
1304
+ error_code = buf.pull_uint_var
1305
+ if frame_type == Quic::Packet::QuicFrameType::TRANSPORT_CLOSE
1306
+ frame_type = buf.pull_uint_var
1307
+ else
1308
+ frame_type = nil
1309
+ end
1310
+ reason_length = buf.pull_uint_var
1311
+ begin
1312
+ reason_phrase = buf.pull_bytes(reason_length).encode(Encoding::UTF_8)
1313
+ rescue EncodingError
1314
+ reason_phrase = ""
1315
+ end
1316
+
1317
+ # log frame
1318
+ if @quic_logger
1319
+ context.quic_logger_frames << @quic_logger.encode_connection_close_frame(error_code: error_code, frame_type: frame_type, reason_phrase: reason_phrase)
1320
+ end
1321
+
1322
+ unless @close_event
1323
+ @close_event = Event::ConnectionTerminated.new.tap do |event|
1324
+ event.error_code = error_code
1325
+ event.frame_type = frame_type
1326
+ event.reason_phrase = reason_phrase
1327
+ end
1328
+ close_begin(is_initiator: false, now: context.time)
1329
+ end
1330
+ end
1331
+
1332
+ # Handle a CRYPTO frame.
1333
+ private def handle_crypto_frame(context:, frame_type:, buf:)
1334
+ # binding.b
1335
+ offset = buf.pull_uint_var
1336
+ length = buf.pull_uint_var
1337
+ if offset + length > Buffer::UINT_VAR_MAX
1338
+ err = QuicConnectionError.new.tap do |e|
1339
+ e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
1340
+ e.frame_type = frame_type
1341
+ e.reason_phrase = "offset + length cannot exceed 2^62 - 1"
1342
+ end
1343
+ raise err
1344
+ end
1345
+ frame = Quic::Packet::QuicStreamFrame.new.tap do |f|
1346
+ f.offset = offset
1347
+ f.data = buf.pull_bytes(length)
1348
+ end
1349
+
1350
+ # log frame
1351
+ if @quic_logger
1352
+ context.quic_logger_frames << @quic_logger.encode_crypto_frame(frame: frame)
1353
+ end
1354
+
1355
+ stream = @crypto_streams[context.epoch]
1356
+ event = stream.receiver.handle_frame(frame: frame)
1357
+ if event
1358
+ # pass data to TLS layer
1359
+ begin
1360
+ # binding.irb
1361
+ @tls.handle_message(input_data: event.data, output_buf: @crypto_buffers)
1362
+ push_crypto_data
1363
+ rescue TLS::Alert => e
1364
+ error = QuicConnectionError.new.tap do |err|
1365
+ err.error_code = Quic::Packet::QuicErrorCode::CRYPTO_ERROR + e.description
1366
+ err.frame_type = frame_type
1367
+ err.reason_phrase = e.message
1368
+ end
1369
+ raise error
1370
+ end
1371
+
1372
+ # parse transport parameters
1373
+ if !@parameters_received && @tls.received_extensions
1374
+ @tls.received_extensions.each do |ext_type, ext_data|
1375
+ if ext_type == Connection.get_transport_parameters_extension(@version)
1376
+ parse_transport_parameters(data: ext_data)
1377
+ @parameters_received = true
1378
+ break
1379
+ end
1380
+ end
1381
+ unless @parameters_received
1382
+ err = QuicConnectionError.new.tap do |e|
1383
+ e.error_code = Quic::Packet::QuicErrorCode::CRYPTO_ERROR + TLS::AlertDescription::MISSING_EXTENSION
1384
+ e.frame_type = frame_type
1385
+ e.reason_phrase = "No QUIC transport parameters received"
1386
+ end
1387
+ raise err
1388
+ end
1389
+ end
1390
+
1391
+ # update current epoch
1392
+ # binding.irb unless @is_client
1393
+ if !@handshake_complete && [TLS::State::CLIENT_POST_HANDSHAKE, TLS::State::SERVER_POST_HANDSHAKE].include?(@tls.state)
1394
+ @handshake_complete = true
1395
+
1396
+ # for servers, the handshake is now confirmed
1397
+ unless @is_client
1398
+ discard_epoch(TLS::Epoch::HANDSHAKE)
1399
+ @handshake_confirmed = true
1400
+ @handshake_done_pending = true
1401
+ end
1402
+ replenish_connection_ids
1403
+ @events.append(
1404
+ Event::HandshakeCompleted.new.tap do |ev|
1405
+ ev.alpn_protocol = @tls.alpn_negotiated
1406
+ ev.early_data_accepted = !!@tls.early_data_accepted
1407
+ ev.session_resumed = @tls.session_resumed
1408
+ end
1409
+ )
1410
+ unblock_streams(false)
1411
+ unblock_streams(true)
1412
+ # TODO: logger
1413
+ end
1414
+ else
1415
+ # TODO: logger
1416
+
1417
+ # if a server receives duplicate CRYPTO in an INITIAL packet, it can assume the client did not receive the server's CRYPTO
1418
+ if !@is_client && context.epoch == TLS::Epoch::INITIAL && !@crypto_retransmitted
1419
+ @loss.reschedule_data(now: context.time)
1420
+ @crypto_retransmitted = true
1421
+ end
1422
+ end
1423
+ end
1424
+
1425
+ # Handle a DATA_BLOCKED frame.
1426
+ private def handle_data_blocked_frame(context:, frame_type:, buf:)
1427
+ limit = buf.pull_uint_var
1428
+
1429
+ # log frame
1430
+ if @quic_logger
1431
+ context.quic_logger_frames << @quic_logger.encode_data_blocked_frame(limit: limit)
1432
+ end
1433
+ end
1434
+
1435
+ # Handle a DATAGRAM frmae.
1436
+ private def handle_datagram_frame(context:, frame_type:, buf:)
1437
+ start = buf.tell
1438
+ if frame_type == Quic::Packet::QuicFrameType::DATAGRAM_WITH_LENGTH
1439
+ length = buf.pull_uint_var
1440
+ else
1441
+ length = buf.capacity - start
1442
+ end
1443
+ data = buf.pull_bytes(length)
1444
+
1445
+ # log frame
1446
+ if @quic_logger
1447
+ context.quic_logger_frames << @quic_logger.encode_datagram_frame(length: length)
1448
+ end
1449
+
1450
+ # check frame is allowed
1451
+ if !@configuration.max_datagram_frame_size || buf.tell - start >= @configuration.max_datagram_frame_size
1452
+ err = QuicConnectionError.new.tap do |e|
1453
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1454
+ e.frame_type = frame_type
1455
+ e.reason_phrase = "Unexpected DATAGRAM frame"
1456
+ end
1457
+ raise err
1458
+ end
1459
+ @events.append(Event::DatagramFrameReceived.new.tap { |e| e.data = data })
1460
+ end
1461
+
1462
+ # Handle a HANDSHAKE_DONE frame.
1463
+ private def handle_handshake_done_frame(context:, frame_type:, buf:)
1464
+ # log frame
1465
+ context.quic_logger_frames << @quic_logger.encode_handshake_done_frame if @quic_logger
1466
+
1467
+ unless @is_client
1468
+ err = QuicConnectionError.new.tap do |e|
1469
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1470
+ e.frame_type = frame_type
1471
+ e.reason_phrase = "Clients must not send HANDSHAKE_DONE frames"
1472
+ end
1473
+ raise err
1474
+ end
1475
+
1476
+ # for clients. the handshake is now confirmed
1477
+ unless @handshake_confirmed
1478
+ discard_epoch(TLS::Epoch::HANDSHAKE)
1479
+ @handshake_confirmed = true
1480
+ @loss.peer_completed_address_validation = true
1481
+ end
1482
+ end
1483
+
1484
+ # Handle a MAX_DATA frame.
1485
+ #
1486
+ # This adjusts the total amount of we can send to the peer.
1487
+ private def handle_max_data_frame(context:, frame_type:, buf:)
1488
+ max_data = buf.pull_uint_var
1489
+ # log frame
1490
+ context.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: frame_type, maximum: max_data) if @quic_logger
1491
+
1492
+ if max_data > @remote_max_data
1493
+ # TODO: logging
1494
+ @remote_max_data = max_data
1495
+ end
1496
+ end
1497
+
1498
+ # Handle a MAX_STREAM_DATA frame.
1499
+ #
1500
+ # This adjusts the total amount of we can send on a specific stream.
1501
+ private def handle_max_stream_data_frame(context:, frame_type:, buf:)
1502
+ stream_id = buf.pull_uint_var
1503
+ max_stream_data = buf.pull_uint_var
1504
+
1505
+ # log frame
1506
+ context.quic_logger_frames << @quic_logger.encode_max_stream_data_frame(maximum: max_stream_data, stream_id: stream_id) if @quic_logger
1507
+
1508
+ # check stream direction
1509
+ assert_stream_can_send(frame_type: frame_type, stream_id: stream_id)
1510
+
1511
+ stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
1512
+ if max_stream_data > stream.max_stream_data_remote
1513
+ # TODO: logging
1514
+ stream.max_stream_data_remote = max_stream_data
1515
+ end
1516
+ end
1517
+
1518
+ # Handle a MAX_STREAMS_BIDI frame.
1519
+ #
1520
+ # This raises number of bidirectional streams we can initiate to the peer.
1521
+ private def handle_max_streams_bidi_frame(context:, frame_type:, buf:)
1522
+ max_streams = buf.pull_uint_var
1523
+ if max_streams > STREAM_COUNT_MAX
1524
+ err = QuicConnectionError.new.tap do |e|
1525
+ e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
1526
+ e.frame_type = frame_type
1527
+ e.reason_phrase = "Maximum Streams cannot exceed 2^60"
1528
+ end
1529
+ raise err
1530
+ end
1531
+
1532
+ # log frame
1533
+ context.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: frame_type, maximum: max_streams) if @quic_logger
1534
+
1535
+ if max_streams > @remote_max_streams_bidi
1536
+ # TODO: logging
1537
+ @remote_max_streams_bidi = max_streams
1538
+ unblock_streams(false)
1539
+ end
1540
+ end
1541
+
1542
+ # Handle a MAX_STREAMS_UNI frame.
1543
+ #
1544
+ # This raises number of unidirectional streams we can initiate to the peer.
1545
+ private def handle_max_streams_uni_frame(context:, frame_type:, buf:)
1546
+ max_streams = buf.pull_uint_var
1547
+ if max_streams > STREAM_COUNT_MAX
1548
+ err = QuicConnectionError.new.tap do |e|
1549
+ e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
1550
+ e.frame_type = frame_type
1551
+ e.reason_phrase = "Maximum Streams cannot exceed 2^60"
1552
+ end
1553
+ raise err
1554
+ end
1555
+
1556
+ # log frame
1557
+ context.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: frame_type, maximum: max_streams) if @quic_logger
1558
+
1559
+ if max_streams > @remote_max_streams_uni
1560
+ # TODO: logging
1561
+ @remote_max_streams_uni = max_streams
1562
+ unblock_streams(true)
1563
+ end
1564
+ end
1565
+
1566
+ # Handle a NEW_CONNECTION_ID frame.
1567
+ private def handle_new_connection_id_frame(context:, frame_type:, buf:)
1568
+ sequence_number = buf.pull_uint_var
1569
+ retire_prior_to = buf.pull_uint_var
1570
+ length = buf.pull_uint8
1571
+ connection_id = buf.pull_bytes(length)
1572
+ stateless_reset_token = buf.pull_bytes(Quic::Packet::STATELESS_RESET_TOKEN_SIZE)
1573
+ if !connection_id || connection_id.bytesize > Quic::Packet::CONNECTION_ID_MAX_SIZE
1574
+ err = QuicConnectionError.new.tap do |e|
1575
+ e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
1576
+ e.frame_type = frame_type
1577
+ e.reason_phrase = "Length must be greater than 0 and less than 20"
1578
+ end
1579
+ raise err
1580
+ end
1581
+
1582
+ # log frame
1583
+ context.quic_logger_frames << @quic_logger.encode_new_connection_id_frame(
1584
+ connection_id: connection_id,
1585
+ retire_prior_to: retire_prior_to,
1586
+ sequence_number: sequence_number,
1587
+ stateless_reset_token: stateless_reset_token,
1588
+ ) if @quic_logger
1589
+
1590
+ # sanity check
1591
+ if retire_prior_to > sequence_number
1592
+ err = QuicConnectionError.new.tap do |e|
1593
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1594
+ e.frame_type = frame_type
1595
+ e.reason_phrase = "Retire Prior To is greater than Sequence Number"
1596
+ end
1597
+ raise err
1598
+ end
1599
+
1600
+ # determine which CIDs to retire
1601
+ change_cid = false
1602
+ retire = @peer_cid_available.filter { |c| c.sequence_number < retire_prior_to }
1603
+ if @peer_cid.sequence_number < retire_prior_to
1604
+ change_cid = true
1605
+ retire.insert(0, @peer_cid)
1606
+ end
1607
+
1608
+ # update available CIDs
1609
+ @peer_cid_available = @peer_cid_available.filter { |c| c.sequence_number >= retire_prior_to }
1610
+ unless @peer_cid_sequence_numbers.include?(sequence_number)
1611
+ @peer_cid_available.append(
1612
+ QuicConnectionId.new.tap do |conn|
1613
+ conn.cid = connection_id
1614
+ conn.sequence_number = sequence_number
1615
+ conn.stateless_reset_token = stateless_reset_token
1616
+ end
1617
+ )
1618
+ @peer_cid_sequence_numbers.add(sequence_number)
1619
+ end
1620
+
1621
+ # retire previous CIDs
1622
+ retire.each { |quic_connection_id| retire_peer_cid(quic_connection_id) }
1623
+
1624
+ # assign new CID if we retired the active one
1625
+ consume_peer_cid if change_cid
1626
+
1627
+ # check number of active connection IDs, including the selected one
1628
+ if 1 + @peer_cid_available.length > @local_active_connection_id_limit
1629
+ err = QuicConnectionError.new.tap do |e|
1630
+ e.error_code = Quic::Packet::QuicErrorCode::CONNECTION_ID_LIMIT_ERROR
1631
+ e.frame_type = frame_type
1632
+ e.reason_phrase = "Too many active connection IDs"
1633
+ end
1634
+ raise err
1635
+ end
1636
+ end
1637
+
1638
+ # Handle a NEW_TOKEN frame.
1639
+ private def handle_new_token_frame(context:, frame_type:, buf:)
1640
+ length = buf.pull_uint_var
1641
+ token = buf.pull_bytes(length)
1642
+
1643
+ # log frame
1644
+ context.quic_logger_frames << @quic_logger.encode_new_token_frame(token: token) if @quic_logger
1645
+
1646
+ unless @is_client
1647
+ err = QuicConnectionError.new.tap do |e|
1648
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1649
+ e.frame_type = frame_type
1650
+ e.reason_phrase = "Clients must not send NEW_TOKEN frames"
1651
+ end
1652
+ raise err
1653
+ end
1654
+ end
1655
+
1656
+ # Handle a PADDING frame.
1657
+ private def handle_padding_frame(context:, frame_type:, buf:)
1658
+ pos = buf.tell
1659
+ # consume padding
1660
+ buf.data_slice(start: pos, ends: buf.capacity).each_byte do |byte|
1661
+ break if byte != 0 # 0x00
1662
+ pos += 1
1663
+ end
1664
+ buf.seek(pos)
1665
+ # log frame
1666
+ context.quic_logger_frames << @quic_logger.encode_padding_frame if @quic_logger
1667
+ end
1668
+
1669
+ # Handle a PATH_CHALLENGE frame.
1670
+ private def handle_path_challenge_frame(context:, frame_type:, buf:)
1671
+ data = buf.pull_bytes(8)
1672
+ # log frame
1673
+ context.quic_logger_frames << @quic_logger.encode_path_challenge_frame(data: data) if @quic_logger
1674
+ context.network_path.remote_challenge = data
1675
+ end
1676
+
1677
+ # Handle a PATH_RESPONSE frame.
1678
+ private def handle_path_response_frame(context:, frame_type:, buf:)
1679
+ data = buf.pull_bytes(8)
1680
+ # log frame
1681
+ context.quic_logger_frames << @quic_logger.encode_path_response_frame(data: data) if @quic_logger
1682
+
1683
+ if data != context.network_path.local_challenge
1684
+ err = QuicConnectionError.new.tap do |e|
1685
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1686
+ e.frame_type = frame_type
1687
+ e.reason_phrase = "Response does not match challenge"
1688
+ end
1689
+ raise err
1690
+ end
1691
+ # TODO: logging
1692
+ context.network_path.is_validated = true
1693
+ end
1694
+
1695
+ # Handle a PING frame.
1696
+ private def handle_ping_frame(context:, frame_type:, buf:)
1697
+ # log frame
1698
+ context.quic_logger_frames << @quic_logger.encode_ping_frame if @quic_logger
1699
+ end
1700
+
1701
+ # Handle a RESET_STREAM frame.
1702
+ private def handle_reset_stream_frame(context:, frame_type:, buf:)
1703
+ stream_id = buf.pull_uint_var
1704
+ error_code = buf.pull_uint_var
1705
+ final_size = buf.pull_uint_var
1706
+
1707
+ # log frame
1708
+ if @quic_logger
1709
+ context.quic_logger_frames << @quic_logger.encode_reset_stream_frame(error_code: error_code, final_size: final_size, stream_id: stream_id)
1710
+ end
1711
+
1712
+ # check stream direction
1713
+ assert_stream_can_receive(frame_type: frame_type, stream_id: stream_id)
1714
+
1715
+ # check flow-control limits
1716
+ stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
1717
+ if final_size > stream.max_stream_data_local
1718
+ err = QuicConnectionError.new.tap do |e|
1719
+ e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
1720
+ e.frame_type = frame_type
1721
+ e.reason_phrase = "Over stream data limit"
1722
+ end
1723
+ raise err
1724
+ end
1725
+ newly_received = [0, final_size - stream.receiver.highest_offset].max
1726
+ if @local_max_data.used + newly_received > @local_max_data.value
1727
+ err = QuicConnectionError.new.tap do |e|
1728
+ e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
1729
+ e.frame_type = frame_type
1730
+ e.reason_phrase = "Over connection data limit"
1731
+ end
1732
+ raise err
1733
+ end
1734
+
1735
+ # process reset
1736
+ # TODO: logging
1737
+ begin
1738
+ event = stream.receiver.handle_reset(final_size: final_size, error_code: error_code)
1739
+ rescue Stream::FinalSizeError => error
1740
+ err = QuicConnectionError.new.tap do |e|
1741
+ e.error_code = Quic::Packet::QuicErrorCode::FINAL_SIZE_ERROR
1742
+ e.frame_type = frame_type
1743
+ e.reason_phrase = error.message
1744
+ end
1745
+ raise err
1746
+ end
1747
+ @events.append(event) if event
1748
+ @local_max_data.used += newly_received
1749
+ end
1750
+
1751
+ # Handle a RETIRE_CONNECTION_ID frame.
1752
+ private def handle_retire_connection_id_frame(context:, frame_type:, buf:)
1753
+ sequence_number = buf.pull_uint_var
1754
+
1755
+ # log frame
1756
+ context.quic_logger_frames << @quic_logger.encode_retire_connection_id_frame(sequence_number: sequence_number) if @quic_logger
1757
+
1758
+ if sequence_number >= @host_cid_seq
1759
+ err = QuicConnectionError.new.tap do |e|
1760
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1761
+ e.frame_type = frame_type
1762
+ e.reason_phrase = "Cannot retire unknown connection ID"
1763
+ end
1764
+ raise err
1765
+ end
1766
+
1767
+ # find the connection ID by sequence number
1768
+ @host_cids.each_with_index do |connection_id, i|
1769
+ if connection_id.sequence_number == sequence_number
1770
+ if connection_id.cid == context.host_cid
1771
+ err = QuicConnectionError.new.tap do |e|
1772
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1773
+ e.frame_type = frame_type
1774
+ e.reason_phrase = "Cannot retire current connection ID"
1775
+ end
1776
+ raise err
1777
+ end
1778
+ # TODO: logging
1779
+ @host_cids.delete_at(i)
1780
+ @events.append(
1781
+ Event::ConnectionIdRetired.new.tap do |ev|
1782
+ ev.connection_id = connection_id.cid
1783
+ end
1784
+ )
1785
+ break
1786
+ end
1787
+ end
1788
+ # issue a new connection ID
1789
+ replenish_connection_ids
1790
+ end
1791
+
1792
+ # Handle a STOP_SENDING frame.
1793
+ private def handle_stop_sending_frame(context:, frame_type:, buf:)
1794
+ stream_id = buf.pull_uint_var
1795
+ error_code = buf.pull_uint_var # application error code
1796
+
1797
+ # log frame
1798
+ context.quic_logger_frames << @quic_logger.encode_stop_sending_frame(error_code: error_code, stream_id: stream_id) if @quic_logger
1799
+
1800
+ # check stream direction
1801
+ assert_stream_can_send(frame_type: frame_type, stream_id: stream_id)
1802
+
1803
+ # reset the stream
1804
+ stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
1805
+ stream.sender.reset(error_code: Quic::Packet::QuicErrorCode::NO_ERROR)
1806
+ end
1807
+
1808
+ # Handle a STREAM frame.
1809
+ private def handle_stream_frame(context:, frame_type:, buf:)
1810
+ stream_id = buf.pull_uint_var
1811
+ offset = if frame_type & 4 != 0
1812
+ buf.pull_uint_var
1813
+ else
1814
+ 0
1815
+ end
1816
+ length = if frame_type & 2 != 0
1817
+ buf.pull_uint_var
1818
+ else
1819
+ buf.capacity - buf.tell
1820
+ end
1821
+ if offset + length > Buffer::UINT_VAR_MAX
1822
+ err = QuicConnectionError.new.tap do |e|
1823
+ e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
1824
+ e.frame_type = frame_type
1825
+ e.reason_phrase = "offset + length cannot exceed 2^62 - 1"
1826
+ end
1827
+ raise err
1828
+ end
1829
+ frame = Quic::Packet::QuicStreamFrame.new.tap do |f|
1830
+ f.offset = offset
1831
+ f.data = buf.pull_bytes(length)
1832
+ f.fin = ((frame_type & 1) == 1)
1833
+ end
1834
+
1835
+ # log frame
1836
+ context.quic_logger_frames << @quic_logger.encode_stream_frame(frame: frame, stream_id: stream_id) if @quic_logger
1837
+
1838
+ # check stream direction
1839
+ assert_stream_can_receive(frame_type: frame_type, stream_id: stream_id)
1840
+
1841
+ # check flow-control limits
1842
+ stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
1843
+ if offset + length > stream.max_stream_data_local
1844
+ err = QuicConnectionError.new.tap do |e|
1845
+ e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
1846
+ e.frame_type = frame_type
1847
+ e.reason_phrase = "Over stream data limit"
1848
+ end
1849
+ raise err
1850
+ end
1851
+ newly_received = [0, offset + length - stream.receiver.highest_offset].max
1852
+ if @local_max_data.used + newly_received > @local_max_data.value
1853
+ err = QuicConnectionError.new.tap do |e|
1854
+ e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
1855
+ e.frame_type = frame_type
1856
+ e.reason_phrase = "Over connection data limit"
1857
+ end
1858
+ raise err
1859
+ end
1860
+
1861
+ # process data
1862
+ begin
1863
+ event = stream.receiver.handle_frame(frame: frame)
1864
+ rescue Stream::FinalSizeError => error
1865
+ err = QuicConnectionError.new.tap do |e|
1866
+ e.error_code = Quic::Packet::QuicErrorCode::FINAL_SIZE_ERROR
1867
+ e.frame_type = frame_type
1868
+ e.reason_phrase = error.message
1869
+ end
1870
+ raise err
1871
+ end
1872
+ @events.append(event) if event
1873
+ @local_max_data.used += newly_received
1874
+ end
1875
+
1876
+ # Handle a STREAM_DATA_BLOCKED frame.
1877
+ private def handle_stream_data_blocked_frame(context:, frame_type:, buf:)
1878
+ stream_id = buf.pull_uint_var
1879
+ limit = buf.pull_uint_var
1880
+
1881
+ # log frame
1882
+ context.quic_logger_frames << @quic_logger.encode_stream_data_blocked_frame(limit: limit, stream_id: stream_id)
1883
+
1884
+ # check stream direction
1885
+ assert_stream_can_receive(frame_type: frame_type, stream_id: stream_id)
1886
+
1887
+ get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
1888
+ end
1889
+
1890
+ # Handle a STREAMS_BLOCKED frame.
1891
+ private def handle_streams_blocked_frame(context:, frame_type:, buf:)
1892
+ limit = buf.pull_uint_var
1893
+ if limit > STREAM_COUNT_MAX
1894
+ err = QuicConnectionError.new.tap do |e|
1895
+ e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
1896
+ e.frame_type = frame_type
1897
+ e.reason_phrase = "Maximum Streams cannot exceed 2^60"
1898
+ end
1899
+ raise err
1900
+ end
1901
+
1902
+ # log frame
1903
+ context.quic_logger_frames << @quic_logger.encode_streams_blocked_frame(
1904
+ is_unidirectional: (frame_type == Quic::Packet::QuicFrameType::STREAMS_BLOCKED_UNI),
1905
+ limit: limit,
1906
+ ) if @quic_logger
1907
+ end
1908
+
1909
+ # Log a key retirement.
1910
+ private def log_key_retired(key_type:, trigger:)
1911
+ # binding.irb
1912
+ @quic_logger&.log_event(category: "security", event: "key_retired", data: { key_type: key_type, trigger: trigger })
1913
+ end
1914
+
1915
+ # Log a key update
1916
+ private def log_key_updated(key_type:, trigger:)
1917
+ @quic_logger&.log_event(category: "security", event: "key_updated", data: { key_type: key_type, trigger: trigger })
1918
+ end
1919
+
1920
+ # Callback when an ACK frame is acknowledged or lost.
1921
+ private def on_ack_delivery(delivery:, space:, highest_acked:)
1922
+ if delivery == Quic::PacketBuilder::QuicDeliveryState::ACKED
1923
+ space.ack_queue.subtract(0, highest_acked + 1)
1924
+ end
1925
+ end
1926
+
1927
+ # Callback when a MAX_DATA or MAX_STREAMS frame is acknowledged or lost.
1928
+ private def on_connection_limit_delivery(delivery:, limit:)
1929
+ limit.sent = 0 if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
1930
+ end
1931
+
1932
+ # Callback when a HANDSHAKE_DONE frame is acknowledged or lost.
1933
+ private def on_handshake_done_delivery(delivery:)
1934
+ @handshake_done_pending = true if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
1935
+ end
1936
+
1937
+ # Callback when a MAX_STREAM_DATA frame is acknowleged or loss.
1938
+ private def on_max_stream_data_delivery(delivery:, stream:)
1939
+ stream.max_stream_data_local_sent = 0 if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
1940
+ end
1941
+
1942
+ # Callback when a NEW_CONNECTION_ID frame is acknowledged or loss.
1943
+ private def on_new_connection_id_delivery(delivery:, connection_id:)
1944
+ connection_id.was_sent = false if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
1945
+ end
1946
+
1947
+ # Callback when a PING frame is acknowledged or lost.
1948
+ private def on_ping_delivery(delivery:, uids:)
1949
+ if delivery == Quic::PacketBuilder::QuicDeliveryState::ACKED
1950
+ # TODO: logging
1951
+ uids.each do |uid|
1952
+ @events.append(Event::PingAcknowledged.new.tap { |e| e.uid = uid })
1953
+ end
1954
+ else
1955
+ @ping_pending += uids
1956
+ end
1957
+ end
1958
+
1959
+ # Callback when a RETIRE_CONNECTION_ID frame is acknowledged or lost.
1960
+ private def on_retire_connection_id_delivery(delivery:, sequence_number:)
1961
+ @retire_connection_ids.append(sequence_number) if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
1962
+ end
1963
+
1964
+ # Handle a QUIC packet payload.
1965
+ private def payload_received(context:, plain:)
1966
+ buf = Buffer.new(data: plain)
1967
+
1968
+ frame_found = false
1969
+ is_ack_eliciting = false
1970
+ is_probing = nil
1971
+ until buf.eof
1972
+ frame_type = buf.pull_uint_var
1973
+
1974
+ # check frame type is known
1975
+ frame_handler, frame_epochs = @frame_handlers[frame_type]
1976
+ # puts "frame_type #{frame_handler}" unless @is_client
1977
+ if frame_handler.nil?
1978
+ err = QuicConnectionError.new.tap do |e|
1979
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1980
+ e.frame_type = frame_type
1981
+ e.reason_phrase = "Unknown frame type"
1982
+ end
1983
+ raise err
1984
+ end
1985
+
1986
+ # check frame is allowed for the epoch
1987
+ unless frame_epochs.include?(context.epoch)
1988
+ err = QuicConnectionError.new.tap do |e|
1989
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
1990
+ e.frame_type = frame_type
1991
+ e.reason_phrase = "Unexpected frame type"
1992
+ end
1993
+ raise err
1994
+ end
1995
+
1996
+ # handle the frame
1997
+ begin
1998
+ # binding.irb if frame_type == :handle_max_data_frame
1999
+ method(frame_handler).call(context: context, frame_type: frame_type, buf: buf)
2000
+ rescue Buffer::BufferReadError
2001
+ # binding.irb
2002
+ err = QuicConnectionError.new.tap do |e|
2003
+ e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
2004
+ e.frame_type = frame_type
2005
+ e.reason_phrase = "Failed to parse frame"
2006
+ end
2007
+ raise err
2008
+ rescue Quic::Stream::StreamFinishedError
2009
+ # we lack the state for the stream, ignore the frame
2010
+ end
2011
+
2012
+ # update ACK only / probing flags
2013
+ frame_found = true
2014
+
2015
+ unless Quic::Packet::NON_ACK_ELICITING_FRAME_TYPES.include?(frame_type)
2016
+ is_ack_eliciting = true
2017
+ end
2018
+
2019
+ if !Quic::Packet::PROBING_FRAME_TYPES.include?(frame_type)
2020
+ is_probing = false
2021
+ elsif is_probing.nil?
2022
+ is_probing = true
2023
+ end
2024
+ end
2025
+
2026
+ unless frame_found
2027
+ err = QuicConnectionError.new.tap do |e|
2028
+ e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
2029
+ e.frame_type = Quic::Packet::QuicFrameType::PADDING
2030
+ e.reason_phrase = "Packet contains no frames"
2031
+ end
2032
+ raise err
2033
+ end
2034
+ return [is_ack_eliciting, is_probing]
2035
+ end
2036
+
2037
+ # Generate new connection IDs.
2038
+ private def replenish_connection_ids
2039
+ while @host_cids.length < [8, @remote_active_connection_id_limit.to_i].min
2040
+ @host_cids.append(
2041
+ QuicConnectionId.new.tap do |conn|
2042
+ conn.cid = Random.urandom(@configuration.connection_id_length)
2043
+ conn.sequence_number = @host_cid_seq
2044
+ conn.stateless_reset_token = Random.urandom(16)
2045
+ end
2046
+ )
2047
+ @host_cid_seq += 1
2048
+ end
2049
+ end
2050
+
2051
+ # Retire a destication connection ID.
2052
+ private def retire_peer_cid(connection_id)
2053
+ # TODO: logger
2054
+ @retire_connection_ids.append(connection_id.sequence_number)
2055
+ end
2056
+
2057
+ private def push_crypto_data
2058
+ @crypto_buffers.each do |epoch, buf|
2059
+ @crypto_streams[epoch].sender.write(data: buf.data)
2060
+ buf.seek(0)
2061
+ end
2062
+ end
2063
+
2064
+ private def send_probe
2065
+ @probe_pending = true
2066
+ end
2067
+
2068
+ # Parse and apply remote transport parameters.
2069
+ #
2070
+ # `from_session_ticket` is `true` when restoring saved transport parameters, and `false` when handling received transport parameters.
2071
+ private def parse_transport_parameters(data:, from_session_ticket: false)
2072
+ begin
2073
+ quic_transport_parameters = Quic::Packet.pull_quic_transport_parameters(Buffer.new(data: data))
2074
+ rescue ::Raioquic::ValueError, ::Raioquic::Buffer::BufferReadError
2075
+ err = QuicConnectionError.new.tap do |e|
2076
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2077
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2078
+ e.reason_phrase = "Could not parse QUIC transport parameters"
2079
+ end
2080
+ raise err
2081
+ end
2082
+
2083
+ # log event
2084
+ if @quic_logger && !from_session_ticket
2085
+ @quic_logger.log_event(
2086
+ category: "transport",
2087
+ event: "parameters_set",
2088
+ data: @quic_logger.encode_transport_parameters(owner: "remote", parameters: quic_transport_parameters),
2089
+ )
2090
+ end
2091
+
2092
+ # validate remote parameters
2093
+ unless @is_client
2094
+ %w[original_destination_connection_id preferred_address retry_source_connection_id stateless_reset_token].each do |att|
2095
+ if quic_transport_parameters[att]
2096
+ err = QuicConnectionError.new.tap do |e|
2097
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2098
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2099
+ e.reason_phrase = "#{att} is not allowed for clients"
2100
+ end
2101
+ raise err
2102
+ end
2103
+ end
2104
+ end
2105
+
2106
+ unless from_session_ticket
2107
+ if quic_transport_parameters.initial_source_connection_id != @remote_initial_source_connection_id
2108
+ err = QuicConnectionError.new.tap do |e|
2109
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2110
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2111
+ e.reason_phrase = "initial_source_connection_id does not match"
2112
+ end
2113
+ raise err
2114
+ end
2115
+
2116
+ if @is_client && quic_transport_parameters.original_destination_connection_id != @original_destination_connection_id
2117
+ err = QuicConnectionError.new.tap do |e|
2118
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2119
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2120
+ e.reason_phrase = "original_source_connection_id does not match"
2121
+ end
2122
+ raise err
2123
+ end
2124
+
2125
+ if @is_client && quic_transport_parameters.retry_source_connection_id != @retry_source_connection_id
2126
+ err = QuicConnectionError.new.tap do |e|
2127
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2128
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2129
+ e.reason_phrase = "retry_source_connection_id does not match"
2130
+ end
2131
+ raise err
2132
+ end
2133
+
2134
+ if quic_transport_parameters.active_connection_id_limit && quic_transport_parameters.active_connection_id_limit.to_i < 2
2135
+ err = QuicConnectionError.new.tap do |e|
2136
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2137
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2138
+ e.reason_phrase = "active_connection_id_limit must be no less than 2"
2139
+ end
2140
+ raise err
2141
+ end
2142
+
2143
+ if quic_transport_parameters.ack_delay_exponent && quic_transport_parameters.ack_delay_exponent > 20
2144
+ err = QuicConnectionError.new.tap do |e|
2145
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2146
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2147
+ e.reason_phrase = "ack_delay_exponent must be <= 20"
2148
+ end
2149
+ raise err
2150
+ end
2151
+
2152
+ if quic_transport_parameters.max_ack_delay && quic_transport_parameters.max_ack_delay >= 2**14
2153
+ err = QuicConnectionError.new.tap do |e|
2154
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2155
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2156
+ e.reason_phrase = "max_ack_delay must be < 2^14"
2157
+ end
2158
+ raise err
2159
+ end
2160
+
2161
+ if quic_transport_parameters.max_udp_payload_size && quic_transport_parameters.max_udp_payload_size < 1200
2162
+ err = QuicConnectionError.new.tap do |e|
2163
+ e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
2164
+ e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
2165
+ e.reason_phrase = "max_udp_payload_size must be >= 1200"
2166
+ end
2167
+ raise err
2168
+ end
2169
+ end
2170
+
2171
+ # store remote parameters
2172
+ unless from_session_ticket
2173
+ # TODO: check original implementation
2174
+ @remote_ack_delay_exponent = quic_transport_parameters.ack_delay_exponent if quic_transport_parameters.ack_delay_exponent
2175
+ @loss.max_ack_delay = quic_transport_parameters.max_ack_delay / 1000.0 if quic_transport_parameters.max_ack_delay
2176
+
2177
+ if @is_client && @peer_cid.sequence_number == 0 && quic_transport_parameters.stateless_reset_token
2178
+ @peer_cid.stateless_reset_token = quic_transport_parameters.stateless_reset_token
2179
+ end
2180
+ end
2181
+
2182
+ if quic_transport_parameters.active_connection_id_limit
2183
+ @remote_active_connection_id_limit = quic_transport_parameters.active_connection_id_limit.to_i
2184
+ end
2185
+
2186
+ if quic_transport_parameters.max_idle_timeout
2187
+ @remote_max_idle_timeout = quic_transport_parameters.max_idle_timeout / 1000.0
2188
+ end
2189
+
2190
+ @remote_max_datagram_frame_size = quic_transport_parameters.max_datagram_frame_size
2191
+
2192
+ %w[max_data max_stream_data_bidi_local max_stream_data_bidi_remote max_stream_data_uni max_streams_bidi max_streams_uni].each do |param|
2193
+ value = quic_transport_parameters["initial_#{param}"]
2194
+ instance_variable_set("@remote_#{param}", value) if value
2195
+ end
2196
+ end
2197
+
2198
+ private def serialize_transport_parameters
2199
+ quic_transport_parameters = Quic::Packet::QuicTransportParameters.new.tap do |param|
2200
+ param.ack_delay_exponent = @local_ack_delay_exponent
2201
+ param.active_connection_id_limit = @local_active_connection_id_limit
2202
+ param.max_idle_timeout = (@configuration.idle_timeout * 1000).to_i
2203
+ param.initial_max_data = @local_max_data.value
2204
+ param.initial_max_stream_data_bidi_local = @local_max_stream_data_bidi_local
2205
+ param.initial_max_stream_data_bidi_remote = @local_max_stream_data_bidi_remote
2206
+ param.initial_max_stream_data_uni = @local_max_stream_data_uni
2207
+ param.initial_max_streams_bidi = @local_max_streams_bidi.value
2208
+ param.initial_max_streams_uni = @local_max_streams_uni.value
2209
+ param.initial_source_connection_id = @local_initial_source_connection_id
2210
+ param.max_ack_delay = 25
2211
+ param.max_datagram_frame_size = @configuration.max_datagram_frame_size
2212
+ param.quantum_readiness = @configuration.quantam_readiness_test ? "Q" * 1200 : nil
2213
+ param.stateless_reset_token = @host_cids[0].stateless_reset_token
2214
+ end
2215
+ unless @is_client
2216
+ quic_transport_parameters.original_destination_connection_id = @original_destination_connection_id
2217
+ quic_transport_parameters.retry_source_connection_id = @retry_source_connection_id
2218
+ end
2219
+
2220
+ # log event
2221
+ if @quic_logger
2222
+ @quic_logger.log_event(
2223
+ category: "transport",
2224
+ event: "parameters_set",
2225
+ data: @quic_logger.encode_transport_parameters(owner: "local", parameters: quic_transport_parameters)
2226
+ )
2227
+ end
2228
+
2229
+ buf = Buffer.new(capacity: 3 * Quic::PacketBuilder::PACKET_MAX_SIZE)
2230
+ Quic::Packet.push_quic_transport_parameters(buf: buf, params: quic_transport_parameters)
2231
+ return buf.data
2232
+ end
2233
+
2234
+ private def set_state(state)
2235
+ # TODO: logging
2236
+ @state = state
2237
+ end
2238
+
2239
+ private def stream_can_receive(stream_id)
2240
+ Connection.stream_is_client_initiated(stream_id) != @is_client || !Connection.stream_is_unidirectional(stream_id)
2241
+ end
2242
+
2243
+ private def stream_can_send(stream_id)
2244
+ Connection.stream_is_client_initiated(stream_id) == @is_client || !Connection.stream_is_unidirectional(stream_id)
2245
+ end
2246
+
2247
+ private def unblock_streams(is_unidirectional)
2248
+ if is_unidirectional
2249
+ max_stream_data_remote = @remote_max_stream_data_uni
2250
+ max_streams = @remote_max_streams_uni
2251
+ streams_blocked = @streams_blocked_uni
2252
+ else
2253
+ max_stream_data_remote = @remote_max_stream_data_bidi_remote
2254
+ max_streams = @remote_max_streams_bidi
2255
+ streams_blocked = @streams_blocked_bidi
2256
+ end
2257
+
2258
+ while streams_blocked && streams_blocked[0] && streams_blocked[0].stream_id.div(4) < max_streams
2259
+ stream = streams_blocked.delete_at(0)
2260
+ stream.is_blocked = false
2261
+ stream.max_stream_data_remote = max_stream_data_remote
2262
+ end
2263
+
2264
+ @streams_blocked_pending = false if !@streams_blocked_bidi && !@streams_blocked_uni
2265
+ end
2266
+
2267
+ # Callback which is invoked by the TLS engine when new traffic keys are available.
2268
+ private def update_traffic_key(direction:, epoch:, cipher_suite:, secret:)
2269
+ secrets_log_file = @configuration.secrets_log_file
2270
+ if secrets_log_file
2271
+ # TODO: logging to file
2272
+ end
2273
+ crypto = @cryptos[epoch]
2274
+ if direction == TLS::Direction::ENCRYPT
2275
+ crypto.send.setup(cipher_suite: cipher_suite, secret: secret, version: @version)
2276
+ else
2277
+ crypto.recv.setup(cipher_suite: cipher_suite, secret: secret, version: @version)
2278
+ end
2279
+ end
2280
+
2281
+ private def write_application(builder:, network_path:, now:)
2282
+ crypto_stream = nil
2283
+ if @cryptos[TLS::Epoch::ONE_RTT].send.is_valid
2284
+ crypto = @cryptos[TLS::Epoch::ONE_RTT]
2285
+ crypto_stream = @crypto_streams[TLS::Epoch::ONE_RTT]
2286
+ packet_type = Quic::Packet::PACKET_TYPE_ONE_RTT
2287
+ elsif @cryptos[TLS::Epoch::ZERO_RTT].send.is_valid
2288
+ crypto = @cryptos[TLS::Epoch::ZERO_RTT]
2289
+ packet_type = Quic::Packet::PACKET_TYPE_ZERO_RTT
2290
+ else
2291
+ return
2292
+ end
2293
+ space = @spaces[TLS::Epoch::ONE_RTT]
2294
+
2295
+ c = -1
2296
+ while true
2297
+ c += 1
2298
+ # apply pacing, except if we have ACKs to send
2299
+ if space.ack_at.nil? || space.ack_at >= now
2300
+ @pacing_at = @loss.pacer.next_send_time(now: now)
2301
+ break if @pacing_at
2302
+ end
2303
+ builder.start_packet(packet_type: packet_type, crypto: crypto)
2304
+
2305
+ if @handshake_complete
2306
+ # ACK
2307
+ write_ack_frame(builder: builder, space: space, now: now) if space.ack_at && space.ack_at <= now
2308
+
2309
+ # HANDSHAKE_DONE
2310
+ if @handshake_done_pending
2311
+ write_handshake_done_frame(builder: builder)
2312
+ @handshake_done_pending = false
2313
+ end
2314
+
2315
+ # PATH CHALLENGE
2316
+ if !network_path.is_validated && network_path.local_challenge.nil?
2317
+ challenge = Random.urandom(8)
2318
+ write_path_challenge_frame(builder: builder, challenge: challenge)
2319
+ network_path.local_challenge = challenge
2320
+ end
2321
+
2322
+ # PATH RESPONSE
2323
+ if network_path.remote_challenge
2324
+ write_path_response_frame(builder: builder, challenge: network_path.remote_challenge)
2325
+ network_path.remote_challenge = nil
2326
+ end
2327
+
2328
+ # NEW_CONNECTION_ID
2329
+ @host_cids.each do |connection_id|
2330
+ unless connection_id.was_sent
2331
+ write_new_connection_id_frame(builder: builder, connection_id: connection_id)
2332
+ end
2333
+ # write_new_connection_id_frame(builder: builder, connection_id: connection_id) unless connection_id.was_sent
2334
+ end
2335
+
2336
+ # RETIRE_CONNECTION_ID
2337
+ @retire_connection_ids.each do |sequence_number|
2338
+ write_retire_connection_id_frame(builder: builder, sequence_number: sequence_number)
2339
+ end
2340
+ @retire_connection_ids.clear
2341
+
2342
+ # STREAMS_BLOCKED
2343
+ if @streams_blocked_pending
2344
+ if @streams_blocked_bidi
2345
+ write_streams_blocked_frame(
2346
+ builder: builder, frame_type: Quic::Packet::QuicFrameType::STREAMS_BLOCKED_BIDI, limit: @remote_max_streams_bidi,
2347
+ )
2348
+ end
2349
+ if @streams_blocked_uni
2350
+ write_streams_blocked_frame(
2351
+ builder: builder, frame_type: Quic::Packet::QuicFrameType::STREAMS_BLOCKED_UNI, limit: @remote_max_streams_uni,
2352
+ )
2353
+ end
2354
+ @streams_blocked_pending = false
2355
+ end
2356
+
2357
+ # MAX_DATA and MAX_STREAMS
2358
+ write_connection_limits(builder: builder, space: space)
2359
+ end
2360
+
2361
+ # stream-level limits
2362
+ @streams.each_value { |stream| write_stream_limits(builder: builder, space: space, stream: stream) }
2363
+
2364
+ # PING (user-request)
2365
+ if @ping_pending.size > 0
2366
+ write_ping_frame(builder: builder, uids: @ping_pending)
2367
+ @ping_pending.clear
2368
+ end
2369
+
2370
+ # PING (probe)
2371
+ if @probe_pending
2372
+ write_ping_frame(builder: builder, comment: "probe")
2373
+ @probe_pending = false
2374
+ end
2375
+
2376
+ # CRYPTO
2377
+ if crypto_stream && !crypto_stream.sender.buffer_is_empty
2378
+ write_crypto_frame(builder: builder, space: space, stream: crypto_stream)
2379
+ end
2380
+
2381
+ # DATAGRAM
2382
+ while @datagrams_pending.length > 0
2383
+ begin
2384
+ write_datagram_frame(builder: builder, data: @datagrams_pending[0], frame_type: Quic::Packet::QuicFrameType::DATAGRAM_WITH_LENGTH)
2385
+ @datagrams_pending.pop
2386
+ rescue Quic::PacketBuilder::QuicPacketBuilderStop
2387
+ break
2388
+ end
2389
+ end
2390
+
2391
+ @streams.each_value do |stream|
2392
+ # if the stream is finished, discard it
2393
+ if stream.is_finished
2394
+ # TODO: logging
2395
+ @streams.delete(stream.stream_id)
2396
+ @streams_finished << stream.stream_id
2397
+ next
2398
+ end
2399
+
2400
+ # STOP_SENDING
2401
+ write_stop_sending_frame(builder: builder, stream: stream) if stream.receiver.stop_pending
2402
+
2403
+ if stream.sender.reset_pending
2404
+ # RESET_STREAM
2405
+ write_reset_stream_frame(builder: builder, stream: stream)
2406
+ elsif !stream.is_blocked && !stream.sender.buffer_is_empty
2407
+ # STREAM
2408
+ @remote_max_data_used += write_stream_frame(
2409
+ builder: builder,
2410
+ space: space,
2411
+ stream: stream,
2412
+ max_offset: [stream.sender.highest_offset + @remote_max_data - @remote_max_data_used, stream.max_stream_data_remote].min,
2413
+ )
2414
+ end
2415
+ end
2416
+ # puts "builder.packer_is_emprty: #{builder.packet_is_empty}"
2417
+ if builder.packet_is_empty
2418
+ break
2419
+ else
2420
+ @loss.pacer.update_after_send(now: now)
2421
+ end
2422
+ end
2423
+ end
2424
+
2425
+ private def write_handshake(builder:, epoch:, now:)
2426
+ crypto = @cryptos[epoch]
2427
+ return unless crypto.send.is_valid
2428
+
2429
+ crypto_stream = @crypto_streams[epoch]
2430
+ space = @spaces[epoch]
2431
+
2432
+ while true
2433
+ if epoch == TLS::Epoch::INITIAL
2434
+ packet_type = Quic::Packet::PACKET_TYPE_INITIAL
2435
+ else
2436
+ packet_type = Quic::Packet::PACKET_TYPE_HANDSHAKE
2437
+ end
2438
+ builder.start_packet(packet_type: packet_type, crypto: crypto)
2439
+
2440
+ # ACK
2441
+ write_ack_frame(builder: builder, space: space, now: now) if space.ack_at
2442
+
2443
+ # CRYPTO
2444
+ unless crypto_stream.sender.buffer_is_empty
2445
+ if write_crypto_frame(builder: builder, space: space, stream: crypto_stream)
2446
+ @probe_pending = false
2447
+ end
2448
+ end
2449
+
2450
+ # PING (probe)
2451
+ if @probe_pending && !@handshake_complete && (epoch == TLS::Epoch::HANDSHAKE || !@cryptos[TLS::Epoch::HANDSHAKE].send.is_valid)
2452
+ write_ping_frame(builder: builder, comment: "probe")
2453
+ @probe_pending = false
2454
+ end
2455
+
2456
+ break if builder.packet_is_empty
2457
+ end
2458
+ end
2459
+
2460
+ private def write_ack_frame(builder:, space:, now:)
2461
+ # calculate AKC delay
2462
+ ack_delay = now - space.largest_received_time
2463
+ ack_delay_encoded = (ack_delay * 1000000).to_i >> @local_ack_delay_exponent
2464
+
2465
+ buf = builder.start_frame(
2466
+ frame_type: Quic::Packet::QuicFrameType::ACK,
2467
+ capacity: ACK_FRAME_CAPACITY,
2468
+ handler: method(:on_ack_delivery),
2469
+ handler_args: [space, space.largest_received_packet],
2470
+ )
2471
+ ranges = Quic::Packet.push_ack_frame(buf: buf, rangeset: space.ack_queue, delay: ack_delay_encoded)
2472
+ space.ack_at = nil
2473
+
2474
+ # log frame
2475
+ builder.quic_logger_frames << @quic_logger.encode_ack_frame(ranges: space.ack_queue, delay: ack_delay) if @quic_logger
2476
+
2477
+ # check if we need to trigger an ACK-of-ACK
2478
+ write_ping_frame(builder: builder, comment: "ACK-of-ACK trigger") if ranges > 1 && builder.packet_number % 8 == 0
2479
+ end
2480
+
2481
+ private def write_connection_close_frame(builder:, epoch:, error_code:, frame_type: nil, reason_phrase: "")
2482
+ # convert application-level close to transport-level close in early stages
2483
+ ec = error_code
2484
+ ft = frame_type
2485
+ rp = reason_phrase
2486
+ if frame_type.nil? && [TLS::Epoch::INITIAL, TLS::Epoch::HANDSHAKE].include?(epoch)
2487
+ ec = Quic::Packet::QuicErrorCode::APPLICATION_ERROR
2488
+ ft = Quic::Packet::QuicFrameType::PADDING
2489
+ rp = ""
2490
+ end
2491
+ reason_bytes = rp # encode to utf8 bytes in original code
2492
+ # binding.irb if reason_bytes.nil?
2493
+ reason_length = reason_bytes.bytesize
2494
+
2495
+ unless ft
2496
+ buf = builder.start_frame(
2497
+ frame_type: Quic::Packet::QuicFrameType::APPLICATION_CLOSE, capacity: APPLICATION_CLOSE_FRAME_CAPACITY + reason_length,
2498
+ )
2499
+ buf.push_uint_var(ec)
2500
+ buf.push_uint_var(reason_length)
2501
+ buf.push_bytes(reason_bytes)
2502
+ else
2503
+ buf = builder.start_frame(
2504
+ frame_type: Quic::Packet::QuicFrameType::TRANSPORT_CLOSE, capacity: TRANSPORT_CLOSE_FLAME_CAPACITY + reason_length,
2505
+ )
2506
+ buf.push_uint_var(ec)
2507
+ buf.push_uint_var(ft)
2508
+ buf.push_uint_var(reason_length)
2509
+ buf.push_bytes(reason_bytes)
2510
+ end
2511
+ # log frame
2512
+ if @quic_logger
2513
+ builder.quic_logger_frames << @quic_logger.encode_connection_close_frame(error_code: error_code, frame_type: frame_type, reason_phrase: reason_phrase)
2514
+ end
2515
+ end
2516
+
2517
+ # Raise MAX_DATA or MAX_STREAMS if needed.
2518
+ private def write_connection_limits(builder:, space:)
2519
+ [@local_max_data, @local_max_streams_bidi, @local_max_streams_uni].each do |limit|
2520
+ if limit.used * 2 > limit.value
2521
+ limit.value *= 2
2522
+ # TODO: logging
2523
+ end
2524
+ if limit.value != limit.sent
2525
+ buf = builder.start_frame(
2526
+ frame_type: limit.frame_type,
2527
+ capacity: CONNECTION_LIMIT_FRAME_CAPACITY,
2528
+ handler: method(:on_connection_limit_delivery),
2529
+ handler_args: [limit, nil],
2530
+ )
2531
+ buf.push_uint_var(limit.value)
2532
+ limit.sent = limit.value
2533
+
2534
+ # log frame
2535
+ builder.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: limit.frame_type, maximum: limit.value) if @quic_logger
2536
+ end
2537
+ end
2538
+ end
2539
+
2540
+ private def write_crypto_frame(builder:, space:, stream:)
2541
+ # binding.b
2542
+ frame_overhead = 3 + Buffer.size_uint_var(stream.sender.next_offset)
2543
+ frame = stream.sender.get_frame(max_size: builder.remaining_flight_space - frame_overhead)
2544
+ if frame
2545
+ buf = builder.start_frame(
2546
+ frame_type: Quic::Packet::QuicFrameType::CRYPTO,
2547
+ capacity: frame_overhead,
2548
+ handler: stream.sender.method(:on_data_delivery),
2549
+ handler_args: [frame.offset, frame.offset + frame.data.bytesize],
2550
+ )
2551
+ buf.push_uint_var(frame.offset)
2552
+ buf.push_uint16(frame.data.bytesize | 0x4000)
2553
+ buf.push_bytes(frame.data)
2554
+
2555
+ # log frame
2556
+ builder.quic_logger_frames << @quic_logger.encode_crypto_frame(frame: frame) if @quic_logger
2557
+
2558
+ return true
2559
+ end
2560
+ return false
2561
+ end
2562
+
2563
+ # Write a DATAGRAM frame.
2564
+ #
2565
+ # Returns true if the frame was processed, false otherwise.
2566
+ private def write_datagram_frame(builder:, data:, frame_type:)
2567
+ raise RuntimeError unless frame_type == Quic::Packet::QuicFrameType::DATAGRAM_WITH_LENGTH
2568
+
2569
+ length = data&.bytesize || 0
2570
+ frame_size = 1 + Buffer.size_uint_var(length) + length
2571
+
2572
+ buf = builder.start_frame(frame_type: frame_type, capacity: frame_size)
2573
+ buf.push_uint_var(length)
2574
+ buf.push_bytes(data)
2575
+
2576
+ # log frame
2577
+ builder.quic_logger_frames << @quic_logger.encode_datagram_frame(length: length) if @quic_logger
2578
+
2579
+ return true
2580
+ end
2581
+
2582
+ private def write_handshake_done_frame(builder:)
2583
+ builder.start_frame(
2584
+ frame_type: Quic::Packet::QuicFrameType::HANDSHAKE_DONE,
2585
+ capacity: HANDSHAKE_DONE_FRAME_CAPACITY,
2586
+ handler: method(:on_handshake_done_delivery),
2587
+ )
2588
+
2589
+ # log frame
2590
+ builder.quic_logger_frames << @quic_logger.encode_handshake_done_frame if @quic_logger
2591
+ end
2592
+
2593
+ private def write_new_connection_id_frame(builder:, connection_id:)
2594
+ retire_prior_to = 0 # FIXME: from original
2595
+
2596
+ buf = builder.start_frame(
2597
+ frame_type: Quic::Packet::QuicFrameType::NEW_CONNECTION_ID,
2598
+ capacity: NEW_CONNECTION_ID_FRAME_CAPACITY,
2599
+ handler: method(:on_new_connection_id_delivery),
2600
+ handler_args: [connection_id],
2601
+ )
2602
+ buf.push_uint_var(connection_id.sequence_number)
2603
+ buf.push_uint_var(retire_prior_to)
2604
+ buf.push_uint8(connection_id.cid.bytesize)
2605
+ buf.push_bytes(connection_id.cid)
2606
+ buf.push_bytes(connection_id.stateless_reset_token)
2607
+
2608
+ connection_id.was_sent = true
2609
+ @events.append(Event::ConnectionIdIssued.new.tap { |e| e.connection_id = connection_id.cid })
2610
+
2611
+ # log frame
2612
+ if @quic_logger
2613
+ builder.quic_logger_frames << @quic_logger.encode_new_connection_id_frame(
2614
+ connection_id: connection_id.cid,
2615
+ retire_prior_to: retire_prior_to,
2616
+ sequence_number: connection_id.sequence_number,
2617
+ stateless_reset_token: connection_id.stateless_reset_token,
2618
+ )
2619
+ end
2620
+ end
2621
+
2622
+ private def write_path_challenge_frame(builder:, challenge:)
2623
+ buf = builder.start_frame(
2624
+ frame_type: Quic::Packet::QuicFrameType::PATH_CHALLENGE,
2625
+ capacity: PATH_CHALLENGE_FRAME_CAPACITY,
2626
+ )
2627
+ buf.push_bytes(challenge)
2628
+
2629
+ # log frame
2630
+ builder.quic_logger_frames << @quic_logger.encode_path_challenge_frame(data: challenge) if @quic_logger
2631
+ end
2632
+
2633
+ private def write_path_response_frame(builder:, challenge:)
2634
+ buf = builder.start_frame(
2635
+ frame_type: Quic::Packet::QuicFrameType::PATH_RESPONSE,
2636
+ capacity: PATH_RESPONSE_FRAME_CAPACITY,
2637
+ )
2638
+ buf.push_bytes(challenge)
2639
+
2640
+ # log frame
2641
+ builder.quic_logger_frames << @quic_logger.encode_path_response_frame(data: challenge) if @quic_logger
2642
+ end
2643
+
2644
+ private def write_ping_frame(builder:, uids: [], comment: "")
2645
+ builder.start_frame(
2646
+ frame_type: Quic::Packet::QuicFrameType::PING,
2647
+ capacity: PING_FRAME_CAPACITY,
2648
+ handler: method(:on_ping_delivery),
2649
+ handler_args: [uids.dup],
2650
+ )
2651
+
2652
+ # log frame
2653
+ builder.quic_logger_frames << @quic_logger.encode_ping_frame if @quic_logger
2654
+ end
2655
+
2656
+ private def write_reset_stream_frame(builder:, stream:)
2657
+ buf = builder.start_frame(
2658
+ frame_type: Quic::Packet::QuicFrameType::RESET_STREAM,
2659
+ capacity: RESET_STREAM_FRAME_CAPACITY,
2660
+ handler: stream.sender.method(:on_reset_delivery),
2661
+ )
2662
+ frame = stream.sender.get_reset_frame
2663
+ buf.push_uint_var(frame.stream_id)
2664
+ buf.push_uint_var(frame.error_code)
2665
+ buf.push_uint_var(frame.final_size)
2666
+
2667
+ # log frame
2668
+ if @quic_logger
2669
+ builder.quic_logger_frames << @quic_logger.encode_reset_stream_frame(error_code: frame.error_code, final_size: frame.final_size, stream_id: frame.stream_id)
2670
+ end
2671
+ end
2672
+
2673
+ private def write_retire_connection_id_frame(builder:, sequence_number:)
2674
+ buf = builder.start_frame(
2675
+ frame_type: Quic::Packet::QuicFrameType::RETIRE_CONNECTION_ID,
2676
+ capacity: RETIRE_CONNECTION_ID_CAPACITY,
2677
+ handler: method(:on_retire_connection_id_delivery),
2678
+ handler_args: [sequence_number],
2679
+ )
2680
+ buf.push_uint_var(sequence_number)
2681
+
2682
+ # log frame
2683
+ builder.quic_logger_frames << @quic_logger.encode_retire_connection_id_frame(sequence_number: sequence_number) if @quic_logger
2684
+ end
2685
+
2686
+ private def write_stop_sending_frame(builder:, stream:)
2687
+ buf = builder.start_frame(
2688
+ frame_type: Quic::Packet::QuicFrameType::STOP_SENDING,
2689
+ capacity: STOP_SENDING_FRAME_CAPACITY,
2690
+ handler: stream.receiver.method(:on_stop_sending_delivery),
2691
+ )
2692
+ frame = stream.receiver.get_stop_frame
2693
+ buf.push_uint_var(frame.stream_id)
2694
+ buf.push_uint_var(frame.error_code)
2695
+
2696
+ # log frame
2697
+ builder.quic_logger_frames << @quic_logger.encode_stop_sending_frame(error_code: frame.error_code, stream_id: frame.stream_id) if @quic_logger
2698
+ end
2699
+
2700
+ private def write_stream_frame(builder:, space:, stream:, max_offset:)
2701
+ # the frame data size is constrained by our peer's MAX_DATA and the space available in the current packet
2702
+ frame_overhead = 3 +
2703
+ Buffer.size_uint_var(stream.stream_id) +
2704
+ (stream.sender.next_offset ? Buffer.size_uint_var(stream.sender.next_offset) : 0)
2705
+ previous_send_highest = stream.sender.highest_offset
2706
+ frame = stream.sender.get_frame(max_size: builder.remaining_flight_space - frame_overhead, max_offset: max_offset)
2707
+
2708
+ if frame
2709
+ frame_type = Quic::Packet::QuicFrameType::STREAM_BASE | 2 # length
2710
+ frame_type = frame_type | 4 if frame.offset
2711
+ frame_type = frame_type | 1 if frame.fin
2712
+ buf = builder.start_frame(
2713
+ frame_type: frame_type,
2714
+ capacity: frame_overhead,
2715
+ handler: stream.sender.method(:on_data_delivery),
2716
+ handler_args: [frame.offset, frame.offset + frame.data.bytesize],
2717
+ )
2718
+ buf.push_uint_var(stream.stream_id)
2719
+ buf.push_uint_var(frame.offset) if frame.offset
2720
+ buf.push_uint16(frame.data.bytesize | 0x4000)
2721
+ buf.push_bytes(frame.data)
2722
+
2723
+ # log frame
2724
+ builder.quic_logger_frames << @quic_logger.encode_stream_frame(frame: frame, stream_id: stream.stream_id) if @quic_logger
2725
+
2726
+ return stream.sender.highest_offset - previous_send_highest
2727
+ else
2728
+ return 0
2729
+ end
2730
+ end
2731
+
2732
+ # Raise MAX_STREAM_DATA if needed.
2733
+ #
2734
+ # The only case where `stream.max_stream_data_local` is zero is for locally created unidirectional streams.
2735
+ # We skip such streams to avoid spurious logging.
2736
+ private def write_stream_limits(builder:, space:, stream:)
2737
+ if stream.max_stream_data_local > 0 && stream.receiver.highest_offset * 2 > stream.max_stream_data_local
2738
+ # binding.irb if stream.max_stream_data_local > 1000
2739
+ stream.max_stream_data_local *= 2
2740
+ # TODO: logging
2741
+ end
2742
+
2743
+ if stream.max_stream_data_local_sent != stream.max_stream_data_local
2744
+ buf = builder.start_frame(
2745
+ frame_type: Quic::Packet::QuicFrameType::MAX_STREAM_DATA,
2746
+ capacity: MAX_STREAM_DATA_FRAME_CAPACITY,
2747
+ handler: method(:on_max_stream_data_delivery),
2748
+ handler_args: [stream],
2749
+ )
2750
+ buf.push_uint_var(stream.stream_id)
2751
+ buf.push_uint_var(stream.max_stream_data_local)
2752
+ stream.max_stream_data_local_sent = stream.max_stream_data_local
2753
+
2754
+ # log frame
2755
+ if @quic_logger
2756
+ builder.quic_logger_frames << @quic_logger.encode_max_stream_data_frame(maximum: stream.max_stream_data_local, stream_id: stream.stream_id)
2757
+ end
2758
+ end
2759
+ end
2760
+
2761
+ private def write_streams_blocked_frame(builder:, frame_type:, limit:)
2762
+ buf = builder.start_frame(frame_type: frame_type, capacity: STREAMS_BLOCKED_CAPACITY)
2763
+ buf.push_uint_var(limit)
2764
+
2765
+ # log frame
2766
+ if @quic_logger
2767
+ builder.quic_logger_frames << @quic_logger.encode_streams_blocked_frame(
2768
+ is_unidirectional: (frame_type == Quic::Packet::QuicFrameType::STREAMS_BLOCKED_UNI),
2769
+ limit: limit,
2770
+ )
2771
+ end
2772
+ end
2773
+ end
2774
+ end
2775
+ end
2776
+ end