paho-mqtt 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,167 @@
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 'socket'
16
+
17
+ module PahoMqtt
18
+ class ConnectionHelper
19
+
20
+ attr_accessor :sender
21
+
22
+ def initialize(host, port, ssl, ssl_context, ack_timeout)
23
+ @cs = MQTT_CS_DISCONNECT
24
+ @socket = nil
25
+ @host = host
26
+ @port = port
27
+ @ssl = ssl
28
+ @ssl_context = ssl_context
29
+ @ack_timeout = ack_timeout
30
+ @sender = Sender.new(ack_timeout)
31
+ end
32
+
33
+ def handler=(handler)
34
+ @handler = handler
35
+ end
36
+
37
+ def do_connect(reconnection=false)
38
+ @handler.socket = @socket
39
+ # Waiting a Connack packet for "ack_timeout" second from the remote
40
+ connect_timeout = Time.now + @ack_timeout
41
+ while (Time.now <= connect_timeout) && (!is_connected?) do
42
+ @cs = @handler.receive_packet
43
+ sleep 0.0001
44
+ end
45
+ unless is_connected?
46
+ PahoMqtt.logger.warn("Connection failed. Couldn't recieve a Connack packet from: #{@host}, socket is \"#{@socket}\".") if PahoMqtt.logger?
47
+ raise Exception.new("Connection failed. Check log for more details.") unless reconnection
48
+ end
49
+ @cs
50
+ end
51
+
52
+ def is_connected?
53
+ @cs == MQTT_CS_CONNECTED
54
+ end
55
+
56
+ def do_disconnect(publisher, explicit, mqtt_thread)
57
+ PahoMqtt.logger.debug("Disconnecting from #{@host}") if PahoMqtt.logger?
58
+ if explicit
59
+ explicit_disconnect(publisher, mqtt_thread)
60
+ end
61
+ @socket.close unless @socket.nil? || @socket.closed?
62
+ @socket = nil
63
+ end
64
+
65
+ def explicit_disconnect(publisher, mqtt_thread)
66
+ @sender.flush_waiting_packet
67
+ send_disconnect
68
+ mqtt_thread.kill if mqtt_thread && mqtt_thread.alive?
69
+ publisher.flush_publisher unless publisher.nil?
70
+ end
71
+
72
+ def setup_connection
73
+ clean_start(@host, @port)
74
+ config_socket
75
+ unless @socket.nil?
76
+ @sender.socket = @socket
77
+ end
78
+ end
79
+
80
+ def config_socket
81
+ PahoMqtt.logger.debug("Atempt to connect to host: #{@host}") if PahoMqtt.logger?
82
+ begin
83
+ tcp_socket = TCPSocket.new(@host, @port)
84
+ rescue StandardError
85
+ PahoMqtt.logger.warn("Could not open a socket with #{@host} on port #{@port}") if PahoMqtt.logger?
86
+ end
87
+ if @ssl
88
+ encrypted_socket(tcp_socket, @ssl_context)
89
+ else
90
+ @socket = tcp_socket
91
+ end
92
+ end
93
+
94
+ def encrypted_socket(tcp_socket, ssl_context)
95
+ unless ssl_context.nil?
96
+ @socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
97
+ @socket.sync_close = true
98
+ @socket.connect
99
+ else
100
+ PahoMqtt.logger.error("The ssl context was found as nil while the socket's opening.") if PahoMqtt.logger?
101
+ raise Exception
102
+ end
103
+ end
104
+
105
+ def clean_start(host, port)
106
+ self.host = host
107
+ self.port = port
108
+ unless @socket.nil?
109
+ @socket.close unless @socket.closed?
110
+ @socket = nil
111
+ end
112
+ end
113
+
114
+ def host=(host)
115
+ if host.nil? || host == ""
116
+ PahoMqtt.logger.error("The host was found as nil while the connection setup.") if PahoMqtt.logger?
117
+ raise ArgumentError
118
+ else
119
+ @host = host
120
+ end
121
+ end
122
+
123
+ def port=(port)
124
+ if port.to_i <= 0
125
+ PahoMqtt.logger.error("The port value is invalid (<= 0). Could not setup the connection.") if PahoMqtt.logger?
126
+ raise ArgumentError
127
+ else
128
+ @port = port
129
+ end
130
+ end
131
+
132
+ def send_connect(session_params)
133
+ setup_connection
134
+ packet = PahoMqtt::Packet::Connect.new(session_params)
135
+ @handler.clean_session = session_params[:clean_session]
136
+ @sender.send_packet(packet)
137
+ MQTT_ERR_SUCCESS
138
+ end
139
+
140
+ def send_disconnect
141
+ packet = PahoMqtt::Packet::Disconnect.new
142
+ @sender.send_packet(packet)
143
+ MQTT_ERR_SUCCESS
144
+ end
145
+
146
+ def send_pingreq
147
+ packet = PahoMqtt::Packet::Pingreq.new
148
+ @sender.send_packet(packet)
149
+ MQTT_ERR_SUCCESS
150
+ end
151
+
152
+ def check_keep_alive(persistent, last_ping_resp, keep_alive)
153
+ now = Time.now
154
+ timeout_req = (@sender.last_ping_req + (keep_alive * 0.7).ceil)
155
+ if timeout_req <= now && persistent
156
+ PahoMqtt.logger.debug("Checking if server is still alive.") if PahoMqtt.logger?
157
+ send_pingreq
158
+ end
159
+ timeout_resp = last_ping_resp + (keep_alive * 1.1).ceil
160
+ if timeout_resp <= now
161
+ PahoMqtt.logger.debug("No activity period over timeout, disconnecting from #{@host}") if PahoMqtt.logger?
162
+ @cs = MQTT_CS_DISCONNECT
163
+ end
164
+ @cs
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,271 @@
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 Handler
17
+
18
+ attr_reader :registered_callback
19
+ attr_accessor :last_ping_resp
20
+ attr_accessor :clean_session
21
+
22
+ def initialize
23
+ @registered_callback = []
24
+ @last_ping_resp = -1
25
+ @publisher = nil
26
+ @subscriber = nil
27
+ end
28
+
29
+ def config_pubsub(publisher, subscriber)
30
+ @publisher = publisher
31
+ @subscriber = subscriber
32
+ end
33
+
34
+ def socket=(socket)
35
+ @socket = socket
36
+ end
37
+
38
+ def receive_packet
39
+ result = IO.select([@socket], [], [], SELECT_TIMEOUT) unless @socket.nil? || @socket.closed?
40
+ unless result.nil?
41
+ packet = PahoMqtt::Packet::Base.read(@socket)
42
+ unless packet.nil?
43
+ if packet.is_a?(PahoMqtt::Packet::Connack)
44
+ @last_ping_resp = Time.now
45
+ handle_connack(packet)
46
+ else
47
+ handle_packet(packet)
48
+ @last_ping_resp = Time.now
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def handle_packet(packet)
55
+ PahoMqtt.logger.info("New packet #{packet.class} recieved.") if PahoMqtt.logger?
56
+ type = packet_type(packet)
57
+ self.send("handle_#{type}", packet)
58
+ end
59
+
60
+ def register_topic_callback(topic, callback, &block)
61
+ if topic.nil?
62
+ PahoMqtt.logger.error("The topics where the callback is trying to be registered have been found nil.") if PahoMqtt.logger?
63
+ raise ArgumentError
64
+ end
65
+ clear_topic_callback(topic)
66
+ if block_given?
67
+ @registered_callback.push([topic, block])
68
+ elsif !(callback.nil?) && callback.is_a?(Proc)
69
+ @registered_callback.push([topic, callback])
70
+ end
71
+ MQTT_ERR_SUCCESS
72
+ end
73
+
74
+ def clear_topic_callback(topic)
75
+ if topic.nil?
76
+ PahoMqtt.logger.error("The topics where the callback is trying to be unregistered have been found nil.") if PahoMqtt.logger?
77
+ raise ArgumentError
78
+ end
79
+ @registered_callback.delete_if {|pair| pair.first == topic}
80
+ MQTT_ERR_SUCCESS
81
+ end
82
+
83
+ def handle_connack(packet)
84
+ if packet.return_code == 0x00
85
+ PahoMqtt.logger.debug("Connack receive and connection accepted.") if PahoMqtt.logger?
86
+ handle_connack_accepted(packet.session_present)
87
+ else
88
+ handle_connack_error(packet.return_code)
89
+ end
90
+ @on_connack.call(packet) unless @on_connack.nil?
91
+ MQTT_CS_CONNECTED
92
+ end
93
+
94
+ def handle_connack_accepted(session_flag)
95
+ clean_session?(session_flag)
96
+ new_session?(session_flag)
97
+ old_session?(session_flag)
98
+ end
99
+
100
+ def new_session?(session_flag)
101
+ if !@clean_session && !session_flag
102
+ PahoMqtt.logger.debug("New session created for the client") if PahoMqtt.logger?
103
+ end
104
+ end
105
+
106
+ def clean_session?(session_flag)
107
+ if @clean_session && !session_flag
108
+ PahoMqtt.logger.debug("No previous session found by server, starting a new one.") if PahoMqtt.logger?
109
+ end
110
+ end
111
+
112
+ def old_session?(session_flag)
113
+ if !@clean_session && session_flag
114
+ PahoMqtt.logger.debug("Previous session restored by the server.") if PahoMqtt.logger?
115
+ end
116
+ end
117
+
118
+ def handle_pingresp(_packet)
119
+ @last_ping_resp = Time.now
120
+ end
121
+
122
+ def handle_suback(packet)
123
+ max_qos = packet.return_codes
124
+ id = packet.id
125
+ topics = []
126
+ if @subscriber.add_subscription(max_qos, id, topics) == MQTT_ERR_SUCCESS
127
+ @on_suback.call(topics) unless @on_suback.nil?
128
+ end
129
+ end
130
+
131
+ def handle_unsuback(packet)
132
+ id = packet.id
133
+ topics = []
134
+ if @subscriber.remove_subscription(id, topics) == MQTT_ERR_SUCCESS
135
+ @on_unsuback.call(topics) unless @on_unsuback.nil?
136
+ end
137
+ end
138
+
139
+ def handle_publish(packet)
140
+ id = packet.id
141
+ qos = packet.qos
142
+ if @publisher.do_publish(qos, id) == MQTT_ERR_SUCCESS
143
+ @on_message.call(packet) unless @on_message.nil?
144
+ @registered_callback.assoc(packet.topic).last.call(packet) if @registered_callback.any? { |pair| pair.first == packet.topic}
145
+ end
146
+ end
147
+
148
+ def handle_puback(packet)
149
+ id = packet.id
150
+ if @publisher.do_puback(id) == MQTT_ERR_SUCCESS
151
+ @on_puback.call(packet) unless @on_puback.nil?
152
+ end
153
+ end
154
+
155
+ def handle_pubrec(packet)
156
+ id = packet.id
157
+ if @publisher.do_pubrec(id) == MQTT_ERR_SUCCESS
158
+ @on_pubrec.call(packet) unless @on_pubrec.nil?
159
+ end
160
+ end
161
+
162
+ def handle_pubrel(packet)
163
+ id = packet.id
164
+ if @publisher.do_pubrel(id) == MQTT_ERR_SUCCESS
165
+ @on_pubrel.call(packet) unless @on_pubrel.nil?
166
+ end
167
+ end
168
+
169
+ def handle_pubcomp(packet)
170
+ id = packet.id
171
+ if @publisher.do_pubcomp(id) == MQTT_ERR_SUCCESS
172
+ @on_pubcomp.call(packet) unless @on_pubcomp.nil?
173
+ end
174
+ end
175
+
176
+ def handle_connack_error(return_code)
177
+ if return_code == 0x01
178
+ raise LowVersionException
179
+ elsif CONNACK_ERROR_MESSAGE.has_key(return_code.to_sym)
180
+ PahoMqtt.logger.warm(CONNACK_ERRO_MESSAGE[return_code])
181
+ MQTT_CS_DISCONNECTED
182
+ else
183
+ PahoMqtt.logger("Unknown return code for CONNACK packet: #{return_code}")
184
+ raise PacketException
185
+ end
186
+ end
187
+
188
+ def on_connack(&block)
189
+ @on_connack = block if block_given?
190
+ @on_connack
191
+ end
192
+
193
+ def on_suback(&block)
194
+ @on_suback = block if block_given?
195
+ @on_suback
196
+ end
197
+
198
+ def on_unsuback(&block)
199
+ @on_unsuback = block if block_given?
200
+ @on_unsuback
201
+ end
202
+
203
+ def on_puback(&block)
204
+ @on_puback = block if block_given?
205
+ @on_puback
206
+ end
207
+
208
+ def on_pubrec(&block)
209
+ @on_pubrec = block if block_given?
210
+ @on_pubrec
211
+ end
212
+
213
+ def on_pubrel(&block)
214
+ @on_pubrel = block if block_given?
215
+ @on_pubrel
216
+ end
217
+
218
+ def on_pubcomp(&block)
219
+ @on_pubcomp = block if block_given?
220
+ @on_pubcomp
221
+ end
222
+
223
+ def on_message(&block)
224
+ @on_message = block if block_given?
225
+ @on_message
226
+ end
227
+
228
+ def on_connack=(callback)
229
+ @on_connack = callback if callback.is_a?(Proc)
230
+ end
231
+
232
+ def on_suback=(callback)
233
+ @on_suback = callback if callback.is_a?(Proc)
234
+ end
235
+
236
+ def on_unsuback=(callback)
237
+ @on_unsuback = callback if callback.is_a?(Proc)
238
+ end
239
+
240
+ def on_puback=(callback)
241
+ @on_puback = callback if callback.is_a?(Proc)
242
+ end
243
+
244
+ def on_pubrec=(callback)
245
+ @on_pubrec = callback if callback.is_a?(Proc)
246
+ end
247
+
248
+ def on_pubrel=(callback)
249
+ @on_pubrel = callback if callback.is_a?(Proc)
250
+ end
251
+
252
+ def on_pubcomp=(callback)
253
+ @on_pubcomp = callback if callback.is_a?(Proc)
254
+ end
255
+
256
+ def on_message=(callback)
257
+ @on_message = callback if callback.is_a?(Proc)
258
+ end
259
+
260
+ def packet_type(packet)
261
+ type = packet.class
262
+ if PahoMqtt::PACKET_TYPES[3..13].include?(type)
263
+ type.to_s.split('::').last.downcase
264
+ else
265
+ puts "Packet: #{packet.inspect}"
266
+ PahoMqtt.logger.error("Received an unexpeceted packet: #{packet}") if PahoMqtt.logger?
267
+ raise PacketException
268
+ end
269
+ end
270
+ end
271
+ end
@@ -1,4 +1,18 @@
1
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
+
2
16
  require "paho_mqtt/packet/base"
3
17
  require "paho_mqtt/packet/connect"
4
18
  require "paho_mqtt/packet/connack"