yarn 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +0 -3
- data/README.md +25 -1
- data/bin/yarn +6 -4
- data/features/concurrency.feature +4 -4
- data/features/dynamic_request.feature +6 -1
- data/features/rack.feature +5 -8
- data/features/static_request.feature +2 -2
- data/features/step_definitions/rack_steps.rb +13 -0
- data/features/step_definitions/server_steps.rb +5 -5
- data/features/step_definitions/web_steps.rb +8 -0
- data/lib/rack/handler/yarn.rb +4 -3
- data/lib/yarn/error_page.rb +2 -1
- data/lib/yarn/logging.rb +1 -1
- data/lib/yarn/parslet_parser.rb +9 -24
- data/lib/yarn/rack_handler.rb +11 -9
- data/lib/yarn/request_handler.rb +21 -12
- data/lib/yarn/server.rb +39 -30
- data/lib/yarn/version.rb +1 -1
- data/spec/helpers.rb +16 -8
- data/spec/rack/handler/yarn_spec.rb +21 -0
- data/spec/spec_helper.rb +1 -4
- data/spec/yarn/directory_lister_spec.rb +0 -5
- data/spec/yarn/logging_spec.rb +3 -2
- data/spec/yarn/parslet_parser_spec.rb +26 -0
- data/spec/yarn/rack_handler_spec.rb +15 -4
- data/spec/yarn/request_handler_spec.rb +17 -7
- data/spec/yarn/server_spec.rb +49 -8
- data/test_objects/.gitignore +1 -0
- data/test_objects/app.rb +0 -2
- data/test_objects/config.ru +1 -49
- data/test_objects/rails_test/.gitignore +5 -0
- data/test_objects/rails_test/Gemfile +34 -0
- data/test_objects/rails_test/README +261 -0
- data/test_objects/rails_test/Rakefile +7 -0
- data/test_objects/rails_test/app/assets/images/rails.png +0 -0
- data/test_objects/rails_test/app/assets/javascripts/application.js +6 -0
- data/test_objects/rails_test/app/assets/stylesheets/application.css +7 -0
- data/test_objects/rails_test/app/assets/stylesheets/scaffolds.css.scss +56 -0
- data/test_objects/rails_test/app/mailers/.gitkeep +0 -0
- data/test_objects/rails_test/app/models/.gitkeep +0 -0
- data/test_objects/rails_test/app/views/layouts/application.html.erb +15 -0
- data/test_objects/rails_test/app/views/posts/_form.html.erb +25 -0
- data/test_objects/rails_test/app/views/posts/edit.html.erb +6 -0
- data/test_objects/rails_test/app/views/posts/index.html.erb +20 -0
- data/test_objects/rails_test/app/views/posts/new.html.erb +5 -0
- data/test_objects/rails_test/app/views/posts/show.html.erb +15 -0
- data/test_objects/rails_test/config.ru +4 -0
- data/test_objects/rails_test/config/database.yml +25 -0
- data/test_objects/rails_test/config/locales/en.yml +5 -0
- data/test_objects/rails_test/doc/README_FOR_APP +2 -0
- data/test_objects/rails_test/lib/assets/.gitkeep +0 -0
- data/test_objects/rails_test/lib/tasks/.gitkeep +0 -0
- data/test_objects/rails_test/log/.gitkeep +0 -0
- data/test_objects/rails_test/public/404.html +26 -0
- data/test_objects/rails_test/public/422.html +26 -0
- data/test_objects/rails_test/public/500.html +26 -0
- data/test_objects/rails_test/public/favicon.ico +0 -0
- data/test_objects/rails_test/public/robots.txt +5 -0
- data/test_objects/rails_test/script/rails +6 -0
- data/test_objects/rails_test/test/fixtures/.gitkeep +0 -0
- data/test_objects/rails_test/test/fixtures/posts.yml +9 -0
- data/test_objects/rails_test/test/functional/.gitkeep +0 -0
- data/test_objects/rails_test/test/integration/.gitkeep +0 -0
- data/test_objects/rails_test/test/unit/.gitkeep +0 -0
- data/test_objects/rails_test/vendor/assets/stylesheets/.gitkeep +0 -0
- data/test_objects/rails_test/vendor/plugins/.gitkeep +0 -0
- data/yarn.gemspec +4 -8
- metadata +59 -59
- data/lib/yarn/worker.rb +0 -19
- data/lib/yarn/worker_pool.rb +0 -36
- data/spec/yarn/worker_pool_spec.rb +0 -23
- data/spec/yarn/worker_spec.rb +0 -26
- data/test_objects/simple_rack.rb +0 -12
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
1
|
# Yarn #
|
2
2
|
|
3
|
-
|
3
|
+
Yarn is a multi-threaded webserver written in Ruby 1.9 by Jesper Kjeldgaard.
|
4
|
+
It handles concurrent requests by means of a set of workers and a job queue for incomming requests.
|
5
|
+
|
6
|
+
Supports:
|
7
|
+
*
|
8
|
+
|
9
|
+
|
10
|
+
## Installation ##
|
11
|
+
`gem install yarn`
|
12
|
+
|
13
|
+
|
14
|
+
## Usage ##
|
15
|
+
To use Yarn with Rack applications:
|
16
|
+
|
17
|
+
`rackup -s Yarn <rackup file (config.ru)>`
|
18
|
+
|
19
|
+
|
20
|
+
To use Yarn for serving static and ruby (*.rb) files:
|
21
|
+
|
22
|
+
|
23
|
+
yarn [options]
|
24
|
+
where [options] are:
|
25
|
+
--host, -h <s>: Hostname or IP address of the server (default: 127.0.0.1)
|
26
|
+
--port, -p <i>: Port number to listen on for incomming requests (default: 3000)
|
27
|
+
--workers, -w <s>: Number of worker threads (default: 32)
|
data/bin/yarn
CHANGED
@@ -7,9 +7,9 @@ require 'trollop'
|
|
7
7
|
require 'yarn'
|
8
8
|
|
9
9
|
opts = Trollop::options do
|
10
|
-
version "Yarn v
|
10
|
+
version "Yarn v#{Yarn::VERSION} 2011 Jesper Kjeldgaard"
|
11
11
|
banner <<-EOS
|
12
|
-
Yarn v
|
12
|
+
Yarn v#{Yarn::VERSION} is a multiprocess webserver written in Ruby 1.9.2
|
13
13
|
|
14
14
|
Usage: yarn [options]
|
15
15
|
where [options] are:
|
@@ -17,8 +17,10 @@ EOS
|
|
17
17
|
|
18
18
|
opt :host, "Hostname or IP address of the server", :default => "127.0.0.1"
|
19
19
|
opt :port, "Port number to listen on for incomming requests", :default => 3000
|
20
|
-
opt :
|
20
|
+
opt :workers, "Number of worker threads", :default => 4
|
21
|
+
opt :rack, "Rackup file <config.ru>", :default => "off"
|
22
|
+
opt :debug, "Output debug messages"
|
21
23
|
end
|
22
24
|
|
23
|
-
server = Yarn::Server.new(
|
25
|
+
server = Yarn::Server.new(opts)
|
24
26
|
server.start
|
@@ -2,14 +2,14 @@ Feature: Concurrency
|
|
2
2
|
|
3
3
|
As a developer
|
4
4
|
I want to be able to serve multiple requests in parallel
|
5
|
-
To increase server
|
5
|
+
To increase server performance
|
6
6
|
|
7
7
|
Background:
|
8
|
-
Given the server is running
|
8
|
+
Given the server is running
|
9
9
|
|
10
10
|
Scenario: Perform two requests in parallel
|
11
11
|
Given a client "A"
|
12
12
|
And a client "B"
|
13
|
-
When client "A" makes a "
|
14
|
-
And client "B" makes a "1" second request
|
13
|
+
When client "A" makes a "1" seconds request
|
14
|
+
And client "B" makes a "0.1" second request
|
15
15
|
Then client "B" receives a response before client "A"
|
@@ -5,9 +5,14 @@ Feature: Dynamic request
|
|
5
5
|
In order to provide dynamic content
|
6
6
|
|
7
7
|
Background:
|
8
|
-
Given the server is running
|
8
|
+
Given the server is running
|
9
9
|
|
10
10
|
Scenario: Serve a dynamic Ruby file
|
11
11
|
Given the file "/app.rb" exist
|
12
12
|
When I go to "/app.rb"
|
13
13
|
Then the response should contain "Dynamic request complete"
|
14
|
+
|
15
|
+
Scenario: Support POST data
|
16
|
+
Given the file "/post_app.rb" exist
|
17
|
+
When I post "field1" as "value1" to "/test_objects/post_app.rb"
|
18
|
+
Then the response should be "Recieved field1=value1"
|
data/features/rack.feature
CHANGED
@@ -4,15 +4,12 @@ Feature: Implement rack interface
|
|
4
4
|
I want to have a rack handler
|
5
5
|
In order to serve rack applications
|
6
6
|
|
7
|
-
@wip
|
8
7
|
Scenario: Serve a one-file rack application
|
9
|
-
Given
|
10
|
-
And the server is running as "rack"
|
8
|
+
Given the rack test app is running
|
11
9
|
When I go to "/"
|
12
|
-
Then the response should contain "
|
10
|
+
Then the response should contain "Rack works"
|
13
11
|
|
14
12
|
Scenario: Serve a rails application
|
15
|
-
Given
|
16
|
-
|
17
|
-
|
18
|
-
Then the response should contain "Rack rails works"
|
13
|
+
Given the rails test app is running
|
14
|
+
When I go to ""
|
15
|
+
Then the response should contain "Yarn Test Blog"
|
@@ -5,7 +5,7 @@ Feature: Static file requests
|
|
5
5
|
To provide fast content on the Internet
|
6
6
|
|
7
7
|
Background:
|
8
|
-
Given the server is running
|
8
|
+
Given the server is running
|
9
9
|
|
10
10
|
Scenario: Serve a static html file
|
11
11
|
Given the file "index.html" exist
|
@@ -22,4 +22,4 @@ Feature: Static file requests
|
|
22
22
|
Given the file "non-existent-file.html" does not exist
|
23
23
|
When I go to "non-existent-file.html"
|
24
24
|
Then the response should contain "404"
|
25
|
-
Then the response should contain "
|
25
|
+
Then the response should contain "does not exist"
|
@@ -1,3 +1,16 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
1
3
|
Given /^I have a rack application "([^"]*)"$/ do |app|
|
2
4
|
testfile_exists?(app).should be_true
|
3
5
|
end
|
6
|
+
|
7
|
+
Given /^the rack test app is running$/ do
|
8
|
+
start_server(3000,"test_objects/config.ru")
|
9
|
+
end
|
10
|
+
|
11
|
+
Given /^the rails test app is running$/ do
|
12
|
+
current_dir = Dir.pwd
|
13
|
+
Dir.chdir("test_objects/rails_test")
|
14
|
+
start_server(3000,"config.ru")
|
15
|
+
Dir.chdir(current_dir)
|
16
|
+
end
|
@@ -9,11 +9,7 @@ When /^I stop the server$/ do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
Given /^the server is running$/ do
|
12
|
-
start_server
|
13
|
-
end
|
14
|
-
|
15
|
-
Given /^the server is running as "([^"]*)"$/ do |handler_type|
|
16
|
-
start_server(3000, handler_type.to_sym)
|
12
|
+
start_server(3000)
|
17
13
|
end
|
18
14
|
|
19
15
|
Given /^the server is not running$/ do
|
@@ -40,3 +36,7 @@ end
|
|
40
36
|
Then /^I should see "([^"]*)"$/ do |message|
|
41
37
|
$console.contains? message
|
42
38
|
end
|
39
|
+
|
40
|
+
Then /^Pry$/ do
|
41
|
+
binding.pry
|
42
|
+
end
|
@@ -5,3 +5,11 @@ end
|
|
5
5
|
Then /^the response should contain "([^"]*)"$/ do |content|
|
6
6
|
@response.body.should include(content)
|
7
7
|
end
|
8
|
+
|
9
|
+
Then /^the response should be "([^"]*)"$/ do |content|
|
10
|
+
@response.body.gsub(/\n?/,"").should == content
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I post "([^"]*)" as "([^"]*)" to "([^"]*)"$/ do |key, value, url|
|
14
|
+
@response = post(url, { key.to_sym => value })
|
15
|
+
end
|
data/lib/rack/handler/yarn.rb
CHANGED
@@ -6,14 +6,15 @@ module Rack
|
|
6
6
|
module Handler
|
7
7
|
class Yarn
|
8
8
|
def self.run(app, options={})
|
9
|
-
|
10
|
-
server.
|
9
|
+
options = options.merge({ rack: app })
|
10
|
+
@server = ::Yarn::Server.new(options)
|
11
|
+
@server.start
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.valid_options
|
14
15
|
{
|
15
16
|
"Host=HOST" => "Hostname to listen on (default: 127.0.0.1)",
|
16
|
-
"Port=PORT" => "Port to listen on (default: 3000)"
|
17
|
+
"Port=PORT" => "Port to listen on (default: 3000)"
|
17
18
|
}
|
18
19
|
end
|
19
20
|
end
|
data/lib/yarn/error_page.rb
CHANGED
@@ -4,7 +4,8 @@ module Yarn
|
|
4
4
|
|
5
5
|
def serve_404_page
|
6
6
|
@response.status = 404
|
7
|
-
|
7
|
+
fn = @request[:uri][:path] if @request
|
8
|
+
@response.body = ["<html><head><title>404</title></head><body><h1>File #{fn} does not exist.</h1></body><html>"]
|
8
9
|
end
|
9
10
|
|
10
11
|
def serve_500_page
|
data/lib/yarn/logging.rb
CHANGED
data/lib/yarn/parslet_parser.rb
CHANGED
@@ -15,7 +15,6 @@ module Yarn
|
|
15
15
|
rule(:spaces) { match('\s+') }
|
16
16
|
|
17
17
|
# header rules
|
18
|
-
|
19
18
|
rule(:header_value) { match['^\r\n'].once }
|
20
19
|
|
21
20
|
rule(:header_name) { match['a-zA-Z\-'].once }
|
@@ -27,28 +26,18 @@ module Yarn
|
|
27
26
|
header_value.as(:value).maybe >>
|
28
27
|
crlf.maybe
|
29
28
|
end
|
30
|
-
|
31
|
-
# request-line rules
|
32
29
|
|
30
|
+
# request-line rules
|
33
31
|
rule(:http_version) { match['HTTP\/\d\.\d'].once }
|
34
32
|
|
35
|
-
rule(:param_value) { match['^&\s+'].once }
|
36
|
-
|
37
|
-
rule(:param_name) { match['^=+'].once }
|
38
|
-
|
39
|
-
rule(:param) do
|
40
|
-
param_name.as(:name) >>
|
41
|
-
str("=") >>
|
42
|
-
param_value.as(:value) >>
|
43
|
-
str("&").maybe
|
44
|
-
end
|
45
|
-
|
46
33
|
rule(:query) do
|
47
34
|
match['\S+'].repeat(1)
|
48
35
|
end
|
49
36
|
|
50
37
|
rule(:path) do
|
51
|
-
match['^\?'].repeat(1).as(:path) >>
|
38
|
+
match['^\?'].repeat(1).as(:path) >>
|
39
|
+
str("?") >>
|
40
|
+
query.as(:query) | match['^\s'].once.as(:path)
|
52
41
|
end
|
53
42
|
|
54
43
|
rule(:port) { match['\d+'].repeat(1) }
|
@@ -78,11 +67,16 @@ module Yarn
|
|
78
67
|
http_version.as(:version) >>
|
79
68
|
crlf.maybe
|
80
69
|
end
|
70
|
+
|
71
|
+
# body rule
|
72
|
+
rule(:body) { match['\S'].once }
|
81
73
|
|
82
74
|
# RFC2616: Request-Line *(( header ) CRLF) CRLF [ message-body ]
|
83
75
|
rule(:request) do
|
84
76
|
request_line >>
|
85
77
|
header.repeat.as(:_process_headers).as(:headers) >>
|
78
|
+
crlf.maybe >>
|
79
|
+
body.as(:body).maybe >>
|
86
80
|
crlf.maybe
|
87
81
|
end
|
88
82
|
|
@@ -91,19 +85,10 @@ module Yarn
|
|
91
85
|
|
92
86
|
def run(input)
|
93
87
|
tree = parse input
|
94
|
-
tree = ParamsTransformer.new.apply tree
|
95
88
|
HeadersTransformer.new.apply tree
|
96
89
|
end
|
97
90
|
end
|
98
91
|
|
99
|
-
class ParamsTransformer < Parslet::Transform
|
100
|
-
rule(:_process_params => subtree(:params)) do
|
101
|
-
hash = {}
|
102
|
-
params.each { |h| hash[h[:name].to_s] = h[:value] }
|
103
|
-
hash
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
92
|
class HeadersTransformer < Parslet::Transform
|
108
93
|
rule(:_process_headers => subtree(:headers)) do
|
109
94
|
hash = {}
|
data/lib/yarn/rack_handler.rb
CHANGED
@@ -4,16 +4,18 @@ require 'pry'
|
|
4
4
|
module Yarn
|
5
5
|
class RackHandler < RequestHandler
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
attr_accessor :env
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
@parser = ParsletParser.new
|
11
|
+
@response = Response.new
|
10
12
|
@app = app
|
11
13
|
end
|
12
14
|
|
13
15
|
def prepare_response
|
14
16
|
begin
|
15
|
-
|
16
|
-
@response.content = @app.call(env)
|
17
|
+
make_env
|
18
|
+
@response.content = @app.call(@env)
|
17
19
|
rescue Exception => e
|
18
20
|
log e.message
|
19
21
|
log e.backtrace
|
@@ -21,18 +23,18 @@ module Yarn
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def make_env
|
24
|
-
env = {
|
26
|
+
@env = {
|
25
27
|
"REQUEST_METHOD" => @request[:method].to_s,
|
26
28
|
"PATH_INFO" => @request[:uri][:path].to_s,
|
27
29
|
"QUERY_STRING" => @request[:uri][:query].to_s,
|
28
|
-
"SERVER_NAME" => @
|
29
|
-
"SERVER_PORT" => @
|
30
|
+
"SERVER_NAME" => @request[:uri][:host].to_s,
|
31
|
+
"SERVER_PORT" => @request[:uri][:port].to_s,
|
30
32
|
"SCRIPT_NAME" => "",
|
31
33
|
"rack.input" => StringIO.new("").set_encoding(Encoding::ASCII_8BIT),
|
32
34
|
"rack.version" => Rack::VERSION,
|
33
35
|
"rack.errors" => $output,
|
34
36
|
"rack.multithread" => true,
|
35
|
-
"rack.multiprocess" =>
|
37
|
+
"rack.multiprocess" => true,
|
36
38
|
"rack.run_once" => false,
|
37
39
|
"rack.url_scheme" => "http"
|
38
40
|
}
|
data/lib/yarn/request_handler.rb
CHANGED
@@ -14,24 +14,26 @@ module Yarn
|
|
14
14
|
|
15
15
|
attr_accessor :session, :parser, :request, :response
|
16
16
|
|
17
|
-
def initialize
|
17
|
+
def initialize
|
18
18
|
@parser = ParsletParser.new
|
19
19
|
@response = Response.new
|
20
20
|
end
|
21
21
|
|
22
22
|
def run(session)
|
23
|
-
@response = Response.new
|
24
23
|
set_common_headers
|
25
24
|
@session = session
|
26
25
|
begin
|
27
26
|
parse_request
|
27
|
+
debug "Request parsed, path: #{request_path}"
|
28
28
|
prepare_response
|
29
|
+
debug "Response prepared: #{@response.status}"
|
29
30
|
return_response
|
30
|
-
log "
|
31
|
+
log "#{STATUS_CODES[@response.status]} #{client_address} #{request_path}"
|
31
32
|
rescue EmptyRequestError
|
32
33
|
log "Empty request from #{client_address}"
|
33
34
|
ensure
|
34
35
|
close_connection
|
36
|
+
debug "Connection closed"
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -72,7 +74,7 @@ module Yarn
|
|
72
74
|
end
|
73
75
|
|
74
76
|
def execute_script(path)
|
75
|
-
response = `ruby #{path}`
|
77
|
+
response = `ruby #{path} #{post_body}`
|
76
78
|
if !! ($?.to_s =~ /1$/)
|
77
79
|
raise ProcessingError
|
78
80
|
else
|
@@ -80,6 +82,10 @@ module Yarn
|
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
85
|
+
def post_body
|
86
|
+
@request ? @request[:body].to_s : ""
|
87
|
+
end
|
88
|
+
|
83
89
|
def return_response
|
84
90
|
@session.puts "HTTP/1.1 #{@response.status} #{STATUS_CODES[@response.status]}"
|
85
91
|
@session.puts @response.headers.map { |k,v| "#{k}: #{v}" }
|
@@ -101,9 +107,17 @@ module Yarn
|
|
101
107
|
def read_request
|
102
108
|
input = []
|
103
109
|
while (line = @session.gets) do
|
104
|
-
|
105
|
-
|
110
|
+
length = line.gsub(/\D/,"") if line =~ /Content-Length/
|
111
|
+
if line == "\r\n"
|
112
|
+
input << line
|
113
|
+
input << @session.read(length.to_i) if length
|
114
|
+
break
|
115
|
+
else
|
116
|
+
input << line
|
117
|
+
end
|
106
118
|
end
|
119
|
+
@session.close_read
|
120
|
+
debug "Done reading request"
|
107
121
|
input.join
|
108
122
|
end
|
109
123
|
|
@@ -141,12 +155,7 @@ module Yarn
|
|
141
155
|
|
142
156
|
def read_file(path)
|
143
157
|
file_contents = []
|
144
|
-
|
145
|
-
File.open(path, "r") do |file|
|
146
|
-
while (line = file.gets) do
|
147
|
-
file_contents << line
|
148
|
-
end
|
149
|
-
end
|
158
|
+
File.open(path).each { |line| file_contents << line }
|
150
159
|
|
151
160
|
file_contents
|
152
161
|
end
|
data/lib/yarn/server.rb
CHANGED
@@ -5,55 +5,64 @@ module Yarn
|
|
5
5
|
|
6
6
|
include Logging
|
7
7
|
|
8
|
-
attr_accessor :host, :port, :socket, :
|
8
|
+
attr_accessor :host, :port, :socket, :workers
|
9
9
|
|
10
|
-
def initialize(
|
10
|
+
def initialize(options={})
|
11
11
|
# merge given options with default values
|
12
|
-
|
12
|
+
opts = {
|
13
13
|
output: $stdout,
|
14
14
|
host: '127.0.0.1',
|
15
|
-
port: 3000
|
16
|
-
|
15
|
+
port: 3000,
|
16
|
+
workers: 4,
|
17
|
+
rack: "off"
|
18
|
+
}.merge(options)
|
17
19
|
|
18
|
-
@app =
|
19
|
-
@
|
20
|
+
@app = nil
|
21
|
+
@app = load_rack_app(opts[:rack]) unless opts[:rack] == "off"
|
20
22
|
|
23
|
+
@host, @port, @num_workers = opts[:host], opts[:port], opts[:workers]
|
24
|
+
@workers = []
|
25
|
+
$output, $debug = opts[:output], opts[:debug]
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_rack_app(app_path)
|
29
|
+
if File.exists?(app_path)
|
30
|
+
config_file = File.read(app_path)
|
31
|
+
rack_application = eval("Rack::Builder.new { #{config_file} }")
|
32
|
+
else
|
33
|
+
log "#{app_path} does not exist. Exiting."
|
34
|
+
Kernel::exit
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def start
|
39
|
+
trap("INT") { stop }
|
21
40
|
@socket = TCPServer.new(@host, @port)
|
41
|
+
log "Yarn started #{@num_workers} workers and is listening on #{@host}:#{@port}"
|
22
42
|
|
23
|
-
|
43
|
+
init_workers
|
24
44
|
|
25
|
-
|
45
|
+
# Waits here for all processes to exit
|
46
|
+
Process.waitall
|
26
47
|
end
|
27
48
|
|
28
|
-
def
|
29
|
-
@
|
30
|
-
|
31
|
-
|
49
|
+
def init_workers
|
50
|
+
@num_workers.times do
|
51
|
+
@workers << fork do
|
52
|
+
trap("INT") { exit }
|
53
|
+
loop do
|
54
|
+
handler ||= @app ? RackHandler.new(@app) : RequestHandler.new
|
32
55
|
session = @socket.accept
|
33
|
-
|
34
|
-
rescue Exception => e
|
35
|
-
session.close
|
36
|
-
log e.message
|
37
|
-
log e.backtrace
|
56
|
+
handler.run session
|
38
57
|
end
|
39
58
|
end
|
40
59
|
end
|
41
|
-
|
42
|
-
begin
|
43
|
-
@socket_listener.join
|
44
|
-
rescue Interrupt => e
|
45
|
-
log "Caught interrupt, stopping..."
|
46
|
-
ensure
|
47
|
-
stop
|
48
|
-
end
|
49
60
|
end
|
50
61
|
|
51
62
|
def stop
|
52
|
-
@socket.close if @socket
|
53
|
-
@socket = nil
|
54
|
-
@socket_listener.kill if @socket_listener
|
63
|
+
@socket.close if (@socket && !@socket.closed?)
|
55
64
|
|
56
|
-
log "Server stopped"
|
65
|
+
log "Server stopped. Have a nice day!"
|
57
66
|
end
|
58
67
|
end
|
59
68
|
end
|