eventflit-client 0.1.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/.document +5 -0
- data/.gitignore +15 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +92 -0
- data/Rakefile +7 -0
- data/certs/cacert.pem +3554 -0
- data/eventflit-client.gemspec +30 -0
- data/examples/hello_eventflit.rb +19 -0
- data/examples/hello_eventflit_async.rb +22 -0
- data/examples/hello_eventflit_ssl.rb +19 -0
- data/examples/subscribe_private.rb +20 -0
- data/lib/eventflit-client/channel.rb +56 -0
- data/lib/eventflit-client/channels.rb +35 -0
- data/lib/eventflit-client/socket.rb +244 -0
- data/lib/eventflit-client/version.rb +3 -0
- data/lib/eventflit-client/websocket.rb +99 -0
- data/lib/eventflit-client.rb +22 -0
- data/spec/eventflitclient_spec.rb +181 -0
- data/spec/spec_helper.rb +51 -0
- metadata +139 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'eventflit-client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'eventflit-client'
|
8
|
+
s.version = EventflitClient::VERSION
|
9
|
+
s.authors = ["Eventflit", "Logan Koester"]
|
10
|
+
s.email = ['support@eventflit.com']
|
11
|
+
s.homepage = 'http://github.com/eventflit/eventflit-websocket-ruby'
|
12
|
+
s.summary = 'Client for consuming WebSockets from http://eventflit.com'
|
13
|
+
s.description = 'Client for consuming WebSockets from http://eventflit.com'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f|
|
18
|
+
File.basename(f)
|
19
|
+
}
|
20
|
+
s.extra_rdoc_files = %w(LICENSE.txt README.md)
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
s.licenses = ['MIT']
|
23
|
+
|
24
|
+
s.add_runtime_dependency 'websocket', '~> 1.0'
|
25
|
+
s.add_runtime_dependency 'json'
|
26
|
+
|
27
|
+
s.add_development_dependency "rspec"
|
28
|
+
s.add_development_dependency "rake"
|
29
|
+
s.add_development_dependency "bundler"
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Usage: $ EVENTFLIT_KEY=YOURKEY ruby examples/hello_eventflit.rb
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
require 'eventflit-client'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
APP_KEY = ENV['EVENTFLIT_KEY'] # || "YOUR_APPLICATION_KEY"
|
8
|
+
|
9
|
+
socket = EventflitClient::Socket.new(APP_KEY)
|
10
|
+
|
11
|
+
# Subscribe to a channel
|
12
|
+
socket.subscribe('helloeventflit')
|
13
|
+
|
14
|
+
# Bind to a channel event
|
15
|
+
socket['helloeventflit'].bind('hello') do |data|
|
16
|
+
pp data
|
17
|
+
end
|
18
|
+
|
19
|
+
socket.connect
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Usage: $ EVENTFLIT_KEY=YOURKEY ruby examples/hello_eventflit_async.rb
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
require 'eventflit-client'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
APP_KEY = ENV['EVENTFLIT_KEY'] # || "YOUR_APPLICATION_KEY"
|
8
|
+
|
9
|
+
socket = EventflitClient::Socket.new(APP_KEY)
|
10
|
+
socket.connect(true)
|
11
|
+
|
12
|
+
# Subscribe to a channel
|
13
|
+
socket.subscribe('helloeventflit')
|
14
|
+
|
15
|
+
# Bind to a channel event
|
16
|
+
socket['helloeventflit'].bind('hello') do |data|
|
17
|
+
pp data
|
18
|
+
end
|
19
|
+
|
20
|
+
loop do
|
21
|
+
sleep 1
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Usage: $ EVENTFLIT_KEY=YOURKEY ruby examples/hello_eventflit_ssl.rb
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
require 'eventflit-client'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
APP_KEY = ENV['EVENTFLIT_KEY'] # || "YOUR_APPLICATION_KEY"
|
8
|
+
|
9
|
+
socket = EventflitClient::Socket.new(APP_KEY, { :encrypted => true } )
|
10
|
+
|
11
|
+
# Subscribe to a channel
|
12
|
+
socket.subscribe('helloeventflit')
|
13
|
+
|
14
|
+
# Bind to a channel event
|
15
|
+
socket['helloeventflit'].bind('hello') do |data|
|
16
|
+
pp data
|
17
|
+
end
|
18
|
+
|
19
|
+
socket.connect
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Usage: $ EVENTFLIT_KEY=YOURKEY ruby examples/subscribe_private.rb
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
require 'eventflit-client'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
APP_KEY = ENV['EVENTFLIT_KEY'] # || "YOUR_APPLICATION_KEY"
|
8
|
+
APP_SECRET = ENV['EVENTFLIT_SECRET'] # || "YOUR_APPLICATION_SECRET"
|
9
|
+
|
10
|
+
socket = EventflitClient::Socket.new(APP_KEY, { :encrypted => true, :secret => APP_SECRET } )
|
11
|
+
|
12
|
+
# Subscribe to a channel
|
13
|
+
socket.subscribe('private-helloeventflit')
|
14
|
+
|
15
|
+
# Bind to a channel event
|
16
|
+
socket['helloeventflit'].bind('hello') do |data|
|
17
|
+
pp data
|
18
|
+
end
|
19
|
+
|
20
|
+
socket.connect
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module EventflitClient
|
2
|
+
|
3
|
+
class Channel
|
4
|
+
attr_accessor :global, :subscribed
|
5
|
+
attr_reader :name, :callbacks, :user_data
|
6
|
+
|
7
|
+
def initialize(channel_name, user_data=nil, logger=EventflitClient.logger)
|
8
|
+
@name = channel_name
|
9
|
+
@user_data = user_data
|
10
|
+
@logger = logger
|
11
|
+
@global = false
|
12
|
+
@callbacks = {}
|
13
|
+
@subscribed = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def bind(event_name, &callback)
|
17
|
+
EventflitClient.logger.debug "Binding #{event_name} to #{name}"
|
18
|
+
@callbacks[event_name] = callbacks[event_name] || []
|
19
|
+
@callbacks[event_name] << callback
|
20
|
+
return self
|
21
|
+
end
|
22
|
+
|
23
|
+
def dispatch_with_all(event_name, data)
|
24
|
+
dispatch(event_name, data)
|
25
|
+
end
|
26
|
+
|
27
|
+
def dispatch(event_name, data)
|
28
|
+
logger.debug("Dispatching #{global ? 'global ' : ''}callbacks for #{event_name}")
|
29
|
+
if @callbacks[event_name]
|
30
|
+
@callbacks[event_name].each do |callback|
|
31
|
+
callback.call(data)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
logger.debug "No #{global ? 'global ' : ''}callbacks to dispatch for #{event_name}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def acknowledge_subscription(data)
|
39
|
+
@subscribed = true
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :logger
|
45
|
+
end
|
46
|
+
|
47
|
+
class NullChannel
|
48
|
+
def initialize(channel_name, *a)
|
49
|
+
@name = channel_name
|
50
|
+
end
|
51
|
+
def method_missing(*a)
|
52
|
+
raise ArgumentError, "Channel `#{@name}` hasn't been subscribed yet."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EventflitClient
|
2
|
+
class Channels
|
3
|
+
|
4
|
+
attr_reader :channels
|
5
|
+
|
6
|
+
def initialize(logger=EventflitClient.logger)
|
7
|
+
@logger = logger
|
8
|
+
@channels = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(channel_name, user_data=nil)
|
12
|
+
@channels[channel_name] ||= Channel.new(channel_name, user_data, @logger)
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(channel_name)
|
16
|
+
@channels[channel_name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove(channel_name)
|
20
|
+
@channels.delete(channel_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty?
|
24
|
+
@channels.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def size
|
28
|
+
@channels.size
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :<< :add
|
32
|
+
alias :[] :find
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'openssl'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module EventflitClient
|
6
|
+
class Socket
|
7
|
+
|
8
|
+
CLIENT_ID = 'eventflit-ruby-client'
|
9
|
+
PROTOCOL = '6'
|
10
|
+
|
11
|
+
attr_reader :path, :connected, :channels, :global_channel, :socket_id
|
12
|
+
|
13
|
+
def initialize(app_key, options={})
|
14
|
+
raise ArgumentError, "Missing app_key" if app_key.to_s.empty?
|
15
|
+
|
16
|
+
@path = "#{options[:ws_path]}/app/#{app_key}?client=#{CLIENT_ID}&version=#{EventflitClient::VERSION}&protocol=#{PROTOCOL}"
|
17
|
+
@key = app_key.to_s
|
18
|
+
@secret = options[:secret]
|
19
|
+
@socket_id = nil
|
20
|
+
@logger = options[:logger] || EventflitClient.logger
|
21
|
+
@channels = Channels.new(@logger)
|
22
|
+
@global_channel = Channel.new('eventflit_global_channel')
|
23
|
+
@global_channel.global = true
|
24
|
+
@connected = false
|
25
|
+
@encrypted = options[:encrypted] || options[:secure] || false
|
26
|
+
# :private_auth_method is deprecated
|
27
|
+
@auth_method = options[:auth_method] || options[:private_auth_method]
|
28
|
+
@cert_file = options[:cert_file]
|
29
|
+
@ws_host = options[:ws_host] || HOST
|
30
|
+
@ws_port = options[:ws_port] || WS_PORT
|
31
|
+
@wss_port = options[:wss_port] || WSS_PORT
|
32
|
+
@ssl_verify = options.fetch(:ssl_verify, true)
|
33
|
+
|
34
|
+
if @encrypted
|
35
|
+
@url = "wss://#{@ws_host}:#{@wss_port}#{@path}"
|
36
|
+
else
|
37
|
+
@url = "ws://#{@ws_host}:#{@ws_port}#{@path}"
|
38
|
+
end
|
39
|
+
|
40
|
+
bind('eventflit:connection_established') do |data|
|
41
|
+
socket = parser(data)
|
42
|
+
@connected = true
|
43
|
+
@socket_id = socket['socket_id']
|
44
|
+
subscribe_all
|
45
|
+
end
|
46
|
+
|
47
|
+
bind('eventflit:connection_disconnected') do |data|
|
48
|
+
@connected = false
|
49
|
+
@channels.channels.each { |c| c.disconnect }
|
50
|
+
end
|
51
|
+
|
52
|
+
bind('eventflit:error') do |data|
|
53
|
+
logger.fatal("Eventflit : error : #{data.inspect}")
|
54
|
+
end
|
55
|
+
|
56
|
+
# Keep this in case we're using a websocket protocol that doesn't
|
57
|
+
# implement ping/pong
|
58
|
+
bind('eventflit:ping') do
|
59
|
+
send_event('eventflit:pong', nil)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def connect(async = false)
|
64
|
+
return if @connection
|
65
|
+
logger.debug("Eventflit : connecting : #{@url}")
|
66
|
+
|
67
|
+
if async
|
68
|
+
@connection_thread = Thread.new do
|
69
|
+
begin
|
70
|
+
connect_internal
|
71
|
+
rescue => ex
|
72
|
+
send_local_event "eventflit:error", ex
|
73
|
+
end
|
74
|
+
end
|
75
|
+
else
|
76
|
+
connect_internal
|
77
|
+
end
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def disconnect
|
82
|
+
return unless @connection
|
83
|
+
logger.debug("Eventflit : disconnecting")
|
84
|
+
@connected = false
|
85
|
+
@connection.close
|
86
|
+
@connection = nil
|
87
|
+
if @connection_thread
|
88
|
+
@connection_thread.kill
|
89
|
+
@connection_thread = nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def subscribe(channel_name, user_data = nil)
|
94
|
+
if user_data.is_a? Hash
|
95
|
+
user_data = user_data.to_json
|
96
|
+
elsif user_data
|
97
|
+
user_data = {:user_id => user_data}.to_json
|
98
|
+
elsif is_presence_channel(channel_name)
|
99
|
+
raise ArgumentError, "user_data is required for presence channels"
|
100
|
+
end
|
101
|
+
|
102
|
+
channel = @channels.add(channel_name, user_data)
|
103
|
+
if @connected
|
104
|
+
authorize(channel, method(:authorize_callback))
|
105
|
+
end
|
106
|
+
return channel
|
107
|
+
end
|
108
|
+
|
109
|
+
def unsubscribe(channel_name)
|
110
|
+
channel = @channels.remove channel_name
|
111
|
+
if channel && @connected
|
112
|
+
send_event('eventflit:unsubscribe', {
|
113
|
+
'channel' => channel_name
|
114
|
+
})
|
115
|
+
end
|
116
|
+
return channel
|
117
|
+
end
|
118
|
+
|
119
|
+
def bind(event_name, &callback)
|
120
|
+
@global_channel.bind(event_name, &callback)
|
121
|
+
return self
|
122
|
+
end
|
123
|
+
|
124
|
+
def [](channel_name)
|
125
|
+
@channels[channel_name] || NullChannel.new(channel_name)
|
126
|
+
end
|
127
|
+
|
128
|
+
def subscribe_all
|
129
|
+
@channels.channels.clone.each { |k,v| subscribe(v.name, v.user_data) }
|
130
|
+
end
|
131
|
+
|
132
|
+
# auth for private and presence
|
133
|
+
def authorize(channel, callback)
|
134
|
+
if is_private_channel(channel.name)
|
135
|
+
auth_data = get_private_auth(channel)
|
136
|
+
elsif is_presence_channel(channel.name)
|
137
|
+
auth_data = get_presence_auth(channel)
|
138
|
+
end
|
139
|
+
# could both be nil if didn't require auth
|
140
|
+
callback.call(channel, auth_data, channel.user_data)
|
141
|
+
end
|
142
|
+
|
143
|
+
def authorize_callback(channel, auth_data, channel_data)
|
144
|
+
send_event('eventflit:subscribe', {
|
145
|
+
'channel' => channel.name,
|
146
|
+
'auth' => auth_data,
|
147
|
+
'channel_data' => channel_data
|
148
|
+
})
|
149
|
+
channel.acknowledge_subscription(nil)
|
150
|
+
end
|
151
|
+
|
152
|
+
def is_private_channel(channel_name)
|
153
|
+
channel_name.match(/^private-/)
|
154
|
+
end
|
155
|
+
|
156
|
+
def is_presence_channel(channel_name)
|
157
|
+
channel_name.match(/^presence-/)
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_private_auth(channel)
|
161
|
+
return @auth_method.call(@socket_id, channel) if @auth_method
|
162
|
+
|
163
|
+
string_to_sign = @socket_id + ':' + channel.name
|
164
|
+
signature = hmac(@secret, string_to_sign)
|
165
|
+
"#{@key}:#{signature}"
|
166
|
+
end
|
167
|
+
|
168
|
+
def get_presence_auth(channel)
|
169
|
+
return @auth_method.call(@socket_id, channel) if @auth_method
|
170
|
+
|
171
|
+
string_to_sign = @socket_id + ':' + channel.name + ':' + channel.user_data
|
172
|
+
signature = hmac(@secret, string_to_sign)
|
173
|
+
"#{@key}:#{signature}"
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
# for compatibility with JavaScript client API
|
178
|
+
alias :subscribeAll :subscribe_all
|
179
|
+
|
180
|
+
def send_event(event_name, data)
|
181
|
+
payload = {'event' => event_name, 'data' => data}.to_json
|
182
|
+
@connection.send(payload)
|
183
|
+
logger.debug("Eventflit : sending event : #{payload}")
|
184
|
+
end
|
185
|
+
|
186
|
+
def send_channel_event(channel, event_name, data)
|
187
|
+
payload = {'channel' => channel, 'event' => event_name, 'data' => data}.to_json
|
188
|
+
@connection.send(payload)
|
189
|
+
logger.debug("Eventflit : sending channel event : #{payload}")
|
190
|
+
end
|
191
|
+
|
192
|
+
protected
|
193
|
+
|
194
|
+
attr_reader :logger
|
195
|
+
|
196
|
+
def connect_internal
|
197
|
+
@connection = EventflitWebSocket.new(@url, {
|
198
|
+
:ssl => @encrypted,
|
199
|
+
:cert_file => @cert_file,
|
200
|
+
:ssl_verify => @ssl_verify
|
201
|
+
})
|
202
|
+
|
203
|
+
logger.debug("Websocket connected")
|
204
|
+
|
205
|
+
loop do
|
206
|
+
@connection.receive.each do |msg|
|
207
|
+
params = parser(msg)
|
208
|
+
|
209
|
+
# why ?
|
210
|
+
next if params['socket_id'] && params['socket_id'] == self.socket_id
|
211
|
+
|
212
|
+
send_local_event(params['event'], params['data'], params['channel'])
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def send_local_event(event_name, event_data, channel_name=nil)
|
218
|
+
if channel_name
|
219
|
+
channel = @channels[channel_name]
|
220
|
+
if channel
|
221
|
+
channel.dispatch_with_all(event_name, event_data)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
@global_channel.dispatch_with_all(event_name, event_data)
|
226
|
+
logger.debug("Eventflit : event received : channel: #{channel_name}; event: #{event_name}")
|
227
|
+
end
|
228
|
+
|
229
|
+
def parser(data)
|
230
|
+
return data if data.is_a? Hash
|
231
|
+
return JSON.parse(data)
|
232
|
+
rescue => err
|
233
|
+
logger.warn(err)
|
234
|
+
logger.warn("Eventflit : data attribute not valid JSON - you may wish to implement your own Eventflit::Client.parser")
|
235
|
+
return data
|
236
|
+
end
|
237
|
+
|
238
|
+
def hmac(secret, string_to_sign)
|
239
|
+
digest = OpenSSL::Digest::SHA256.new
|
240
|
+
signature = OpenSSL::HMAC.hexdigest(digest, secret, string_to_sign)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'websocket'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module EventflitClient
|
6
|
+
class EventflitWebSocket
|
7
|
+
WAIT_EXCEPTIONS = [Errno::EAGAIN, Errno::EWOULDBLOCK]
|
8
|
+
WAIT_EXCEPTIONS << IO::WaitReadable if defined?(IO::WaitReadable)
|
9
|
+
|
10
|
+
CA_FILE = File.expand_path('../../../certs/cacert.pem', __FILE__)
|
11
|
+
|
12
|
+
attr_accessor :socket
|
13
|
+
|
14
|
+
def initialize(url, params = {})
|
15
|
+
@hs ||= WebSocket::Handshake::Client.new(:url => url)
|
16
|
+
@frame ||= WebSocket::Frame::Incoming::Server.new(:version => @hs.version)
|
17
|
+
@socket = TCPSocket.new(@hs.host, @hs.port || 80)
|
18
|
+
@cert_file = params[:cert_file]
|
19
|
+
@logger = params[:logger] || EventflitClient.logger
|
20
|
+
|
21
|
+
if params[:ssl] == true
|
22
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
23
|
+
if params[:ssl_verify]
|
24
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
25
|
+
# http://curl.haxx.se/ca/cacert.pem
|
26
|
+
ctx.ca_file = @cert_file || CA_FILE
|
27
|
+
else
|
28
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
29
|
+
end
|
30
|
+
|
31
|
+
ssl_sock = OpenSSL::SSL::SSLSocket.new(@socket, ctx)
|
32
|
+
ssl_sock.sync_close = true
|
33
|
+
ssl_sock.connect
|
34
|
+
|
35
|
+
@socket = ssl_sock
|
36
|
+
end
|
37
|
+
|
38
|
+
@socket.write(@hs.to_s)
|
39
|
+
@socket.flush
|
40
|
+
|
41
|
+
loop do
|
42
|
+
data = @socket.getc
|
43
|
+
next if data.nil?
|
44
|
+
|
45
|
+
@hs << data
|
46
|
+
|
47
|
+
if @hs.finished?
|
48
|
+
raise @hs.error.to_s unless @hs.valid?
|
49
|
+
@handshaked = true
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def send(data, type = :text)
|
56
|
+
raise "no handshake!" unless @handshaked
|
57
|
+
|
58
|
+
data = WebSocket::Frame::Outgoing::Client.new(
|
59
|
+
:version => @hs.version,
|
60
|
+
:data => data,
|
61
|
+
:type => type
|
62
|
+
).to_s
|
63
|
+
@socket.write data
|
64
|
+
@socket.flush
|
65
|
+
end
|
66
|
+
|
67
|
+
def receive
|
68
|
+
raise "no handshake!" unless @handshaked
|
69
|
+
|
70
|
+
begin
|
71
|
+
data = @socket.read_nonblock(1024)
|
72
|
+
rescue *WAIT_EXCEPTIONS
|
73
|
+
IO.select([@socket])
|
74
|
+
retry
|
75
|
+
end
|
76
|
+
@frame << data
|
77
|
+
|
78
|
+
messages = []
|
79
|
+
while message = @frame.next
|
80
|
+
if message.type === :ping
|
81
|
+
send(message.data, :pong)
|
82
|
+
return messages
|
83
|
+
end
|
84
|
+
messages << message.to_s
|
85
|
+
end
|
86
|
+
messages
|
87
|
+
end
|
88
|
+
|
89
|
+
def close
|
90
|
+
@socket.close
|
91
|
+
rescue IOError => error
|
92
|
+
logger.debug error.message
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
attr_reader :logger
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module EventflitClient
|
2
|
+
HOST = 'service.eventflit.com'
|
3
|
+
WS_PORT = 80
|
4
|
+
WSS_PORT = 443
|
5
|
+
|
6
|
+
def self.logger
|
7
|
+
@logger ||= begin
|
8
|
+
require 'logger'
|
9
|
+
Logger.new(STDOUT)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.logger=(logger)
|
14
|
+
@logger = logger
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'eventflit-client/version'
|
19
|
+
require 'eventflit-client/websocket'
|
20
|
+
require 'eventflit-client/socket'
|
21
|
+
require 'eventflit-client/channel'
|
22
|
+
require 'eventflit-client/channels'
|