raioquic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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