protocol-zmtp 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/protocol/zmtp/codec/command.rb +6 -2
- data/lib/protocol/zmtp/connection.rb +6 -0
- data/lib/protocol/zmtp/mechanism/curve.rb +18 -4
- data/lib/protocol/zmtp/mechanism/null.rb +26 -4
- data/lib/protocol/zmtp/mechanism/plain.rb +31 -8
- data/lib/protocol/zmtp/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c147d32b0109fa000aadcd519b73c1e808996aee42b0565e4fd1840af28c984d
|
|
4
|
+
data.tar.gz: 83ec8f366229b893b6347f5cb62a2651980d1408da473e2c629a9a01098feeeb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 96984a2b7e2a30b792ca73b066fdcaf79a1661b65397d139e97bf6e92f4ad9b234357e0610bd7025dff29812fad88753b0875f2b7f3be8c3bffffe4ae042274c
|
|
7
|
+
data.tar.gz: a320a61ed574017490604a1fd80959a11655292135e3f370321c07eefdbf00e2b1f21bd0e3df712d3d3d3158399c8128c7515eb91641c53bca543874fa0467ca
|
|
@@ -70,17 +70,21 @@ module Protocol
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
# Builds a READY command with Socket-Type, Identity,
|
|
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).
|
|
74
76
|
#
|
|
75
77
|
# @param qos [Integer] QoS level (0 = omitted)
|
|
76
78
|
# @param qos_hash [String] supported hash algorithms in preference order (e.g. "xXsS")
|
|
79
|
+
# @param metadata [Hash{String => String}] additional READY properties
|
|
77
80
|
# @return [Command]
|
|
78
|
-
def self.ready(socket_type:, identity: "", qos: 0, qos_hash: "")
|
|
81
|
+
def self.ready(socket_type:, identity: "", qos: 0, qos_hash: "", metadata: nil)
|
|
79
82
|
props = { "Socket-Type" => socket_type, "Identity" => identity }
|
|
80
83
|
if qos > 0
|
|
81
84
|
props["X-QoS"] = qos.to_s
|
|
82
85
|
props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
|
|
83
86
|
end
|
|
87
|
+
props.merge!(metadata) if metadata && !metadata.empty?
|
|
84
88
|
new("READY", encode_properties(props))
|
|
85
89
|
end
|
|
86
90
|
|
|
@@ -24,6 +24,10 @@ module Protocol
|
|
|
24
24
|
# @return [String] peer's supported hash algorithms in preference order
|
|
25
25
|
attr_reader :peer_qos_hash
|
|
26
26
|
|
|
27
|
+
# @return [Hash{String => String}, nil] full peer READY property hash
|
|
28
|
+
# (set after a successful handshake; nil before)
|
|
29
|
+
attr_reader :peer_properties
|
|
30
|
+
|
|
27
31
|
# @return [Object] transport IO (#read_exactly, #write, #flush, #close)
|
|
28
32
|
attr_reader :io
|
|
29
33
|
|
|
@@ -47,6 +51,7 @@ module Protocol
|
|
|
47
51
|
@peer_identity = nil
|
|
48
52
|
@peer_qos = nil
|
|
49
53
|
@peer_qos_hash = nil
|
|
54
|
+
@peer_properties = nil
|
|
50
55
|
@qos = qos
|
|
51
56
|
@qos_hash = qos_hash
|
|
52
57
|
@mutex = Mutex.new
|
|
@@ -78,6 +83,7 @@ module Protocol
|
|
|
78
83
|
@peer_identity = result[:peer_identity]
|
|
79
84
|
@peer_qos = result[:peer_qos] || 0
|
|
80
85
|
@peer_qos_hash = result[:peer_qos_hash] || ""
|
|
86
|
+
@peer_properties = result[:peer_properties]
|
|
81
87
|
|
|
82
88
|
unless @peer_socket_type
|
|
83
89
|
raise Error, "peer READY missing Socket-Type"
|
|
@@ -23,6 +23,10 @@ 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
|
+
|
|
26
30
|
|
|
27
31
|
# Nonce prefixes.
|
|
28
32
|
NONCE_PREFIX_HELLO = "CurveZMQHELLO---"
|
|
@@ -96,9 +100,10 @@ module Protocol
|
|
|
96
100
|
end
|
|
97
101
|
end
|
|
98
102
|
|
|
99
|
-
@session_box
|
|
100
|
-
@send_nonce
|
|
101
|
-
@recv_nonce
|
|
103
|
+
@session_box = nil
|
|
104
|
+
@send_nonce = 0
|
|
105
|
+
@recv_nonce = -1
|
|
106
|
+
@metadata = nil
|
|
102
107
|
end
|
|
103
108
|
|
|
104
109
|
|
|
@@ -285,6 +290,7 @@ module Protocol
|
|
|
285
290
|
props["X-QoS"] = qos.to_s
|
|
286
291
|
props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
|
|
287
292
|
end
|
|
293
|
+
props.merge!(@metadata) if @metadata
|
|
288
294
|
metadata = Codec::Command.encode_properties(props)
|
|
289
295
|
|
|
290
296
|
initiate_box_plaintext = "".b
|
|
@@ -336,7 +342,13 @@ module Protocol
|
|
|
336
342
|
@recv_nonce = 0
|
|
337
343
|
init_nonce_buffers!
|
|
338
344
|
|
|
339
|
-
{
|
|
345
|
+
{
|
|
346
|
+
peer_socket_type: peer_socket_type,
|
|
347
|
+
peer_identity: peer_identity,
|
|
348
|
+
peer_qos: peer_qos,
|
|
349
|
+
peer_qos_hash: peer_qos_hash,
|
|
350
|
+
peer_properties: props,
|
|
351
|
+
}
|
|
340
352
|
end
|
|
341
353
|
|
|
342
354
|
|
|
@@ -474,6 +486,7 @@ module Protocol
|
|
|
474
486
|
ready_props["X-QoS"] = qos.to_s
|
|
475
487
|
ready_props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
|
|
476
488
|
end
|
|
489
|
+
ready_props.merge!(@metadata) if @metadata
|
|
477
490
|
ready_metadata = Codec::Command.encode_properties(ready_props)
|
|
478
491
|
|
|
479
492
|
r_short_nonce = [1].pack("Q>")
|
|
@@ -500,6 +513,7 @@ module Protocol
|
|
|
500
513
|
peer_identity: props["Identity"] || "",
|
|
501
514
|
peer_qos: (props["X-QoS"] || "0").to_i,
|
|
502
515
|
peer_qos_hash: props["X-QoS-Hash"] || "",
|
|
516
|
+
peer_properties: props,
|
|
503
517
|
}
|
|
504
518
|
end
|
|
505
519
|
|
|
@@ -11,18 +11,28 @@ 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
|
+
|
|
14
24
|
|
|
15
25
|
# Performs the full NULL handshake over +io+.
|
|
16
26
|
#
|
|
17
27
|
# 1. Exchange 64-byte greetings
|
|
18
28
|
# 2. Validate peer greeting (version, mechanism)
|
|
19
|
-
# 3. Exchange READY commands (socket type + identity)
|
|
29
|
+
# 3. Exchange READY commands (socket type + identity + any extras)
|
|
20
30
|
#
|
|
21
31
|
# @param io [#read_exactly, #write, #flush] transport IO
|
|
22
32
|
# @param as_server [Boolean]
|
|
23
33
|
# @param socket_type [String]
|
|
24
34
|
# @param identity [String]
|
|
25
|
-
# @return [Hash] { peer_socket_type:, peer_identity: }
|
|
35
|
+
# @return [Hash] { peer_socket_type:, peer_identity:, peer_qos:, peer_qos_hash:, peer_properties: }
|
|
26
36
|
# @raise [Error]
|
|
27
37
|
def handshake!(io, as_server:, socket_type:, identity:, qos: 0, qos_hash: "")
|
|
28
38
|
io.write(Codec::Greeting.encode(mechanism: MECHANISM_NAME, as_server: as_server))
|
|
@@ -35,7 +45,13 @@ module Protocol
|
|
|
35
45
|
raise Error, "unsupported mechanism: #{peer_greeting[:mechanism]}"
|
|
36
46
|
end
|
|
37
47
|
|
|
38
|
-
ready_cmd = Codec::Command.ready(
|
|
48
|
+
ready_cmd = Codec::Command.ready(
|
|
49
|
+
socket_type: socket_type,
|
|
50
|
+
identity: identity,
|
|
51
|
+
qos: qos,
|
|
52
|
+
qos_hash: qos_hash,
|
|
53
|
+
metadata: @metadata,
|
|
54
|
+
)
|
|
39
55
|
io.write(ready_cmd.to_frame.to_wire)
|
|
40
56
|
io.flush
|
|
41
57
|
|
|
@@ -59,7 +75,13 @@ module Protocol
|
|
|
59
75
|
raise Error, "peer READY missing Socket-Type"
|
|
60
76
|
end
|
|
61
77
|
|
|
62
|
-
{
|
|
78
|
+
{
|
|
79
|
+
peer_socket_type: peer_socket_type,
|
|
80
|
+
peer_identity: peer_identity,
|
|
81
|
+
peer_qos: peer_qos,
|
|
82
|
+
peer_qos_hash: peer_qos_hash,
|
|
83
|
+
peer_properties: props,
|
|
84
|
+
}
|
|
63
85
|
end
|
|
64
86
|
|
|
65
87
|
|
|
@@ -16,15 +16,22 @@ 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
|
+
|
|
19
25
|
# @param username [String] client username (max 255 bytes)
|
|
20
26
|
# @param password [String] client password (max 255 bytes)
|
|
21
27
|
# @param authenticator [#call, nil] server-side credential verifier;
|
|
22
28
|
# called as +authenticator.call(username, password)+ and must return
|
|
23
29
|
# truthy to accept the connection. When +nil+, all credentials pass.
|
|
24
30
|
def initialize(username: "", password: "", authenticator: nil)
|
|
25
|
-
@username
|
|
26
|
-
@password
|
|
27
|
-
@authenticator
|
|
31
|
+
@username = username
|
|
32
|
+
@password = password
|
|
33
|
+
@authenticator = authenticator
|
|
34
|
+
@metadata = nil
|
|
28
35
|
end
|
|
29
36
|
|
|
30
37
|
|
|
@@ -48,9 +55,11 @@ module Protocol
|
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
if as_server
|
|
51
|
-
server_handshake!
|
|
58
|
+
server_handshake! io, socket_type: socket_type, identity: identity,
|
|
59
|
+
qos: qos, qos_hash: qos_hash
|
|
52
60
|
else
|
|
53
|
-
client_handshake!
|
|
61
|
+
client_handshake! io, socket_type: socket_type, identity: identity,
|
|
62
|
+
qos: qos, qos_hash: qos_hash
|
|
54
63
|
end
|
|
55
64
|
end
|
|
56
65
|
|
|
@@ -70,11 +79,17 @@ module Protocol
|
|
|
70
79
|
cmd = read_command(io)
|
|
71
80
|
raise Error, "expected WELCOME, got #{cmd.name}" unless cmd.name == "WELCOME"
|
|
72
81
|
|
|
73
|
-
props = {
|
|
82
|
+
props = {
|
|
83
|
+
"Socket-Type" => socket_type,
|
|
84
|
+
"Identity" => identity
|
|
85
|
+
}
|
|
86
|
+
|
|
74
87
|
if qos > 0
|
|
75
88
|
props["X-QoS"] = qos.to_s
|
|
76
89
|
props["X-QoS-Hash"] = qos_hash unless qos_hash.empty?
|
|
77
90
|
end
|
|
91
|
+
|
|
92
|
+
props.merge!(@metadata) if @metadata && !@metadata.empty?
|
|
78
93
|
initiate = Codec::Command.new("INITIATE", Codec::Command.encode_properties(props))
|
|
79
94
|
send_command(io, initiate)
|
|
80
95
|
|
|
@@ -102,7 +117,9 @@ module Protocol
|
|
|
102
117
|
|
|
103
118
|
peer_info = extract_peer_info(cmd)
|
|
104
119
|
|
|
105
|
-
|
|
120
|
+
ready = Codec::Command.ready socket_type: socket_type, identity: identity,
|
|
121
|
+
qos: qos, qos_hash: qos_hash, metadata: @metadata
|
|
122
|
+
send_command io, ready
|
|
106
123
|
|
|
107
124
|
peer_info
|
|
108
125
|
end
|
|
@@ -146,7 +163,13 @@ module Protocol
|
|
|
146
163
|
|
|
147
164
|
raise Error, "peer command missing Socket-Type" unless peer_socket_type
|
|
148
165
|
|
|
149
|
-
{
|
|
166
|
+
{
|
|
167
|
+
peer_socket_type: peer_socket_type,
|
|
168
|
+
peer_identity: props["Identity"] || "",
|
|
169
|
+
peer_qos: (props["X-QoS"] || "0").to_i,
|
|
170
|
+
peer_qos_hash: props["X-QoS-Hash"] || "",
|
|
171
|
+
peer_properties: props
|
|
172
|
+
}
|
|
150
173
|
end
|
|
151
174
|
|
|
152
175
|
|