zzq 0.1.0 → 0.2.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/CHANGELOG.md +35 -0
- data/LICENSE-APACHE +202 -0
- data/LICENSE-ISC +15 -0
- data/README.md +67 -122
- data/ext/zzq_native/Cargo.toml +35 -0
- data/ext/zzq_native/extconf.rb +8 -0
- data/ext/zzq_native/src/broker.rs +163 -0
- data/ext/zzq_native/src/client.rs +294 -0
- data/ext/zzq_native/src/context.rs +51 -0
- data/ext/zzq_native/src/error.rs +117 -0
- data/ext/zzq_native/src/lib.rs +52 -0
- data/ext/zzq_native/src/op.rs +140 -0
- data/ext/zzq_native/src/options.rs +168 -0
- data/ext/zzq_native/src/rb.rs +424 -0
- data/ext/zzq_native/src/wake.rs +155 -0
- data/lib/zzq/broker.rb +90 -21
- data/lib/zzq/client.rb +202 -35
- data/lib/zzq/errors.rb +37 -0
- data/lib/zzq/message.rb +16 -107
- data/lib/zzq/options.rb +27 -86
- data/lib/zzq/version.rb +2 -1
- data/lib/zzq/waiting.rb +65 -0
- data/lib/zzq.rb +22 -19
- metadata +33 -77
- data/LICENSE +0 -13
- data/lib/zzq/engine/connection_lifecycle.rb +0 -139
- data/lib/zzq/engine/keepalive.rb +0 -75
- data/lib/zzq/engine/socket_lifecycle.rb +0 -79
- data/lib/zzq/engine.rb +0 -163
- data/lib/zzq/error.rb +0 -42
- data/lib/zzq/monitor_event.rb +0 -14
- data/lib/zzq/persistence/interface.rb +0 -43
- data/lib/zzq/persistence/memory.rb +0 -37
- data/lib/zzq/persistence/pstore.rb +0 -66
- data/lib/zzq/routing/broker.rb +0 -380
- data/lib/zzq/routing/client.rb +0 -327
- data/lib/zzq/routing/packet_id_allocator.rb +0 -54
- data/lib/zzq/routing/qos_tracker.rb +0 -108
- data/lib/zzq/routing/retained_store.rb +0 -67
- data/lib/zzq/routing/topic_match.rb +0 -49
- data/lib/zzq/routing/topic_trie.rb +0 -232
- data/lib/zzq/socket.rb +0 -146
- data/lib/zzq/subscription.rb +0 -70
- data/lib/zzq/transport/ipc.rb +0 -92
- data/lib/zzq/transport/mqtt.rb +0 -83
- data/lib/zzq/transport/mqtts.rb +0 -173
data/lib/zzq/transport/mqtts.rb
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "socket"
|
|
4
|
-
require "openssl"
|
|
5
|
-
require "uri"
|
|
6
|
-
require "io/stream"
|
|
7
|
-
require_relative "../engine"
|
|
8
|
-
|
|
9
|
-
module ZZQ
|
|
10
|
-
module Transport
|
|
11
|
-
# MQTT-over-TLS transport — endpoint scheme `mqtts://host:port`
|
|
12
|
-
# (default port 8883).
|
|
13
|
-
#
|
|
14
|
-
# Both bind and connect accept a `tls_context:` option — any
|
|
15
|
-
# +OpenSSL::SSL::SSLContext+. On bind the context must carry a
|
|
16
|
-
# cert + key; on connect a default client context is used if
|
|
17
|
-
# none is given. The transport does not constrain the context;
|
|
18
|
-
# see the README for a recommended TLS 1.3 +
|
|
19
|
-
# `TLS_CHACHA20_POLY1305_SHA256` configuration.
|
|
20
|
-
#
|
|
21
|
-
# Per-connection application data is capped at
|
|
22
|
-
# {DATA_LIMIT} bytes, after which the socket is torn down and
|
|
23
|
-
# the peer must reconnect. This keeps AEAD nonce counters well
|
|
24
|
-
# below their unsafe bound (e.g. ChaCha20-Poly1305's ~256 GiB
|
|
25
|
-
# per-key integrity bound) without relying on a specific cipher
|
|
26
|
-
# choice.
|
|
27
|
-
module MQTTS
|
|
28
|
-
Engine.transports["mqtts"] = self
|
|
29
|
-
|
|
30
|
-
# 128 GiB — conservative for every AEAD in TLS 1.3 (ChaCha20-
|
|
31
|
-
# Poly1305 at ~256 GiB, AES-GCM limits are higher under TLS 1.3
|
|
32
|
-
# record sizes). Reconnection negotiates fresh keys, so this
|
|
33
|
-
# caps nonce reuse exposure regardless of cipher choice.
|
|
34
|
-
DATA_LIMIT = 128 * 1024 * 1024 * 1024
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class << self
|
|
38
|
-
def bind(endpoint, engine, tls_context: nil, **)
|
|
39
|
-
raise Error, "mqtts:// bind requires tls_context:" unless tls_context
|
|
40
|
-
|
|
41
|
-
host, port = parse_endpoint(endpoint)
|
|
42
|
-
host = "0.0.0.0" if host == "*"
|
|
43
|
-
server = TCPServer.new(host, port)
|
|
44
|
-
actual = server.local_address.ip_port
|
|
45
|
-
host_part = host.include?(":") ? "[#{host}]" : host
|
|
46
|
-
Listener.new("mqtts://#{host_part}:#{actual}", server, tls_context, engine)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def connect(endpoint, engine, tls_context: nil, **)
|
|
51
|
-
host, port = parse_endpoint(endpoint)
|
|
52
|
-
ctx = tls_context || OpenSSL::SSL::SSLContext.new
|
|
53
|
-
tcp = ::Socket.tcp(host, port, connect_timeout: connect_timeout(engine.options))
|
|
54
|
-
ssl = OpenSSL::SSL::SSLSocket.new(tcp, ctx)
|
|
55
|
-
ssl.hostname = host # SNI
|
|
56
|
-
ssl.sync_close = true
|
|
57
|
-
ssl.connect
|
|
58
|
-
engine.handle_connected(LimitedStream.wrap(ssl), endpoint: endpoint)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def connect_timeout(options)
|
|
63
|
-
ri = options.reconnect_interval
|
|
64
|
-
ri = ri.end if ri.is_a?(Range)
|
|
65
|
-
[ri, 0.5].max
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def parse_endpoint(endpoint)
|
|
70
|
-
uri = URI.parse(endpoint.sub("mqtts://", "tcp://"))
|
|
71
|
-
[uri.hostname, uri.port]
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class Listener
|
|
77
|
-
attr_reader :endpoint
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def initialize(endpoint, server, tls_context, engine)
|
|
81
|
-
@endpoint = endpoint
|
|
82
|
-
@server = server
|
|
83
|
-
@ctx = tls_context
|
|
84
|
-
@engine = engine
|
|
85
|
-
@task = nil
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def start_accept_loop(parent_task, &on_accepted)
|
|
90
|
-
@task = parent_task.async(annotation: "zzq mqtts accept #{@endpoint}") do |task|
|
|
91
|
-
loop do
|
|
92
|
-
client = @server.accept
|
|
93
|
-
# TLS handshake per-connection, off the accept loop, so
|
|
94
|
-
# one slow handshake can't block the next client.
|
|
95
|
-
task.async(annotation: "zzq mqtts handshake") do
|
|
96
|
-
ssl = OpenSSL::SSL::SSLSocket.new(client, @ctx)
|
|
97
|
-
ssl.sync_close = true
|
|
98
|
-
ssl.accept
|
|
99
|
-
on_accepted.call(LimitedStream.wrap(ssl))
|
|
100
|
-
rescue Async::Stop, IOError, OpenSSL::SSL::SSLError
|
|
101
|
-
ssl&.close rescue nil
|
|
102
|
-
client.close rescue nil
|
|
103
|
-
end
|
|
104
|
-
rescue Async::Stop, IOError
|
|
105
|
-
break
|
|
106
|
-
end
|
|
107
|
-
ensure
|
|
108
|
-
@server.close rescue nil
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def stop
|
|
114
|
-
@task&.stop
|
|
115
|
-
@server.close rescue nil
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# Buffered stream with a per-connection byte cap. When the cap
|
|
121
|
-
# is reached on either direction the underlying socket is
|
|
122
|
-
# closed and the next I/O raises — forcing the peer to
|
|
123
|
-
# reconnect and negotiate fresh keys before ChaCha20-Poly1305's
|
|
124
|
-
# nonce counter approaches its unsafe range.
|
|
125
|
-
class LimitedStream < IO::Stream::Buffered
|
|
126
|
-
def self.wrap(io, limit: DATA_LIMIT)
|
|
127
|
-
if io.respond_to?(:buffered=)
|
|
128
|
-
io.buffered = false
|
|
129
|
-
elsif io.respond_to?(:sync=)
|
|
130
|
-
io.sync = true
|
|
131
|
-
end
|
|
132
|
-
new(io, limit)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def initialize(io, limit = DATA_LIMIT)
|
|
137
|
-
super(io)
|
|
138
|
-
@limit = limit
|
|
139
|
-
@bytes = 0
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
protected
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def syswrite(buffer)
|
|
147
|
-
enforce!(buffer.bytesize)
|
|
148
|
-
super
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def sysread(size, buffer)
|
|
153
|
-
result = super
|
|
154
|
-
enforce!(result.bytesize) if result.is_a?(String)
|
|
155
|
-
result
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
private
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def enforce!(n)
|
|
163
|
-
@bytes += n
|
|
164
|
-
return if @bytes < @limit
|
|
165
|
-
|
|
166
|
-
close rescue nil
|
|
167
|
-
raise ZZQ::Error,
|
|
168
|
-
"mqtts:// data limit reached (#{@limit} B): dropping connection to force fresh keys"
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
end
|