yarn 0.0.9 → 0.1.0
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/features/concurrency.feature +3 -5
- data/features/parser.feature +15 -0
- data/features/step_definitions/parser_steps.rb +23 -0
- data/lib/yarn/abstract_handler.rb +15 -7
- data/lib/yarn/rack_handler.rb +2 -2
- data/lib/yarn/server.rb +25 -6
- data/lib/yarn/version.rb +1 -1
- data/spec/yarn/abstract_handler_spec.rb +7 -1
- data/spec/yarn/server_spec.rb +39 -0
- data/test_objects/rails_test/Gemfile +1 -1
- metadata +20 -19
@@ -4,12 +4,10 @@ Feature: Concurrency
|
|
4
4
|
I want to be able to serve multiple requests in parallel
|
5
5
|
To increase server performance
|
6
6
|
|
7
|
-
Background:
|
8
|
-
Given the server is running
|
9
|
-
|
10
7
|
Scenario: Perform two requests in parallel
|
11
|
-
Given
|
8
|
+
Given the server is running
|
9
|
+
And a client "A"
|
12
10
|
And a client "B"
|
13
|
-
When client "A" makes a "1"
|
11
|
+
When client "A" makes a "1" second request
|
14
12
|
And client "B" makes a "0.1" second request
|
15
13
|
Then client "B" receives a response before client "A"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: Parse HTTP requests
|
2
|
+
|
3
|
+
As a web-developer
|
4
|
+
I want to be able to parse HTTP requests
|
5
|
+
To be able to respond accordingly
|
6
|
+
|
7
|
+
Scenario: Parse HTTP request
|
8
|
+
Given a HTTP request "GET /index.html HTTP/1.1\r\nUser-Agent: cucumber\r\n"
|
9
|
+
And a parser
|
10
|
+
When I feed the request to the parser
|
11
|
+
Then the result "method" should be "GET"
|
12
|
+
And the result "uri" should include "path" with "/index.html"
|
13
|
+
And the result "version" should be "HTTP/1.1"
|
14
|
+
And the result "headers" should have "User-Agent" with "cucumber"
|
15
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Given /^a parser$/ do
|
2
|
+
@parser = Yarn::Parser.new
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I feed the request to the parser$/ do
|
6
|
+
@result = @parser.run(@request)
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^a HTTP request "([^"]*)"$/ do |req|
|
10
|
+
@request = req.gsub('\\r\\n') { "\r\n" }
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^the result "([^"]*)" should be "([^"]*)"$/ do |key, value|
|
14
|
+
@result[key.to_sym].to_s.should == value
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^the result "([^"]*)" should include "([^"]*)" with "([^"]*)"$/ do |outer_key, inner_key, value|
|
18
|
+
@result[outer_key.to_sym][inner_key.to_sym].to_s.should == value
|
19
|
+
end
|
20
|
+
|
21
|
+
Then /^the result "([^"]*)" should have "([^"]*)" with "([^"]*)"$/ do |outer_key, inner_key, value|
|
22
|
+
@result[outer_key.to_sym][inner_key].to_s.should == value
|
23
|
+
end
|
@@ -53,12 +53,16 @@ module Yarn
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def return_response
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@
|
56
|
+
begin
|
57
|
+
@session.puts "HTTP/1.1 #{@response.status} #{STATUS_CODES[@response.status]}"
|
58
|
+
@session.puts @response.headers.map { |k,v| "#{k}: #{v}" }
|
59
|
+
@session.puts ""
|
60
|
+
|
61
|
+
@response.body.each do |line|
|
62
|
+
@session.puts line
|
63
|
+
end
|
64
|
+
rescue Exception => exception
|
65
|
+
log "An error occured returning the response to the client"
|
62
66
|
end
|
63
67
|
end
|
64
68
|
|
@@ -110,7 +114,11 @@ module Yarn
|
|
110
114
|
end
|
111
115
|
|
112
116
|
def client_address
|
113
|
-
|
117
|
+
begin
|
118
|
+
@session.peeraddr(:numeric)[2] if @session
|
119
|
+
rescue Errno::ENOTCONN
|
120
|
+
return ""
|
121
|
+
end
|
114
122
|
end
|
115
123
|
end
|
116
124
|
end
|
data/lib/yarn/rack_handler.rb
CHANGED
@@ -17,6 +17,7 @@ module Yarn
|
|
17
17
|
begin
|
18
18
|
make_env
|
19
19
|
@response.content = @app.call(@env)
|
20
|
+
@response.body.close if @response.body.respond_to?("close")
|
20
21
|
rescue Exception => e
|
21
22
|
log e.message
|
22
23
|
log e.backtrace
|
@@ -38,7 +39,7 @@ module Yarn
|
|
38
39
|
"rack.input" => input,
|
39
40
|
"rack.version" => Rack::VERSION,
|
40
41
|
"rack.errors" => $output,
|
41
|
-
"rack.multithread" =>
|
42
|
+
"rack.multithread" => false,
|
42
43
|
"rack.multiprocess" => true,
|
43
44
|
"rack.run_once" => false,
|
44
45
|
"rack.url_scheme" => "http"
|
@@ -50,7 +51,6 @@ module Yarn
|
|
50
51
|
|
51
52
|
def has_body?
|
52
53
|
value ||= !! @request[:body]
|
53
|
-
value
|
54
54
|
end
|
55
55
|
|
56
56
|
end
|
data/lib/yarn/server.rb
CHANGED
@@ -4,6 +4,16 @@ module Yarn
|
|
4
4
|
class Server
|
5
5
|
|
6
6
|
include Logging
|
7
|
+
include Socket::Constants
|
8
|
+
|
9
|
+
# TCP optimizations
|
10
|
+
TCP_OPTS = [
|
11
|
+
# delays accepting connections until clients send data
|
12
|
+
[Socket::SOL_TCP, TCP_DEFER_ACCEPT, 1],
|
13
|
+
# send ACK flags in their own packets (faster)
|
14
|
+
[Socket::SOL_TCP, TCP_QUICKACK, 1],
|
15
|
+
# set maximum number of
|
16
|
+
]
|
7
17
|
|
8
18
|
attr_accessor :host, :port, :socket, :workers
|
9
19
|
|
@@ -41,6 +51,8 @@ module Yarn
|
|
41
51
|
def start
|
42
52
|
trap("INT") { stop }
|
43
53
|
@socket = TCPServer.new(@host, @port)
|
54
|
+
@socket.listen(1024)
|
55
|
+
::BasicSocket.do_not_reverse_lookup=true
|
44
56
|
log "Yarn started #{@num_workers} workers and is listening on #{@host}:#{@port}"
|
45
57
|
|
46
58
|
init_workers
|
@@ -49,10 +61,12 @@ module Yarn
|
|
49
61
|
Process.waitall
|
50
62
|
end
|
51
63
|
|
64
|
+
def configure_socket
|
65
|
+
TCP_OPTS.each { |opt| @session.setsockopt(*opt) }
|
66
|
+
end
|
67
|
+
|
52
68
|
def init_workers
|
53
|
-
@num_workers.times
|
54
|
-
@workers << fork_worker
|
55
|
-
end
|
69
|
+
@num_workers.times { @workers << fork_worker }
|
56
70
|
end
|
57
71
|
|
58
72
|
def fork_worker
|
@@ -61,13 +75,18 @@ module Yarn
|
|
61
75
|
|
62
76
|
def worker
|
63
77
|
trap("INT") { exit }
|
78
|
+
handler = get_handler
|
64
79
|
loop do
|
65
|
-
@
|
66
|
-
|
67
|
-
|
80
|
+
@session = @socket.accept
|
81
|
+
configure_socket
|
82
|
+
handler.run @session
|
68
83
|
end
|
69
84
|
end
|
70
85
|
|
86
|
+
def get_handler
|
87
|
+
@app ? RackHandler.new(@app,@opts) : RequestHandler.new
|
88
|
+
end
|
89
|
+
|
71
90
|
def stop
|
72
91
|
@socket.close if (@socket && !@socket.closed?)
|
73
92
|
|
data/lib/yarn/version.rb
CHANGED
@@ -85,8 +85,14 @@ module Yarn
|
|
85
85
|
@handler.run(@session)
|
86
86
|
end
|
87
87
|
end
|
88
|
-
end
|
89
88
|
|
89
|
+
describe "#client_address" do
|
90
|
+
it "should return the clients address" do
|
91
|
+
@handler.session.stub(:peeraddr).and_return([nil,nil,"some_host"])
|
90
92
|
|
93
|
+
@handler.client_address.should == "some_host"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
91
97
|
end
|
92
98
|
end
|
data/spec/yarn/server_spec.rb
CHANGED
@@ -8,6 +8,11 @@ module Yarn
|
|
8
8
|
end
|
9
9
|
|
10
10
|
describe "#new" do
|
11
|
+
it "should set the app if parameter is given" do
|
12
|
+
@server = Server.new({ rack: "test_objects/config.ru" })
|
13
|
+
|
14
|
+
@server.instance_variable_get("@app").should_not be_nil
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
describe "#load_rack_app" do
|
@@ -47,6 +52,40 @@ module Yarn
|
|
47
52
|
end
|
48
53
|
end
|
49
54
|
|
55
|
+
describe "#worker" do
|
56
|
+
it "should start the handler" do
|
57
|
+
# modify loop to only run once
|
58
|
+
class Yarn::Server
|
59
|
+
private
|
60
|
+
def loop
|
61
|
+
yield
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@server = Server.new
|
65
|
+
@server.stub(:init_workers)
|
66
|
+
@server.stub(:configure_socket)
|
67
|
+
@server.socket.stub(:accept).and_return("GET / HTTP/1.1")
|
68
|
+
|
69
|
+
RequestHandler.any_instance.should_receive(:run)
|
70
|
+
|
71
|
+
@server.worker
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#get_handler" do
|
76
|
+
it "should return the RequestHandler if an app is not given" do
|
77
|
+
@server = Server.new
|
78
|
+
|
79
|
+
@server.get_handler.class.should == RequestHandler
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should return the Rackhandler if an app is given" do
|
83
|
+
@server = Server.new(rack: "test_objects/config.ru")
|
84
|
+
|
85
|
+
@server.get_handler.class.should == RackHandler
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
50
89
|
describe "#stop" do
|
51
90
|
it "should notify the server is stopped" do
|
52
91
|
$console = MockIO.new
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yarn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: parslet
|
16
|
-
requirement: &
|
16
|
+
requirement: &23448020 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '1.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *23448020
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: trollop
|
27
|
-
requirement: &
|
27
|
+
requirement: &23447520 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '1.16'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *23447520
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rack
|
38
|
-
requirement: &
|
38
|
+
requirement: &23447040 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '1.3'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *23447040
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: cucumber
|
49
|
-
requirement: &
|
49
|
+
requirement: &23446660 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *23446660
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: faraday
|
60
|
-
requirement: &
|
60
|
+
requirement: &23446200 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *23446200
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: nokogiri
|
71
|
-
requirement: &
|
71
|
+
requirement: &23445780 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *23445780
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: rspec
|
82
|
-
requirement: &
|
82
|
+
requirement: &23445360 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *23445360
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: simplecov
|
93
|
-
requirement: &
|
93
|
+
requirement: &23444940 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *23444940
|
102
102
|
description: A multi-process web-server written in Ruby 1.9.
|
103
103
|
email:
|
104
104
|
- jkjeldgaard@gmail.com
|
@@ -118,14 +118,15 @@ files:
|
|
118
118
|
- Rakefile
|
119
119
|
- bin/yarn
|
120
120
|
- cucumber.yml
|
121
|
-
- doc/lib/yarn.html
|
122
121
|
- features/concurrency.feature
|
123
122
|
- features/dynamic_request.feature
|
124
123
|
- features/logger.feature
|
124
|
+
- features/parser.feature
|
125
125
|
- features/rack.feature
|
126
126
|
- features/server.feature
|
127
127
|
- features/static_request.feature
|
128
128
|
- features/step_definitions/concurrency_steps.rb
|
129
|
+
- features/step_definitions/parser_steps.rb
|
129
130
|
- features/step_definitions/rack_steps.rb
|
130
131
|
- features/step_definitions/server_steps.rb
|
131
132
|
- features/step_definitions/web_steps.rb
|