omq 0.11.0 → 0.12.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.
@@ -1,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "securerandom"
4
-
5
- module OMQ
6
- module Routing
7
- # SERVER socket routing: identity-based routing 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 Server
14
- # @param engine [Engine]
15
- #
16
- def initialize(engine)
17
- @engine = engine
18
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
19
- @send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
20
- @connections_by_routing_id = {}
21
- @routing_id_by_connection = {}
22
- @tasks = []
23
- @send_pump_started = false
24
- @send_pump_idle = true
25
- @written = Set.new
26
- end
27
-
28
- # @return [Async::LimitedQueue]
29
- #
30
- attr_reader :recv_queue, :send_queue
31
-
32
- # @param connection [Connection]
33
- #
34
- def connection_added(connection)
35
- routing_id = SecureRandom.bytes(4)
36
- @connections_by_routing_id[routing_id] = connection
37
- @routing_id_by_connection[connection] = routing_id
38
-
39
- task = @engine.start_recv_pump(connection, @recv_queue) do |msg|
40
- [routing_id, *msg]
41
- end
42
- @tasks << task if task
43
-
44
- start_send_pump unless @send_pump_started
45
- end
46
-
47
- # @param connection [Connection]
48
- #
49
- def connection_removed(connection)
50
- routing_id = @routing_id_by_connection.delete(connection)
51
- @connections_by_routing_id.delete(routing_id) if routing_id
52
- end
53
-
54
- # @param parts [Array<String>]
55
- #
56
- def enqueue(parts)
57
- @send_queue.enqueue(parts)
58
- end
59
-
60
- def stop
61
- @tasks.each(&:stop)
62
- @tasks.clear
63
- end
64
-
65
- def send_pump_idle? = @send_pump_idle
66
-
67
- private
68
-
69
- def start_send_pump
70
- @send_pump_started = true
71
- @tasks << @engine.spawn_pump_task(annotation: "send pump") do
72
- loop do
73
- @send_pump_idle = true
74
- batch = [@send_queue.dequeue]
75
- @send_pump_idle = false
76
- Routing.drain_send_queue(@send_queue, batch)
77
-
78
- @written.clear
79
- batch.each do |parts|
80
- routing_id = parts.first
81
- conn = @connections_by_routing_id[routing_id]
82
- next unless conn # silently drop if peer gone
83
- begin
84
- conn.write_message(parts[1..])
85
- @written << conn
86
- rescue *CONNECTION_LOST
87
- # will be cleaned up
88
- end
89
- end
90
-
91
- @written.each do |conn|
92
- conn.flush
93
- rescue *CONNECTION_LOST
94
- # will be cleaned up
95
- end
96
- end
97
- end
98
- end
99
- end
100
- end
101
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OMQ
4
- class SCATTER < Socket
5
- include Writable
6
- include SingleFrame
7
-
8
- def initialize(endpoints = nil, linger: 0, send_hwm: nil, send_timeout: nil, backend: nil)
9
- _init_engine(:SCATTER, linger: linger, send_hwm: send_hwm, send_timeout: send_timeout, backend: backend)
10
- _attach(endpoints, default: :connect)
11
- end
12
- end
13
-
14
- class GATHER < Socket
15
- include Readable
16
- include SingleFrame
17
-
18
- def initialize(endpoints = nil, linger: 0, recv_hwm: nil, recv_timeout: nil, backend: nil)
19
- _init_engine(:GATHER, linger: linger, recv_hwm: recv_hwm, recv_timeout: recv_timeout, backend: backend)
20
- _attach(endpoints, default: :bind)
21
- end
22
- end
23
- end
@@ -1,18 +0,0 @@
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
- def send(message)
12
- if message.is_a?(Array) && message.size > 1
13
- raise ArgumentError, "#{self.class} does not support multipart messages"
14
- end
15
- super
16
- end
17
- end
18
- end
@@ -1,146 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "socket"
4
- require "uri"
5
- require "openssl"
6
- require "io/stream"
7
-
8
- module OMQ
9
- module Transport
10
- # TLS transport — TLS v1.3 on top of TCP.
11
- #
12
- # Requires the socket's +tls_context+ to be set to an
13
- # +OpenSSL::SSL::SSLContext+ before bind or connect.
14
- #
15
- module TLS
16
- TLS_PREFIX = "tls+tcp://"
17
-
18
- class << self
19
- # Binds a TLS server.
20
- #
21
- # @param endpoint [String] e.g. "tls+tcp://127.0.0.1:5555" or "tls+tcp://*:0"
22
- # @param engine [Engine]
23
- # @return [Listener]
24
- #
25
- def bind(endpoint, engine)
26
- ctx = require_context!(engine)
27
- host, port = parse_endpoint(endpoint)
28
- host = "0.0.0.0" if host == "*"
29
-
30
- addrs = Addrinfo.getaddrinfo(host, port, nil, :STREAM, nil, ::Socket::AI_PASSIVE)
31
- raise ::Socket::ResolutionError, "no addresses for #{host}" if addrs.empty?
32
-
33
- servers = []
34
- actual_port = nil
35
-
36
- addrs.each do |addr|
37
- server = TCPServer.new(addr.ip_address, actual_port || port)
38
- actual_port ||= server.local_address.ip_port
39
- servers << server
40
- end
41
-
42
- host_part = host.include?(":") ? "[#{host}]" : host
43
- resolved = "#{TLS_PREFIX}#{host_part}:#{actual_port}"
44
- Listener.new(resolved, servers, actual_port, ctx)
45
- end
46
-
47
- # Connects to a TLS endpoint.
48
- #
49
- # @param endpoint [String] e.g. "tls+tcp://127.0.0.1:5555"
50
- # @param engine [Engine]
51
- # @return [void]
52
- #
53
- def connect(endpoint, engine)
54
- ctx = require_context!(engine)
55
- host, port = parse_endpoint(endpoint)
56
- tcp_sock = TCPSocket.new(host, port)
57
-
58
- ssl = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
59
- ssl.sync_close = true
60
- ssl.hostname = host
61
- ssl.connect
62
-
63
- engine.handle_connected(IO::Stream::Buffered.wrap(ssl), endpoint: endpoint)
64
- rescue
65
- tcp_sock&.close unless ssl&.sync_close
66
- raise
67
- end
68
-
69
- private
70
-
71
- # Validates and freezes the TLS context from engine options.
72
- #
73
- # The context SHOULD have min_version set to TLS1_3_VERSION.
74
- # We cannot validate this because SSLContext#min_version is
75
- # write-only in Ruby's OpenSSL binding.
76
- #
77
- # @return [OpenSSL::SSL::SSLContext]
78
- # @raise [ArgumentError] if no context is set
79
- #
80
- def require_context!(engine)
81
- ctx = engine.options.tls_context
82
- raise ArgumentError, "tls_context must be set for tls+tcp:// endpoints" unless ctx
83
- ctx.freeze unless ctx.frozen?
84
- ctx
85
- end
86
-
87
- # Parses a tls+tcp:// endpoint URI into host and port.
88
- #
89
- # @param endpoint [String]
90
- # @return [Array(String, Integer)]
91
- #
92
- def parse_endpoint(endpoint)
93
- uri = URI.parse("http://#{endpoint.delete_prefix(TLS_PREFIX)}")
94
- [uri.hostname, uri.port]
95
- end
96
- end
97
-
98
- # A bound TLS listener.
99
- #
100
- class Listener
101
- # @return [String] resolved endpoint with actual port
102
- attr_reader :endpoint
103
-
104
- # @return [Integer] bound port
105
- attr_reader :port
106
-
107
- # @return [Array<TCPServer>] bound server sockets
108
- attr_reader :servers
109
-
110
- # @return [OpenSSL::SSL::SSLContext]
111
- attr_reader :ssl_context
112
-
113
-
114
- # @param endpoint [String] resolved endpoint URI
115
- # @param servers [Array<TCPServer>]
116
- # @param port [Integer] bound port number
117
- # @param ssl_context [OpenSSL::SSL::SSLContext]
118
- #
119
- def initialize(endpoint, servers, port, ssl_context)
120
- @endpoint = endpoint
121
- @servers = servers
122
- @port = port
123
- @ssl_context = ssl_context
124
- @tasks = []
125
- end
126
-
127
-
128
- # Registers accept loop tasks owned by the engine.
129
- #
130
- # @param tasks [Array<Async::Task>]
131
- #
132
- def accept_tasks=(tasks)
133
- @tasks = tasks
134
- end
135
-
136
-
137
- # Stops the listener.
138
- #
139
- def stop
140
- @tasks.each(&:stop)
141
- @servers.each { |s| s.close rescue nil }
142
- end
143
- end
144
- end
145
- end
146
- end