garufa 0.0.1.alpha.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/LICENSE +19 -0
- data/README.md +34 -0
- data/Rakefile +7 -0
- data/bin/garufa +16 -0
- data/bin/garufa.log +1 -0
- data/bin/garufa.log_stdout.log +2 -0
- data/bin/garufa.pid +1 -0
- data/garufa.gemspec +31 -0
- data/lib/garufa.rb +1 -0
- data/lib/garufa/api_server.rb +35 -0
- data/lib/garufa/config.rb +17 -0
- data/lib/garufa/connection.rb +105 -0
- data/lib/garufa/cuba/authentication.rb +14 -0
- data/lib/garufa/faye_websocket_patch.rb +16 -0
- data/lib/garufa/garufa_app.rb +39 -0
- data/lib/garufa/message.rb +47 -0
- data/lib/garufa/subscriptions.rb +121 -0
- data/lib/garufa/version.rb +4 -0
- data/lib/garufa/ws_server.rb +23 -0
- data/test/helper.rb +4 -0
- data/test/message.rb +85 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e75a892c17eda812e7d24f2eb34282c2cb34b992
|
4
|
+
data.tar.gz: c2a7430113bb000c5c2c061a70c8f1cbe24bbe8e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bb546c89b9fb29bf16bfd7bcdf9c883aeedc9fef507cf33b2b6b353567c3cbadf67d073b639df3157bb412ddc1a383d8d51b2d02e6854a1e87c0edd6660a014
|
7
|
+
data.tar.gz: 40f0cdd7e0a2c2e6246de48332e082190be57ec66fd297806c5fc80cdddfc79cf34efe099d8b57954d731e53613fa94a0267e72e8d35951be22c40e20104ef1c
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2013 Juan Manuel Cuello
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Garufa
|
2
|
+
====
|
3
|
+
|
4
|
+
A websocket server compatible with the Pusher protocol.
|
5
|
+
|
6
|
+
**IMPORTANT:** Garufa is currently in alpha version, which means it is not
|
7
|
+
production ready, but you are free to use it and test it. Any feedback is
|
8
|
+
welcome.
|
9
|
+
|
10
|
+
Intro
|
11
|
+
-----
|
12
|
+
|
13
|
+
Garufa is a websocket server which implements the [Pusher][pusher] protocol. It
|
14
|
+
was built on top of [Goliath][goliath], a high performance non-blocking web
|
15
|
+
server.
|
16
|
+
|
17
|
+
It was based on [Slanger][slanger], another server compatible with Pusher.
|
18
|
+
|
19
|
+
[pusher]: http://pusher.com
|
20
|
+
[goliath]: https://github.com/postrank-labs/goliath/
|
21
|
+
[slanger]: https://github.com/stevegraham/slanger
|
22
|
+
|
23
|
+
Install
|
24
|
+
-------
|
25
|
+
|
26
|
+
Make sure you have a ruby version >= 1.9.2
|
27
|
+
|
28
|
+
``` console
|
29
|
+
$ gem install garufa --pre
|
30
|
+
|
31
|
+
$ garufa --help
|
32
|
+
|
33
|
+
$ garufa -sv -p 4567 --app_key my-application-key --secret my-secret-string
|
34
|
+
```
|
data/Rakefile
ADDED
data/bin/garufa
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'goliath/runner'
|
4
|
+
require 'garufa'
|
5
|
+
|
6
|
+
module Garufa
|
7
|
+
runner = Goliath::Runner.new(ARGV, GarufaApp.new)
|
8
|
+
runner.app = Goliath::Rack::Builder.build(GarufaApp, runner.api)
|
9
|
+
|
10
|
+
if runner.daemonize
|
11
|
+
runner.log_file ||= './garufa.log'
|
12
|
+
runner.pid_file ||= './garufa.pid'
|
13
|
+
end
|
14
|
+
|
15
|
+
runner.run
|
16
|
+
end
|
data/bin/garufa.log
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
[4707:INFO] 2013-12-21 18:43:01 :: Starting server on 0.0.0.0:9000 in development mode. Watch out for stones.
|
data/bin/garufa.pid
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
4707
|
data/garufa.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require 'garufa/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "garufa"
|
8
|
+
s.version = Garufa::VERSION
|
9
|
+
s.summary = "Websocket server compatible with Pusher."
|
10
|
+
s.description = "Garufa is a websocket server compatible with the Pusher service protocol."
|
11
|
+
s.authors = ["Juan Manuel Cuello"]
|
12
|
+
s.email = ["juanmacuello@gmail.com"]
|
13
|
+
s.homepage = "http://github.com/Juanmcuello/garufa"
|
14
|
+
s.bindir = 'bin'
|
15
|
+
s.executables << 'garufa'
|
16
|
+
|
17
|
+
s.files = Dir[
|
18
|
+
"LICENSE",
|
19
|
+
"README.md",
|
20
|
+
"Rakefile",
|
21
|
+
"lib/**/*.rb",
|
22
|
+
"bin/*",
|
23
|
+
"*.gemspec",
|
24
|
+
"test/*.*"
|
25
|
+
]
|
26
|
+
|
27
|
+
s.add_dependency "goliath"
|
28
|
+
s.add_dependency "faye-websocket"
|
29
|
+
s.add_dependency "cuba"
|
30
|
+
s.add_dependency "signature"
|
31
|
+
end
|
data/lib/garufa.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'garufa/garufa_app'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cuba'
|
2
|
+
require 'garufa/cuba/authentication'
|
3
|
+
require 'garufa/message'
|
4
|
+
require 'garufa/subscriptions'
|
5
|
+
|
6
|
+
module Garufa
|
7
|
+
Cuba.plugin Cuba::Authentication
|
8
|
+
|
9
|
+
ApiServer = Cuba.new do
|
10
|
+
|
11
|
+
on "apps/:app_id" do |app_id|
|
12
|
+
|
13
|
+
authenticate
|
14
|
+
|
15
|
+
# Events
|
16
|
+
on post, "events" do
|
17
|
+
message = Message.new(JSON.parse(req.body.read))
|
18
|
+
options = { data: message.data, socket_id: message.socket_id }
|
19
|
+
Subscriptions.notify message.channels, message.name, options
|
20
|
+
res.write "{}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Channels
|
24
|
+
on get, "channels" do
|
25
|
+
end
|
26
|
+
|
27
|
+
on get, "channels/:channel" do
|
28
|
+
end
|
29
|
+
|
30
|
+
# Users
|
31
|
+
on get, "channels/:channel/users" do
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Garufa
|
4
|
+
class Connection
|
5
|
+
|
6
|
+
attr_reader :socket_id
|
7
|
+
|
8
|
+
def initialize(socket, logger)
|
9
|
+
@socket = socket
|
10
|
+
@logger = logger
|
11
|
+
@socket_id = SecureRandom.uuid
|
12
|
+
@subscriptions = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def establish
|
16
|
+
if valid_app_key?
|
17
|
+
send_message Message.connection_established(@socket_id)
|
18
|
+
else
|
19
|
+
error(4001, "Could not find app by key #{app_key}")
|
20
|
+
close
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_incomming_data(data)
|
25
|
+
@logger.debug "Incomming message. #{@socket_id}: #{data}"
|
26
|
+
|
27
|
+
message = Message.new(JSON.parse(data))
|
28
|
+
event, data = message.event, message.data
|
29
|
+
|
30
|
+
case event
|
31
|
+
when /^pusher:/
|
32
|
+
handle_pusher_event(event, data)
|
33
|
+
when /^client-/
|
34
|
+
handle_client_event(event, data)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def error(code, message)
|
39
|
+
send_message Message.error(code, message)
|
40
|
+
end
|
41
|
+
|
42
|
+
def send_message(message)
|
43
|
+
@logger.debug "Outgoing message: #{message.to_json}"
|
44
|
+
|
45
|
+
@socket.send message.to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
def close
|
49
|
+
@socket.close
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def handle_pusher_event(event, data)
|
55
|
+
accepted_events = %w{ping pong subscribe unsubscribe}
|
56
|
+
event_name = event.partition(':').last
|
57
|
+
|
58
|
+
if accepted_events.include?(event_name)
|
59
|
+
method("pusher_#{event_name}").call data
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def handle_client_event(event, data)
|
64
|
+
# NOTE: not supported yet
|
65
|
+
error(nil, 'Client events are not supported yet')
|
66
|
+
end
|
67
|
+
|
68
|
+
def pusher_ping(data)
|
69
|
+
send_message Message.pong
|
70
|
+
end
|
71
|
+
|
72
|
+
def pusher_pong(data)
|
73
|
+
# There is nothing to do with a pong message
|
74
|
+
end
|
75
|
+
|
76
|
+
def pusher_subscribe(data)
|
77
|
+
subscription = Subscription.new(data, self)
|
78
|
+
subscription.subscribe
|
79
|
+
|
80
|
+
if subscription.success?
|
81
|
+
@subscriptions[subscription.channel] = subscription
|
82
|
+
send_subscription_succeeded(subscription) unless subscription.public_channel?
|
83
|
+
else
|
84
|
+
error(subscription.error.code, subscription.error.message)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def pusher_unsubscribe(data)
|
89
|
+
subscription = @subscriptions.delete data["channel"]
|
90
|
+
subscription.unsubscribe if subscription
|
91
|
+
end
|
92
|
+
|
93
|
+
def valid_app_key?
|
94
|
+
app_key && (app_key == Config[:app_key])
|
95
|
+
end
|
96
|
+
|
97
|
+
def app_key
|
98
|
+
@app_key ||= URI.parse(@socket.url).path.split('/').last
|
99
|
+
end
|
100
|
+
|
101
|
+
def send_subscription_succeeded(subscription)
|
102
|
+
send_message Message.subscription_succeeded(subscription.channel)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'signature'
|
2
|
+
require 'garufa/config'
|
3
|
+
|
4
|
+
class Cuba
|
5
|
+
module Authentication
|
6
|
+
def authenticate
|
7
|
+
request = Signature::Request.new(req.request_method, env['REQUEST_PATH'], req.params)
|
8
|
+
request.authenticate { |key| Signature::Token.new(key, Garufa::Config[:secret]) }
|
9
|
+
|
10
|
+
rescue Signature::AuthenticationError
|
11
|
+
halt([401, {}, ['401 Unauthorized']])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This is included only to provide a fix when the 'env' was not set.
|
2
|
+
# This fix was already included in 'master' of faye-websocket-ruby,
|
3
|
+
# but not released as a gem yet.
|
4
|
+
#
|
5
|
+
# See https://github.com/faye/faye-websocket-ruby/issues/38
|
6
|
+
#
|
7
|
+
module Faye
|
8
|
+
class WebSocket
|
9
|
+
module Adapter
|
10
|
+
def websocket?
|
11
|
+
e = defined?(@env) ? @env : env
|
12
|
+
e && WebSocket.websocket?(e)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'goliath/api'
|
2
|
+
require 'goliath/connection'
|
3
|
+
require 'faye/websocket'
|
4
|
+
require 'garufa/config'
|
5
|
+
require 'garufa/ws_server'
|
6
|
+
require 'garufa/api_server'
|
7
|
+
|
8
|
+
# Remove this require after next release of faye-websocket-ruby.
|
9
|
+
# See https://github.com/faye/faye-websocket-ruby/issues/38
|
10
|
+
require 'garufa/faye_websocket_patch'
|
11
|
+
|
12
|
+
module Garufa
|
13
|
+
Faye::WebSocket.load_adapter('goliath')
|
14
|
+
|
15
|
+
class GarufaApp < Goliath::API
|
16
|
+
|
17
|
+
# Extend goliath options with our own options.
|
18
|
+
def options_parser(opts, options)
|
19
|
+
opts.separator ""
|
20
|
+
opts.separator "Pusher options:"
|
21
|
+
|
22
|
+
new_options = {
|
23
|
+
app_key: ['--app_key APP_KEY', 'Pusher application key (required)'],
|
24
|
+
secret: ['--secret SECRET', 'Pusher application secret (required)']
|
25
|
+
}
|
26
|
+
new_options.each do |k, v|
|
27
|
+
opts.on(v.first, v.last) { |value| Garufa::Config[k] = value }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def response(env)
|
32
|
+
if Faye::WebSocket.websocket?(env)
|
33
|
+
WsServer.call(env)
|
34
|
+
else
|
35
|
+
ApiServer.call(env)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Garufa
|
4
|
+
class Message
|
5
|
+
|
6
|
+
ACTIVITY_TIMEOUT = 120
|
7
|
+
|
8
|
+
ATTRIBUTES = [:channels, :channel, :event, :data, :name, :socket_id]
|
9
|
+
|
10
|
+
def initialize(attributes)
|
11
|
+
@attributes = ATTRIBUTES.each_with_object({}) do |key, hash|
|
12
|
+
hash[key] = attributes[key] || attributes[key.to_s]
|
13
|
+
end
|
14
|
+
|
15
|
+
@attributes.each do |name, value|
|
16
|
+
instance_variable_set("@#{name}", value)
|
17
|
+
self.class.send(:attr_reader, name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json
|
22
|
+
@attributes.delete_if { |k, v| v.nil? }.to_json
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.channel_event(channel, event, data)
|
26
|
+
new(channel: channel, event: event, data: data)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.connection_established(socket_id)
|
30
|
+
data = { socket_id: socket_id, activity_timeout: ACTIVITY_TIMEOUT }.to_json
|
31
|
+
new(event: 'pusher:connection_established', data: data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.subscription_succeeded(channel)
|
35
|
+
new(event: 'pusher_internal:subscription_succeeded', channel: channel)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.pong
|
39
|
+
new(event: 'pusher:pong')
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.error(code, message)
|
43
|
+
data = { code: code, message: message }.to_json
|
44
|
+
new(event: 'pusher:error', data: data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Garufa
|
4
|
+
module Subscriptions
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def all
|
8
|
+
subscriptions
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(subscription)
|
12
|
+
subscriptions[subscription.channel].add subscription
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove(subscription)
|
16
|
+
subscriptions[subscription.channel].delete subscription
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify(channels, event, options = {})
|
20
|
+
channels.each do |channel|
|
21
|
+
connections = subscriptions[channel].map { |s| s.connection }
|
22
|
+
next unless connections.any?
|
23
|
+
|
24
|
+
connections.each do |connection|
|
25
|
+
next if connection.socket_id == options[:socket_id]
|
26
|
+
|
27
|
+
message = Message.channel_event(channel, event, options[:data])
|
28
|
+
connection.send_message(message)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def include?(subscription)
|
34
|
+
subscriptions[subscription.channel].include? subscription
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def subscriptions
|
40
|
+
@subscriptions ||= Hash.new(Set.new)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Subscription
|
45
|
+
attr_reader :data, :connection, :error
|
46
|
+
|
47
|
+
def initialize(data, connection)
|
48
|
+
@data, @connection = data, connection
|
49
|
+
end
|
50
|
+
|
51
|
+
def subscribe
|
52
|
+
case true
|
53
|
+
when invalid_channel?
|
54
|
+
set_error(nil, 'Invalid channnel or not present')
|
55
|
+
when invalid_signature?
|
56
|
+
set_error(nil, 'Invalid signature')
|
57
|
+
when already_subscribed?
|
58
|
+
set_error(nil, "Already subscribed to channel: #{channel}")
|
59
|
+
else
|
60
|
+
Subscriptions.add self
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def unsubscribe
|
65
|
+
Subscriptions.remove self
|
66
|
+
end
|
67
|
+
|
68
|
+
def public_channel?
|
69
|
+
!(private_channel? || presence_channel?)
|
70
|
+
end
|
71
|
+
|
72
|
+
def private_channel?
|
73
|
+
channel_prefix == 'private'
|
74
|
+
end
|
75
|
+
|
76
|
+
def presence_channel?
|
77
|
+
channel_prefix == 'presence'
|
78
|
+
end
|
79
|
+
|
80
|
+
def set_error(code, message)
|
81
|
+
@error = SubscriptionError.new(code, message)
|
82
|
+
end
|
83
|
+
|
84
|
+
def success?
|
85
|
+
@error.nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
def channel
|
89
|
+
@data['channel']
|
90
|
+
end
|
91
|
+
|
92
|
+
def channel_prefix
|
93
|
+
channel[/^private-|presence-/].to_s[0...-1]
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def invalid_channel?
|
99
|
+
!channel.is_a?(String) || channel.empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
def invalid_signature?
|
103
|
+
return false if public_channel?
|
104
|
+
|
105
|
+
string_to_sign = [@connection.socket_id, channel].compact.join(':')
|
106
|
+
token(string_to_sign) != @data["auth"].split(':').last
|
107
|
+
end
|
108
|
+
|
109
|
+
def token(string_to_sign)
|
110
|
+
digest = OpenSSL::Digest::SHA256.new
|
111
|
+
OpenSSL::HMAC.hexdigest(digest, Config[:secret], string_to_sign)
|
112
|
+
end
|
113
|
+
|
114
|
+
def already_subscribed?
|
115
|
+
Subscriptions.include? self
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class SubscriptionError < Struct.new(:code, :message)
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'garufa/connection'
|
2
|
+
|
3
|
+
module Garufa
|
4
|
+
WsServer = lambda do |env|
|
5
|
+
socket = Faye::WebSocket.new(env)
|
6
|
+
connection = Connection.new(socket, env.logger)
|
7
|
+
|
8
|
+
socket.on :open do |event|
|
9
|
+
connection.establish
|
10
|
+
end
|
11
|
+
|
12
|
+
socket.on :message do |event|
|
13
|
+
connection.handle_incomming_data(event.data)
|
14
|
+
end
|
15
|
+
|
16
|
+
socket.on :close do |event|
|
17
|
+
socket = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return async Rack response
|
21
|
+
socket.rack_response
|
22
|
+
end
|
23
|
+
end
|
data/test/helper.rb
ADDED
data/test/message.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Garufa
|
4
|
+
describe Message do
|
5
|
+
describe '.connection_established' do
|
6
|
+
before do
|
7
|
+
@socket_id = '123123-123123'
|
8
|
+
@message = Message.connection_established @socket_id
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should response with data attribute as string' do
|
12
|
+
@message.data.class.must_equal String
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should response with expected event' do
|
16
|
+
@message.event.must_equal 'pusher:connection_established'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should response with expected data' do
|
20
|
+
data = JSON.parse(@message.data)
|
21
|
+
data["socket_id"].must_equal @socket_id
|
22
|
+
data["activity_timeout"].must_equal 120
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.channel_event' do
|
27
|
+
before do
|
28
|
+
@channel = 'channel-123'
|
29
|
+
@event = 'my-event'
|
30
|
+
@data = { itemId: 1, value: 'Sample Item' }
|
31
|
+
@message = Message.channel_event @channel, @event, @data
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should response with expected event' do
|
35
|
+
@message.event.must_equal @event
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should response with expected data' do
|
39
|
+
@message.data.must_equal @data
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should response with expected channel' do
|
43
|
+
@message.channel.must_equal @channel
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.subscription_succeeded' do
|
48
|
+
before do
|
49
|
+
@channel = 'channel-123'
|
50
|
+
@message = Message.subscription_succeeded @channel
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should response with expected event' do
|
54
|
+
@message.event.must_equal 'pusher_internal:subscription_succeeded'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should response with expected channel' do
|
58
|
+
@message.channel.must_equal @channel
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.pong' do
|
63
|
+
it 'should response with a pong event' do
|
64
|
+
Message.pong.event.must_equal 'pusher:pong'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.error' do
|
69
|
+
before do
|
70
|
+
@code, @error_message = 4000, 'There was an error!'
|
71
|
+
@message = Message.error @code, @error_message
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should response with expected event' do
|
75
|
+
@message.event.must_equal 'pusher:error'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should response with expected data' do
|
79
|
+
data = JSON.parse(@message.data)
|
80
|
+
data["code"].must_equal @code
|
81
|
+
data["message"].must_equal @error_message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: garufa
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Juan Manuel Cuello
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: goliath
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faye-websocket
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cuba
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: signature
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Garufa is a websocket server compatible with the Pusher service protocol.
|
70
|
+
email:
|
71
|
+
- juanmacuello@gmail.com
|
72
|
+
executables:
|
73
|
+
- garufa
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- lib/garufa/version.rb
|
81
|
+
- lib/garufa/ws_server.rb
|
82
|
+
- lib/garufa/message.rb
|
83
|
+
- lib/garufa/config.rb
|
84
|
+
- lib/garufa/cuba/authentication.rb
|
85
|
+
- lib/garufa/subscriptions.rb
|
86
|
+
- lib/garufa/garufa_app.rb
|
87
|
+
- lib/garufa/connection.rb
|
88
|
+
- lib/garufa/faye_websocket_patch.rb
|
89
|
+
- lib/garufa/api_server.rb
|
90
|
+
- lib/garufa.rb
|
91
|
+
- bin/garufa.pid
|
92
|
+
- bin/garufa
|
93
|
+
- bin/garufa.log_stdout.log
|
94
|
+
- bin/garufa.log
|
95
|
+
- garufa.gemspec
|
96
|
+
- test/helper.rb
|
97
|
+
- test/message.rb
|
98
|
+
homepage: http://github.com/Juanmcuello/garufa
|
99
|
+
licenses: []
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - '>'
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 1.3.1
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.0.3
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Websocket server compatible with Pusher.
|
121
|
+
test_files: []
|
122
|
+
has_rdoc:
|