rocket-server 0.0.1

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.
@@ -0,0 +1,22 @@
1
+ module Rocket
2
+ module Server
3
+ module Helpers
4
+
5
+ include Rocket::Helpers
6
+
7
+ # Proxy to Rocket's configured logger.
8
+ def log
9
+ Rocket::Server.logger
10
+ end
11
+
12
+ %w[ info debug warn error fatal ].each do |level|
13
+ define_method("log_#{level}") do |*args|
14
+ message = args.shift
15
+ message = self.class::LOG_MESSAGES[message] if message.is_a?(Symbol)
16
+ log.send(level, message % args)
17
+ end
18
+ end
19
+
20
+ end # Helpers
21
+ end # Server
22
+ end # Rocket
@@ -0,0 +1,17 @@
1
+ module Rocket
2
+ module Server
3
+ module Misc
4
+
5
+ # Generates example config file for Rocket server.
6
+ def self.generate_config_file(fname)
7
+ File.open(fname, "w+") do |f|
8
+ f.write(File.read(File.expand_path("../templates/config.yml.tpl", __FILE__)))
9
+ end
10
+ rescue => e
11
+ puts e.to_s
12
+ exit 1
13
+ end
14
+
15
+ end # Misc
16
+ end # Server
17
+ end # Rocket
@@ -0,0 +1,78 @@
1
+ require 'daemons'
2
+
3
+ module Rocket
4
+ module Server
5
+ class Runner
6
+
7
+ include Helpers
8
+ include Daemonize
9
+
10
+ LOG_MESSAGES = {
11
+ :starting_server => "Server is listening at %s:%s (CTRL+C to stop)",
12
+ :stopping_server => "Stopping Rocket server..."
13
+ }
14
+
15
+ attr_reader :options
16
+ attr_reader :host
17
+ attr_reader :port
18
+ attr_reader :pidfile
19
+ attr_reader :daemon
20
+
21
+ def initialize(options={})
22
+ @options = options.dup
23
+ @host = @options[:host] || 'localhost'
24
+ @port = @options[:port] || 9772
25
+ @pidfile = @options.delete(:pid) || "/var/run/rocket/server.pid"
26
+ @daemon = @options.delete(:daemon)
27
+
28
+ # Remove unnecessary options.
29
+ @options.reject!{|k,v| [:verbose, :quiet, :log, :apps, :plugins].include?(k) }
30
+ end
31
+
32
+ def start!(&block)
33
+ if daemon
34
+ daemonize!(&block)
35
+ else
36
+ EM.epoll
37
+ EM.run do
38
+ trap("TERM") { stop! }
39
+ trap("INT") { stop! }
40
+
41
+ log_info(:starting_server, host, port.to_s)
42
+ EM::start_server(host, port, Connection, options, &block)
43
+ end
44
+ end
45
+ end
46
+
47
+ def stop!
48
+ log_info(:stopping_server)
49
+ EM.stop
50
+ end
51
+
52
+ def kill!
53
+ if File.exists?(pidfile)
54
+ pid = File.read(pidfile).chomp.to_i
55
+ FileUtils.rm pidfile
56
+ Process.kill(9, pid)
57
+ pid
58
+ end
59
+ rescue => e
60
+ # nothing to show
61
+ end
62
+
63
+ def daemonize!(&block)
64
+ daemonize
65
+ start!(&block)
66
+ end
67
+
68
+ def save_pid
69
+ FileUtils.mkdir_p(File.dirname(pidfile))
70
+ File.open(pidfile, "w+"){|f| f.write("#{Process.pid}\n")}
71
+ rescue => e
72
+ puts e.to_s
73
+ exit 1
74
+ end
75
+
76
+ end # Runner
77
+ end # Server
78
+ end # Rocket
@@ -0,0 +1,58 @@
1
+ module Rocket
2
+ module Server
3
+ class Session
4
+
5
+ attr_reader :subscriptions
6
+ attr_reader :app
7
+
8
+ def initialize(app_id)
9
+ @app = Rocket::Server::App.find(app_id)
10
+ @subscriptions = {}
11
+ end
12
+
13
+ # Returns id of current application.
14
+ def app_id
15
+ @app.id
16
+ end
17
+
18
+ # Returns +true+ when current session is authenticated with secret key.
19
+ def authenticated?
20
+ !!@authenticated
21
+ end
22
+
23
+ # Authenticate current session with your secret key.
24
+ def authenticate!(secret)
25
+ @authenticated = (app.secret == secret)
26
+ end
27
+
28
+ # Subscribes specified channel by given connected client.
29
+ #
30
+ # subscribe("my-awesome-channel", connection) # => subscription ID
31
+ #
32
+ def subscribe(channel, connection)
33
+ sid = Channel[app_id => channel].subscribe {|msg| connection.send(msg) }
34
+ subscriptions[channel => connection.signature] = sid
35
+ sid
36
+ end
37
+
38
+ # Unsubscribes specified channel for given client.
39
+ #
40
+ # unsubscribe("my-awesome-channel", connection)
41
+ #
42
+ def unsubscribe(channel, connection)
43
+ if sid = subscriptions.delete(channel => connection.signature)
44
+ Channel[app_id => channel].unsubscribe(sid)
45
+ end
46
+ end
47
+
48
+ # Close current session and kill all active subscriptions.
49
+ def close
50
+ subscriptions.keys.each do |id|
51
+ channel, sig = id.to_a.flatten
52
+ Channel[app_id => channel].unsubscribe(subscriptions.delete(channel => sig))
53
+ end
54
+ end
55
+
56
+ end # Session
57
+ end # Server
58
+ end # Rocket
@@ -0,0 +1,40 @@
1
+ # On which host and port server should listen for connections.
2
+ host: localhost
3
+ port: 9772
4
+
5
+ # Run applicaiton in secured mode (wss).
6
+ #secure: true
7
+ secure: false
8
+
9
+ # Specify prefered verbosity level.
10
+ #verbose: true # Increase verbosity
11
+ #quiet: true # Decrease verbosity
12
+ verbose: true
13
+
14
+ # Run web sockets server in debug mode.
15
+ #debug: true
16
+ debug: false
17
+
18
+ # Run daemonized instance of Rocket server.
19
+ #daemon: true
20
+ daemon: false
21
+
22
+ # Specify path to server process' PID file. This option works only
23
+ # when server is daemonized.
24
+ #pid: /var/run/rocket.pid
25
+
26
+ # Specify path to server's output log.
27
+ #log: /var/log/rocket.log
28
+
29
+ # SSL certificates configuration.
30
+ #tls_options:
31
+ # private_key_file: /home/ssl/private.key
32
+ # cert_chain_file: /home/ssl/certificate
33
+
34
+ # Here you can specify all static apps allowed to run on this server.
35
+ # Apps are kind of namespaces with separated authentication.
36
+ apps:
37
+ test_app_1:
38
+ secret: secretkey1
39
+ test_app_2:
40
+ secret: secretkey2
@@ -0,0 +1,14 @@
1
+ module Rocket
2
+ module Server
3
+ module Version # :nodoc:
4
+ MAJOR = 0
5
+ MINOR = 0
6
+ TINY = 1
7
+ STRING = [MAJOR, MINOR, TINY].join(".")
8
+ end # Version
9
+
10
+ def self.version # :nodoc:
11
+ Version::STRING
12
+ end
13
+ end # Server
14
+ end # Rocket
@@ -0,0 +1,96 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rocket-server}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Araneo", "Chris Kowalik"]
12
+ s.date = %q{2010-11-03}
13
+ s.description = %q{
14
+ This is a fast web socket server built on awesome EventMachine with em-websockets help.
15
+ It provides easy to use, event-oriented middleware for your web-socket powered applications.
16
+ }
17
+ s.email = %q{chris@nu7hat.ch}
18
+ s.extra_rdoc_files = [
19
+ "LICENSE",
20
+ "README.md"
21
+ ]
22
+ s.files = [
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.md",
26
+ "Rakefile",
27
+ "bin/rocket-server",
28
+ "etc/testing.yml",
29
+ "lib/rocket-server.rb",
30
+ "lib/rocket/server.rb",
31
+ "lib/rocket/server/app.rb",
32
+ "lib/rocket/server/channel.rb",
33
+ "lib/rocket/server/cli.rb",
34
+ "lib/rocket/server/connection.rb",
35
+ "lib/rocket/server/helpers.rb",
36
+ "lib/rocket/server/misc.rb",
37
+ "lib/rocket/server/runner.rb",
38
+ "lib/rocket/server/session.rb",
39
+ "lib/rocket/server/templates/config.yml.tpl",
40
+ "lib/rocket/server/version.rb",
41
+ "rocket-server.gemspec",
42
+ "spec/apps_spec.rb",
43
+ "spec/channel_spec.rb",
44
+ "spec/cli_spec.rb",
45
+ "spec/connection_spec.rb",
46
+ "spec/helpers_spec.rb",
47
+ "spec/misc_spec.rb",
48
+ "spec/runner_spec.rb",
49
+ "spec/server_spec.rb",
50
+ "spec/session_spec.rb",
51
+ "spec/spec_helper.rb"
52
+ ]
53
+ s.homepage = %q{http://github.com/araneo/rocket}
54
+ s.rdoc_options = ["--charset=UTF-8"]
55
+ s.require_paths = ["lib"]
56
+ s.rubygems_version = %q{1.3.7}
57
+ s.summary = %q{Fast and extensible web socket server built upon em-websockets.}
58
+
59
+ if s.respond_to? :specification_version then
60
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
61
+ s.specification_version = 3
62
+
63
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
+ s.add_runtime_dependency(%q<optitron>, ["~> 0.2"])
65
+ s.add_runtime_dependency(%q<json>, ["~> 1.4"])
66
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0.12"])
67
+ s.add_runtime_dependency(%q<em-websocket>, [">= 0.1.4"])
68
+ s.add_runtime_dependency(%q<logging>, ["~> 1.4"])
69
+ s.add_runtime_dependency(%q<daemons>, ["~> 1.1"])
70
+ s.add_runtime_dependency(%q<konfigurator>, [">= 0.1.1"])
71
+ s.add_development_dependency(%q<rspec>, ["~> 2.0"])
72
+ s.add_development_dependency(%q<mocha>, ["~> 0.9"])
73
+ else
74
+ s.add_dependency(%q<optitron>, ["~> 0.2"])
75
+ s.add_dependency(%q<json>, ["~> 1.4"])
76
+ s.add_dependency(%q<eventmachine>, [">= 0.12"])
77
+ s.add_dependency(%q<em-websocket>, [">= 0.1.4"])
78
+ s.add_dependency(%q<logging>, ["~> 1.4"])
79
+ s.add_dependency(%q<daemons>, ["~> 1.1"])
80
+ s.add_dependency(%q<konfigurator>, [">= 0.1.1"])
81
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
82
+ s.add_dependency(%q<mocha>, ["~> 0.9"])
83
+ end
84
+ else
85
+ s.add_dependency(%q<optitron>, ["~> 0.2"])
86
+ s.add_dependency(%q<json>, ["~> 1.4"])
87
+ s.add_dependency(%q<eventmachine>, [">= 0.12"])
88
+ s.add_dependency(%q<em-websocket>, [">= 0.1.4"])
89
+ s.add_dependency(%q<logging>, ["~> 1.4"])
90
+ s.add_dependency(%q<daemons>, ["~> 1.1"])
91
+ s.add_dependency(%q<konfigurator>, [">= 0.1.1"])
92
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
93
+ s.add_dependency(%q<mocha>, ["~> 0.9"])
94
+ end
95
+ end
96
+
@@ -0,0 +1,54 @@
1
+ require File.expand_path("../spec_helper", __FILE__)
2
+
3
+ describe Rocket::Server::App do
4
+ subject do
5
+ Rocket::Server.expects(:apps).returns({"test-app" => {"secret" => "my-secret"}})
6
+ Rocket::Server::App
7
+ end
8
+
9
+ describe ".all" do
10
+ it "should return list of all registered apps" do
11
+ all = subject.all
12
+ all.should have(1).item
13
+ all.first.id.should == "test-app"
14
+ end
15
+ end
16
+
17
+ describe ".find" do
18
+ context "when there is app with given id" do
19
+ it "should return it" do
20
+ app = subject.find("test-app")
21
+ app.should be
22
+ app.id.should == "test-app"
23
+ end
24
+ end
25
+
26
+ context "when there is no app with given id" do
27
+ it "should raise error" do
28
+ expect { subject.find("no-such-app") }.to raise_error(Rocket::Server::App::NotFoundError)
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "instance" do
34
+ subject do
35
+ Rocket::Server::App.new(@attrs = {"id" => "test", "secret" => "my-secret"})
36
+ end
37
+
38
+ describe "#id" do
39
+ it "should return app identifier" do
40
+ subject.id.should == "test"
41
+ end
42
+ end
43
+
44
+ describe "#initialize" do
45
+ it "should set given attributes" do
46
+ subject.attributes.should == @attrs
47
+ end
48
+ end
49
+
50
+ it "should redirect missing methods to attibutes" do
51
+ subject.secret.should == "my-secret"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path("../spec_helper", __FILE__)
2
+
3
+ describe Rocket::Server::Channel do
4
+ subject do
5
+ Rocket::Server::Channel
6
+ end
7
+
8
+ describe ".[]" do
9
+ context "when given channel doesn't exist" do
10
+ it "should create it and return" do
11
+ subject.channels.replace({})
12
+ channel = subject["my-app" => "my-channel"]
13
+ channel.should be_kind_of subject
14
+ subject.channels[["my-app", "my-channel"]].should == channel
15
+ end
16
+ end
17
+
18
+ context "when given channel exists" do
19
+ it "should return it" do
20
+ subject["my-second-app" => "my-channel"]
21
+ subject["my-second-app" => "my-channel"].should == subject.channels[["my-second-app", "my-channel"]]
22
+ end
23
+ end
24
+ end
25
+
26
+ describe ".channels" do
27
+ it "should keep list of registered channels" do
28
+ subject.channels.should have(2).items
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,85 @@
1
+ require File.expand_path("../spec_helper", __FILE__)
2
+
3
+ describe Rocket::Server::CLI do
4
+ subject do
5
+ Rocket::Server::CLI
6
+ end
7
+
8
+ describe "#start" do
9
+ it "should properly start server" do
10
+ out, err = capture_output {
11
+ mock_server = mock(:start! => true)
12
+ Rocket::Server.expects(:load_settings).with('test.yml', {
13
+ :verbose => false, :debug => false, :secure => false, :quiet => false,
14
+ :pid => '/var/run/rocket/server.pid', :daemon => false, :host => 'localhost',
15
+ :plugins => [], :port => 9772, :help => false
16
+ }).returns(true)
17
+ Rocket::Server::Runner.expects(:new).returns(mock_server)
18
+ subject.dispatch(%w[start -c test.yml])
19
+ }
20
+ end
21
+ end
22
+
23
+ describe "#stop" do
24
+ context "when proper pidfile given" do
25
+ it "should kill that process" do
26
+ out, err = capture_output {
27
+ mock_server = mock(:kill! => 123)
28
+ Rocket::Server.expects(:load_settings).with('test.yml', :pid => 'test.pid', :help => false).returns(true)
29
+ Rocket::Server::Runner.expects(:new).returns(mock_server)
30
+ subject.dispatch(%w[stop -c test.yml -P test.pid])
31
+ }
32
+ out.should == "Rocket server killed (PID: 123)\n"
33
+ end
34
+ end
35
+
36
+ context "when invalid pid given" do
37
+ it "should do nothing" do
38
+ out, err = capture_output {
39
+ mock_server = mock(:kill! => false)
40
+ Rocket::Server.expects(:load_settings).with('test.yml', :pid => 'test.pid', :help => false).returns(true)
41
+ Rocket::Server::Runner.expects(:new).returns(mock_server)
42
+ subject.dispatch(%w[stop -c test.yml -P test.pid])
43
+ }
44
+ out.should == "No processes were killed!\n"
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "#version" do
50
+ it "should display current version of Rocket::Server server" do
51
+ capture_output { subject.dispatch(%w[version]) }
52
+ end
53
+ end
54
+
55
+ describe "#configure" do
56
+ context "when no file name given" do
57
+ it "should create rocket.yml file in current directory" do
58
+ capture_output {
59
+ begin
60
+ fname = File.join(Dir.pwd, "rocket.yml")
61
+ subject.dispatch(%w[configure])
62
+ File.should be_exist(fname)
63
+ ensure
64
+ FileUtils.rm_rf(fname)
65
+ end
66
+ }
67
+ end
68
+ end
69
+
70
+ context "when file name given" do
71
+ it "should create it" do
72
+ capture_output {
73
+ begin
74
+ fname = File.join(Dir.pwd, "tmp/test.yml")
75
+ FileUtils.mkdir_p(File.dirname(fname))
76
+ subject.dispatch(%w[configure tmp/test.yml])
77
+ File.should be_exist(fname)
78
+ ensure
79
+ FileUtils.rm_rf(File.dirname(fname))
80
+ end
81
+ }
82
+ end
83
+ end
84
+ end
85
+ end