rity 0.0.2 → 0.0.3
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/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
|