protocol-sp 0.1.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 +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE +13 -0
- data/README.md +26 -0
- data/lib/protocol/sp/codec/frame.rb +65 -0
- data/lib/protocol/sp/codec/greeting.rb +54 -0
- data/lib/protocol/sp/codec.rb +11 -0
- data/lib/protocol/sp/connection.rb +222 -0
- data/lib/protocol/sp/error.rb +9 -0
- data/lib/protocol/sp/protocols.rb +61 -0
- data/lib/protocol/sp/version.rb +7 -0
- data/lib/protocol/sp.rb +13 -0
- metadata +53 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b3a9f50418b5ea8885c5e54d7d8245bf47fea17bd6f2f32e5ed84707fec6868a
|
|
4
|
+
data.tar.gz: 3c9dfd7d357ef8dea0b325c99c5b326a39e25ee4eb292e3a7ec655f80de3c213
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9964fdb2780489536856044c5686af01b2b63b685589c43c96b9c48929b9e5afc7ee4cba5ea56ab0ccb9b3f0d72909e53ab6d394b08e076aa107d2ca60a3e0f7
|
|
7
|
+
data.tar.gz: ea786df241e7777e6715bdc97eeb73e52270f238c0e43b45fbd7efd2a533efd3db604eab27156526d6230ca1fa5804fb559d82db19488257ef5e9aae4e94caf4
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-04-09
|
|
4
|
+
|
|
5
|
+
Initial release.
|
|
6
|
+
|
|
7
|
+
- `Protocol::SP::Codec::Frame` — length-prefixed framing. SP/TCP uses an 8-byte big-endian length; SP/IPC prepends a 1-byte message type to match nng's wire format.
|
|
8
|
+
- `Protocol::SP::Codec::Greeting` — 8-byte SP/TCP greeting.
|
|
9
|
+
- `Protocol::SP::Protocols` — protocol identifier constants.
|
|
10
|
+
- `Protocol::SP::Connection` — mutex-protected handshake, `#send_message`, `#write_message`, `#write_messages` (batched), `#receive_message`. `framing:` selects `:tcp` or `:ipc`.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (c) 2026, Patrik Wenger
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
8
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
9
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
10
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
11
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
12
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
13
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# protocol-sp
|
|
2
|
+
|
|
3
|
+
[](https://github.com/paddor/protocol-sp/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/protocol-sp)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.ruby-lang.org)
|
|
7
|
+
|
|
8
|
+
Pure Ruby codec and connection for the [Scalability Protocols](https://nanomsg.org/documentation-zeromq.html) (SP) wire format used by [nanomsg](https://nanomsg.org) and [nng](https://nng.nanomsg.org). Zero runtime dependencies. Sister gem to [protocol-zmtp](https://github.com/paddor/protocol-zmtp).
|
|
9
|
+
|
|
10
|
+
## What's in the box
|
|
11
|
+
|
|
12
|
+
- `Protocol::SP::Codec::Frame` — length-prefixed framing. SP/TCP uses an 8-byte big-endian length; SP/IPC prepends a 1-byte message type (0x00 control, 0x01 user) to match nng's wire format.
|
|
13
|
+
- `Protocol::SP::Codec::Greeting` — 8-byte handshake: `00 'S' 'P' 00 <peer-proto:u16-BE> 00 00`.
|
|
14
|
+
- `Protocol::SP::Protocols` — protocol identifier constants (PUSH=0x50, PULL=0x51, PUB=0x20, SUB=0x21, REQ=0x30, REP=0x31, PAIR_V0=0x10, PAIR_V1=0x11, BUS=0x70, SURVEYOR=0x62, RESPONDENT=0x63).
|
|
15
|
+
- `Protocol::SP::Connection` — mutex-protected `#handshake!`, `#send_message`, `#write_message` (no flush), `#write_messages` (batched under a single mutex acquisition), `#receive_message` over any IO-like object. `framing:` selects `:tcp` or `:ipc`.
|
|
16
|
+
|
|
17
|
+
## Notes
|
|
18
|
+
|
|
19
|
+
- SP messages are single-frame (no multipart, unlike ZMTP).
|
|
20
|
+
- No security mechanisms in the SP wire protocol — encryption is layered via TLS/WebSocket transports, not the SP framing.
|
|
21
|
+
- No commands at the wire level — protocol-specific control bytes (e.g. REQ request IDs, SUB topics) live inside the message body.
|
|
22
|
+
- Zero-alloc frame headers on the unencrypted hot send path via `Array#pack(buffer:)`.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
`protocol-sp` is a low-level codec. For a full socket API (PUSH/PULL, REQ/REP, PAIR, transports, reconnect), see [nnq](https://github.com/paddor/nnq).
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protocol
|
|
4
|
+
module SP
|
|
5
|
+
module Codec
|
|
6
|
+
# SP/TCP frame encode/decode.
|
|
7
|
+
#
|
|
8
|
+
# Wire format (per nng `src/sp/transport/tcp/tcp.c`):
|
|
9
|
+
# 8 bytes body length, big-endian unsigned 64-bit
|
|
10
|
+
# N bytes body
|
|
11
|
+
#
|
|
12
|
+
# SP messages are single-frame — there is no MORE flag and no
|
|
13
|
+
# multipart concept at the transport level.
|
|
14
|
+
#
|
|
15
|
+
class Frame
|
|
16
|
+
HEADER_SIZE = 8
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# @return [String] frame body (binary)
|
|
20
|
+
attr_reader :body
|
|
21
|
+
|
|
22
|
+
# @param body [String] frame body
|
|
23
|
+
def initialize(body)
|
|
24
|
+
@body = body.b
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Encodes to wire bytes.
|
|
29
|
+
#
|
|
30
|
+
# @return [String] binary wire representation (length + body)
|
|
31
|
+
def to_wire
|
|
32
|
+
[@body.bytesize].pack("Q>") + @body
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Encodes a body into wire bytes without allocating a Frame.
|
|
37
|
+
#
|
|
38
|
+
# @param body [String]
|
|
39
|
+
# @return [String] frozen binary wire representation
|
|
40
|
+
def self.encode(body)
|
|
41
|
+
([body.bytesize].pack("Q>") + body.b).freeze
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Reads one frame from an IO-like object.
|
|
46
|
+
#
|
|
47
|
+
# @param io [#read_exactly] must support read_exactly(n)
|
|
48
|
+
# @param max_message_size [Integer, nil] maximum body size, nil = unlimited
|
|
49
|
+
# @return [Frame]
|
|
50
|
+
# @raise [Error] on oversized frame
|
|
51
|
+
# @raise [EOFError] if the connection is closed
|
|
52
|
+
def self.read_from(io, max_message_size: nil)
|
|
53
|
+
size = io.read_exactly(HEADER_SIZE).unpack1("Q>")
|
|
54
|
+
|
|
55
|
+
if max_message_size && size > max_message_size
|
|
56
|
+
raise Error, "frame size #{size} exceeds max_message_size #{max_message_size}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
body = size > 0 ? io.read_exactly(size) : "".b
|
|
60
|
+
new(body)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protocol
|
|
4
|
+
module SP
|
|
5
|
+
module Codec
|
|
6
|
+
# SP/TCP greeting encode/decode.
|
|
7
|
+
#
|
|
8
|
+
# The greeting is exactly 8 bytes (per nng `src/sp/transport/tcp/tcp.c`,
|
|
9
|
+
# `tcptran_pipe_nego_cb` and `tcptran_pipe_send_start`):
|
|
10
|
+
#
|
|
11
|
+
# Offset Bytes Field
|
|
12
|
+
# 0 1 0x00
|
|
13
|
+
# 1 1 'S' (0x53)
|
|
14
|
+
# 2 1 'P' (0x50)
|
|
15
|
+
# 3 1 0x00
|
|
16
|
+
# 4-5 2 protocol id (u16, big-endian)
|
|
17
|
+
# 6-7 2 reserved (must be 0x00 0x00)
|
|
18
|
+
#
|
|
19
|
+
module Greeting
|
|
20
|
+
SIZE = 8
|
|
21
|
+
SIGNATURE = "\x00SP\x00".b.freeze
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Encodes an SP/TCP greeting.
|
|
25
|
+
#
|
|
26
|
+
# @param protocol [Integer] our protocol id (e.g. Protocols::PUSH_V0)
|
|
27
|
+
# @return [String] 8-byte binary greeting
|
|
28
|
+
def self.encode(protocol:)
|
|
29
|
+
SIGNATURE + [protocol].pack("n") + "\x00\x00".b
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Decodes an SP/TCP greeting.
|
|
34
|
+
#
|
|
35
|
+
# @param data [String] 8-byte binary greeting
|
|
36
|
+
# @return [Integer] peer protocol id
|
|
37
|
+
# @raise [Error] on invalid greeting
|
|
38
|
+
def self.decode(data)
|
|
39
|
+
raise Error, "greeting too short (#{data.bytesize} bytes)" if data.bytesize < SIZE
|
|
40
|
+
|
|
41
|
+
data = data.b
|
|
42
|
+
unless data.byteslice(0, 4) == SIGNATURE
|
|
43
|
+
raise Error, "invalid SP greeting signature"
|
|
44
|
+
end
|
|
45
|
+
unless data.getbyte(6) == 0 && data.getbyte(7) == 0
|
|
46
|
+
raise Error, "invalid SP greeting reserved bytes"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
data.byteslice(4, 2).unpack1("n")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protocol
|
|
4
|
+
module SP
|
|
5
|
+
# Manages one SP peer connection over any transport IO.
|
|
6
|
+
#
|
|
7
|
+
# The SP wire protocol has no commands, no security mechanisms, and no
|
|
8
|
+
# multipart messages — `#handshake!` is just an exchange of two 8-byte
|
|
9
|
+
# greetings, and `#send_message` / `#receive_message` work on single
|
|
10
|
+
# binary bodies framed by an 8-byte big-endian length.
|
|
11
|
+
#
|
|
12
|
+
class Connection
|
|
13
|
+
# SP/IPC data messages are prefixed with a 1-byte message type.
|
|
14
|
+
# 0x01 = user message. 0x00 is reserved in nng for control frames
|
|
15
|
+
# (keepalive) that we don't emit, but we still accept and skip on
|
|
16
|
+
# read for forward-compatibility with nng peers.
|
|
17
|
+
IPC_MSG_TYPE = 0x01
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# @return [Integer] peer's protocol id (set after handshake)
|
|
21
|
+
attr_reader :peer_protocol
|
|
22
|
+
|
|
23
|
+
# @return [Object] transport IO (#read_exactly, #write, #flush, #close)
|
|
24
|
+
attr_reader :io
|
|
25
|
+
|
|
26
|
+
# @return [Float, nil] monotonic timestamp of last received frame
|
|
27
|
+
attr_reader :last_received_at
|
|
28
|
+
|
|
29
|
+
# @return [Symbol] :tcp or :ipc
|
|
30
|
+
attr_reader :framing
|
|
31
|
+
|
|
32
|
+
# @param io [#read_exactly, #write, #flush, #close] transport IO
|
|
33
|
+
# @param protocol [Integer] our protocol id (e.g. Protocols::PUSH_V0)
|
|
34
|
+
# @param max_message_size [Integer, nil] max body size, nil = unlimited
|
|
35
|
+
# @param framing [Symbol] :tcp (default) uses 8-byte length headers;
|
|
36
|
+
# :ipc prepends a 1-byte message-type marker to each frame
|
|
37
|
+
# (nng's SP/IPC wire format)
|
|
38
|
+
def initialize(io, protocol:, max_message_size: nil, framing: :tcp)
|
|
39
|
+
@io = io
|
|
40
|
+
@protocol = protocol
|
|
41
|
+
@peer_protocol = nil
|
|
42
|
+
@max_message_size = max_message_size
|
|
43
|
+
@framing = framing
|
|
44
|
+
@mutex = Mutex.new
|
|
45
|
+
@last_received_at = nil
|
|
46
|
+
# Reusable scratch buffer for frame headers — written into by
|
|
47
|
+
# Array#pack(buffer:), then flushed to @io. Capacity 9 covers
|
|
48
|
+
# both :tcp (8B) and :ipc (1+8B) framings.
|
|
49
|
+
@header_buf = String.new(capacity: 9, encoding: Encoding::BINARY)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Performs the SP/TCP greeting exchange.
|
|
54
|
+
#
|
|
55
|
+
# @return [void]
|
|
56
|
+
# @raise [Error] on greeting mismatch or peer-incompatibility
|
|
57
|
+
def handshake!
|
|
58
|
+
@io.write(Codec::Greeting.encode(protocol: @protocol))
|
|
59
|
+
@io.flush
|
|
60
|
+
|
|
61
|
+
peer = Codec::Greeting.decode(@io.read_exactly(Codec::Greeting::SIZE))
|
|
62
|
+
@peer_protocol = peer
|
|
63
|
+
|
|
64
|
+
valid = Protocols::VALID_PEERS[@protocol]
|
|
65
|
+
unless valid&.include?(peer)
|
|
66
|
+
raise Error, "incompatible SP protocols: 0x#{@protocol.to_s(16)} cannot speak to 0x#{peer.to_s(16)}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Sends one message (write + flush).
|
|
72
|
+
#
|
|
73
|
+
# @param body [String] message body (single frame)
|
|
74
|
+
# @return [void]
|
|
75
|
+
def send_message(body)
|
|
76
|
+
@mutex.synchronize do
|
|
77
|
+
write_header_nolock(body.bytesize)
|
|
78
|
+
@io.write(body)
|
|
79
|
+
@io.flush
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Writes one message to the buffer without flushing.
|
|
85
|
+
# Call {#flush} after batching writes.
|
|
86
|
+
#
|
|
87
|
+
# Two writes — header then body — into the buffered IO; avoids
|
|
88
|
+
# the per-message intermediate String allocation that
|
|
89
|
+
# {Codec::Frame.encode} would otherwise produce.
|
|
90
|
+
#
|
|
91
|
+
# @param body [String]
|
|
92
|
+
# @return [void]
|
|
93
|
+
def write_message(body)
|
|
94
|
+
@mutex.synchronize do
|
|
95
|
+
write_header_nolock(body.bytesize)
|
|
96
|
+
@io.write(body)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Writes a batch of messages to the buffer under a single mutex
|
|
102
|
+
# acquisition. Used by work-stealing send pumps that dequeue up
|
|
103
|
+
# to N messages at once — avoids N lock/unlock pairs per batch.
|
|
104
|
+
# Call {#flush} after to push the buffer to the socket.
|
|
105
|
+
#
|
|
106
|
+
# @param bodies [Array<String>]
|
|
107
|
+
# @return [void]
|
|
108
|
+
def write_messages(bodies)
|
|
109
|
+
@mutex.synchronize do
|
|
110
|
+
i = 0
|
|
111
|
+
n = bodies.size
|
|
112
|
+
while i < n
|
|
113
|
+
body = bodies[i]
|
|
114
|
+
write_header_nolock(body.bytesize)
|
|
115
|
+
@io.write(body)
|
|
116
|
+
i += 1
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Writes the frame header into the already-held @mutex. Hotpath:
|
|
123
|
+
# keep the branch monomorphic per-connection and avoid fresh pack
|
|
124
|
+
# allocations by packing into a per-connection scratch buffer.
|
|
125
|
+
#
|
|
126
|
+
# @param size [Integer] body size
|
|
127
|
+
# @return [void]
|
|
128
|
+
private def write_header_nolock(size)
|
|
129
|
+
buf = @header_buf
|
|
130
|
+
buf.clear
|
|
131
|
+
if @framing == :ipc
|
|
132
|
+
[IPC_MSG_TYPE, size].pack("CQ>", buffer: buf)
|
|
133
|
+
else
|
|
134
|
+
[size].pack("Q>", buffer: buf)
|
|
135
|
+
end
|
|
136
|
+
@io.write(buf)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Writes pre-encoded wire bytes without flushing. Used for fan-out:
|
|
141
|
+
# encode once with `Codec::Frame.encode`, write to many connections.
|
|
142
|
+
#
|
|
143
|
+
# @param wire_bytes [String]
|
|
144
|
+
# @return [void]
|
|
145
|
+
def write_wire(wire_bytes)
|
|
146
|
+
@mutex.synchronize do
|
|
147
|
+
@io.write(wire_bytes)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Flushes the write buffer to the underlying IO.
|
|
153
|
+
#
|
|
154
|
+
# @return [void]
|
|
155
|
+
def flush
|
|
156
|
+
@mutex.synchronize do
|
|
157
|
+
@io.flush
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Receives one message body.
|
|
163
|
+
#
|
|
164
|
+
# @return [String] binary body (NOT frozen — let callers freeze if
|
|
165
|
+
# they want, the freeze cost shows up in hot loops)
|
|
166
|
+
# @raise [EOFError] if connection is closed
|
|
167
|
+
def receive_message
|
|
168
|
+
if @framing == :ipc
|
|
169
|
+
loop do
|
|
170
|
+
# One read_exactly(9) is cheaper than separate 1+8 reads:
|
|
171
|
+
# halves the io-stream dispatch overhead per message.
|
|
172
|
+
header = @io.read_exactly(9)
|
|
173
|
+
type, size = header.unpack("CQ>")
|
|
174
|
+
if @max_message_size && size > @max_message_size
|
|
175
|
+
raise Error, "frame size #{size} exceeds max_message_size #{@max_message_size}"
|
|
176
|
+
end
|
|
177
|
+
body = size > 0 ? @io.read_exactly(size) : "".b
|
|
178
|
+
touch_heartbeat
|
|
179
|
+
# Skip nng IPC control frames (0x00 — keepalive/etc.); only
|
|
180
|
+
# deliver user messages (0x01) to the caller.
|
|
181
|
+
return body if type == IPC_MSG_TYPE
|
|
182
|
+
end
|
|
183
|
+
else
|
|
184
|
+
frame = Codec::Frame.read_from(@io, max_message_size: @max_message_size)
|
|
185
|
+
touch_heartbeat
|
|
186
|
+
frame.body
|
|
187
|
+
end
|
|
188
|
+
rescue Error
|
|
189
|
+
close
|
|
190
|
+
raise
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# Records that a frame was received (for inactivity tracking).
|
|
195
|
+
#
|
|
196
|
+
# @return [void]
|
|
197
|
+
def touch_heartbeat
|
|
198
|
+
@last_received_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# Returns true if no frame has been received within +timeout+ seconds.
|
|
203
|
+
#
|
|
204
|
+
# @param timeout [Numeric] seconds
|
|
205
|
+
# @return [Boolean]
|
|
206
|
+
def heartbeat_expired?(timeout)
|
|
207
|
+
return false unless @last_received_at
|
|
208
|
+
(Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_received_at) > timeout
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# Closes the connection.
|
|
213
|
+
#
|
|
214
|
+
# @return [void]
|
|
215
|
+
def close
|
|
216
|
+
@io.close
|
|
217
|
+
rescue IOError
|
|
218
|
+
# already closed
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protocol
|
|
4
|
+
module SP
|
|
5
|
+
# Wire-level protocol identifiers (16-bit, big-endian on the wire).
|
|
6
|
+
#
|
|
7
|
+
# Encoding: `(major << 4) | minor`, matching nng's `NNI_PROTO(major,
|
|
8
|
+
# minor)` macro in `src/core/protocol.h`. Sourced from
|
|
9
|
+
# `src/sp/protocol/*/`.
|
|
10
|
+
#
|
|
11
|
+
module Protocols
|
|
12
|
+
PAIR_V0 = 0x10 # (1, 0)
|
|
13
|
+
PAIR_V1 = 0x11 # (1, 1)
|
|
14
|
+
|
|
15
|
+
PUB_V0 = 0x20 # (2, 0)
|
|
16
|
+
SUB_V0 = 0x21 # (2, 1)
|
|
17
|
+
|
|
18
|
+
REQ_V0 = 0x30 # (3, 0)
|
|
19
|
+
REP_V0 = 0x31 # (3, 1)
|
|
20
|
+
|
|
21
|
+
PUSH_V0 = 0x50 # (5, 0)
|
|
22
|
+
PULL_V0 = 0x51 # (5, 1)
|
|
23
|
+
|
|
24
|
+
SURVEYOR_V0 = 0x62 # (6, 2)
|
|
25
|
+
RESPONDENT_V0 = 0x63 # (6, 3)
|
|
26
|
+
|
|
27
|
+
BUS_V0 = 0x70 # (7, 0)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Compatibility table: which peer ID is acceptable for each self ID.
|
|
31
|
+
VALID_PEERS = {
|
|
32
|
+
PAIR_V0 => [PAIR_V0],
|
|
33
|
+
PAIR_V1 => [PAIR_V1],
|
|
34
|
+
PUB_V0 => [SUB_V0],
|
|
35
|
+
SUB_V0 => [PUB_V0],
|
|
36
|
+
REQ_V0 => [REP_V0],
|
|
37
|
+
REP_V0 => [REQ_V0],
|
|
38
|
+
PUSH_V0 => [PULL_V0],
|
|
39
|
+
PULL_V0 => [PUSH_V0],
|
|
40
|
+
SURVEYOR_V0 => [RESPONDENT_V0],
|
|
41
|
+
RESPONDENT_V0 => [SURVEYOR_V0],
|
|
42
|
+
BUS_V0 => [BUS_V0],
|
|
43
|
+
}.freeze
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
NAMES = {
|
|
47
|
+
PAIR_V0 => "pair",
|
|
48
|
+
PAIR_V1 => "pair1",
|
|
49
|
+
PUB_V0 => "pub",
|
|
50
|
+
SUB_V0 => "sub",
|
|
51
|
+
REQ_V0 => "req",
|
|
52
|
+
REP_V0 => "rep",
|
|
53
|
+
PUSH_V0 => "push",
|
|
54
|
+
PULL_V0 => "pull",
|
|
55
|
+
SURVEYOR_V0 => "surveyor",
|
|
56
|
+
RESPONDENT_V0 => "respondent",
|
|
57
|
+
BUS_V0 => "bus",
|
|
58
|
+
}.freeze
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/protocol/sp.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protocol
|
|
4
|
+
# Scalability Protocols (nanomsg/nng) wire codec and connection.
|
|
5
|
+
module SP
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative "sp/version"
|
|
10
|
+
require_relative "sp/error"
|
|
11
|
+
require_relative "sp/protocols"
|
|
12
|
+
require_relative "sp/codec"
|
|
13
|
+
require_relative "sp/connection"
|
metadata
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: protocol-sp
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Patrik Wenger
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Pure Ruby implementation of the Scalability Protocols wire format used
|
|
13
|
+
by nanomsg and nng. Includes the SP/TCP framing codec, 8-byte greeting, protocol
|
|
14
|
+
identifiers, and connection management. No runtime dependencies.
|
|
15
|
+
email:
|
|
16
|
+
- paddor@gmail.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- CHANGELOG.md
|
|
22
|
+
- LICENSE
|
|
23
|
+
- README.md
|
|
24
|
+
- lib/protocol/sp.rb
|
|
25
|
+
- lib/protocol/sp/codec.rb
|
|
26
|
+
- lib/protocol/sp/codec/frame.rb
|
|
27
|
+
- lib/protocol/sp/codec/greeting.rb
|
|
28
|
+
- lib/protocol/sp/connection.rb
|
|
29
|
+
- lib/protocol/sp/error.rb
|
|
30
|
+
- lib/protocol/sp/protocols.rb
|
|
31
|
+
- lib/protocol/sp/version.rb
|
|
32
|
+
homepage: https://github.com/paddor/protocol-sp
|
|
33
|
+
licenses:
|
|
34
|
+
- ISC
|
|
35
|
+
metadata: {}
|
|
36
|
+
rdoc_options: []
|
|
37
|
+
require_paths:
|
|
38
|
+
- lib
|
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '3.3'
|
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '0'
|
|
49
|
+
requirements: []
|
|
50
|
+
rubygems_version: 4.0.6
|
|
51
|
+
specification_version: 4
|
|
52
|
+
summary: Scalability Protocols (nanomsg/nng) wire codec and connection
|
|
53
|
+
test_files: []
|