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.
@@ -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 a client "A"
8
+ Given the server is running
9
+ And a client "A"
12
10
  And a client "B"
13
- When client "A" makes a "1" seconds request
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
- @session.puts "HTTP/1.1 #{@response.status} #{STATUS_CODES[@response.status]}"
57
- @session.puts @response.headers.map { |k,v| "#{k}: #{v}" }
58
- @session.puts ""
59
-
60
- @response.body.each do |line|
61
- @session.puts line
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
- @session.peeraddr(:numeric)[2] if @session
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
@@ -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" => true,
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
@@ -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 do
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
- @handler ||= @app ? RackHandler.new(@app,@opts) : RequestHandler.new
66
- session = @socket.accept
67
- @handler.run session
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
 
@@ -1,3 +1,3 @@
1
1
  module Yarn
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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
@@ -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
@@ -6,7 +6,7 @@ gem 'rails', '3.1.0'
6
6
  # gem 'rails', :git => 'git://github.com/rails/rails.git'
7
7
 
8
8
  gem 'sqlite3'
9
- gem 'yarn', '~> 0.1'
9
+ gem 'yarn'
10
10
  gem 'pry'
11
11
 
12
12
  # Gems used only for assets and not required
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.9
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-09 00:00:00.000000000 Z
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: &12100240 !ruby/object:Gem::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: *12100240
24
+ version_requirements: *23448020
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: trollop
27
- requirement: &12099740 !ruby/object:Gem::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: *12099740
35
+ version_requirements: *23447520
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rack
38
- requirement: &12099280 !ruby/object:Gem::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: *12099280
46
+ version_requirements: *23447040
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: cucumber
49
- requirement: &12098900 !ruby/object:Gem::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: *12098900
57
+ version_requirements: *23446660
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: faraday
60
- requirement: &12098440 !ruby/object:Gem::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: *12098440
68
+ version_requirements: *23446200
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: nokogiri
71
- requirement: &12098020 !ruby/object:Gem::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: *12098020
79
+ version_requirements: *23445780
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &12097600 !ruby/object:Gem::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: *12097600
90
+ version_requirements: *23445360
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: simplecov
93
- requirement: &12097180 !ruby/object:Gem::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: *12097180
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