spacebunny 1.0.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/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +190 -0
- data/Rakefile +6 -0
- data/assets/logo.png +0 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/examples/device/auto_config_publish.rb +87 -0
- data/examples/device/manual_config.rb +83 -0
- data/examples/device/publish_with_confirm.rb +85 -0
- data/examples/device/receive_messages.rb +109 -0
- data/examples/device/tls_connection.rb +65 -0
- data/examples/live_stream/receive_messages.rb +128 -0
- data/examples/live_stream/tls_connection.rb +22 -0
- data/lib/spacebunny.rb +23 -0
- data/lib/spacebunny/device/amqp.rb +158 -0
- data/lib/spacebunny/device/base.rb +229 -0
- data/lib/spacebunny/device/message.rb +56 -0
- data/lib/spacebunny/endpoint_connection.rb +128 -0
- data/lib/spacebunny/exceptions.rb +143 -0
- data/lib/spacebunny/live_stream/amqp.rb +124 -0
- data/lib/spacebunny/live_stream/base.rb +198 -0
- data/lib/spacebunny/live_stream/message.rb +38 -0
- data/lib/spacebunny/logger.rb +35 -0
- data/lib/spacebunny/utils.rb +185 -0
- data/lib/spacebunny/version.rb +3 -0
- data/spacebunny.gemspec +27 -0
- metadata +157 -0
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'http'
|
3
|
+
|
4
|
+
module Spacebunny
|
5
|
+
module Device
|
6
|
+
|
7
|
+
# Proxy to Base.new
|
8
|
+
def self.new(*args)
|
9
|
+
Amqp.new *args
|
10
|
+
end
|
11
|
+
|
12
|
+
class Base
|
13
|
+
attr_accessor :key, :api_endpoint, :auto_recover, :raise_on_error, :id, :name, :host, :secret, :vhost, :channels
|
14
|
+
attr_reader :log_to, :log_level, :logger, :custom_connection_configs, :auto_connection_configs,
|
15
|
+
:connection_configs, :auto_configs, :tls, :tls_cert, :tls_key, :tls_ca_certificates, :verify_peer
|
16
|
+
|
17
|
+
def initialize(protocol, *args)
|
18
|
+
@protocol = protocol
|
19
|
+
@custom_connection_configs = {}
|
20
|
+
@auto_connection_configs = {}
|
21
|
+
options = args.extract_options.deep_symbolize_keys
|
22
|
+
key = args.first
|
23
|
+
|
24
|
+
@key = key || options[:key]
|
25
|
+
@api_endpoint = options[:api_endpoint] || {}
|
26
|
+
|
27
|
+
extract_custom_connection_configs_from options
|
28
|
+
set_channels options[:channels]
|
29
|
+
|
30
|
+
@raise_on_error = options[:raise_on_error]
|
31
|
+
@log_to = options[:log_to] || STDOUT
|
32
|
+
@log_level = options[:log_level] || ::Logger::WARN
|
33
|
+
@logger = options[:logger] || build_logger
|
34
|
+
end
|
35
|
+
|
36
|
+
def api_endpoint=(options)
|
37
|
+
unless options.is_a? Hash
|
38
|
+
raise ArgumentError, 'api_endpoint must be an Hash. See doc for further info'
|
39
|
+
end
|
40
|
+
@api_endpoint = options.deep_symbolize_keys
|
41
|
+
end
|
42
|
+
|
43
|
+
def connection_configs
|
44
|
+
return @connection_configs if @connection_configs
|
45
|
+
if auto_configure?
|
46
|
+
# If key is specified, retrieve configs from APIs endpoint
|
47
|
+
@auto_configs = EndpointConnection.new(@api_endpoint.merge(key: @key)).configs
|
48
|
+
normalize_and_add_channels @auto_configs[:channels]
|
49
|
+
@auto_connection_configs = normalize_auto_connection_configs
|
50
|
+
end
|
51
|
+
# Build final connection_configs
|
52
|
+
@connection_configs = merge_connection_configs
|
53
|
+
# Check for required params presence
|
54
|
+
check_connection_configs
|
55
|
+
@connection_configs
|
56
|
+
end
|
57
|
+
|
58
|
+
def connect
|
59
|
+
logger.warn "connect method must be implemented on class responsibile to handle protocol '#{@protocol}'"
|
60
|
+
end
|
61
|
+
|
62
|
+
def connection_options=(options)
|
63
|
+
unless options.is_a? Hash
|
64
|
+
raise ArgumentError, 'connection_options must be an Hash. See doc for further info'
|
65
|
+
end
|
66
|
+
extract_custom_connection_configs_from options.with_indifferent_access
|
67
|
+
end
|
68
|
+
|
69
|
+
def disconnect
|
70
|
+
@connection_configs = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Stub method: must be implemented on the class responsible to handle the protocol
|
74
|
+
def publish(channel, message, options = {})
|
75
|
+
logger.warn "publish method must be implemented on class responsibile to handle protocol '#{@protocol}'"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Stub method: must be implemented on the class responsible to handle the protocol
|
79
|
+
def on_receive(options = {}, &block)
|
80
|
+
logger.warn "on_receive method must be implemented on class responsibile to handle protocol '#{@protocol}'"
|
81
|
+
end
|
82
|
+
|
83
|
+
def auto_recover
|
84
|
+
connection_configs[:auto_recover]
|
85
|
+
end
|
86
|
+
|
87
|
+
def id
|
88
|
+
connection_configs[:device_id]
|
89
|
+
end
|
90
|
+
|
91
|
+
def name
|
92
|
+
connection_configs[:device_name]
|
93
|
+
end
|
94
|
+
|
95
|
+
def host
|
96
|
+
connection_configs[:host]
|
97
|
+
end
|
98
|
+
|
99
|
+
def secret
|
100
|
+
connection_configs[:secret]
|
101
|
+
end
|
102
|
+
|
103
|
+
def vhost
|
104
|
+
connection_configs[:vhost]
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
# @protected
|
110
|
+
def auto_configure?
|
111
|
+
!@key.nil?
|
112
|
+
end
|
113
|
+
|
114
|
+
def with_channel_check(name)
|
115
|
+
unless res = channels.include?(name)
|
116
|
+
logger.warn <<-MSG
|
117
|
+
|
118
|
+
You're going to publish on channel '#{name}', but it does not appear a configured channel.
|
119
|
+
If using auto-configuration (device-key) associate the channel to device '#{@auto_configs[:connection][:name]}'
|
120
|
+
from web interface.
|
121
|
+
If providing manual configuration, please specify channels list through the :channels option
|
122
|
+
or through given setter, e.g. client.channels = [:first_channel, :second_channel, ... ])
|
123
|
+
|
124
|
+
MSG
|
125
|
+
end
|
126
|
+
if block_given?
|
127
|
+
yield
|
128
|
+
else
|
129
|
+
res
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# @private
|
136
|
+
# Check if channels are an array
|
137
|
+
def set_channels(channels)
|
138
|
+
if channels && !channels.is_a?(Array)
|
139
|
+
raise ChannelsMustBeAnArray
|
140
|
+
end
|
141
|
+
normalize_and_add_channels(channels)
|
142
|
+
end
|
143
|
+
|
144
|
+
# @private
|
145
|
+
# Check for required params presence
|
146
|
+
def check_connection_configs
|
147
|
+
raise DeviceIdMissing unless @connection_configs[:device_id]
|
148
|
+
end
|
149
|
+
|
150
|
+
# @private
|
151
|
+
# Merge auto_connection_configs and custom_connection_configs
|
152
|
+
def merge_connection_configs
|
153
|
+
auto_connection_configs.merge(custom_connection_configs) do |key, old_val, new_val|
|
154
|
+
if new_val.nil?
|
155
|
+
old_val
|
156
|
+
else
|
157
|
+
new_val
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# @private
|
163
|
+
def build_logger
|
164
|
+
logger = ::Logger.new(@log_to)
|
165
|
+
logger.level = normalize_log_level
|
166
|
+
logger.progname = 'Spacebunny'
|
167
|
+
Spacebunny.logger = logger
|
168
|
+
end
|
169
|
+
|
170
|
+
# @private
|
171
|
+
# Copy options to custom_connection_configs and normalize some of the attributes overwriting it
|
172
|
+
def extract_custom_connection_configs_from(options)
|
173
|
+
@custom_connection_configs = options
|
174
|
+
# Auto_recover from connection.close by default
|
175
|
+
@custom_connection_configs[:auto_recover] = @custom_connection_configs.delete(:auto_recover) || true
|
176
|
+
@custom_connection_configs[:host] = @custom_connection_configs.delete :host
|
177
|
+
if @custom_connection_configs[:protocols] && custom_connection_configs[:protocols][@protocol]
|
178
|
+
@custom_connection_configs[:port] = @custom_connection_configs[:protocols][@protocol].delete :port
|
179
|
+
@custom_connection_configs[:tls_port] = @custom_connection_configs[:protocols][@protocol].delete :tls_port
|
180
|
+
end
|
181
|
+
@custom_connection_configs[:vhost] = @custom_connection_configs.delete :vhost
|
182
|
+
@custom_connection_configs[:device_id] = @custom_connection_configs.delete :device_id
|
183
|
+
@custom_connection_configs[:device_name] = @custom_connection_configs.delete :device_name
|
184
|
+
@custom_connection_configs[:secret] = @custom_connection_configs.delete :secret
|
185
|
+
end
|
186
|
+
|
187
|
+
# @private
|
188
|
+
def normalize_and_add_channels(chs)
|
189
|
+
@channels = [] unless @channels
|
190
|
+
return unless chs
|
191
|
+
chs.each do |ch|
|
192
|
+
case ch
|
193
|
+
when Hash
|
194
|
+
@channels << ch[:name].to_sym
|
195
|
+
else
|
196
|
+
ch.to_sym
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# @private
|
202
|
+
# Translate from auto configs given by APIs endpoint to a common format
|
203
|
+
def normalize_auto_connection_configs
|
204
|
+
{
|
205
|
+
host: @auto_configs[:connection][:host],
|
206
|
+
port: @auto_configs[:connection][:protocols][@protocol][:port],
|
207
|
+
tls_port: @auto_configs[:connection][:protocols][@protocol][:tls_port],
|
208
|
+
vhost: @auto_configs[:connection][:vhost],
|
209
|
+
device_id: @auto_configs[:connection][:device_id],
|
210
|
+
device_name: @auto_configs[:connection][:device_name],
|
211
|
+
secret: @auto_configs[:connection][:secret]
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
# @private
|
216
|
+
def normalize_log_level
|
217
|
+
case @log_level
|
218
|
+
when :debug, ::Logger::DEBUG, 'debug' then ::Logger::DEBUG
|
219
|
+
when :info, ::Logger::INFO, 'info' then ::Logger::INFO
|
220
|
+
when :warn, ::Logger::WARN, 'warn' then ::Logger::WARN
|
221
|
+
when :error, ::Logger::ERROR, 'error' then ::Logger::ERROR
|
222
|
+
when :fatal, ::Logger::FATAL, 'fatal' then ::Logger::FATAL
|
223
|
+
else
|
224
|
+
Logger::WARN
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Spacebunny
|
2
|
+
module Device
|
3
|
+
class Message
|
4
|
+
attr_reader :device, :sender_id, :channel_name, :delivery_info, :metadata, :payload
|
5
|
+
|
6
|
+
def initialize(device, options, delivery_info, metadata, payload)
|
7
|
+
@device = device
|
8
|
+
@options = options
|
9
|
+
@delivery_info = delivery_info
|
10
|
+
@metadata = metadata
|
11
|
+
@payload = payload
|
12
|
+
|
13
|
+
extract_options
|
14
|
+
set_sender_id_and_channel
|
15
|
+
end
|
16
|
+
|
17
|
+
def ack(options = {})
|
18
|
+
multiple = options.fetch :multiple, false
|
19
|
+
@device.input_channel.acknowledge @delivery_info.delivery_tag, multiple
|
20
|
+
end
|
21
|
+
|
22
|
+
def nack(options = {})
|
23
|
+
multiple = options.fetch :multiple, false
|
24
|
+
requeue = options.fetch :requeue, false
|
25
|
+
@device.input_channel.nack @delivery_info.delivery_tag, multiple, requeue
|
26
|
+
end
|
27
|
+
|
28
|
+
def blacklisted?
|
29
|
+
# Discard packet if it has been sent from me
|
30
|
+
if @discard_mine && @device.id.eql?(@sender_id) && !from_api?
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
# Discard packet if has been published from APIs
|
34
|
+
if @discard_from_api && from_api?
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def from_api?
|
41
|
+
!@metadata[:headers].nil? && @metadata[:headers]['x-from-sb-api']
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def extract_options
|
47
|
+
@discard_mine = @options.fetch :discard_mine, false
|
48
|
+
@discard_from_api = @options.fetch :discard_from_api, false
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_sender_id_and_channel
|
52
|
+
@sender_id, @channel_name = @delivery_info[:routing_key].split('.')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'uri/http'
|
2
|
+
require 'uri/https'
|
3
|
+
|
4
|
+
# Handle retrieve of Device and LiveStream configs from APIs endpoint
|
5
|
+
|
6
|
+
module Spacebunny
|
7
|
+
class EndpointConnection
|
8
|
+
DEFAULT_OPTIONS = {
|
9
|
+
scheme: 'https',
|
10
|
+
host: 'api.spacebunny.io',
|
11
|
+
port: 443,
|
12
|
+
api_version: '/v1',
|
13
|
+
configs_path: {
|
14
|
+
device: '/device_configurations',
|
15
|
+
live_stream: '/live_stream_key_configurations'
|
16
|
+
}
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
attr_accessor :scheme, :host, :port, :api_version, :configs_path
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
unless options.is_a? Hash
|
23
|
+
fail ArgumentError, 'connection options must be an Hash'
|
24
|
+
end
|
25
|
+
options = merge_with_default options
|
26
|
+
@key = options[:key]
|
27
|
+
@client = options[:client]
|
28
|
+
@secret = options[:secret]
|
29
|
+
|
30
|
+
ensure_credentials_have_been_provided
|
31
|
+
# API endpoint params
|
32
|
+
@scheme = options[:scheme]
|
33
|
+
@host = options[:host]
|
34
|
+
@port = options[:port]
|
35
|
+
@api_version = options[:api_version]
|
36
|
+
@configs_path = options[:configs_path]
|
37
|
+
end
|
38
|
+
|
39
|
+
def configs
|
40
|
+
unless @configs
|
41
|
+
@configs = fetch
|
42
|
+
end
|
43
|
+
@configs
|
44
|
+
end
|
45
|
+
|
46
|
+
def configs_path
|
47
|
+
if @configs_path.is_a? Hash
|
48
|
+
if device?
|
49
|
+
@configs_path[:device]
|
50
|
+
else
|
51
|
+
@configs_path[:live_stream]
|
52
|
+
end
|
53
|
+
else
|
54
|
+
@configs_path
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Contact APIs endpoint to retrieve configs
|
59
|
+
def fetch
|
60
|
+
uri_builder = case scheme
|
61
|
+
when 'http'
|
62
|
+
URI::HTTP
|
63
|
+
when 'https'
|
64
|
+
URI::HTTPS
|
65
|
+
end
|
66
|
+
|
67
|
+
unless uri = uri_builder.build(host: host, port: port, path: "#{api_version}#{configs_path}")
|
68
|
+
raise SchemeNotValid.new(scheme)
|
69
|
+
end
|
70
|
+
|
71
|
+
response = contact_endpoint_with uri
|
72
|
+
content = JSON.parse(response, symbolize_names: true) rescue nil
|
73
|
+
status = response.status
|
74
|
+
if status != 200
|
75
|
+
if content
|
76
|
+
phrase = "Auto-configuration failed: #{response.status} => #{content[:error]}"
|
77
|
+
if status == 401
|
78
|
+
if device?
|
79
|
+
phrase = "#{phrase}. Is Device Key correct?"
|
80
|
+
else
|
81
|
+
phrase = "#{phrase} Are Client and Secret correct?"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
phrase = "#{response.status}"
|
86
|
+
end
|
87
|
+
raise EndpointError, phrase
|
88
|
+
end
|
89
|
+
content
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def ensure_credentials_have_been_provided
|
95
|
+
if !@key && !(@client && @secret)
|
96
|
+
raise DeviceKeyOrClientAndSecretRequired
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def device?
|
101
|
+
!@key.nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
def contact_endpoint_with(uri)
|
105
|
+
if device?
|
106
|
+
request = HTTP.headers('Device-Key' => @key)
|
107
|
+
else
|
108
|
+
request = HTTP.headers('Live-Stream-Key-Client' => @client, 'Live-Stream-Key-Secret' => @secret)
|
109
|
+
end
|
110
|
+
request = request.headers(content_type: 'application/json', accept: 'application/json')
|
111
|
+
begin
|
112
|
+
request.get(uri.to_s)
|
113
|
+
rescue => e
|
114
|
+
logger.error e.message
|
115
|
+
logger.error e.backtrace.join "\n"
|
116
|
+
raise EndPointNotReachable
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def logger
|
121
|
+
Spacebunny.logger
|
122
|
+
end
|
123
|
+
|
124
|
+
def merge_with_default(options)
|
125
|
+
DEFAULT_OPTIONS.merge(options) { |key, old_val, new_val| new_val.nil? ? old_val : new_val }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Spacebunny
|
2
|
+
class DeviceKeyOrClientAndSecretRequired < Exception
|
3
|
+
def initialize(message = nil)
|
4
|
+
message = message || "A valid 'Api Key' or valid 'Client' and 'Secret' are required for auto-configuration"
|
5
|
+
super(message)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class DeviceKeyOrConfigurationsRequired < Exception
|
10
|
+
def initialize(message = nil)
|
11
|
+
message = message || 'Neither key or connection options provided!'
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class AckTypeError < Exception
|
17
|
+
def initialize(message = nil)
|
18
|
+
message = message || "Ack type not valid. Use one of #{Spacebunny::AmqpClient::ACK_TYPES.map{ |t| ":#{t}" }.join(', ')}"
|
19
|
+
super(message)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class BlockRequired < Exception
|
24
|
+
def initialize(message = nil)
|
25
|
+
message = message || 'block missing. Please provide a block'
|
26
|
+
super(message)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ChannelsMustBeAnArray < Exception
|
31
|
+
def initialize
|
32
|
+
message = "channels option must be an Array. E.g. [:data, :alarms]"
|
33
|
+
super(message)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class ChannelNotExists < Exception
|
38
|
+
def initialize(channel = nil)
|
39
|
+
message = if channel
|
40
|
+
"Channel '#{channel}' does not exists. Is this channel enabled for the device or did you specified it on client initialization?"
|
41
|
+
else
|
42
|
+
'Channel does not exists'
|
43
|
+
end
|
44
|
+
super(message)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class ClientRequired < Exception
|
49
|
+
def initialize(message = nil)
|
50
|
+
message = message || "Missing mandatory 'client'. Spacebunny::LiveStream.new(:client => 'a_valid_client', :secret: 'a_valid_secret')"
|
51
|
+
super(message)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class LiveStreamFormatError < Exception
|
56
|
+
def initialize
|
57
|
+
message = "Live Stream not correctly formatted. It must be an Hash with at least 'name' and 'id' attributes"
|
58
|
+
super(message)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class LiveStreamNotFound < Exception
|
63
|
+
def initialize(name = nil)
|
64
|
+
message = if name
|
65
|
+
"Live Stream '#{name}' not found. Did you created and configured it?"
|
66
|
+
else
|
67
|
+
'Live Stream not found'
|
68
|
+
end
|
69
|
+
super(message)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class LiveStreamParamError < Exception
|
74
|
+
def initialize(live_stream_name, param_name)
|
75
|
+
live_stream_name = live_stream_name || 'no-name-provided'
|
76
|
+
"Live Stream '#{live_stream_name}' misses mandatory '#{param_name}' param"
|
77
|
+
super(message)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class SecretRequired < Exception
|
82
|
+
def initialize(message = nil)
|
83
|
+
message = message || "Missing mandatory 'secret' Spacebunny::LiveStream.new(:client => 'a_valid_client', :secret: 'a_valid_secret')"
|
84
|
+
super(message)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class ClientNotConnected < Exception
|
89
|
+
def initialize(message = nil)
|
90
|
+
message = message || 'Client not connected! Check internet connection'
|
91
|
+
super(message)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class ClientNotSetup < Exception
|
96
|
+
def initialize(message = nil)
|
97
|
+
message = message || "'Client not setup. Did you call 'connect'?'"
|
98
|
+
super(message)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class DeviceIdMissing < Exception
|
103
|
+
def initialize(message = nil)
|
104
|
+
message = message || "missing mandatory 'device_id' parameter. Please provide it on client initialization (see doc) or use auto-configuration"
|
105
|
+
super(message)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class EndpointError < Exception
|
110
|
+
def initialize(message = nil)
|
111
|
+
message = message || 'Error while contacting endpoint for auto-configuration'
|
112
|
+
super(message)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class EndPointNotReachable < Exception
|
117
|
+
def initialize(message = nil)
|
118
|
+
message = message || 'Endpoint not reachable'
|
119
|
+
super(message)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class ProtocolNotRegistered < Exception
|
124
|
+
def initialize(protocol)
|
125
|
+
message = "protocol #{protocol} is not registered"
|
126
|
+
super(message)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class SchemeNotValid < Exception
|
131
|
+
def initialize(scheme)
|
132
|
+
message = "Provided scheme #{scheme} is not valid"
|
133
|
+
super(message)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class StreamsMustBeAnArray < Exception
|
138
|
+
def initialize
|
139
|
+
message = "streams option must be an Array"
|
140
|
+
super(message)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|