rity 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -3
- data/lib/rity.rb +0 -1
- data/lib/rity/cli.rb +43 -3
- data/lib/rity/connection.rb +27 -11
- data/lib/rity/request.rb +11 -4
- data/lib/rity/version.rb +1 -1
- data/rity.gemspec +2 -0
- data/spec/integration/start_spec.rb +140 -0
- data/spec/spec_helper.rb +37 -1
- data/spec/unit/connection_spec.rb +6 -6
- metadata +27 -5
- data/lib/rity/server.rb +0 -27
data/README.md
CHANGED
@@ -6,11 +6,9 @@ Rity is a lightweight Ruby webserver that runs inside an EventMachine loop and p
|
|
6
6
|
TODO
|
7
7
|
----
|
8
8
|
|
9
|
-
- Command Line Interface
|
10
|
-
- Let Request handle parsing and building errors
|
11
|
-
- More robust error handling in general
|
12
9
|
- Proxying via EM.enable_proxy
|
13
10
|
- Rack Handler
|
14
11
|
- Support for keep-alive connections
|
15
12
|
- Investigate MVM support in JRuby/Rubinius/MRI
|
16
13
|
- Support for SPDY
|
14
|
+
- Investigate preforking and letting multiple EventMachine loops listen on a shared socket
|
data/lib/rity.rb
CHANGED
data/lib/rity/cli.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
require "rity"
|
2
2
|
require "thor"
|
3
3
|
|
4
|
+
require "eventmachine"
|
5
|
+
require "em-synchrony"
|
6
|
+
require "rack"
|
7
|
+
require "logger"
|
8
|
+
|
4
9
|
module Rity
|
5
10
|
class CLI < Thor
|
6
|
-
map "--version" => :version
|
11
|
+
map "--version" => :version
|
12
|
+
map "-v" => :version
|
13
|
+
|
14
|
+
default_task :start
|
7
15
|
|
8
16
|
desc :version, "Print version information"
|
9
17
|
def version
|
@@ -11,9 +19,41 @@ module Rity
|
|
11
19
|
end
|
12
20
|
|
13
21
|
desc :start, "Start an instance of Rity"
|
22
|
+
method_option :bind, :aliases => "-b", :type => :string,
|
23
|
+
:banner => "Bind to the specified TCP interface (default: 127.0.0.1)"
|
24
|
+
method_option :port, :aliases => "-p", :type => :numeric,
|
25
|
+
:banner => "Bind to the specified port (default: 3000)"
|
26
|
+
method_option :rackup, :aliases => "-r", :type => :string,
|
27
|
+
:banner => "Load specified rackup (.ru) file (default: config.ru)"
|
28
|
+
method_option :quiet, :aliases => "-q", :type => :boolean,
|
29
|
+
:banner => "Don't log to stderr"
|
14
30
|
def start
|
15
|
-
|
16
|
-
|
31
|
+
address = options[:bind] || "127.0.0.1"
|
32
|
+
port = options[:port] || 3000
|
33
|
+
|
34
|
+
rackup = options[:rackup] || "config.ru"
|
35
|
+
app = Rack::Builder.parse_file(rackup)[0]
|
36
|
+
|
37
|
+
unless options[:quiet]
|
38
|
+
log = Logger.new($stderr)
|
39
|
+
log.formatter = proc do |severity, time, progname, message|
|
40
|
+
"[#{time}] #{severity}: #{message}\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
log.info("Binding to #{address}:#{port}")
|
44
|
+
end
|
45
|
+
|
46
|
+
EM.synchrony do
|
47
|
+
trap("INT") { EM.stop }
|
48
|
+
trap("TERM") { EM.stop }
|
49
|
+
|
50
|
+
EM.epoll
|
51
|
+
|
52
|
+
EM.start_server(address, port, Connection) do |conn|
|
53
|
+
conn.app = app
|
54
|
+
conn.log = log if defined? log
|
55
|
+
end
|
56
|
+
end
|
17
57
|
end
|
18
58
|
end
|
19
59
|
end
|
data/lib/rity/connection.rb
CHANGED
@@ -16,7 +16,7 @@ module Rity
|
|
16
16
|
request = Request.new(@app, verb, url)
|
17
17
|
request.log = log
|
18
18
|
@requests.push(request)
|
19
|
-
@requests.last.callback
|
19
|
+
@requests.last.callback &method(:write_responses)
|
20
20
|
end
|
21
21
|
|
22
22
|
p.on_header do |name, value|
|
@@ -33,6 +33,15 @@ module Rity
|
|
33
33
|
|
34
34
|
p.on_complete do
|
35
35
|
@requests.last.call
|
36
|
+
request = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
p.on_error do |e|
|
40
|
+
if request
|
41
|
+
request.error(e)
|
42
|
+
else
|
43
|
+
raise(e)
|
44
|
+
end
|
36
45
|
end
|
37
46
|
end
|
38
47
|
|
@@ -42,22 +51,29 @@ module Rity
|
|
42
51
|
b.on_complete do
|
43
52
|
close_connection_after_writing if @requests.empty?
|
44
53
|
end
|
54
|
+
|
55
|
+
b.on_error &method(:error)
|
45
56
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
Fiber.yield
|
54
|
-
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def write_responses
|
60
|
+
while requests[0] && requests[0].response
|
61
|
+
request = requests.shift
|
62
|
+
builder.response(request.response)
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
58
66
|
def receive_data(data)
|
59
67
|
@parser << data
|
60
|
-
rescue Exception
|
68
|
+
rescue Exception => e
|
69
|
+
error(e)
|
70
|
+
end
|
71
|
+
|
72
|
+
def error(e)
|
73
|
+
if log
|
74
|
+
log.error(e.message)
|
75
|
+
log << e.backtrace.join("\n") + "\n"
|
76
|
+
end
|
61
77
|
close_connection
|
62
78
|
end
|
63
79
|
end
|
data/lib/rity/request.rb
CHANGED
@@ -54,16 +54,23 @@ module Rity
|
|
54
54
|
def postcall(response)
|
55
55
|
return if @response || response[0] < 0
|
56
56
|
@response = response
|
57
|
-
log.info("#{response[0]} - #{env["REQUEST_METHOD"]} #{env["REQUEST_URI"]}") if log
|
58
57
|
succeed
|
59
58
|
end
|
60
59
|
|
60
|
+
def error(e)
|
61
|
+
if log
|
62
|
+
log.error(e.message)
|
63
|
+
log << e.backtrace.join("\n") + "\n"
|
64
|
+
end
|
65
|
+
postcall [500, {"Content-Type" => "text/html"},
|
66
|
+
["<h1>#{Hatetepe::STATUS_CODES[500]}</h1>"]]
|
67
|
+
end
|
68
|
+
|
61
69
|
def rescue_errors
|
62
70
|
begin
|
63
71
|
yield
|
64
|
-
rescue Exception
|
65
|
-
|
66
|
-
["<h1>#{Hatetepe::STATUS_CODES[500]}</h1>"]])
|
72
|
+
rescue Exception => e
|
73
|
+
error(e)
|
67
74
|
end
|
68
75
|
end
|
69
76
|
end
|
data/lib/rity/version.rb
CHANGED
data/rity.gemspec
CHANGED
@@ -20,6 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_dependency "thor"
|
21
21
|
|
22
22
|
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "fakefs"
|
24
|
+
s.add_development_dependency "em-http-request"
|
23
25
|
|
24
26
|
s.files = `git ls-files`.split("\n") - [".gitignore"]
|
25
27
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "rity/cli"
|
3
|
+
require "socket"
|
4
|
+
require "em-synchrony/em-http"
|
5
|
+
|
6
|
+
describe "start command" do
|
7
|
+
def hook_event_loop(&block)
|
8
|
+
EM.spec_hooks << block
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_stop_timer(timeout)
|
12
|
+
hook_event_loop do
|
13
|
+
EM.add_timer(timeout) { EM.stop }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
before do
|
18
|
+
$stderr = StringIO.new ""
|
19
|
+
|
20
|
+
FakeFS.activate!
|
21
|
+
File.open("config.ru", "w") do |f|
|
22
|
+
f.write %q{run proc {|e| [200, {"Content-Type" => "text/plain"}, [e["REQUEST_URI"]]] }}
|
23
|
+
end
|
24
|
+
File.open("config2.ru", "w") do |f|
|
25
|
+
f.write %q{run proc {|e| [200, {"Content-Type" => "text/plain"}, ["config2.ru loaded"]] }}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
after do
|
30
|
+
$stderr = STDERR
|
31
|
+
|
32
|
+
FakeFS.deactivate!
|
33
|
+
FakeFS::FileSystem.clear
|
34
|
+
end
|
35
|
+
|
36
|
+
it "starts an instance of Rity" do
|
37
|
+
add_stop_timer 0.01
|
38
|
+
hook_event_loop do
|
39
|
+
Socket.tcp("127.0.0.1", 3000) {|*| }
|
40
|
+
end
|
41
|
+
Rity::CLI.start %w{}
|
42
|
+
|
43
|
+
$stderr.string.should include("127.0.0.1:3000")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "answers HTTP requests" do
|
47
|
+
add_stop_timer 0.02
|
48
|
+
hook_event_loop do
|
49
|
+
request = EM::HttpRequest.new("http://127.0.0.1:3000").aget
|
50
|
+
response = EM::Synchrony.sync(request)
|
51
|
+
|
52
|
+
response.response_header.status.should == 200
|
53
|
+
response.response_header["CONTENT_TYPE"].should == "text/plain"
|
54
|
+
response.response.should == "/"
|
55
|
+
end
|
56
|
+
Rity::CLI.start %w{}
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "--port option" do
|
60
|
+
it "changes the listen port" do
|
61
|
+
add_stop_timer 0.01
|
62
|
+
hook_event_loop do
|
63
|
+
Socket.tcp("127.0.0.1", 3001) {|*| }
|
64
|
+
end
|
65
|
+
Rity::CLI.start %w{--port=3001}
|
66
|
+
|
67
|
+
$stderr.string.should include(":3001")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "has an alias: -p" do
|
71
|
+
add_stop_timer 0.01
|
72
|
+
hook_event_loop do
|
73
|
+
Socket.tcp("127.0.0.1", 3002) {|*| }
|
74
|
+
end
|
75
|
+
Rity::CLI.start %w{-p 3002}
|
76
|
+
|
77
|
+
$stderr.string.should include(":3002")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "--bind option" do
|
82
|
+
it "changes the listen interface" do
|
83
|
+
add_stop_timer 0.01
|
84
|
+
hook_event_loop do
|
85
|
+
Socket.tcp("127.0.0.2", 3000) {|*| }
|
86
|
+
end
|
87
|
+
Rity::CLI.start %w{--bind=127.0.0.2}
|
88
|
+
|
89
|
+
$stderr.string.should include("127.0.0.2:")
|
90
|
+
end
|
91
|
+
|
92
|
+
it "has an alias: -b" do
|
93
|
+
add_stop_timer 0.01
|
94
|
+
hook_event_loop do
|
95
|
+
Socket.tcp("127.0.0.3", 3000) {|*| }
|
96
|
+
end
|
97
|
+
Rity::CLI.start %w{-b 127.0.0.3}
|
98
|
+
|
99
|
+
$stderr.string.should include("127.0.0.3:")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "--rackup option" do
|
104
|
+
it "changes the rackup file that'll be loaded" do
|
105
|
+
add_stop_timer 0.01
|
106
|
+
hook_event_loop do
|
107
|
+
request = EM::HttpRequest.new("http://127.0.0.1:3000").aget
|
108
|
+
response = EM::Synchrony.sync(request)
|
109
|
+
response.response.should include("config2.ru")
|
110
|
+
end
|
111
|
+
Rity::CLI.start %w{--rackup=config2.ru}
|
112
|
+
end
|
113
|
+
|
114
|
+
it "has an alias: -r" do
|
115
|
+
add_stop_timer 0.01
|
116
|
+
hook_event_loop do
|
117
|
+
request = EM::HttpRequest.new("http://127.0.0.1:3000").aget
|
118
|
+
response = EM::Synchrony.sync(request)
|
119
|
+
response.response.should include("config2.ru")
|
120
|
+
end
|
121
|
+
Rity::CLI.start %w{-r config2.ru}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "--quiet option" do
|
126
|
+
it "discards all output" do
|
127
|
+
add_stop_timer 0.01
|
128
|
+
Rity::CLI.start %w{--quiet}
|
129
|
+
|
130
|
+
$stderr.string.should be_empty
|
131
|
+
end
|
132
|
+
|
133
|
+
it "has an alias: -q" do
|
134
|
+
add_stop_timer 0.01
|
135
|
+
Rity::CLI.start %w{-q}
|
136
|
+
|
137
|
+
$stderr.string.should be_empty
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,4 +3,40 @@ Bundler.setup :default
|
|
3
3
|
|
4
4
|
require "rity"
|
5
5
|
require "rspec"
|
6
|
-
require "
|
6
|
+
require "fakefs"
|
7
|
+
|
8
|
+
begin
|
9
|
+
require "awesome_print"
|
10
|
+
rescue LoadError; end
|
11
|
+
|
12
|
+
require "em-synchrony"
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.before do
|
16
|
+
EM.class_eval do
|
17
|
+
@spec_hooks = []
|
18
|
+
class << self
|
19
|
+
attr_reader :spec_hooks
|
20
|
+
def synchrony_with_hooks(blk = nil, tail = nil, &block)
|
21
|
+
synchrony_without_hooks do
|
22
|
+
(blk || block).call
|
23
|
+
@spec_hooks.each {|sh| sh.call }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method :synchrony_without_hooks, :synchrony
|
27
|
+
alias_method :synchrony, :synchrony_with_hooks
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
config.after do
|
33
|
+
EM.class_eval do
|
34
|
+
@spec_hooks = nil
|
35
|
+
class << self
|
36
|
+
remove_method :spec_hooks
|
37
|
+
alias_method :synchrony, :synchrony_without_hooks
|
38
|
+
remove_method :synchrony_with_hooks
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -38,7 +38,7 @@ describe Rity::Connection do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it "adds the responder as request's callback" do
|
41
|
-
@conn.
|
41
|
+
@conn.should_receive(:write_responses)
|
42
42
|
@conn.parser.on_request[0].call("GET", "/")
|
43
43
|
@conn.requests[0].succeed
|
44
44
|
end
|
@@ -74,13 +74,13 @@ describe Rity::Connection do
|
|
74
74
|
@conn.requests[0].stub(:response => response)
|
75
75
|
|
76
76
|
@conn.builder.should_receive(:response).with(response)
|
77
|
-
@conn.
|
77
|
+
@conn.write_responses
|
78
78
|
end
|
79
79
|
|
80
80
|
it "doesn't push the response until it's ready" do
|
81
81
|
@conn.requests.push(Rity::Request.new(nil, nil, nil))
|
82
82
|
@conn.builder.should_not_receive(:response)
|
83
|
-
@conn.
|
83
|
+
@conn.write_responses
|
84
84
|
end
|
85
85
|
|
86
86
|
it "pushes responses in the order the resp. requests came in" do
|
@@ -91,20 +91,20 @@ describe Rity::Connection do
|
|
91
91
|
request1.stub(:response => nil)
|
92
92
|
request2.stub(:response => response2)
|
93
93
|
@conn.builder.should_not_receive(:response)
|
94
|
-
@conn.
|
94
|
+
@conn.write_responses
|
95
95
|
|
96
96
|
request1.stub(:response => response1)
|
97
97
|
request2.stub(:response => nil)
|
98
98
|
@conn.builder.rspec_reset
|
99
99
|
@conn.builder.should_receive(:response).with(response1)
|
100
|
-
@conn.
|
100
|
+
@conn.write_responses
|
101
101
|
|
102
102
|
@conn.requests.should_not include(request1)
|
103
103
|
|
104
104
|
request2.stub(:response => response2)
|
105
105
|
@conn.builder.rspec_reset
|
106
106
|
@conn.builder.should_receive(:response).with(response2)
|
107
|
-
@conn.
|
107
|
+
@conn.write_responses
|
108
108
|
|
109
109
|
@conn.requests.should be_empty
|
110
110
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Lars Gierth
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-12 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: eventmachine
|
@@ -89,6 +89,28 @@ dependencies:
|
|
89
89
|
type: :development
|
90
90
|
prerelease: false
|
91
91
|
version_requirements: *id007
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: fakefs
|
94
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: "0"
|
100
|
+
type: :development
|
101
|
+
prerelease: false
|
102
|
+
version_requirements: *id008
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: em-http-request
|
105
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: "0"
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: *id009
|
92
114
|
description: Rity is a lightweight Ruby webserver that runs inside an EventMachine loop and puts each request into a fiber.
|
93
115
|
email:
|
94
116
|
- lars.gierth@gmail.com
|
@@ -109,10 +131,10 @@ files:
|
|
109
131
|
- lib/rity/cli.rb
|
110
132
|
- lib/rity/connection.rb
|
111
133
|
- lib/rity/request.rb
|
112
|
-
- lib/rity/server.rb
|
113
134
|
- lib/rity/version.rb
|
114
135
|
- myapp.rb
|
115
136
|
- rity.gemspec
|
137
|
+
- spec/integration/start_spec.rb
|
116
138
|
- spec/spec_helper.rb
|
117
139
|
- spec/unit/connection_spec.rb
|
118
140
|
- spec/unit/request_spec.rb
|
@@ -129,7 +151,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
151
|
requirements:
|
130
152
|
- - ">="
|
131
153
|
- !ruby/object:Gem::Version
|
132
|
-
hash:
|
154
|
+
hash: -888247411
|
133
155
|
segments:
|
134
156
|
- 0
|
135
157
|
version: "0"
|
@@ -138,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
160
|
requirements:
|
139
161
|
- - ">="
|
140
162
|
- !ruby/object:Gem::Version
|
141
|
-
hash:
|
163
|
+
hash: -888247411
|
142
164
|
segments:
|
143
165
|
- 0
|
144
166
|
version: "0"
|
data/lib/rity/server.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require "eventmachine"
|
2
|
-
require "em-synchrony"
|
3
|
-
require "logger"
|
4
|
-
|
5
|
-
require "rity/connection"
|
6
|
-
|
7
|
-
module Rity
|
8
|
-
class Server
|
9
|
-
def self.start(address, port, app)
|
10
|
-
EM.synchrony do
|
11
|
-
trap("INT") { EM.stop }
|
12
|
-
trap("TERM") { EM.stop }
|
13
|
-
|
14
|
-
EM.epoll
|
15
|
-
|
16
|
-
puts "Binding to #{address}:#{port}"
|
17
|
-
EM.start_server address, port, Connection do |conn|
|
18
|
-
conn.app = app
|
19
|
-
conn.log = Logger.new(STDERR)
|
20
|
-
conn.log.formatter = proc do |severity, time, progname, message|
|
21
|
-
"[#{time}] #{severity}: #{message}\n"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|