socky-server 0.4.1 → 0.5.0.beta1
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/.gitignore +0 -4
- data/.travis.yml +6 -0
- data/CHANGELOG.md +11 -5
- data/Gemfile +2 -0
- data/README.md +47 -68
- data/Rakefile +5 -7
- data/config.ru +19 -0
- data/example/config.yml +4 -0
- data/lib/socky/server.rb +23 -0
- data/lib/socky/server/application.rb +51 -0
- data/lib/socky/server/channel.rb +30 -0
- data/lib/socky/server/channel/base.rb +80 -0
- data/lib/socky/server/channel/presence.rb +49 -0
- data/lib/socky/server/channel/private.rb +44 -0
- data/lib/socky/server/channel/public.rb +43 -0
- data/lib/socky/server/channel/stub.rb +17 -0
- data/lib/socky/server/config.rb +52 -0
- data/lib/socky/server/connection.rb +66 -0
- data/lib/socky/server/http.rb +95 -0
- data/lib/socky/server/logger.rb +24 -0
- data/lib/socky/server/message.rb +35 -0
- data/lib/socky/server/misc.rb +18 -0
- data/lib/socky/server/version.rb +5 -0
- data/lib/socky/server/websocket.rb +43 -0
- data/socky-server.gemspec +5 -7
- data/spec/fixtures/example_config.yml +3 -0
- data/spec/integration/ws_channels_spec.rb +144 -0
- data/spec/integration/ws_connection_spec.rb +48 -0
- data/spec/integration/ws_presence_spec.rb +118 -0
- data/spec/integration/ws_rights_spec.rb +133 -0
- data/spec/spec_helper.rb +24 -2
- data/spec/support/websocket_application.rb +14 -0
- data/spec/unit/socky/server/application_spec.rb +54 -0
- data/spec/unit/socky/server/config_spec.rb +50 -0
- data/spec/unit/socky/server/connection_spec.rb +67 -0
- data/spec/unit/socky/server/message_spec.rb +64 -0
- metadata +93 -126
- data/bin/socky +0 -5
- data/lib/em-websocket_hacks.rb +0 -15
- data/lib/socky.rb +0 -75
- 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/support/stallion.rb +0 -96
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
|
data/lib/socky/options/parser.rb
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
|
3
|
-
module Socky
|
4
|
-
class Options
|
5
|
-
# STDIN options parser - used by Socky::Options
|
6
|
-
class Parser
|
7
|
-
|
8
|
-
class << self
|
9
|
-
# parse options(usually from STDIN)
|
10
|
-
# see source code for available options
|
11
|
-
# @param [Array] argv options for parser
|
12
|
-
# @return [Hash] parsed options from array
|
13
|
-
def parse(argv)
|
14
|
-
result = {}
|
15
|
-
opts = OptionParser.new do |opts|
|
16
|
-
opts.summary_width = 25
|
17
|
-
opts.banner = "Usage: socky [options]\n"
|
18
|
-
|
19
|
-
opts.separator ""
|
20
|
-
opts.separator "Configuration:"
|
21
|
-
|
22
|
-
opts.on("-g", "--generate FILE", String, "Generate config file") do |path|
|
23
|
-
result[:config_path] = File.expand_path(path) if path
|
24
|
-
Config.generate(result[:config_path])
|
25
|
-
end
|
26
|
-
|
27
|
-
opts.on("-c", "--config FILE", String, "Path to configuration file.", "(default: #{Socky.config_path})") do |path|
|
28
|
-
result[:config_path] = File.expand_path(path)
|
29
|
-
end
|
30
|
-
|
31
|
-
opts.separator ""; opts.separator "Network:"
|
32
|
-
|
33
|
-
opts.on("-p", "--port PORT", Integer, "Specify port", "(default: 8080)") do |port|
|
34
|
-
result[:port] = port
|
35
|
-
end
|
36
|
-
|
37
|
-
opts.on("-s", "--secure", "Run in wss/ssl mode") do
|
38
|
-
result[:secure] = true
|
39
|
-
end
|
40
|
-
|
41
|
-
opts.separator ""; opts.separator "Daemonization:"
|
42
|
-
|
43
|
-
opts.on("-d", "--daemon", "Daemonize mode") do
|
44
|
-
result[:daemonize] = true
|
45
|
-
end
|
46
|
-
|
47
|
-
opts.on("-P", "--pid FILE", String, "Path to PID file when using -d option") do |path|
|
48
|
-
result[:pid_path] = File.expand_path(path)
|
49
|
-
end
|
50
|
-
|
51
|
-
opts.on("-k", "--kill", "Kill daemon from specified pid file path") do
|
52
|
-
result[:kill] = true
|
53
|
-
end
|
54
|
-
|
55
|
-
opts.separator ""; opts.separator "Logging:"
|
56
|
-
|
57
|
-
opts.on("-l", "--log FILE", String, "Path to print debugging information.", "(Print to STDOUT if empty)") do |path|
|
58
|
-
result[:log_path] = File.expand_path(path)
|
59
|
-
end
|
60
|
-
|
61
|
-
opts.on("--debug", "Run in debug mode") do
|
62
|
-
result[:debug] = true
|
63
|
-
end
|
64
|
-
|
65
|
-
opts.on("--deep-debug", "Run in debug mode that is even more verbose") do
|
66
|
-
result[:debug] = true
|
67
|
-
result[:deep_debug] = true
|
68
|
-
end
|
69
|
-
|
70
|
-
opts.separator ""; opts.separator "Miscellaneous:"
|
71
|
-
|
72
|
-
opts.on_tail("-?", "--help", "Display this usage information.") do
|
73
|
-
puts "#{opts}\n"
|
74
|
-
exit
|
75
|
-
end
|
76
|
-
|
77
|
-
opts.on_tail("-v", "--version", "Display version") do
|
78
|
-
puts "Socky #{VERSION}"
|
79
|
-
exit
|
80
|
-
end
|
81
|
-
end
|
82
|
-
opts.parse!(argv)
|
83
|
-
result
|
84
|
-
rescue OptionParser::InvalidOption => error
|
85
|
-
puts "#{opts}\n"
|
86
|
-
puts error.message
|
87
|
-
exit
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
data/lib/socky/runner.rb
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
module Socky
|
2
|
-
# default runner class - creates server and runs it
|
3
|
-
class Runner
|
4
|
-
include Socky::Misc
|
5
|
-
|
6
|
-
class << self
|
7
|
-
# create new eventmachine server
|
8
|
-
def run(argv = ARGV)
|
9
|
-
server = self.new(argv)
|
10
|
-
|
11
|
-
if options[:kill]
|
12
|
-
server.kill_pid
|
13
|
-
elsif options[:daemonize]
|
14
|
-
server.daemonize
|
15
|
-
else
|
16
|
-
server.start
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# set server-wide options from args
|
22
|
-
def initialize(argv = ARGV)
|
23
|
-
Options.prepare(argv)
|
24
|
-
end
|
25
|
-
|
26
|
-
# start eventmachine server
|
27
|
-
# require options to be parsed earlier
|
28
|
-
def start
|
29
|
-
EventMachine.epoll
|
30
|
-
|
31
|
-
EventMachine.run do
|
32
|
-
|
33
|
-
trap("TERM") { stop }
|
34
|
-
trap("INT") { stop }
|
35
|
-
|
36
|
-
EventMachine::start_server("0.0.0.0", options[:port], EventMachine::WebSocket::Connection,
|
37
|
-
:debug => options[:deep_debug], :secure => options[:secure], :tls_options => options[:tls_options]) do |ws|
|
38
|
-
|
39
|
-
connection = Socky::Connection.new(ws)
|
40
|
-
ws.onopen { connection.subscribe }
|
41
|
-
ws.onmessage { |msg| connection.process_message(msg) }
|
42
|
-
ws.onclose { connection.unsubscribe }
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
info ["Server started"]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# stop eventmachine server
|
51
|
-
def stop
|
52
|
-
info ["Server stopping"]
|
53
|
-
EventMachine.stop
|
54
|
-
end
|
55
|
-
|
56
|
-
# run server in daemon mode
|
57
|
-
def daemonize
|
58
|
-
fork do
|
59
|
-
Process.setsid
|
60
|
-
exit if fork
|
61
|
-
store_pid(Process.pid)
|
62
|
-
# Dir.chdir "/" # Mucks up logs
|
63
|
-
File.umask 0000
|
64
|
-
STDIN.reopen "/dev/null"
|
65
|
-
STDOUT.reopen "/dev/null", "a"
|
66
|
-
STDERR.reopen STDOUT
|
67
|
-
start
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# kill daemonized server according to pid file in pid_path
|
72
|
-
def kill_pid
|
73
|
-
begin
|
74
|
-
pid = IO.read(pid_path).chomp.to_i
|
75
|
-
FileUtils.rm pid_path
|
76
|
-
Process.kill(9, pid)
|
77
|
-
puts "killed PID: #{pid}"
|
78
|
-
rescue => e
|
79
|
-
puts e
|
80
|
-
end
|
81
|
-
exit
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def store_pid(pid)
|
87
|
-
FileUtils.mkdir_p(File.dirname(pid_path))
|
88
|
-
File.open(pid_path, 'w'){|f| f.write("#{pid}\n")}
|
89
|
-
rescue => e
|
90
|
-
puts e
|
91
|
-
exit
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
end
|
data/spec/em-websocket_spec.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe EM::WebSocket::Connection do
|
4
|
-
|
5
|
-
it "should not receive debug message if :debug option is false" do
|
6
|
-
EM.run do
|
7
|
-
Socky.logger.should_not_receive(:debug)
|
8
|
-
EM.add_timer(0.1) do
|
9
|
-
http = EventMachine::HttpRequest.new('ws://127.0.0.1:9999/').get(:timeout => 0)
|
10
|
-
http.errback { http.close_connection }
|
11
|
-
http.callback { http.close_connection }
|
12
|
-
end
|
13
|
-
|
14
|
-
EM::WebSocket.start(:host => "127.0.0.1", :port => 9999, :debug => false) do |ws|
|
15
|
-
ws.onclose { EM.stop }
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should use Socky.logger.debug instead of pp when instance call #debug" do
|
21
|
-
EM.run do
|
22
|
-
Socky.logger.should_receive(:debug).with("Socket initialize")
|
23
|
-
Socky.logger.should_receive(:debug).with(anything()).at_least(:once)
|
24
|
-
EM.add_timer(0.1) do
|
25
|
-
http = EventMachine::HttpRequest.new('ws://127.0.0.1:9999/').get(:timeout => 0)
|
26
|
-
http.errback { http.close_connection }
|
27
|
-
http.callback { http.close_connection }
|
28
|
-
end
|
29
|
-
|
30
|
-
EM::WebSocket.start(:host => "127.0.0.1", :port => 9999, :debug => true) do |ws|
|
31
|
-
ws.onclose { EM.stop }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|