protocol-zmtp 0.4.0 → 0.5.1
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 +7 -4
- data/lib/protocol/zmtp/codec/frame.rb +23 -6
- data/lib/protocol/zmtp/connection.rb +58 -23
- 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: a29e580def88791398cee267152d16bd7e187a518bd409f2e8a2acb1e364cab1
|
|
4
|
+
data.tar.gz: a27e2d1335fe8e0376abf7d17e3b9e4ed2d82e3c98d9dd70733327d789144576
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92318c07232337b3b108e5a98b4735c293e15810a8b41b7d1abe046f7f0ec7ed602072a91baa2ba6a6394ab05a4d260768e01a4302652ac96ca9ed61c9ad621d
|
|
7
|
+
data.tar.gz: 5e5fe073b34866af67c8b0e820afb95d37b2404cab2a986b26da24d70bb30fa809afef47a27c303b396f83a3f94c3f29da9e9a95f54f4413f83605b9d5fef8b1
|
|
@@ -23,11 +23,13 @@ module Protocol
|
|
|
23
23
|
# @return [String] command data (binary)
|
|
24
24
|
attr_reader :data
|
|
25
25
|
|
|
26
|
+
EMPTY_DATA = "".b.freeze
|
|
27
|
+
|
|
26
28
|
# @param name [String] command name
|
|
27
29
|
# @param data [String] command data
|
|
28
|
-
def initialize(name, data =
|
|
30
|
+
def initialize(name, data = EMPTY_DATA)
|
|
29
31
|
@name = name
|
|
30
|
-
@data = data.b
|
|
32
|
+
@data = data.encoding == Encoding::BINARY ? data : data.b
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
|
|
@@ -35,8 +37,9 @@ module Protocol
|
|
|
35
37
|
#
|
|
36
38
|
# @return [String] binary body (name-length + name + data)
|
|
37
39
|
def to_body
|
|
38
|
-
name_bytes = @name.b
|
|
39
|
-
|
|
40
|
+
name_bytes = @name.encoding == Encoding::BINARY ? @name : @name.b
|
|
41
|
+
buf = String.new(capacity: 1 + name_bytes.bytesize + @data.bytesize, encoding: Encoding::BINARY)
|
|
42
|
+
buf << Frame::FLAG_BYTES[name_bytes.bytesize] << name_bytes << @data
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
|
|
@@ -19,6 +19,12 @@ module Protocol
|
|
|
19
19
|
# Short frame: 1-byte size, max body 255 bytes.
|
|
20
20
|
SHORT_MAX = 255
|
|
21
21
|
|
|
22
|
+
# Pre-computed single-byte flag strings (avoids Integer#chr + String#b per frame).
|
|
23
|
+
FLAG_BYTES = Array.new(256) { |i| i.chr.b.freeze }.freeze
|
|
24
|
+
|
|
25
|
+
# Frozen empty binary string for zero-length frame bodies.
|
|
26
|
+
EMPTY_BODY = "".b.freeze
|
|
27
|
+
|
|
22
28
|
|
|
23
29
|
# @return [String] frame body (binary)
|
|
24
30
|
attr_reader :body
|
|
@@ -49,9 +55,9 @@ module Protocol
|
|
|
49
55
|
flags |= FLAGS_COMMAND if @command
|
|
50
56
|
|
|
51
57
|
if size > SHORT_MAX
|
|
52
|
-
|
|
58
|
+
FLAG_BYTES[flags | FLAGS_LONG] + [size].pack("Q>") + @body
|
|
53
59
|
else
|
|
54
|
-
flags
|
|
60
|
+
FLAG_BYTES[flags] + FLAG_BYTES[size] + @body
|
|
55
61
|
end
|
|
56
62
|
end
|
|
57
63
|
|
|
@@ -64,9 +70,20 @@ module Protocol
|
|
|
64
70
|
# @return [String] frozen binary wire representation
|
|
65
71
|
#
|
|
66
72
|
def self.encode_message(parts)
|
|
67
|
-
buf
|
|
68
|
-
parts.
|
|
69
|
-
|
|
73
|
+
buf = String.new(encoding: Encoding::BINARY)
|
|
74
|
+
last = parts.size - 1
|
|
75
|
+
i = 0
|
|
76
|
+
while i < parts.size
|
|
77
|
+
body = parts[i]
|
|
78
|
+
body = body.b unless body.encoding == Encoding::BINARY
|
|
79
|
+
size = body.bytesize
|
|
80
|
+
flags = i < last ? FLAGS_MORE : 0
|
|
81
|
+
if size > SHORT_MAX
|
|
82
|
+
buf << FLAG_BYTES[flags | FLAGS_LONG] << [size].pack("Q>") << body
|
|
83
|
+
else
|
|
84
|
+
buf << FLAG_BYTES[flags] << FLAG_BYTES[size] << body
|
|
85
|
+
end
|
|
86
|
+
i += 1
|
|
70
87
|
end
|
|
71
88
|
buf.freeze
|
|
72
89
|
end
|
|
@@ -95,7 +112,7 @@ module Protocol
|
|
|
95
112
|
raise Error, "frame size #{size} exceeds max_message_size #{max_message_size}"
|
|
96
113
|
end
|
|
97
114
|
|
|
98
|
-
body = size > 0 ? io.read_exactly(size) :
|
|
115
|
+
body = size > 0 ? io.read_exactly(size) : EMPTY_BODY
|
|
99
116
|
|
|
100
117
|
new(body, more: more, command: command)
|
|
101
118
|
end
|
|
@@ -95,9 +95,11 @@ module Protocol
|
|
|
95
95
|
# @param parts [Array<String>] message frames
|
|
96
96
|
# @return [void]
|
|
97
97
|
def send_message(parts)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
with_deferred_cancel do
|
|
99
|
+
@mutex.synchronize do
|
|
100
|
+
write_frames(parts)
|
|
101
|
+
@io.flush
|
|
102
|
+
end
|
|
101
103
|
end
|
|
102
104
|
end
|
|
103
105
|
|
|
@@ -108,8 +110,10 @@ module Protocol
|
|
|
108
110
|
# @param parts [Array<String>] message frames
|
|
109
111
|
# @return [void]
|
|
110
112
|
def write_message(parts)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
with_deferred_cancel do
|
|
114
|
+
@mutex.synchronize do
|
|
115
|
+
write_frames(parts)
|
|
116
|
+
end
|
|
113
117
|
end
|
|
114
118
|
end
|
|
115
119
|
|
|
@@ -124,12 +128,14 @@ module Protocol
|
|
|
124
128
|
# multi-frame message
|
|
125
129
|
# @return [void]
|
|
126
130
|
def write_messages(messages)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
with_deferred_cancel do
|
|
132
|
+
@mutex.synchronize do
|
|
133
|
+
i = 0
|
|
134
|
+
n = messages.size
|
|
135
|
+
while i < n
|
|
136
|
+
write_frames(messages[i])
|
|
137
|
+
i += 1
|
|
138
|
+
end
|
|
133
139
|
end
|
|
134
140
|
end
|
|
135
141
|
end
|
|
@@ -141,8 +147,10 @@ module Protocol
|
|
|
141
147
|
# @param wire_bytes [String] ZMTP wire-format bytes
|
|
142
148
|
# @return [void]
|
|
143
149
|
def write_wire(wire_bytes)
|
|
144
|
-
|
|
145
|
-
@
|
|
150
|
+
with_deferred_cancel do
|
|
151
|
+
@mutex.synchronize do
|
|
152
|
+
@io.write(wire_bytes)
|
|
153
|
+
end
|
|
146
154
|
end
|
|
147
155
|
end
|
|
148
156
|
|
|
@@ -191,13 +199,15 @@ module Protocol
|
|
|
191
199
|
# @param command [Codec::Command]
|
|
192
200
|
# @return [void]
|
|
193
201
|
def send_command(command)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
@
|
|
197
|
-
|
|
198
|
-
|
|
202
|
+
with_deferred_cancel do
|
|
203
|
+
@mutex.synchronize do
|
|
204
|
+
if @mechanism.encrypted?
|
|
205
|
+
@io.write(@mechanism.encrypt(command.to_body, command: true))
|
|
206
|
+
else
|
|
207
|
+
@io.write(command.to_frame.to_wire)
|
|
208
|
+
end
|
|
209
|
+
@io.flush
|
|
199
210
|
end
|
|
200
|
-
@io.flush
|
|
201
211
|
end
|
|
202
212
|
end
|
|
203
213
|
|
|
@@ -266,6 +276,26 @@ module Protocol
|
|
|
266
276
|
|
|
267
277
|
private
|
|
268
278
|
|
|
279
|
+
# Defers task cancellation around a block of wire writes so the
|
|
280
|
+
# peer never sees a half-written frame. Without this, an
|
|
281
|
+
# +Async::Cancel+ arriving between the header write and the body
|
|
282
|
+
# write (the unencrypted path issues two separate +@io.write+
|
|
283
|
+
# calls per frame) would desync the peer's framer
|
|
284
|
+
# unrecoverably.
|
|
285
|
+
#
|
|
286
|
+
# When called outside an Async task (test fixtures, blocking
|
|
287
|
+
# callers), the block runs directly -- there is no task to defer
|
|
288
|
+
# on. Cancellation arriving from inside the block (peer
|
|
289
|
+
# disconnect raising +EPIPE+/+EOFError+) propagates normally.
|
|
290
|
+
def with_deferred_cancel
|
|
291
|
+
if defined?(Async::Task) && (task = Async::Task.current?)
|
|
292
|
+
task.defer_cancel { yield }
|
|
293
|
+
else
|
|
294
|
+
yield
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
|
|
269
299
|
# Writes message parts as ZMTP frames, encrypting if needed.
|
|
270
300
|
#
|
|
271
301
|
# For the unencrypted path, writes the frame header and body
|
|
@@ -276,13 +306,17 @@ module Protocol
|
|
|
276
306
|
def write_frames(parts)
|
|
277
307
|
encrypted = @mechanism.encrypted?
|
|
278
308
|
buf = @header_buf
|
|
309
|
+
last = parts.size - 1
|
|
279
310
|
|
|
280
|
-
|
|
281
|
-
|
|
311
|
+
i = 0
|
|
312
|
+
while i < parts.size
|
|
313
|
+
part = parts[i]
|
|
314
|
+
more = i < last
|
|
282
315
|
if encrypted
|
|
283
|
-
|
|
316
|
+
body = part.encoding == Encoding::BINARY ? part : part.b
|
|
317
|
+
@io.write(@mechanism.encrypt(body, more: more))
|
|
284
318
|
else
|
|
285
|
-
body = part.b
|
|
319
|
+
body = part.encoding == Encoding::BINARY ? part : part.b
|
|
286
320
|
size = body.bytesize
|
|
287
321
|
flags = more ? Codec::Frame::FLAGS_MORE : 0
|
|
288
322
|
buf.clear
|
|
@@ -294,6 +328,7 @@ module Protocol
|
|
|
294
328
|
@io.write(buf)
|
|
295
329
|
@io.write(body)
|
|
296
330
|
end
|
|
331
|
+
i += 1
|
|
297
332
|
end
|
|
298
333
|
end
|
|
299
334
|
end
|