apiotics-paho-mqtt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/README.md +322 -0
- data/Rakefile +6 -0
- data/apiotics-paho-mqtt.gemspec +33 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/paho-mqtt.rb +165 -0
- data/lib/paho_mqtt/client.rb +417 -0
- data/lib/paho_mqtt/connection_helper.rb +169 -0
- data/lib/paho_mqtt/exception.rb +43 -0
- data/lib/paho_mqtt/handler.rb +273 -0
- data/lib/paho_mqtt/packet/base.rb +315 -0
- data/lib/paho_mqtt/packet/connack.rb +102 -0
- data/lib/paho_mqtt/packet/connect.rb +183 -0
- data/lib/paho_mqtt/packet/disconnect.rb +38 -0
- data/lib/paho_mqtt/packet/pingreq.rb +29 -0
- data/lib/paho_mqtt/packet/pingresp.rb +38 -0
- data/lib/paho_mqtt/packet/puback.rb +44 -0
- data/lib/paho_mqtt/packet/pubcomp.rb +44 -0
- data/lib/paho_mqtt/packet/publish.rb +148 -0
- data/lib/paho_mqtt/packet/pubrec.rb +44 -0
- data/lib/paho_mqtt/packet/pubrel.rb +62 -0
- data/lib/paho_mqtt/packet/suback.rb +75 -0
- data/lib/paho_mqtt/packet/subscribe.rb +124 -0
- data/lib/paho_mqtt/packet/unsuback.rb +49 -0
- data/lib/paho_mqtt/packet/unsubscribe.rb +84 -0
- data/lib/paho_mqtt/packet.rb +33 -0
- data/lib/paho_mqtt/publisher.rb +191 -0
- data/lib/paho_mqtt/sender.rb +86 -0
- data/lib/paho_mqtt/ssl_helper.rb +42 -0
- data/lib/paho_mqtt/subscriber.rb +163 -0
- data/lib/paho_mqtt/version.rb +3 -0
- data/samples/client_blocking(reading).rb +30 -0
- data/samples/client_blocking(writing).rb +18 -0
- data/samples/getting_started.rb +49 -0
- data/samples/test_client.rb +70 -0
- metadata +127 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
### original file from the ruby-mqtt gem
|
3
|
+
### located at https://github.com/njh/ruby-mqtt/blob/master/lib/mqtt/packet.rb
|
4
|
+
### Copyright (c) 2009-2013 Nicholas J Humfrey
|
5
|
+
|
6
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
7
|
+
#
|
8
|
+
# All rights reserved. This program and the accompanying materials
|
9
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
10
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
11
|
+
#
|
12
|
+
# The Eclipse Public License is available at
|
13
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
14
|
+
# and the Eclipse Distribution License is available at
|
15
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
16
|
+
#
|
17
|
+
# Contributors:
|
18
|
+
# Pierre Goudet - initial committer
|
19
|
+
|
20
|
+
module PahoMqtt
|
21
|
+
module Packet
|
22
|
+
class Suback < PahoMqtt::Packet::Base
|
23
|
+
# An array of return codes, ordered by the topics that were subscribed to
|
24
|
+
attr_accessor :return_codes
|
25
|
+
|
26
|
+
# Default attribute values
|
27
|
+
ATTR_DEFAULTS = {
|
28
|
+
:return_codes => [],
|
29
|
+
}
|
30
|
+
|
31
|
+
# Create a new Subscribe Acknowledgment packet
|
32
|
+
def initialize(args={})
|
33
|
+
super(ATTR_DEFAULTS.merge(args))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Set the granted QoS value for each of the topics that were subscribed to
|
37
|
+
# Can either be an integer or an array or integers.
|
38
|
+
def return_codes=(value)
|
39
|
+
if value.is_a?(Array)
|
40
|
+
@return_codes = value
|
41
|
+
elsif value.is_a?(Integer)
|
42
|
+
@return_codes = [value]
|
43
|
+
else
|
44
|
+
raise PahoMqtt::PacketFormatException.new(
|
45
|
+
"return_codes should be an integer or an array of return codes")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get serialisation of packet's body
|
50
|
+
def encode_body
|
51
|
+
if @return_codes.empty?
|
52
|
+
raise PahoMqtt::PacketFormatException.new(
|
53
|
+
"No granted QoS given when serialising packet")
|
54
|
+
end
|
55
|
+
body = encode_short(@id)
|
56
|
+
return_codes.each { |qos| body += encode_bytes(qos) }
|
57
|
+
return body
|
58
|
+
end
|
59
|
+
|
60
|
+
# Parse the body (variable header and payload) of a packet
|
61
|
+
def parse_body(buffer)
|
62
|
+
super(buffer)
|
63
|
+
@id = shift_short(buffer)
|
64
|
+
while buffer.bytesize > 0
|
65
|
+
@return_codes << shift_byte(buffer)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns a human readable string, summarising the properties of the packet
|
70
|
+
def inspect
|
71
|
+
"\#<#{self.class}: 0x%2.2X, rc=%s>" % [id, return_codes.map { |rc| "0x%2.2X" % rc }.join(',')]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
### original file from the ruby-mqtt gem
|
3
|
+
### located at https://github.com/njh/ruby-mqtt/blob/master/lib/mqtt/packet.rb
|
4
|
+
### Copyright (c) 2009-2013 Nicholas J Humfrey
|
5
|
+
|
6
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
7
|
+
#
|
8
|
+
# All rights reserved. This program and the accompanying materials
|
9
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
10
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
11
|
+
#
|
12
|
+
# The Eclipse Public License is available at
|
13
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
14
|
+
# and the Eclipse Distribution License is available at
|
15
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
16
|
+
#
|
17
|
+
# Contributors:
|
18
|
+
# Pierre Goudet - initial committer
|
19
|
+
|
20
|
+
module PahoMqtt
|
21
|
+
module Packet
|
22
|
+
class Subscribe < PahoMqtt::Packet::Base
|
23
|
+
# One or more topic filters to subscribe to
|
24
|
+
attr_accessor :topics
|
25
|
+
|
26
|
+
# Default attribute values
|
27
|
+
ATTR_DEFAULTS = {
|
28
|
+
:topics => [],
|
29
|
+
:flags => [false, true, false, false],
|
30
|
+
}
|
31
|
+
|
32
|
+
# Create a new Subscribe packet
|
33
|
+
def initialize(args={})
|
34
|
+
super(ATTR_DEFAULTS.merge(args))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set one or more topic filters for the Subscribe packet
|
38
|
+
# The topics parameter should be one of the following:
|
39
|
+
# * String: subscribe to one topic with QoS 0
|
40
|
+
# * Array: subscribe to multiple topics with QoS 0
|
41
|
+
# * Hash: subscribe to multiple topics where the key is the topic and the value is the QoS level
|
42
|
+
#
|
43
|
+
# For example:
|
44
|
+
# packet.topics = 'a/b'
|
45
|
+
# packet.topics = ['a/b', 'c/d']
|
46
|
+
# packet.topics = [['a/b',0], ['c/d',1]]
|
47
|
+
# packet.topics = {'a/b' => 0, 'c/d' => 1}
|
48
|
+
#
|
49
|
+
def topics=(value)
|
50
|
+
# Get input into a consistent state
|
51
|
+
if value.is_a?(Array)
|
52
|
+
input = value.flatten
|
53
|
+
else
|
54
|
+
input = [value]
|
55
|
+
end
|
56
|
+
|
57
|
+
@topics = []
|
58
|
+
while(input.length>0)
|
59
|
+
item = input.shift
|
60
|
+
if item.is_a?(Hash)
|
61
|
+
# Convert hash into an ordered array of arrays
|
62
|
+
@topics += item.sort
|
63
|
+
elsif item.is_a?(String)
|
64
|
+
# Peek at the next item in the array, and remove it if it is an integer
|
65
|
+
if input.first.is_a?(Integer)
|
66
|
+
qos = input.shift
|
67
|
+
@topics << [item, qos]
|
68
|
+
else
|
69
|
+
@topics << [item, 0]
|
70
|
+
end
|
71
|
+
else
|
72
|
+
# Meh?
|
73
|
+
raise PahoMqtt::PacketFormatException.new(
|
74
|
+
"Invalid topics input: #{value.inspect}")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@topics
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get serialisation of packet's body
|
81
|
+
def encode_body
|
82
|
+
if @topics.empty?
|
83
|
+
raise PahoMqtt::PacketFormatException.new(
|
84
|
+
"No topics given when serialising packet")
|
85
|
+
end
|
86
|
+
body = encode_short(@id)
|
87
|
+
topics.each do |item|
|
88
|
+
body += encode_string(item[0])
|
89
|
+
body += encode_bytes(item[1])
|
90
|
+
end
|
91
|
+
return body
|
92
|
+
end
|
93
|
+
|
94
|
+
# Parse the body (variable header and payload) of a packet
|
95
|
+
def parse_body(buffer)
|
96
|
+
super(buffer)
|
97
|
+
@id = shift_short(buffer)
|
98
|
+
@topics = []
|
99
|
+
while(buffer.bytesize>0)
|
100
|
+
topic_name = shift_string(buffer)
|
101
|
+
topic_qos = shift_byte(buffer)
|
102
|
+
@topics << [topic_name, topic_qos]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Check that fixed header flags are valid for this packet type
|
107
|
+
# @private
|
108
|
+
def validate_flags
|
109
|
+
if @flags != [false, true, false, false]
|
110
|
+
raise PahoMqtt::PacketFormatException.new(
|
111
|
+
"Invalid flags in SUBSCRIBE packet header")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns a human readable string, summarising the properties of the packet
|
116
|
+
def inspect
|
117
|
+
_str = "\#<#{self.class}: 0x%2.2X, %s>" % [
|
118
|
+
id,
|
119
|
+
topics.map { |t| "'#{t[0]}':#{t[1]}" }.join(', ')
|
120
|
+
]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
### original file from the ruby-mqtt gem
|
3
|
+
### located at https://github.com/njh/ruby-mqtt/blob/master/lib/mqtt/packet.rb
|
4
|
+
### Copyright (c) 2009-2013 Nicholas J Humfrey
|
5
|
+
|
6
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
7
|
+
#
|
8
|
+
# All rights reserved. This program and the accompanying materials
|
9
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
10
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
11
|
+
#
|
12
|
+
# The Eclipse Public License is available at
|
13
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
14
|
+
# and the Eclipse Distribution License is available at
|
15
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
16
|
+
#
|
17
|
+
# Contributors:
|
18
|
+
# Pierre Goudet - initial committer
|
19
|
+
|
20
|
+
module PahoMqtt
|
21
|
+
module Packet
|
22
|
+
class Unsuback < PahoMqtt::Packet::Base
|
23
|
+
# Create a new Unsubscribe Acknowledgment packet
|
24
|
+
def initialize(args={})
|
25
|
+
super(args)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get serialisation of packet's body
|
29
|
+
def encode_body
|
30
|
+
encode_short(@id)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Parse the body (variable header and payload) of a packet
|
34
|
+
def parse_body(buffer)
|
35
|
+
super(buffer)
|
36
|
+
@id = shift_short(buffer)
|
37
|
+
unless buffer.empty?
|
38
|
+
raise PahoMqtt::PacketFormatException.new(
|
39
|
+
"Extra bytes at end of Unsubscribe Acknowledgment packet")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a human readable string, summarising the properties of the packet
|
44
|
+
def inspect
|
45
|
+
"\#<#{self.class}: 0x%2.2X>" % id
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
### original file from the ruby-mqtt gem
|
3
|
+
### located at https://github.com/njh/ruby-mqtt/blob/master/lib/mqtt/packet.rb
|
4
|
+
### Copyright (c) 2009-2013 Nicholas J Humfrey
|
5
|
+
|
6
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
7
|
+
#
|
8
|
+
# All rights reserved. This program and the accompanying materials
|
9
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
10
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
11
|
+
#
|
12
|
+
# The Eclipse Public License is available at
|
13
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
14
|
+
# and the Eclipse Distribution License is available at
|
15
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
16
|
+
#
|
17
|
+
# Contributors:
|
18
|
+
# Pierre Goudet - initial committer
|
19
|
+
|
20
|
+
module PahoMqtt
|
21
|
+
module Packet
|
22
|
+
class Unsubscribe < PahoMqtt::Packet::Base
|
23
|
+
# One or more topic paths to unsubscribe from
|
24
|
+
attr_accessor :topics
|
25
|
+
|
26
|
+
# Default attribute values
|
27
|
+
ATTR_DEFAULTS = {
|
28
|
+
:topics => [],
|
29
|
+
:flags => [false, true, false, false],
|
30
|
+
}
|
31
|
+
|
32
|
+
# Create a new Unsubscribe packet
|
33
|
+
def initialize(args={})
|
34
|
+
super(ATTR_DEFAULTS.merge(args))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set one or more topic paths to unsubscribe from
|
38
|
+
def topics=(value)
|
39
|
+
if value.is_a?(Array)
|
40
|
+
@topics = value
|
41
|
+
else
|
42
|
+
@topics = [value]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get serialisation of packet's body
|
47
|
+
def encode_body
|
48
|
+
if @topics.empty?
|
49
|
+
raise PahoMqtt::PacketFormatException.new(
|
50
|
+
"No topics given when serialising packet")
|
51
|
+
end
|
52
|
+
body = encode_short(@id)
|
53
|
+
topics.each { |topic| body += encode_string(topic) }
|
54
|
+
return body
|
55
|
+
end
|
56
|
+
|
57
|
+
# Parse the body (variable header and payload) of a packet
|
58
|
+
def parse_body(buffer)
|
59
|
+
super(buffer)
|
60
|
+
@id = shift_short(buffer)
|
61
|
+
while buffer.bytesize > 0
|
62
|
+
@topics << shift_string(buffer)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check that fixed header flags are valid for this packet type
|
67
|
+
# @private
|
68
|
+
def validate_flags
|
69
|
+
if @flags != [false, true, false, false]
|
70
|
+
raise PahoMqtt::PacketFormatException.new(
|
71
|
+
"Invalid flags in UNSUBSCRIBE packet header")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns a human readable string, summarising the properties of the packet
|
76
|
+
def inspect
|
77
|
+
"\#<#{self.class}: 0x%2.2X, %s>" % [
|
78
|
+
id,
|
79
|
+
topics.map { |t| "'#{t}'" }.join(', ')
|
80
|
+
]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
3
|
+
#
|
4
|
+
# All rights reserved. This program and the accompanying materials
|
5
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
6
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
7
|
+
#
|
8
|
+
# The Eclipse Public License is available at
|
9
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
10
|
+
# and the Eclipse Distribution License is available at
|
11
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
12
|
+
#
|
13
|
+
# Contributors:
|
14
|
+
# Pierre Goudet - initial committer
|
15
|
+
|
16
|
+
require "paho_mqtt/packet/base"
|
17
|
+
require "paho_mqtt/packet/connect"
|
18
|
+
require "paho_mqtt/packet/connack"
|
19
|
+
require "paho_mqtt/packet/publish"
|
20
|
+
require "paho_mqtt/packet/puback"
|
21
|
+
require "paho_mqtt/packet/pubrec"
|
22
|
+
require "paho_mqtt/packet/pubrel"
|
23
|
+
require "paho_mqtt/packet/pubcomp"
|
24
|
+
require "paho_mqtt/packet/subscribe"
|
25
|
+
require "paho_mqtt/packet/suback"
|
26
|
+
require "paho_mqtt/packet/unsubscribe"
|
27
|
+
require "paho_mqtt/packet/unsuback"
|
28
|
+
require "paho_mqtt/packet/pingreq"
|
29
|
+
require "paho_mqtt/packet/pingresp"
|
30
|
+
require "paho_mqtt/packet/disconnect"
|
31
|
+
|
32
|
+
module PahoMqtt
|
33
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
2
|
+
#
|
3
|
+
# All rights reserved. This program and the accompanying materials
|
4
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
5
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
6
|
+
#
|
7
|
+
# The Eclipse Public License is available at
|
8
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
9
|
+
# and the Eclipse Distribution License is available at
|
10
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
11
|
+
#
|
12
|
+
# Contributors:
|
13
|
+
# Pierre Goudet - initial committer
|
14
|
+
|
15
|
+
module PahoMqtt
|
16
|
+
class Publisher
|
17
|
+
|
18
|
+
def initialize(sender)
|
19
|
+
@waiting_puback = []
|
20
|
+
@waiting_pubrec = []
|
21
|
+
@waiting_pubrel = []
|
22
|
+
@waiting_pubcomp = []
|
23
|
+
@puback_mutex = Mutex.new
|
24
|
+
@pubrec_mutex = Mutex.new
|
25
|
+
@pubrel_mutex = Mutex.new
|
26
|
+
@pubcomp_mutex = Mutex.new
|
27
|
+
@sender = sender
|
28
|
+
end
|
29
|
+
|
30
|
+
def sender=(sender)
|
31
|
+
@sender = sender
|
32
|
+
end
|
33
|
+
|
34
|
+
def send_publish(topic, payload, retain, qos, new_id)
|
35
|
+
packet = PahoMqtt::Packet::Publish.new(
|
36
|
+
:id => new_id,
|
37
|
+
:topic => topic,
|
38
|
+
:payload => payload,
|
39
|
+
:retain => retain,
|
40
|
+
:qos => qos
|
41
|
+
)
|
42
|
+
case qos
|
43
|
+
when 1
|
44
|
+
@puback_mutex.synchronize do
|
45
|
+
if @waiting_puback.length >= MAX_PUBACK
|
46
|
+
PahoMqtt.logger.error('PUBACK queue is full, could not send with qos=1') if PahoMqtt.logger?
|
47
|
+
return MQTT_ERR_FAIL
|
48
|
+
end
|
49
|
+
@waiting_puback.push(:id => new_id, :packet => packet, :timestamp => Time.now)
|
50
|
+
end
|
51
|
+
when 2
|
52
|
+
@pubrec_mutex.synchronize do
|
53
|
+
if @waiting_pubrec.length >= MAX_PUBREC
|
54
|
+
PahoMqtt.logger.error('PUBREC queue is full, could not send with qos=2') if PahoMqtt.logger?
|
55
|
+
return MQTT_ERR_FAIL
|
56
|
+
end
|
57
|
+
@waiting_pubrec.push(:id => new_id, :packet => packet, :timestamp => Time.now)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@sender.append_to_writing(packet)
|
61
|
+
MQTT_ERR_SUCCESS
|
62
|
+
end
|
63
|
+
|
64
|
+
def do_publish(qos, packet_id)
|
65
|
+
case qos
|
66
|
+
when 0
|
67
|
+
when 1
|
68
|
+
send_puback(packet_id)
|
69
|
+
when 2
|
70
|
+
send_pubrec(packet_id)
|
71
|
+
else
|
72
|
+
PahoMqtt.logger.error("The packet QoS value is invalid in publish.") if PahoMqtt.logger?
|
73
|
+
raise PacketException.new('Invalid publish QoS value')
|
74
|
+
end
|
75
|
+
MQTT_ERR_SUCCESS
|
76
|
+
end
|
77
|
+
|
78
|
+
def send_puback(packet_id)
|
79
|
+
packet = PahoMqtt::Packet::Puback.new(
|
80
|
+
:id => packet_id
|
81
|
+
)
|
82
|
+
@sender.append_to_writing(packet)
|
83
|
+
MQTT_ERR_SUCCESS
|
84
|
+
end
|
85
|
+
|
86
|
+
def do_puback(packet_id)
|
87
|
+
@puback_mutex.synchronize do
|
88
|
+
@waiting_puback.delete_if { |pck| pck[:id] == packet_id }
|
89
|
+
end
|
90
|
+
MQTT_ERR_SUCCESS
|
91
|
+
end
|
92
|
+
|
93
|
+
def send_pubrec(packet_id)
|
94
|
+
packet = PahoMqtt::Packet::Pubrec.new(
|
95
|
+
:id => packet_id
|
96
|
+
)
|
97
|
+
@pubrel_mutex.synchronize do
|
98
|
+
if @waiting_pubrel.length >= MAX_PUBREL
|
99
|
+
PahoMqtt.logger.error('PUBREL queue is full, could not acknowledge qos=2') if PahoMqtt.logger?
|
100
|
+
return MQTT_ERR_FAIL
|
101
|
+
end
|
102
|
+
@waiting_pubrel.push(:id => packet_id , :packet => packet, :timestamp => Time.now)
|
103
|
+
end
|
104
|
+
@sender.append_to_writing(packet)
|
105
|
+
MQTT_ERR_SUCCESS
|
106
|
+
end
|
107
|
+
|
108
|
+
def do_pubrec(packet_id)
|
109
|
+
@pubrec_mutex.synchronize do
|
110
|
+
@waiting_pubrec.delete_if { |pck| pck[:id] == packet_id }
|
111
|
+
end
|
112
|
+
send_pubrel(packet_id)
|
113
|
+
MQTT_ERR_SUCCESS
|
114
|
+
end
|
115
|
+
|
116
|
+
def send_pubrel(packet_id)
|
117
|
+
packet = PahoMqtt::Packet::Pubrel.new(
|
118
|
+
:id => packet_id
|
119
|
+
)
|
120
|
+
@pubcomp_mutex.synchronize do
|
121
|
+
if @waiting_pubcomp.length >= MAX_PUBCOMP
|
122
|
+
PahoMqtt.logger.error('PUBCOMP queue is full, could not acknowledge qos=2') if PahoMqtt.logger?
|
123
|
+
return MQTT_ERR_FAIL
|
124
|
+
end
|
125
|
+
@waiting_pubcomp.push(:id => packet_id, :packet => packet, :timestamp => Time.now)
|
126
|
+
end
|
127
|
+
@sender.append_to_writing(packet)
|
128
|
+
MQTT_ERR_SUCCESS
|
129
|
+
end
|
130
|
+
|
131
|
+
def do_pubrel(packet_id)
|
132
|
+
@pubrel_mutex.synchronize do
|
133
|
+
@waiting_pubrel.delete_if { |pck| pck[:id] == packet_id }
|
134
|
+
end
|
135
|
+
send_pubcomp(packet_id)
|
136
|
+
MQTT_ERR_SUCCESS
|
137
|
+
end
|
138
|
+
|
139
|
+
def send_pubcomp(packet_id)
|
140
|
+
packet = PahoMqtt::Packet::Pubcomp.new(
|
141
|
+
:id => packet_id
|
142
|
+
)
|
143
|
+
@sender.append_to_writing(packet)
|
144
|
+
MQTT_ERR_SUCCESS
|
145
|
+
end
|
146
|
+
|
147
|
+
def do_pubcomp(packet_id)
|
148
|
+
@pubcomp_mutex.synchronize do
|
149
|
+
@waiting_pubcomp.delete_if { |pck| pck[:id] == packet_id }
|
150
|
+
end
|
151
|
+
MQTT_ERR_SUCCESS
|
152
|
+
end
|
153
|
+
|
154
|
+
def config_all_message_queue
|
155
|
+
config_message_queue(@waiting_puback, @puback_mutex)
|
156
|
+
config_message_queue(@waiting_pubrec, @pubrec_mutex)
|
157
|
+
config_message_queue(@waiting_pubrel, @pubrel_mutex)
|
158
|
+
config_message_queue(@waiting_pubcomp, @pubcomp_mutex)
|
159
|
+
end
|
160
|
+
|
161
|
+
def config_message_queue(queue, mutex)
|
162
|
+
mutex.synchronize do
|
163
|
+
queue.each do |pck|
|
164
|
+
pck[:timestamp] = Time.now
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def check_waiting_publisher
|
170
|
+
@sender.check_ack_alive(@waiting_puback, @puback_mutex)
|
171
|
+
@sender.check_ack_alive(@waiting_pubrec, @pubrec_mutex)
|
172
|
+
@sender.check_ack_alive(@waiting_pubrel, @pubrel_mutex)
|
173
|
+
@sender.check_ack_alive(@waiting_pubcomp, @pubcomp_mutex)
|
174
|
+
end
|
175
|
+
|
176
|
+
def flush_publisher
|
177
|
+
@puback_mutex.synchronize do
|
178
|
+
@waiting_puback = []
|
179
|
+
end
|
180
|
+
@pubrec_mutex.synchronize do
|
181
|
+
@waiting_pubrec = []
|
182
|
+
end
|
183
|
+
@pubrel_mutex.synchronize do
|
184
|
+
@waiting_pubrel = []
|
185
|
+
end
|
186
|
+
@pubcomp_mutex.synchronize do
|
187
|
+
@waiting_pubcomp = []
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
2
|
+
#
|
3
|
+
# All rights reserved. This program and the accompanying materials
|
4
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
5
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
6
|
+
#
|
7
|
+
# The Eclipse Public License is available at
|
8
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
9
|
+
# and the Eclipse Distribution License is available at
|
10
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
11
|
+
#
|
12
|
+
# Contributors:
|
13
|
+
# Pierre Goudet - initial committer
|
14
|
+
|
15
|
+
module PahoMqtt
|
16
|
+
class Sender
|
17
|
+
|
18
|
+
attr_accessor :last_ping_req
|
19
|
+
|
20
|
+
def initialize(ack_timeout)
|
21
|
+
@socket = nil
|
22
|
+
@writing_queue = []
|
23
|
+
@writing_mutex = Mutex.new
|
24
|
+
@last_ping_req = -1
|
25
|
+
@ack_timeout = ack_timeout
|
26
|
+
end
|
27
|
+
|
28
|
+
def socket=(socket)
|
29
|
+
@socket = socket
|
30
|
+
end
|
31
|
+
|
32
|
+
def send_packet(packet)
|
33
|
+
begin
|
34
|
+
@socket.write(packet.to_s) unless @socket.nil? || @socket.closed?
|
35
|
+
@last_ping_req = Time.now
|
36
|
+
MQTT_ERR_SUCCESS
|
37
|
+
rescue StandardError
|
38
|
+
raise WritingException
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def append_to_writing(packet)
|
43
|
+
@writing_mutex.synchronize do
|
44
|
+
@writing_queue.push(packet) unless @writing_queue.length >= MAX_WRITING
|
45
|
+
end
|
46
|
+
MQTT_ERR_SUCCESS
|
47
|
+
end
|
48
|
+
|
49
|
+
def writing_loop(max_packet)
|
50
|
+
@writing_mutex.synchronize do
|
51
|
+
cnt = 0
|
52
|
+
while !@writing_queue.empty? && cnt < max_packet do
|
53
|
+
packet = @writing_queue.shift
|
54
|
+
send_packet(packet)
|
55
|
+
cnt += 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
MQTT_ERR_SUCCESS
|
59
|
+
end
|
60
|
+
|
61
|
+
def flush_waiting_packet(sending=true)
|
62
|
+
if sending
|
63
|
+
@writing_mutex.synchronize do
|
64
|
+
@writing_queue.each do |m|
|
65
|
+
send_packet(m)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
else
|
69
|
+
@writing_queue = []
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def check_ack_alive(queue, mutex)
|
74
|
+
mutex.synchronize do
|
75
|
+
now = Time.now
|
76
|
+
queue.each do |pck|
|
77
|
+
if now >= pck[:timestamp] + @ack_timeout
|
78
|
+
pck[:packet].dup ||= true unless pck[:packet].class == PahoMqtt::Packet::Subscribe || pck[:packet].class == PahoMqtt::Packet::Unsubscribe
|
79
|
+
append_to_writing(pck[:packet])
|
80
|
+
pck[:timestamp] = now
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
2
|
+
#
|
3
|
+
# All rights reserved. This program and the accompanying materials
|
4
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
5
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
6
|
+
#
|
7
|
+
# The Eclipse Public License is available at
|
8
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
9
|
+
# and the Eclipse Distribution License is available at
|
10
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
11
|
+
#
|
12
|
+
# Contributors:
|
13
|
+
# Pierre Goudet - initial committer
|
14
|
+
|
15
|
+
require 'openssl'
|
16
|
+
|
17
|
+
module PahoMqtt
|
18
|
+
module SSLHelper
|
19
|
+
extend self
|
20
|
+
|
21
|
+
def config_ssl_context(cert_path, key_path, ca_path=nil)
|
22
|
+
ssl_context = OpenSSL::SSL::SSLContext.new
|
23
|
+
set_cert(cert_path, ssl_context)
|
24
|
+
set_key(key_path, ssl_context)
|
25
|
+
set_root_ca(ca_path, ssl_context)
|
26
|
+
#ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER unless ca_path.nil?
|
27
|
+
ssl_context
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_cert(cert_path, ssl_context)
|
31
|
+
ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_key(key_path, ssl_context)
|
35
|
+
ssl_context.key = OpenSSL::PKey::RSA.new(File.read(key_path))
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_root_ca(ca_path, ssl_context)
|
39
|
+
ssl_context.ca_file = ca_path
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|