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.
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.md +0 -0
- data/Rakefile +60 -0
- data/bin/rocket-server +13 -0
- data/etc/testing.yml +38 -0
- data/lib/rocket-server.rb +2 -0
- data/lib/rocket/server.rb +87 -0
- data/lib/rocket/server/app.rb +44 -0
- data/lib/rocket/server/channel.rb +22 -0
- data/lib/rocket/server/cli.rb +52 -0
- data/lib/rocket/server/connection.rb +137 -0
- data/lib/rocket/server/helpers.rb +22 -0
- data/lib/rocket/server/misc.rb +17 -0
- data/lib/rocket/server/runner.rb +78 -0
- data/lib/rocket/server/session.rb +58 -0
- data/lib/rocket/server/templates/config.yml.tpl +40 -0
- data/lib/rocket/server/version.rb +14 -0
- data/rocket-server.gemspec +96 -0
- data/spec/apps_spec.rb +54 -0
- data/spec/channel_spec.rb +31 -0
- data/spec/cli_spec.rb +85 -0
- data/spec/connection_spec.rb +257 -0
- data/spec/helpers_spec.rb +23 -0
- data/spec/misc_spec.rb +31 -0
- data/spec/runner_spec.rb +138 -0
- data/spec/server_spec.rb +146 -0
- data/spec/session_spec.rb +110 -0
- data/spec/spec_helper.rb +22 -0
- metadata +237 -0
@@ -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,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
|
+
|
data/spec/apps_spec.rb
ADDED
@@ -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
|
data/spec/cli_spec.rb
ADDED
@@ -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
|