protocol-zmtp 0.8.1 → 0.9.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/frame.rb +99 -80
- data/lib/protocol/zmtp/connection.rb +2 -2
- 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: edfecc19ca6c0befbc5835da0fbfc1fc364007207f10e6f1de64c733d48f2336
|
|
4
|
+
data.tar.gz: d6616b661ee77e73191ed6f33e0a3c5b73b04e6b3fe8e8e5cac30067ae75bb42
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4520a2435a1490fcfb7aebd070f58d10e97a05da685a3c36a47417db2e82b8d7ef48d0c5d19ee6c7db99c7b5da552cd4fec2f061aee3fd9e55908d7d30cf301d
|
|
7
|
+
data.tar.gz: d8c6f26fe3197a25f32e5c5a6668d53eb8cecf109dbe084b00c63b2fcb396cbe05dfd37c9b01ecbc39a960f35ce56ca70fa02c49761e9e0065246ffffeb36437
|
|
@@ -28,51 +28,6 @@ module Protocol
|
|
|
28
28
|
FLAG_BYTES = Array.new(256) { |i| i.chr.b.freeze }.freeze
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
# @return [String] frame body (binary)
|
|
32
|
-
attr_reader :body
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# @param body [String] frame body
|
|
36
|
-
# @param more [Boolean] more frames follow
|
|
37
|
-
# @param command [Boolean] this is a command frame
|
|
38
|
-
def initialize(body, more: false, command: false)
|
|
39
|
-
@body = body.encoding == Encoding::BINARY ? body : body.b
|
|
40
|
-
@more = more
|
|
41
|
-
@command = command
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# @return [Boolean] true if more frames follow in this message
|
|
46
|
-
def more?
|
|
47
|
-
@more
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# @return [Boolean] true if this is a command frame
|
|
52
|
-
def command?
|
|
53
|
-
@command
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# Encodes to wire bytes.
|
|
58
|
-
#
|
|
59
|
-
# @return [String] binary wire representation (flags + size + body)
|
|
60
|
-
def to_wire
|
|
61
|
-
size = @body.bytesize
|
|
62
|
-
flags = 0
|
|
63
|
-
flags |= FLAGS_MORE if @more
|
|
64
|
-
flags |= FLAGS_COMMAND if @command
|
|
65
|
-
|
|
66
|
-
if size > SHORT_MAX
|
|
67
|
-
buf = String.new(capacity: 9 + size, encoding: Encoding::BINARY)
|
|
68
|
-
buf << FLAG_BYTES[flags | FLAGS_LONG] << [size].pack("Q>") << @body
|
|
69
|
-
else
|
|
70
|
-
buf = String.new(capacity: 2 + size, encoding: Encoding::BINARY)
|
|
71
|
-
buf << FLAG_BYTES[flags] << FLAG_BYTES[size] << @body
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
|
|
76
31
|
# Encodes a multi-part message into a single wire-format string.
|
|
77
32
|
# The result can be written to multiple connections without
|
|
78
33
|
# re-encoding each time (useful for fan-out patterns like PUB).
|
|
@@ -82,19 +37,20 @@ module Protocol
|
|
|
82
37
|
#
|
|
83
38
|
def self.encode_message(parts)
|
|
84
39
|
if parts.size == 1
|
|
85
|
-
s
|
|
86
|
-
|
|
40
|
+
s = parts.first.bytesize
|
|
41
|
+
wire = s > SHORT_MAX ? 9 + s : 2 + s
|
|
87
42
|
else
|
|
88
|
-
|
|
89
|
-
j
|
|
43
|
+
wire = 0
|
|
44
|
+
j = 0
|
|
45
|
+
|
|
90
46
|
while j < parts.size
|
|
91
|
-
s
|
|
92
|
-
|
|
93
|
-
j
|
|
47
|
+
s = parts[j].bytesize
|
|
48
|
+
wire += s > SHORT_MAX ? 9 + s : 2 + s
|
|
49
|
+
j += 1
|
|
94
50
|
end
|
|
95
51
|
end
|
|
96
52
|
|
|
97
|
-
buf = String.new(capacity:
|
|
53
|
+
buf = String.new(capacity: wire, encoding: Encoding::BINARY)
|
|
98
54
|
last = parts.size - 1
|
|
99
55
|
i = 0
|
|
100
56
|
|
|
@@ -105,10 +61,15 @@ module Protocol
|
|
|
105
61
|
flags = i < last ? FLAGS_MORE : 0
|
|
106
62
|
|
|
107
63
|
if size > SHORT_MAX
|
|
108
|
-
buf << FLAG_BYTES[flags | FLAGS_LONG]
|
|
64
|
+
buf << FLAG_BYTES[flags | FLAGS_LONG]
|
|
65
|
+
buf << [size].pack("Q>")
|
|
66
|
+
buf << body
|
|
109
67
|
else
|
|
110
|
-
buf << FLAG_BYTES[flags]
|
|
68
|
+
buf << FLAG_BYTES[flags]
|
|
69
|
+
buf << FLAG_BYTES[size]
|
|
70
|
+
buf << body
|
|
111
71
|
end
|
|
72
|
+
|
|
112
73
|
i += 1
|
|
113
74
|
end
|
|
114
75
|
|
|
@@ -118,49 +79,107 @@ module Protocol
|
|
|
118
79
|
|
|
119
80
|
# Reads one frame from an IO-like object.
|
|
120
81
|
#
|
|
121
|
-
#
|
|
82
|
+
# Uses #peek to buffer just enough header bytes (2 for short frames,
|
|
83
|
+
# 9 for long), then drains header + body in a single #read_exactly.
|
|
84
|
+
# This is 2 calls for both short and long frames, vs the naive 3 for
|
|
85
|
+
# long. A speculative read_exactly(9) would be unsafe: a <7-byte
|
|
86
|
+
# short frame at idle would hang waiting for bytes that never arrive,
|
|
87
|
+
# or consume bytes from the next frame on a mixed stream.
|
|
88
|
+
#
|
|
89
|
+
# @param io [#peek, #read_exactly]
|
|
122
90
|
# @return [Frame]
|
|
123
91
|
# @raise [Error] on invalid frame
|
|
124
92
|
# @raise [EOFError] if the connection is closed
|
|
125
93
|
def self.read_from(io, max_message_size: nil)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
94
|
+
buf = io.peek do |b|
|
|
95
|
+
next false if b.bytesize < 2
|
|
96
|
+
(b.getbyte(0) & FLAGS_LONG) == 0 || b.bytesize >= 9
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
raise EOFError, "Stream finished before reading frame header" if buf.bytesize < 2
|
|
132
100
|
|
|
101
|
+
flags = buf.getbyte(0)
|
|
133
102
|
more = (flags & FLAGS_MORE) != 0
|
|
134
103
|
long = (flags & FLAGS_LONG) != 0
|
|
135
104
|
command = (flags & FLAGS_COMMAND) != 0
|
|
136
|
-
|
|
105
|
+
|
|
106
|
+
if long
|
|
107
|
+
raise EOFError, "Stream finished before reading long frame size" if buf.bytesize < 9
|
|
108
|
+
|
|
109
|
+
size = (buf.getbyte(1) << 56) |
|
|
110
|
+
(buf.getbyte(2) << 48) |
|
|
111
|
+
(buf.getbyte(3) << 40) |
|
|
112
|
+
(buf.getbyte(4) << 32) |
|
|
113
|
+
(buf.getbyte(5) << 24) |
|
|
114
|
+
(buf.getbyte(6) << 16) |
|
|
115
|
+
(buf.getbyte(7) << 8) |
|
|
116
|
+
buf.getbyte(8)
|
|
117
|
+
header_size = 9
|
|
118
|
+
else
|
|
119
|
+
size = buf.getbyte(1)
|
|
120
|
+
header_size = 2
|
|
121
|
+
end
|
|
137
122
|
|
|
138
123
|
if max_message_size && size > max_message_size
|
|
139
124
|
raise Error, "frame size #{size} exceeds max_message_size #{max_message_size}"
|
|
140
125
|
end
|
|
141
126
|
|
|
142
|
-
|
|
127
|
+
if size.zero?
|
|
128
|
+
io.read_exactly(header_size)
|
|
129
|
+
return new(EMPTY_BINARY, more: more, command: command)
|
|
130
|
+
end
|
|
143
131
|
|
|
144
|
-
|
|
132
|
+
wire = io.read_exactly(header_size + size)
|
|
133
|
+
new(wire.byteslice(header_size, size), more: more, command: command)
|
|
145
134
|
end
|
|
146
135
|
|
|
147
136
|
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# @param
|
|
153
|
-
# @param
|
|
154
|
-
# @
|
|
137
|
+
# @return [String] frame body (binary)
|
|
138
|
+
attr_reader :body
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# @param body [String] frame body
|
|
142
|
+
# @param more [Boolean] more frames follow
|
|
143
|
+
# @param command [Boolean] this is a command frame
|
|
144
|
+
def initialize(body, more: false, command: false)
|
|
145
|
+
@body = body.encoding == Encoding::BINARY ? body : body.b
|
|
146
|
+
@more = more
|
|
147
|
+
@command = command
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# @return [Boolean] true if more frames follow in this message
|
|
152
|
+
def more?
|
|
153
|
+
@more
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# @return [Boolean] true if this is a command frame
|
|
158
|
+
def command?
|
|
159
|
+
@command
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# Encodes to wire bytes.
|
|
155
164
|
#
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
# @return [String] binary wire representation (flags + size + body)
|
|
166
|
+
def to_wire
|
|
167
|
+
size = @body.bytesize
|
|
168
|
+
flags = 0
|
|
169
|
+
flags |= FLAGS_MORE if @more
|
|
170
|
+
flags |= FLAGS_COMMAND if @command
|
|
171
|
+
|
|
172
|
+
if size > SHORT_MAX
|
|
173
|
+
buf = String.new(capacity: 9 + size, encoding: Encoding::BINARY)
|
|
174
|
+
buf << FLAG_BYTES[flags | FLAGS_LONG]
|
|
175
|
+
buf << [size].pack("Q>")
|
|
176
|
+
buf << @body
|
|
177
|
+
else
|
|
178
|
+
buf = String.new(capacity: 2 + size, encoding: Encoding::BINARY)
|
|
179
|
+
buf << FLAG_BYTES[flags]
|
|
180
|
+
buf << FLAG_BYTES[size]
|
|
181
|
+
buf << @body
|
|
182
|
+
end
|
|
164
183
|
end
|
|
165
184
|
|
|
166
185
|
end
|
|
@@ -42,7 +42,7 @@ module Protocol
|
|
|
42
42
|
attr_reader :peer_minor
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
# @return [Object] transport IO (#read_exactly, #write, #flush, #close)
|
|
45
|
+
# @return [Object] transport IO (#peek, #read_exactly, #write, #flush, #close)
|
|
46
46
|
attr_reader :io
|
|
47
47
|
|
|
48
48
|
|
|
@@ -50,7 +50,7 @@ module Protocol
|
|
|
50
50
|
attr_reader :last_received_at
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
# @param io [#read_exactly, #write, #flush, #close] transport IO
|
|
53
|
+
# @param io [#peek, #read_exactly, #write, #flush, #close] transport IO
|
|
54
54
|
# @param socket_type [String] our socket type name (e.g. "REQ")
|
|
55
55
|
# @param identity [String] our identity
|
|
56
56
|
# @param as_server [Boolean] whether we are the server side
|