socky 0.2.1 → 0.4.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.
- data/README.textile +3 -25
- data/lib/socky.rb +1 -75
- metadata +20 -91
- data/CHANGELOG.textile +0 -88
- data/Rakefile +0 -30
- data/VERSION +0 -1
- data/bin/socky +0 -5
- data/lib/em-websocket_hacks.rb +0 -15
- data/lib/socky/connection.rb +0 -137
- data/lib/socky/connection/authentication.rb +0 -99
- data/lib/socky/connection/finders.rb +0 -67
- data/lib/socky/message.rb +0 -85
- data/lib/socky/misc.rb +0 -74
- data/lib/socky/net_request.rb +0 -27
- data/lib/socky/options.rb +0 -39
- data/lib/socky/options/config.rb +0 -79
- data/lib/socky/options/parser.rb +0 -93
- data/lib/socky/runner.rb +0 -95
- data/spec/em-websocket_spec.rb +0 -36
- data/spec/files/default.yml +0 -18
- data/spec/files/invalid.yml +0 -1
- data/spec/socky/connection/authentication_spec.rb +0 -183
- data/spec/socky/connection/finders_spec.rb +0 -188
- data/spec/socky/connection_spec.rb +0 -151
- data/spec/socky/message_spec.rb +0 -102
- data/spec/socky/misc_spec.rb +0 -74
- data/spec/socky/net_request_spec.rb +0 -42
- data/spec/socky/options/config_spec.rb +0 -72
- data/spec/socky/options/parser_spec.rb +0 -76
- data/spec/socky/options_spec.rb +0 -60
- data/spec/socky/runner_spec.rb +0 -88
- data/spec/socky_spec.rb +0 -89
- data/spec/spec_helper.rb +0 -6
- data/spec/support/stallion.rb +0 -96
@@ -1,99 +0,0 @@
|
|
1
|
-
module Socky
|
2
|
-
class Connection
|
3
|
-
# authentication module - included in Socky::Connection
|
4
|
-
module Authentication
|
5
|
-
include Socky::Misc
|
6
|
-
|
7
|
-
# check if user is valid and then send him authentication data and add to pool
|
8
|
-
# if not then user is given failure response(so client javascript
|
9
|
-
# will know that is should not reconnect again) and then is disconnected
|
10
|
-
# admin user is automaticaly authenticated but isn't added to pool
|
11
|
-
# he will be authenticated when he will try to send message
|
12
|
-
# thanks to that admin don't need to wait for authentication confirmation
|
13
|
-
# on every connection so it will fasten things for him
|
14
|
-
def subscribe_request
|
15
|
-
send_subscribe_request do |response|
|
16
|
-
if response
|
17
|
-
debug [self.name, "authentication successed"]
|
18
|
-
add_to_pool
|
19
|
-
EventMachine.add_timer(0.1) do
|
20
|
-
send_authentication("success")
|
21
|
-
end
|
22
|
-
@authenticated_by_url = true
|
23
|
-
else
|
24
|
-
debug [self.name, "authentication failed"]
|
25
|
-
EventMachine.add_timer(0.1) do
|
26
|
-
send_authentication("failure")
|
27
|
-
disconnect
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end unless admin || authenticated?
|
31
|
-
end
|
32
|
-
|
33
|
-
# if user is authenticated then he is removed from pool and
|
34
|
-
# unsubscribe notification is sent to server unless he is admin
|
35
|
-
# if user is not authenticated then nothing will happen
|
36
|
-
def unsubscribe_request
|
37
|
-
if authenticated?
|
38
|
-
remove_from_pool
|
39
|
-
send_unsubscribe_request{} unless admin
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# if user is admin then his secred is compared with server secred
|
44
|
-
# in user isn't admin then it checks if user is authenticated by
|
45
|
-
# server request(defaults to true if subscribe_url is nil)
|
46
|
-
def authenticated?
|
47
|
-
@authenticated ||= (admin ? authenticate_as_admin : authenticate_as_user)
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def send_authentication(msg)
|
53
|
-
send_data({:type => :authentication, :body => msg})
|
54
|
-
end
|
55
|
-
|
56
|
-
def authenticate_as_admin
|
57
|
-
options[:secret].nil? || secret == options[:secret]
|
58
|
-
end
|
59
|
-
|
60
|
-
def authenticate_as_user
|
61
|
-
authenticated_by_url?
|
62
|
-
end
|
63
|
-
|
64
|
-
def authenticated_by_url?
|
65
|
-
@authenticated_by_url
|
66
|
-
end
|
67
|
-
|
68
|
-
def send_subscribe_request(&block)
|
69
|
-
subscribe_url = options[:subscribe_url]
|
70
|
-
if subscribe_url
|
71
|
-
debug [self.name, "sending subscribe request to", subscribe_url]
|
72
|
-
Socky::NetRequest.post(subscribe_url, params_for_request, &block)
|
73
|
-
true
|
74
|
-
else
|
75
|
-
yield true
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def send_unsubscribe_request(&block)
|
80
|
-
unsubscribe_url = options[:unsubscribe_url]
|
81
|
-
if unsubscribe_url
|
82
|
-
debug [self.name, "sending unsubscribe request to", unsubscribe_url]
|
83
|
-
Socky::NetRequest.post(unsubscribe_url, params_for_request, &block)
|
84
|
-
else
|
85
|
-
yield true
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def params_for_request
|
90
|
-
{
|
91
|
-
:client_id => client,
|
92
|
-
:client_secret => secret,
|
93
|
-
:channels => channels
|
94
|
-
}.reject{|key,value| value.nil? || value.empty?}
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
module Socky
|
2
|
-
class Connection
|
3
|
-
# finders module - extends Socky::Connection
|
4
|
-
module Finders
|
5
|
-
|
6
|
-
# Return list of all connections
|
7
|
-
def find_all
|
8
|
-
Socky::Connection.connections
|
9
|
-
end
|
10
|
-
|
11
|
-
# Return filtered list of connections
|
12
|
-
# @param [Hash] opts the options for filters.
|
13
|
-
# @option opts [Hash] :to ({}) return only listed clients/channels. keys supported: clients, channels
|
14
|
-
# @option opts [Hash] :except ({}) return all clients/channels except listed. keys supported: clients, channels
|
15
|
-
# @return [Array] list of connections
|
16
|
-
# @example return all connections
|
17
|
-
# Socky::Connection.find
|
18
|
-
# @example return no connections
|
19
|
-
# # empty array as param means "no channels"
|
20
|
-
# # nil is handles as "ignore param" so all clients/channels will be executed
|
21
|
-
# Socky::Connection.find(:to => { :clients => [] })
|
22
|
-
# Socky::Connection.find(:to => { :channels => [] })
|
23
|
-
# @example return connections of users "first" and "second" from channels "some_channel"
|
24
|
-
# Socky::Connection.find(:to => { :clients => ["first","second"], :channels => "some_channel" })
|
25
|
-
# @example return all connections from channel "some_channel" except of ones belonging to "first"
|
26
|
-
# Socky::Connection.find(:to => { :channels => "some_channel" }, :except => { :clients => "first" })
|
27
|
-
def find(opts = {})
|
28
|
-
to = symbolize_keys(opts[:to]) || {}
|
29
|
-
exclude = symbolize_keys(opts[:except]) || {}
|
30
|
-
|
31
|
-
connections = find_all
|
32
|
-
connections = filter_by_clients(connections, to[:clients], exclude[:clients])
|
33
|
-
connections = filter_by_channels(connections, to[:channels], exclude[:channels])
|
34
|
-
|
35
|
-
connections
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def filter_by_clients(connections, included_clients = nil, excluded_clients = nil)
|
41
|
-
# Empty table means "no users" - nil means "all users"
|
42
|
-
return [] if (included_clients.is_a?(Array) && included_clients.empty?)
|
43
|
-
|
44
|
-
included_clients = Array(included_clients)
|
45
|
-
excluded_clients = Array(excluded_clients)
|
46
|
-
|
47
|
-
connections.collect do |connection|
|
48
|
-
connection if (included_clients.empty? || included_clients.include?(connection.client)) && !excluded_clients.include?(connection.client)
|
49
|
-
end.compact
|
50
|
-
end
|
51
|
-
|
52
|
-
def filter_by_channels(connections, included_channels = nil, excluded_channels = nil)
|
53
|
-
# Empty table means "no channels" - nil means "all channels"
|
54
|
-
return [] if (included_channels.is_a?(Array) && included_channels.empty?)
|
55
|
-
|
56
|
-
included_channels = Array(included_channels)
|
57
|
-
excluded_channels = Array(excluded_channels)
|
58
|
-
|
59
|
-
connections.collect do |connection|
|
60
|
-
connection if connection.channels.any? do |channel|
|
61
|
-
(included_channels.empty? || included_channels.include?(channel) ) && !excluded_channels.include?(channel)
|
62
|
-
end
|
63
|
-
end.compact
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
data/lib/socky/message.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module Socky
|
4
|
-
# every message from admin is stored as instance of Message
|
5
|
-
# and then processed by #process method
|
6
|
-
class Message
|
7
|
-
include Socky::Misc
|
8
|
-
|
9
|
-
class InvalidJSON < Socky::SockyError; end #:nodoc:
|
10
|
-
class UnauthorisedQuery < Socky::SockyError; end #:nodoc:
|
11
|
-
class InvalidQuery < Socky::SockyError; end #:nodoc:
|
12
|
-
|
13
|
-
# message params like command type or message content
|
14
|
-
attr_reader :params
|
15
|
-
# message sender(admin) required when some data are returned
|
16
|
-
attr_reader :creator
|
17
|
-
|
18
|
-
class << self
|
19
|
-
# create new message and process it
|
20
|
-
# @see #process
|
21
|
-
# @param [Connection] connection creator of message
|
22
|
-
# @param [String] message message content
|
23
|
-
def process(connection, message)
|
24
|
-
message = new(connection, message)
|
25
|
-
message.process
|
26
|
-
rescue SockyError => error
|
27
|
-
error connection.name, error
|
28
|
-
connection.send_message(error.message)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# initialize new message
|
33
|
-
# @param [Connection] creator creator of message
|
34
|
-
# @param [String] message valid json containing hash of params
|
35
|
-
# @raise [InvalidJSON] if message is invalid json or don't evaluate to hash
|
36
|
-
def initialize(creator, message)
|
37
|
-
@params = symbolize_keys(JSON.parse(message)) rescue raise(InvalidJSON, "invalid request")
|
38
|
-
@creator = creator
|
39
|
-
end
|
40
|
-
|
41
|
-
# process message - check command('broadcast' or 'query')
|
42
|
-
# and send message to correct connections
|
43
|
-
# 'broadcast' command require 'body' of message and allows 'to' and 'except' hashes for filters
|
44
|
-
# 'query' command require 'type' of query - currently only 'show_connections' is supported
|
45
|
-
# @see Socky::Connection::Finders.find filtering options
|
46
|
-
# @raise [InvalidQuery, 'unknown command'] when 'command' param is invalid
|
47
|
-
# @raise [InvalidQuery, 'unknown query type'] when 'command' is 'queru' but no 'type' is provided
|
48
|
-
def process
|
49
|
-
debug [self.name, "processing", params.inspect]
|
50
|
-
|
51
|
-
case params.delete(:command).to_s
|
52
|
-
when "broadcast" then broadcast
|
53
|
-
when "query" then query
|
54
|
-
else raise(InvalidQuery, "unknown command")
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def broadcast
|
61
|
-
connections = Socky::Connection.find(params)
|
62
|
-
send_message(params[:body], connections)
|
63
|
-
end
|
64
|
-
|
65
|
-
def query
|
66
|
-
case params[:type].to_s
|
67
|
-
when "show_connections" then query_show_connections
|
68
|
-
else raise(InvalidQuery, "unknown query type")
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def query_show_connections
|
73
|
-
respond Socky::Connection.find_all
|
74
|
-
end
|
75
|
-
|
76
|
-
def respond(message)
|
77
|
-
creator.send_message(message)
|
78
|
-
end
|
79
|
-
|
80
|
-
def send_message(message, connections)
|
81
|
-
connections.each{|connection| connection.send_message message}
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
data/lib/socky/misc.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
module Socky
|
2
|
-
# common methods for all other classes
|
3
|
-
module Misc
|
4
|
-
|
5
|
-
# extend including class by itself
|
6
|
-
def self.included(base)
|
7
|
-
base.extend Socky::Misc
|
8
|
-
end
|
9
|
-
|
10
|
-
# return server-wide options
|
11
|
-
# @see Socky.options
|
12
|
-
def options
|
13
|
-
Socky.options
|
14
|
-
end
|
15
|
-
|
16
|
-
# write server-wide options
|
17
|
-
def options=(ob)
|
18
|
-
Socky.options = ob
|
19
|
-
end
|
20
|
-
|
21
|
-
# return name of current object
|
22
|
-
# @example when included in connection
|
23
|
-
# @connection.name #=> "Connection(2149785820)"
|
24
|
-
def name
|
25
|
-
"#{self.class.to_s.split("::").last}(#{self.object_id})"
|
26
|
-
end
|
27
|
-
|
28
|
-
# return log path
|
29
|
-
def log_path
|
30
|
-
Socky.log_path
|
31
|
-
end
|
32
|
-
|
33
|
-
# return pid path
|
34
|
-
def pid_path
|
35
|
-
Socky.pid_path
|
36
|
-
end
|
37
|
-
|
38
|
-
# return config path
|
39
|
-
def config_path
|
40
|
-
Socky.config_path
|
41
|
-
end
|
42
|
-
|
43
|
-
# log message at info level
|
44
|
-
# @param [Array] args data for logging
|
45
|
-
def info(args)
|
46
|
-
Socky.logger.info args.join(" ")
|
47
|
-
end
|
48
|
-
|
49
|
-
# log message at debug level
|
50
|
-
# @param [Array] args data for logging
|
51
|
-
def debug(args)
|
52
|
-
Socky.logger.debug args.join(" ")
|
53
|
-
end
|
54
|
-
|
55
|
-
# log message at error level
|
56
|
-
# @param [String] name object name with raised error
|
57
|
-
# @param [Error] error error instance that was raised
|
58
|
-
def error(name, error)
|
59
|
-
debug [name, "raised:", error.class, error.message]
|
60
|
-
end
|
61
|
-
|
62
|
-
# convert keys of hash to symbol
|
63
|
-
# @param [Hash] hash hash to symbolize
|
64
|
-
# @return [Hash] with symbolized keys
|
65
|
-
# @return [Object] if hash isn't instance of Hash
|
66
|
-
def symbolize_keys(hash)
|
67
|
-
return hash unless hash.is_a?(Hash)
|
68
|
-
hash.inject({}) do |options, (key, value)|
|
69
|
-
options[(key.to_sym if key.respond_to?(:to_sym)) || key] = value
|
70
|
-
options
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/lib/socky/net_request.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'em-http'
|
2
|
-
|
3
|
-
module Socky
|
4
|
-
# this class provide unobtrusive http request methods
|
5
|
-
class NetRequest
|
6
|
-
include Socky::Misc
|
7
|
-
|
8
|
-
class << self
|
9
|
-
|
10
|
-
# send unobtrusive http POST request to gived address and return status of request as block response
|
11
|
-
# @param [String] url address to send request in format 'http://address[:port]/[path]'
|
12
|
-
# @param [Hash] params params for request(will be attached in post message)
|
13
|
-
# @yield [Boolean] called after request is finished - if response status is 200 then it's true, else false
|
14
|
-
def post(url, params = {}, &block)
|
15
|
-
http = EventMachine::HttpRequest.new(url).post :body => params, :timeout => options[:timeout] || 3
|
16
|
-
http.errback { yield false }
|
17
|
-
http.callback { yield http.response_header.status == 200 }
|
18
|
-
true
|
19
|
-
rescue => error
|
20
|
-
error "Bad request", error
|
21
|
-
false
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
data/lib/socky/options.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'socky/options/config'
|
2
|
-
require 'socky/options/parser'
|
3
|
-
|
4
|
-
module Socky
|
5
|
-
# options parser - reads options from STDIN and config file and set Socky.options
|
6
|
-
class Options
|
7
|
-
include Socky::Misc
|
8
|
-
|
9
|
-
class << self
|
10
|
-
# prepare server-wide options from config and parser
|
11
|
-
# @param [Array] argv arguments that will be provided to parser
|
12
|
-
# @see default_options default options
|
13
|
-
# @see Config.read merged with default options
|
14
|
-
# @see Parser.parse merges with default options after config
|
15
|
-
def prepare(argv)
|
16
|
-
self.options = default_options
|
17
|
-
|
18
|
-
parsed_options = Parser.parse(argv)
|
19
|
-
config_options = Config.read(parsed_options[:config_path] || config_path, :kill => parsed_options[:kill])
|
20
|
-
|
21
|
-
self.options.merge!(config_options)
|
22
|
-
self.options.merge!(parsed_options)
|
23
|
-
end
|
24
|
-
|
25
|
-
# default options for server
|
26
|
-
def default_options
|
27
|
-
{
|
28
|
-
:config_path => config_path,
|
29
|
-
:port => 8080,
|
30
|
-
:debug => false,
|
31
|
-
:deep_debug => false,
|
32
|
-
:secure => false,
|
33
|
-
:log_path => log_path
|
34
|
-
}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
data/lib/socky/options/config.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'erb'
|
3
|
-
|
4
|
-
module Socky
|
5
|
-
class Options
|
6
|
-
# config parser class - used by Socky::Options
|
7
|
-
class Config
|
8
|
-
|
9
|
-
class NoConfigFile < Socky::SockyError; end #:nodoc:
|
10
|
-
class InvalidConfig < Socky::SockyError; end #:nodoc:
|
11
|
-
class AlreadyExists < Socky::SockyError; end #:nodoc:
|
12
|
-
class ConfigUnavailable < Socky::SockyError; end #:nodoc:
|
13
|
-
class SuccessfullyCreated < Socky::SockyError; end #:nodoc:
|
14
|
-
|
15
|
-
class << self
|
16
|
-
# read config file or exits if file don't exists or is invalid
|
17
|
-
# @param [String] path path to valid yaml file
|
18
|
-
# @param [Hash] args args to rescue eventual problems
|
19
|
-
# @option args [Any] kill (nil) if not nil then empty hash will be returned if config file isn't found
|
20
|
-
# @return [Hash] parsed config options
|
21
|
-
# @raise [NoConfigFile] if file doesn't exists
|
22
|
-
# @raise [InvalidConfig] if file isn't valid yaml
|
23
|
-
def read(path, args = {})
|
24
|
-
raise(NoConfigFile, "You must generate a config file (socky -g filename.yml)") unless File.exists?(path)
|
25
|
-
result = YAML::load(ERB.new(IO.read(path)).result)
|
26
|
-
raise(InvalidConfig, "Provided config file is invalid.") unless result.is_a?(Hash)
|
27
|
-
result
|
28
|
-
rescue SockyError => error
|
29
|
-
if args[:kill]
|
30
|
-
return {}
|
31
|
-
else
|
32
|
-
puts error.message
|
33
|
-
exit
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# generate default config file
|
38
|
-
# @see DEFAULT_CONFIG_FILE
|
39
|
-
# @param [String] path path to file that will be created
|
40
|
-
# @raise [AlreadyExists] if file exists(you must delete it manually)
|
41
|
-
# @raise [ConfigUnavailable] if file cannot be created(wrong privilages?)
|
42
|
-
# @raise [SuccessfullyCreated] if file is successfully created
|
43
|
-
def generate(path)
|
44
|
-
raise(AlreadyExists, "Config file already exists. You must remove it before generating a new one.") if File.exists?(path)
|
45
|
-
File.open(path, 'w+') do |file|
|
46
|
-
file.write DEFAULT_CONFIG_FILE
|
47
|
-
end rescue raise(ConfigUnavailable, "Config file is unavailable - please choose another.")
|
48
|
-
raise(SuccessfullyCreated, "Config file generated at #{path}")
|
49
|
-
rescue SockyError => error
|
50
|
-
puts error.message
|
51
|
-
exit
|
52
|
-
end
|
53
|
-
|
54
|
-
# default config file content
|
55
|
-
DEFAULT_CONFIG_FILE= <<-EOF
|
56
|
-
:port: 8080
|
57
|
-
:debug: false
|
58
|
-
|
59
|
-
# :subscribe_url: http://localhost:3000/socky/subscribe
|
60
|
-
# :unsubscribe_url: http://localhost:3000/socky/unsubscribe
|
61
|
-
|
62
|
-
:secret: my_secret_key
|
63
|
-
|
64
|
-
:secure: false
|
65
|
-
|
66
|
-
# :timeout: 3
|
67
|
-
|
68
|
-
# :log_path: /var/log/socky.log
|
69
|
-
# :pid_path: /var/run/socky.pid
|
70
|
-
|
71
|
-
# :tls_options:
|
72
|
-
# :private_key_file: /private/key
|
73
|
-
# :cert_chain_file: /ssl/certificate
|
74
|
-
EOF
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|