protocol-zmtp 0.9.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: edfecc19ca6c0befbc5835da0fbfc1fc364007207f10e6f1de64c733d48f2336
4
- data.tar.gz: d6616b661ee77e73191ed6f33e0a3c5b73b04e6b3fe8e8e5cac30067ae75bb42
3
+ metadata.gz: a99947180a584a86331021fac44327f3bde68cc44c50fe6264fdb7e6018cf3af
4
+ data.tar.gz: 00c1b152a3763603452a42b0af92455ab02cb864d55b4cd331d0dba26396f65b
5
5
  SHA512:
6
- metadata.gz: 4520a2435a1490fcfb7aebd070f58d10e97a05da685a3c36a47417db2e82b8d7ef48d0c5d19ee6c7db99c7b5da552cd4fec2f061aee3fd9e55908d7d30cf301d
7
- data.tar.gz: d8c6f26fe3197a25f32e5c5a6668d53eb8cecf109dbe084b00c63b2fcb396cbe05dfd37c9b01ecbc39a960f35ce56ca70fa02c49761e9e0065246ffffeb36437
6
+ metadata.gz: 6e6997a555bc1ed27bd68f51935cfc0b68bf4b7b3f5055a47b2de44f63561116b309be5d933df1ffc9e8a62d72deac7a3ed918b9b323fcb41584934a8b76a684
7
+ data.tar.gz: 5a495e661e2e4c829a6e33d5cb3d98a0976c4606efb676f17201a7184a82753405816d7f66057154b95a2af78c3385d960e1d0a319674945454eb2218f8c81fd
@@ -70,20 +70,14 @@ module Protocol
70
70
  end
71
71
 
72
72
 
73
- # Builds a READY command with Socket-Type, Identity, optional X-QoS,
74
- # and any extra properties supplied by upper layers (e.g. an
75
- # extension that injects an `X-Compression` property).
73
+ # Builds a READY command with Socket-Type, Identity, and any
74
+ # extra properties supplied by upper layers (e.g. an extension
75
+ # that injects +X-QoS+ or +X-Compression+).
76
76
  #
77
- # @param qos [Integer] QoS level (0 = omitted)
78
- # @param qos_hash [String] supported hash algorithms in preference order (e.g. "xXsS")
79
- # @param metadata [Hash{String => String}] additional READY properties
77
+ # @param metadata [Hash{String => String}, nil] additional READY properties
80
78
  # @return [Command]
81
- def self.ready(socket_type:, identity: "", qos: 0, qos_hash: "", metadata: nil)
79
+ def self.ready(socket_type:, identity: "", metadata: nil)
82
80
  props = { "Socket-Type" => socket_type, "Identity" => identity }
83
- if qos > 0
84
- props["X-QoS"] = qos.to_s
85
- props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
86
- end
87
81
  props.merge!(metadata) if metadata && !metadata.empty?
88
82
  new("READY", encode_properties(props))
89
83
  end
@@ -56,7 +56,6 @@ module Protocol
56
56
 
57
57
  while i < parts.size
58
58
  body = parts[i]
59
- body = body.b unless body.encoding == Encoding::BINARY
60
59
  size = body.bytesize
61
60
  flags = i < last ? FLAGS_MORE : 0
62
61
 
@@ -142,7 +141,7 @@ module Protocol
142
141
  # @param more [Boolean] more frames follow
143
142
  # @param command [Boolean] this is a command frame
144
143
  def initialize(body, more: false, command: false)
145
- @body = body.encoding == Encoding::BINARY ? body : body.b
144
+ @body = body
146
145
  @more = more
147
146
  @command = command
148
147
  end
@@ -20,16 +20,14 @@ module Protocol
20
20
  attr_reader :peer_identity
21
21
 
22
22
 
23
- # @return [Integer] peer's QoS level (from READY handshake, 0 if absent)
24
- attr_reader :peer_qos
25
-
26
-
27
- # @return [String] peer's supported hash algorithms in preference order
28
- attr_reader :peer_qos_hash
23
+ # @return [Object, nil] peer's CURVE long-term public key post-handshake
24
+ # (+crypto::PublicKey+ when the mechanism is CURVE; +nil+ for NULL).
25
+ attr_reader :peer_public_key
29
26
 
30
27
 
31
28
  # @return [Hash{String => String}, nil] full peer READY property hash
32
- # (set after a successful handshake; nil before)
29
+ # (set after a successful handshake; nil before).
30
+ # Upper layers extract their own X-* properties from here.
33
31
  attr_reader :peer_properties
34
32
 
35
33
 
@@ -56,8 +54,12 @@ module Protocol
56
54
  # @param as_server [Boolean] whether we are the server side
57
55
  # @param mechanism [Mechanism::Null, Mechanism::Curve] security mechanism
58
56
  # @param max_message_size [Integer, nil] max frame size in bytes, nil = unlimited
57
+ # @param opts [Hash{String => String}] extra READY properties to
58
+ # advertise (e.g. +"X-QoS" => "1"+). Upper-layer extensions use
59
+ # this to inject their own negotiated properties without the
60
+ # codec needing to know about them.
59
61
  def initialize(io, socket_type:, identity: "", as_server: false,
60
- mechanism: nil, max_message_size: nil, qos: 0, qos_hash: "")
62
+ mechanism: nil, max_message_size: nil, **opts)
61
63
  @io = io
62
64
  @socket_type = socket_type
63
65
  @identity = identity
@@ -65,13 +67,11 @@ module Protocol
65
67
  @mechanism = mechanism || Mechanism::Null.new
66
68
  @peer_socket_type = nil
67
69
  @peer_identity = nil
68
- @peer_qos = nil
69
- @peer_qos_hash = nil
70
+ @peer_public_key = nil
70
71
  @peer_properties = nil
71
72
  @peer_major = nil
72
73
  @peer_minor = nil
73
- @qos = qos
74
- @qos_hash = qos_hash
74
+ @metadata = opts.empty? ? nil : opts.transform_keys(&:to_s)
75
75
  @mutex = Mutex.new
76
76
  @max_message_size = max_message_size
77
77
  @last_received_at = nil
@@ -80,6 +80,7 @@ module Protocol
80
80
  # writes in place so the per-message 2-or-9 byte String allocation
81
81
  # in write_frames disappears on the hot send path.
82
82
  @header_buf = String.new(capacity: 9, encoding: Encoding::BINARY)
83
+ @frame_buf = String.new(capacity: 257, encoding: Encoding::BINARY)
83
84
  end
84
85
 
85
86
 
@@ -92,13 +93,11 @@ module Protocol
92
93
  as_server: @as_server,
93
94
  socket_type: @socket_type,
94
95
  identity: @identity,
95
- qos: @qos,
96
- qos_hash: @qos_hash
96
+ metadata: @metadata
97
97
 
98
98
  @peer_socket_type = result[:peer_socket_type]
99
99
  @peer_identity = result[:peer_identity]
100
- @peer_qos = result[:peer_qos] || 0
101
- @peer_qos_hash = result[:peer_qos_hash] || ""
100
+ @peer_public_key = result[:peer_public_key]
102
101
  @peer_properties = result[:peer_properties]
103
102
  @peer_major = result[:peer_major]
104
103
  @peer_minor = result[:peer_minor]
@@ -114,6 +113,17 @@ module Protocol
114
113
  end
115
114
 
116
115
 
116
+ # Returns a {PeerInfo} value bundling the peer's CURVE public key
117
+ # and identity for use as a stable per-peer key (frozen, hash-usable).
118
+ # Nil before the handshake has completed.
119
+ #
120
+ # @return [PeerInfo, nil]
121
+ def peer_info
122
+ return nil unless @peer_socket_type
123
+ PeerInfo.new(public_key: @peer_public_key, identity: @peer_identity)
124
+ end
125
+
126
+
117
127
  # Sends a multi-frame message (write + flush).
118
128
  #
119
129
  # @param parts [Array<String>] message frames
@@ -179,6 +189,25 @@ module Protocol
179
189
  end
180
190
 
181
191
 
192
+ # Writes multiple pre-encoded wire byte strings under a single
193
+ # mutex acquisition.
194
+ #
195
+ # @param wire_strings [Array<String>]
196
+ # @return [void]
197
+ def write_wire_batch(wire_strings)
198
+ with_deferred_cancel do
199
+ @mutex.synchronize do
200
+ i = 0
201
+ n = wire_strings.size
202
+ while i < n
203
+ @io.write(wire_strings[i])
204
+ i += 1
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+
182
211
  # Returns true if the ZMTP mechanism encrypts at the frame level
183
212
  # (e.g. CURVE, BLAKE3ZMQ).
184
213
  #
@@ -214,11 +243,11 @@ module Protocol
214
243
  next
215
244
  end
216
245
 
217
- frames << frame.body.freeze
246
+ frames << frame.body
218
247
  break unless frame.more?
219
248
  end
220
249
 
221
- frames.freeze
250
+ frames
222
251
  end
223
252
 
224
253
 
@@ -307,11 +336,10 @@ module Protocol
307
336
  private
308
337
 
309
338
  # Defers task cancellation around a block of wire writes so the
310
- # peer never sees a half-written frame. Without this, an
311
- # +Async::Cancel+ arriving between the header write and the body
312
- # write (the unencrypted path issues two separate +@io.write+
313
- # calls per frame) would desync the peer's framer
314
- # unrecoverably.
339
+ # peer never sees a half-written frame or partial multipart
340
+ # message. Without this, an +Async::Cancel+ arriving between
341
+ # successive frames (or between header and body writes for long
342
+ # frames) would desync the peer's framer unrecoverably.
315
343
  #
316
344
  # When called outside an Async task (test fixtures, blocking
317
345
  # callers), the block runs directly -- there is no task to defer
@@ -328,15 +356,16 @@ module Protocol
328
356
 
329
357
  # Writes message parts as ZMTP frames, encrypting if needed.
330
358
  #
331
- # For the unencrypted path, writes the frame header and body
332
- # separately to the IO instead of allocating a wire String. This
333
- # avoids copying the body just to glue a 1- or 9-byte header onto
334
- # it -- significant for large messages where the body copy was
335
- # the dominant allocation per send.
359
+ # Short frames (body <= 255 B) combine the 2-byte header and
360
+ # body into a reusable buffer for a single +@io.write+, halving
361
+ # the per-frame mutex overhead in io-stream. Long frames write
362
+ # header and body separately to avoid copying the body.
336
363
  def write_frames(parts)
337
- encrypted = @mechanism.encrypted?
338
- buf = @header_buf
339
- last = parts.size - 1
364
+ encrypted = @mechanism.encrypted?
365
+ buf = @header_buf
366
+ fbuf = @frame_buf
367
+ flag_bytes = Codec::Frame::FLAG_BYTES
368
+ last = parts.size - 1
340
369
 
341
370
  i = 0
342
371
 
@@ -352,16 +381,18 @@ module Protocol
352
381
  size = body.bytesize
353
382
  flags = more ? Codec::Frame::FLAGS_MORE : 0
354
383
 
355
- buf.clear
356
-
357
384
  if size > Codec::Frame::SHORT_MAX
385
+ buf.clear
358
386
  [flags | Codec::Frame::FLAGS_LONG, size].pack("CQ>", buffer: buf)
387
+ @io.write(buf)
388
+ @io.write(body)
359
389
  else
360
- [flags, size].pack("CC", buffer: buf)
390
+ fbuf.clear
391
+ fbuf << flag_bytes[flags]
392
+ fbuf << flag_bytes[size]
393
+ fbuf << body
394
+ @io.write(fbuf)
361
395
  end
362
-
363
- @io.write(buf)
364
- @io.write(body)
365
396
  end
366
397
 
367
398
  i += 1
@@ -23,10 +23,6 @@ module Protocol
23
23
  class Curve
24
24
  MECHANISM_NAME = "CURVE"
25
25
 
26
- # Extra READY/INITIATE properties merged in by extensions
27
- # (e.g. ZMTP-Zstd's X-Compression). nil = none.
28
- attr_accessor :metadata
29
-
30
26
 
31
27
  # Nonce prefixes.
32
28
  NONCE_PREFIX_HELLO = "CurveZMQHELLO---"
@@ -100,10 +96,9 @@ module Protocol
100
96
  end
101
97
  end
102
98
 
103
- @session_box = nil
104
- @send_nonce = 0
105
- @recv_nonce = -1
106
- @metadata = nil
99
+ @session_box = nil
100
+ @send_nonce = 0
101
+ @recv_nonce = -1
107
102
  end
108
103
 
109
104
 
@@ -139,15 +134,14 @@ module Protocol
139
134
  # @param as_server [Boolean] ignored -- uses the value from #initialize
140
135
  # @param socket_type [String] our socket type name
141
136
  # @param identity [String] our identity
142
- # @param qos [Integer] QoS level
143
- # @param qos_hash [String] supported hash algorithms
144
- # @return [Hash] { peer_socket_type:, peer_identity:, peer_qos:, peer_qos_hash: }
137
+ # @param metadata [Hash{String => String}, nil] extra READY properties
138
+ # @return [Hash] { peer_socket_type:, peer_identity:, peer_properties: }
145
139
  # @raise [Error] on handshake failure
146
- def handshake!(io, as_server:, socket_type:, identity:, qos: 0, qos_hash: "")
140
+ def handshake!(io, as_server:, socket_type:, identity:, metadata: nil)
147
141
  if @as_server
148
- server_handshake!(io, socket_type:, identity:, qos:, qos_hash:)
142
+ server_handshake!(io, socket_type:, identity:, metadata:)
149
143
  else
150
- client_handshake!(io, socket_type:, identity:, qos:, qos_hash:)
144
+ client_handshake!(io, socket_type:, identity:, metadata:)
151
145
  end
152
146
  end
153
147
 
@@ -227,7 +221,7 @@ module Protocol
227
221
  # Client-side handshake
228
222
  # ----------------------------------------------------------------
229
223
 
230
- def client_handshake!(io, socket_type:, identity:, qos: 0, qos_hash: "")
224
+ def client_handshake!(io, socket_type:, identity:, metadata: nil)
231
225
  cn_secret = @crypto::PrivateKey.generate
232
226
  cn_public = cn_secret.public_key
233
227
 
@@ -288,18 +282,14 @@ module Protocol
288
282
  vouch = @crypto::Box.new(sn_public, @permanent_secret).encrypt(vouch_nonce, vouch_plaintext)
289
283
 
290
284
  props = { "Socket-Type" => socket_type, "Identity" => identity }
291
- if qos > 0
292
- props["X-QoS"] = qos.to_s
293
- props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
294
- end
295
- props.merge!(@metadata) if @metadata
296
- metadata = Codec::Command.encode_properties(props)
285
+ props.merge!(metadata) if metadata && !metadata.empty?
286
+ metadata_bytes = Codec::Command.encode_properties(props)
297
287
 
298
288
  initiate_box_plaintext = "".b
299
289
  initiate_box_plaintext << @permanent_public.to_s
300
290
  initiate_box_plaintext << vouch_nonce.byteslice(8, 16)
301
291
  initiate_box_plaintext << vouch
302
- initiate_box_plaintext << metadata
292
+ initiate_box_plaintext << metadata_bytes
303
293
 
304
294
  init_short_nonce = [1].pack("Q>")
305
295
  init_nonce = NONCE_PREFIX_INITIATE + init_short_nonce
@@ -336,8 +326,6 @@ module Protocol
336
326
  props = Codec::Command.decode_properties(r_plaintext)
337
327
  peer_socket_type = props["Socket-Type"]
338
328
  peer_identity = props["Identity"] || ""
339
- peer_qos = (props["X-QoS"] || "0").to_i
340
- peer_qos_hash = props["X-QoS-Hash"] || ""
341
329
 
342
330
  @session_box = session
343
331
  @send_nonce = 1
@@ -347,8 +335,7 @@ module Protocol
347
335
  {
348
336
  peer_socket_type: peer_socket_type,
349
337
  peer_identity: peer_identity,
350
- peer_qos: peer_qos,
351
- peer_qos_hash: peer_qos_hash,
338
+ peer_public_key: @server_public,
352
339
  peer_properties: props,
353
340
  peer_major: @peer_major,
354
341
  peer_minor: @peer_minor,
@@ -360,7 +347,7 @@ module Protocol
360
347
  # Server-side handshake
361
348
  # ----------------------------------------------------------------
362
349
 
363
- def server_handshake!(io, socket_type:, identity:, qos: 0, qos_hash: "")
350
+ def server_handshake!(io, socket_type:, identity:, metadata: nil)
364
351
  io.write(Codec::Greeting.encode(mechanism: MECHANISM_NAME, as_server: true))
365
352
  io.flush
366
353
  peer_greeting = Codec::Greeting.read_from(io)
@@ -478,7 +465,7 @@ module Protocol
478
465
  end
479
466
 
480
467
  if @authenticator
481
- peer = PeerInfo.new(public_key: client_permanent)
468
+ peer = PeerInfo.new(public_key: client_permanent, identity: "")
482
469
  unless @authenticator.call(peer)
483
470
  send_error(io, "client key not authorized")
484
471
  raise Error, "client key not authorized"
@@ -488,11 +475,7 @@ module Protocol
488
475
 
489
476
  # --- READY ---
490
477
  ready_props = { "Socket-Type" => socket_type, "Identity" => identity }
491
- if qos > 0
492
- ready_props["X-QoS"] = qos.to_s
493
- ready_props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
494
- end
495
- ready_props.merge!(@metadata) if @metadata
478
+ ready_props.merge!(metadata) if metadata && !metadata.empty?
496
479
  ready_metadata = Codec::Command.encode_properties(ready_props)
497
480
 
498
481
  r_short_nonce = [1].pack("Q>")
@@ -517,8 +500,7 @@ module Protocol
517
500
  {
518
501
  peer_socket_type: props["Socket-Type"],
519
502
  peer_identity: props["Identity"] || "",
520
- peer_qos: (props["X-QoS"] || "0").to_i,
521
- peer_qos_hash: props["X-QoS-Hash"] || "",
503
+ peer_public_key: client_permanent,
522
504
  peer_properties: props,
523
505
  peer_major: @peer_major,
524
506
  peer_minor: @peer_minor,
@@ -11,16 +11,6 @@ module Protocol
11
11
  class Null
12
12
  MECHANISM_NAME = "NULL"
13
13
 
14
- # Extra READY properties an upper layer (e.g. an OMQ extension)
15
- # wants this side to advertise. Mutated before #handshake!.
16
- # @return [Hash{String => String}]
17
- attr_accessor :metadata
18
-
19
-
20
- def initialize
21
- @metadata = nil
22
- end
23
-
24
14
 
25
15
  # Performs the full NULL handshake over +io+.
26
16
  #
@@ -32,10 +22,11 @@ module Protocol
32
22
  # @param as_server [Boolean]
33
23
  # @param socket_type [String]
34
24
  # @param identity [String]
35
- # @return [Hash] { peer_socket_type:, peer_identity:, peer_qos:, peer_qos_hash:, peer_properties:, peer_major:, peer_minor: }
25
+ # @param metadata [Hash{String => String}, nil] extra READY properties
26
+ # @return [Hash] { peer_socket_type:, peer_identity:, peer_public_key:, peer_properties:, peer_major:, peer_minor: }
36
27
  # @raise [Error]
37
- def handshake!(io, as_server:, socket_type:, identity:, qos: 0, qos_hash: "")
38
- io.write(Codec::Greeting.encode(mechanism: MECHANISM_NAME, as_server: as_server))
28
+ def handshake!(io, as_server:, socket_type:, identity:, metadata: nil)
29
+ io.write(Codec::Greeting.encode(mechanism: MECHANISM_NAME, as_server: false))
39
30
  io.flush
40
31
 
41
32
  peer_greeting = Codec::Greeting.read_from(io)
@@ -45,11 +36,9 @@ module Protocol
45
36
  end
46
37
 
47
38
  ready_cmd = Codec::Command.ready(
48
- socket_type: socket_type,
49
- identity: identity,
50
- qos: qos,
51
- qos_hash: qos_hash,
52
- metadata: @metadata,
39
+ socket_type: socket_type,
40
+ identity: identity,
41
+ metadata: metadata,
53
42
  )
54
43
  io.write(ready_cmd.to_frame.to_wire)
55
44
  io.flush
@@ -67,8 +56,6 @@ module Protocol
67
56
  props = peer_cmd.properties
68
57
  peer_socket_type = props["Socket-Type"]
69
58
  peer_identity = props["Identity"] || ""
70
- peer_qos = (props["X-QoS"] || "0").to_i
71
- peer_qos_hash = props["X-QoS-Hash"] || ""
72
59
 
73
60
  unless peer_socket_type
74
61
  raise Error, "peer READY missing Socket-Type"
@@ -77,8 +64,7 @@ module Protocol
77
64
  {
78
65
  peer_socket_type: peer_socket_type,
79
66
  peer_identity: peer_identity,
80
- peer_qos: peer_qos,
81
- peer_qos_hash: peer_qos_hash,
67
+ peer_public_key: nil,
82
68
  peer_properties: props,
83
69
  peer_major: peer_greeting[:major],
84
70
  peer_minor: peer_greeting[:minor],
@@ -16,22 +16,15 @@ module Protocol
16
16
  MECHANISM_NAME = "PLAIN"
17
17
 
18
18
 
19
- # Extra READY/INITIATE properties an upper layer wants this side to
20
- # advertise. Mutated before #handshake!.
21
- # @return [Hash{String => String}]
22
- attr_accessor :metadata
23
-
24
-
25
19
  # @param username [String] client username (max 255 bytes)
26
20
  # @param password [String] client password (max 255 bytes)
27
21
  # @param authenticator [#call, nil] server-side credential verifier;
28
22
  # called as +authenticator.call(username, password)+ and must return
29
23
  # truthy to accept the connection. When +nil+, all credentials pass.
30
24
  def initialize(username: "", password: "", authenticator: nil)
31
- @username = username
32
- @password = password
33
- @authenticator = authenticator
34
- @metadata = nil
25
+ @username = username
26
+ @password = password
27
+ @authenticator = authenticator
35
28
  end
36
29
 
37
30
 
@@ -41,9 +34,10 @@ module Protocol
41
34
  # @param as_server [Boolean]
42
35
  # @param socket_type [String]
43
36
  # @param identity [String]
44
- # @return [Hash] { peer_socket_type:, peer_identity: }
37
+ # @param metadata [Hash{String => String}, nil] extra READY properties
38
+ # @return [Hash] { peer_socket_type:, peer_identity:, peer_properties: }
45
39
  # @raise [Error]
46
- def handshake!(io, as_server:, socket_type:, identity:, qos: 0, qos_hash: "")
40
+ def handshake!(io, as_server:, socket_type:, identity:, metadata: nil)
47
41
  io.write(Codec::Greeting.encode(mechanism: MECHANISM_NAME, as_server: as_server))
48
42
  io.flush
49
43
 
@@ -54,11 +48,9 @@ module Protocol
54
48
  end
55
49
 
56
50
  if as_server
57
- server_handshake! io, socket_type: socket_type, identity: identity,
58
- qos: qos, qos_hash: qos_hash
51
+ server_handshake! io, socket_type: socket_type, identity: identity, metadata: metadata
59
52
  else
60
- client_handshake! io, socket_type: socket_type, identity: identity,
61
- qos: qos, qos_hash: qos_hash
53
+ client_handshake! io, socket_type: socket_type, identity: identity, metadata: metadata
62
54
  end
63
55
  end
64
56
 
@@ -72,7 +64,7 @@ module Protocol
72
64
  private
73
65
 
74
66
 
75
- def client_handshake!(io, socket_type:, identity:, qos: 0, qos_hash: "")
67
+ def client_handshake!(io, socket_type:, identity:, metadata: nil)
76
68
  send_command(io, hello_command)
77
69
 
78
70
  cmd = read_command(io)
@@ -80,15 +72,9 @@ module Protocol
80
72
 
81
73
  props = {
82
74
  "Socket-Type" => socket_type,
83
- "Identity" => identity
75
+ "Identity" => identity,
84
76
  }
85
-
86
- if qos > 0
87
- props["X-QoS"] = qos.to_s
88
- props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
89
- end
90
-
91
- props.merge!(@metadata) if @metadata && !@metadata.empty?
77
+ props.merge!(metadata) if metadata && !metadata.empty?
92
78
  initiate = Codec::Command.new("INITIATE", Codec::Command.encode_properties(props))
93
79
  send_command(io, initiate)
94
80
 
@@ -99,7 +85,7 @@ module Protocol
99
85
  end
100
86
 
101
87
 
102
- def server_handshake!(io, socket_type:, identity:, qos: 0, qos_hash: "")
88
+ def server_handshake!(io, socket_type:, identity:, metadata: nil)
103
89
  cmd = read_command(io)
104
90
  raise Error, "expected HELLO, got #{cmd.name}" unless cmd.name == "HELLO"
105
91
 
@@ -116,8 +102,7 @@ module Protocol
116
102
 
117
103
  peer_info = extract_peer_info(cmd)
118
104
 
119
- ready = Codec::Command.ready socket_type: socket_type, identity: identity,
120
- qos: qos, qos_hash: qos_hash, metadata: @metadata
105
+ ready = Codec::Command.ready socket_type: socket_type, identity: identity, metadata: metadata
121
106
  send_command io, ready
122
107
 
123
108
  peer_info
@@ -165,9 +150,7 @@ module Protocol
165
150
  {
166
151
  peer_socket_type: peer_socket_type,
167
152
  peer_identity: props["Identity"] || "",
168
- peer_qos: (props["X-QoS"] || "0").to_i,
169
- peer_qos_hash: props["X-QoS-Hash"] || "",
170
- peer_properties: props
153
+ peer_properties: props,
171
154
  }
172
155
  end
173
156
 
@@ -2,8 +2,19 @@
2
2
 
3
3
  module Protocol
4
4
  module ZMTP
5
- # Context passed to an authenticator during authentication.
6
- # +public_key+ is a +crypto::PublicKey+ instance.
7
- PeerInfo = Data.define(:public_key)
5
+ # Post-handshake identity of the peer on a {Connection}.
6
+ #
7
+ # - +public_key+ is the peer's CURVE long-term +crypto::PublicKey+,
8
+ # or +nil+ when the mechanism does not authenticate a key (e.g. NULL).
9
+ # - +identity+ is the peer's +ZMQ_IDENTITY+ string, or +""+ when no
10
+ # identity was advertised in the READY metadata.
11
+ #
12
+ # Used as a peer key by upper layers (e.g. omq-qos levels 2/3): the
13
+ # whole value object is frozen and equality-comparable, so it can be
14
+ # stored directly in a Hash without picking one anchor over the other.
15
+ #
16
+ # Also passed to CURVE authenticators during the handshake (at that
17
+ # point +identity+ is +""+, since it arrives post-auth).
18
+ PeerInfo = Data.define(:public_key, :identity)
8
19
  end
9
20
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Protocol
4
4
  module ZMTP
5
- VERSION = "0.9.0"
5
+ VERSION = "0.10.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol-zmtp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -53,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  requirements: []
56
- rubygems_version: 4.0.6
56
+ rubygems_version: 4.0.10
57
57
  specification_version: 4
58
58
  summary: ZMTP 3.1 wire protocol codec and connection
59
59
  test_files: []