omq-rfc-p2p 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/LICENSE +15 -0
- data/README.md +38 -0
- data/lib/omq/peer.rb +36 -0
- data/lib/omq/rfc/p2p/version.rb +10 -0
- data/lib/omq/rfc/p2p.rb +18 -0
- data/lib/omq/routing/peer.rb +83 -0
- data/lib/omq/single_frame.rb +23 -0
- metadata +62 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 15ad54bfc9669cc2110eab224a4ed6fdcd4027dbf433f36714833c407762bae1
|
|
4
|
+
data.tar.gz: 91d6a39b4023ad98bfa5c55c727640ea6a1a513694908bf0d1f08c21aa81ed45
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 75284f283dfb5180ac99d36a246126ea1cf935e15c3f48fed2bed6ba6a1bedebf99f11efc5b2e228d85128609e7a5a96e644eceb55d53965c79ce64853e60a11
|
|
7
|
+
data.tar.gz: c2829e5c3ddc37e9fc6afb9d3035e728d30f7252374c8ee536061943d8ed31219842876b30c04340b199ebf6899b80aa941956bfe8d3f43d2a61901ed65312fc
|
data/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026, Patrik Wenger
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# OMQ::PEER
|
|
2
|
+
|
|
3
|
+
[](https://github.com/paddor/omq-rfc-p2p/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/omq-rfc-p2p)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.ruby-lang.org)
|
|
7
|
+
|
|
8
|
+
PEER socket type ([RFC 51](https://rfc.zeromq.org/spec/51/)) for
|
|
9
|
+
[OMQ](https://github.com/paddor/omq).
|
|
10
|
+
|
|
11
|
+
True peer-to-peer messaging. Each PEER can both bind and connect,
|
|
12
|
+
routing by 4-byte connection ID.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
require "omq"
|
|
18
|
+
require "omq/rfc/p2p"
|
|
19
|
+
|
|
20
|
+
peer1 = OMQ::PEER.bind("tcp://127.0.0.1:5555")
|
|
21
|
+
peer2 = OMQ::PEER.connect("tcp://127.0.0.1:5555")
|
|
22
|
+
|
|
23
|
+
peer2 << "hello from peer2"
|
|
24
|
+
msg, routing_id = peer1.receive_with_routing_id
|
|
25
|
+
peer1.send_to(routing_id, "hello back")
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem "omq-rfc-p2p"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Requires `omq >= 0.12`.
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
[ISC](LICENSE)
|
data/lib/omq/peer.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OMQ
|
|
4
|
+
# Bidirectional multi-peer socket with routing IDs (ZeroMQ RFC 51).
|
|
5
|
+
#
|
|
6
|
+
# Each connected peer is assigned a 4-byte routing ID. Supports
|
|
7
|
+
# directed sends via #send_to and fair-queued receives.
|
|
8
|
+
class PEER < Socket
|
|
9
|
+
include Readable
|
|
10
|
+
include Writable
|
|
11
|
+
include SingleFrame
|
|
12
|
+
|
|
13
|
+
# Creates a new PEER socket.
|
|
14
|
+
#
|
|
15
|
+
# @param endpoints [String, Array<String>, nil] endpoint(s) to connect to
|
|
16
|
+
# @param linger [Integer] linger period in milliseconds
|
|
17
|
+
# @param backend [Object, nil] optional transport backend
|
|
18
|
+
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
19
|
+
_init_engine(:PEER, linger: linger, backend: backend)
|
|
20
|
+
_attach(endpoints, default: :connect)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Sends a message to a specific peer by routing ID.
|
|
25
|
+
#
|
|
26
|
+
# @param routing_id [String] 4-byte routing ID
|
|
27
|
+
# @param message [String] message body
|
|
28
|
+
# @return [self]
|
|
29
|
+
#
|
|
30
|
+
def send_to(routing_id, message)
|
|
31
|
+
parts = [routing_id.b.freeze, message.b.freeze]
|
|
32
|
+
with_timeout(@options.write_timeout) { @engine.enqueue_send(parts) }
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/omq/rfc/p2p.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# OMQ PEER socket type (ZeroMQ RFC 51).
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# require "omq/rfc/p2p"
|
|
7
|
+
#
|
|
8
|
+
# a = OMQ::PEER.bind("tcp://127.0.0.1:5555")
|
|
9
|
+
# b = OMQ::PEER.connect("tcp://127.0.0.1:5555")
|
|
10
|
+
|
|
11
|
+
require "omq"
|
|
12
|
+
|
|
13
|
+
require_relative "p2p/version"
|
|
14
|
+
require_relative "../single_frame"
|
|
15
|
+
require_relative "../routing/peer"
|
|
16
|
+
require_relative "../peer"
|
|
17
|
+
|
|
18
|
+
OMQ::Routing.register(:PEER, OMQ::Routing::Peer)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module OMQ
|
|
6
|
+
module Routing
|
|
7
|
+
# PEER socket routing: bidirectional multi-peer with auto-generated
|
|
8
|
+
# 4-byte routing IDs.
|
|
9
|
+
#
|
|
10
|
+
# Prepends routing ID on receive. Strips routing ID on send and
|
|
11
|
+
# routes to the identified connection.
|
|
12
|
+
#
|
|
13
|
+
class Peer
|
|
14
|
+
include FairRecv
|
|
15
|
+
|
|
16
|
+
# @param engine [Engine]
|
|
17
|
+
#
|
|
18
|
+
def initialize(engine)
|
|
19
|
+
@engine = engine
|
|
20
|
+
@recv_queue = FairQueue.new
|
|
21
|
+
@connections_by_routing_id = {}
|
|
22
|
+
@routing_id_by_connection = {}
|
|
23
|
+
@conn_queues = {}
|
|
24
|
+
@conn_send_tasks = {}
|
|
25
|
+
@tasks = []
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# @return [FairQueue]
|
|
30
|
+
#
|
|
31
|
+
attr_reader :recv_queue
|
|
32
|
+
|
|
33
|
+
# @param connection [Connection]
|
|
34
|
+
#
|
|
35
|
+
def connection_added(connection)
|
|
36
|
+
routing_id = SecureRandom.bytes(4)
|
|
37
|
+
@connections_by_routing_id[routing_id] = connection
|
|
38
|
+
@routing_id_by_connection[connection] = routing_id
|
|
39
|
+
|
|
40
|
+
add_fair_recv_connection(connection) { |msg| [routing_id, *msg] }
|
|
41
|
+
|
|
42
|
+
q = Routing.build_queue(@engine.options.send_hwm, :block)
|
|
43
|
+
@conn_queues[connection] = q
|
|
44
|
+
@conn_send_tasks[connection] = ConnSendPump.start(@engine, connection, q, @tasks)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# @param connection [Connection]
|
|
49
|
+
#
|
|
50
|
+
def connection_removed(connection)
|
|
51
|
+
routing_id = @routing_id_by_connection.delete(connection)
|
|
52
|
+
@connections_by_routing_id.delete(routing_id) if routing_id
|
|
53
|
+
@recv_queue.remove_queue(connection)
|
|
54
|
+
@conn_queues.delete(connection)
|
|
55
|
+
@conn_send_tasks.delete(connection)&.stop
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# @param parts [Array<String>]
|
|
60
|
+
#
|
|
61
|
+
def enqueue(parts)
|
|
62
|
+
routing_id = parts.first
|
|
63
|
+
conn = @connections_by_routing_id[routing_id]
|
|
64
|
+
return unless conn
|
|
65
|
+
@conn_queues[conn]&.enqueue(parts[1..])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Stops all background tasks (send pumps).
|
|
70
|
+
def stop
|
|
71
|
+
@tasks.each(&:stop)
|
|
72
|
+
@tasks.clear
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# True when all per-connection send queues are empty.
|
|
77
|
+
#
|
|
78
|
+
def send_queues_drained?
|
|
79
|
+
@conn_queues.values.all?(&:empty?)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OMQ
|
|
4
|
+
# Mixin that rejects multipart messages.
|
|
5
|
+
#
|
|
6
|
+
# All draft socket types (CLIENT, SERVER, RADIO, DISH, SCATTER,
|
|
7
|
+
# GATHER, PEER, CHANNEL) require single-frame messages for
|
|
8
|
+
# thread-safe atomic operations.
|
|
9
|
+
#
|
|
10
|
+
module SingleFrame
|
|
11
|
+
# Sends a message, rejecting multipart messages.
|
|
12
|
+
#
|
|
13
|
+
# @param message [String, Array<String>] message to send (must be single-frame)
|
|
14
|
+
# @raise [ArgumentError] if a multipart message is provided
|
|
15
|
+
# @return [void]
|
|
16
|
+
def send(message)
|
|
17
|
+
if message.is_a?(Array) && message.size > 1
|
|
18
|
+
raise ArgumentError, "#{self.class} does not support multipart messages"
|
|
19
|
+
end
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: omq-rfc-p2p
|
|
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
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: omq
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.12'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.12'
|
|
26
|
+
description: PEER socket type implementing ZeroMQ RFC 51 for the OMQ pure-Ruby ZeroMQ
|
|
27
|
+
library.
|
|
28
|
+
email:
|
|
29
|
+
- paddor@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- LICENSE
|
|
35
|
+
- README.md
|
|
36
|
+
- lib/omq/peer.rb
|
|
37
|
+
- lib/omq/rfc/p2p.rb
|
|
38
|
+
- lib/omq/rfc/p2p/version.rb
|
|
39
|
+
- lib/omq/routing/peer.rb
|
|
40
|
+
- lib/omq/single_frame.rb
|
|
41
|
+
homepage: https://github.com/paddor/omq-rfc-p2p
|
|
42
|
+
licenses:
|
|
43
|
+
- ISC
|
|
44
|
+
metadata: {}
|
|
45
|
+
rdoc_options: []
|
|
46
|
+
require_paths:
|
|
47
|
+
- lib
|
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '3.3'
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
requirements: []
|
|
59
|
+
rubygems_version: 4.0.6
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: ZMQ PEER socket type (RFC 51) for OMQ
|
|
62
|
+
test_files: []
|