socky-server 0.4.1 → 0.5.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +0 -4
  2. data/.travis.yml +6 -0
  3. data/CHANGELOG.md +11 -5
  4. data/Gemfile +2 -0
  5. data/README.md +47 -68
  6. data/Rakefile +5 -7
  7. data/config.ru +19 -0
  8. data/example/config.yml +4 -0
  9. data/lib/socky/server.rb +23 -0
  10. data/lib/socky/server/application.rb +51 -0
  11. data/lib/socky/server/channel.rb +30 -0
  12. data/lib/socky/server/channel/base.rb +80 -0
  13. data/lib/socky/server/channel/presence.rb +49 -0
  14. data/lib/socky/server/channel/private.rb +44 -0
  15. data/lib/socky/server/channel/public.rb +43 -0
  16. data/lib/socky/server/channel/stub.rb +17 -0
  17. data/lib/socky/server/config.rb +52 -0
  18. data/lib/socky/server/connection.rb +66 -0
  19. data/lib/socky/server/http.rb +95 -0
  20. data/lib/socky/server/logger.rb +24 -0
  21. data/lib/socky/server/message.rb +35 -0
  22. data/lib/socky/server/misc.rb +18 -0
  23. data/lib/socky/server/version.rb +5 -0
  24. data/lib/socky/server/websocket.rb +43 -0
  25. data/socky-server.gemspec +5 -7
  26. data/spec/fixtures/example_config.yml +3 -0
  27. data/spec/integration/ws_channels_spec.rb +144 -0
  28. data/spec/integration/ws_connection_spec.rb +48 -0
  29. data/spec/integration/ws_presence_spec.rb +118 -0
  30. data/spec/integration/ws_rights_spec.rb +133 -0
  31. data/spec/spec_helper.rb +24 -2
  32. data/spec/support/websocket_application.rb +14 -0
  33. data/spec/unit/socky/server/application_spec.rb +54 -0
  34. data/spec/unit/socky/server/config_spec.rb +50 -0
  35. data/spec/unit/socky/server/connection_spec.rb +67 -0
  36. data/spec/unit/socky/server/message_spec.rb +64 -0
  37. metadata +93 -126
  38. data/bin/socky +0 -5
  39. data/lib/em-websocket_hacks.rb +0 -15
  40. data/lib/socky.rb +0 -75
  41. data/lib/socky/connection.rb +0 -137
  42. data/lib/socky/connection/authentication.rb +0 -99
  43. data/lib/socky/connection/finders.rb +0 -67
  44. data/lib/socky/message.rb +0 -85
  45. data/lib/socky/misc.rb +0 -74
  46. data/lib/socky/net_request.rb +0 -27
  47. data/lib/socky/options.rb +0 -39
  48. data/lib/socky/options/config.rb +0 -79
  49. data/lib/socky/options/parser.rb +0 -93
  50. data/lib/socky/runner.rb +0 -95
  51. data/spec/em-websocket_spec.rb +0 -36
  52. data/spec/files/default.yml +0 -18
  53. data/spec/files/invalid.yml +0 -1
  54. data/spec/socky/connection/authentication_spec.rb +0 -183
  55. data/spec/socky/connection/finders_spec.rb +0 -188
  56. data/spec/socky/connection_spec.rb +0 -151
  57. data/spec/socky/message_spec.rb +0 -102
  58. data/spec/socky/misc_spec.rb +0 -74
  59. data/spec/socky/net_request_spec.rb +0 -42
  60. data/spec/socky/options/config_spec.rb +0 -72
  61. data/spec/socky/options/parser_spec.rb +0 -76
  62. data/spec/socky/options_spec.rb +0 -60
  63. data/spec/socky/runner_spec.rb +0 -88
  64. data/spec/socky_spec.rb +0 -89
  65. data/spec/support/stallion.rb +0 -96
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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