yarn 0.0.2 → 0.0.9

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/.gitignore CHANGED
@@ -8,3 +8,4 @@ pkg/*
8
8
  *.rbc
9
9
  doc/*
10
10
  .rvmrc
11
+ .rbx/
data/LICENCE ADDED
@@ -0,0 +1,22 @@
1
+ Portions copyright (c) 2011 Jesper Kjeldgaard
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,10 +1,6 @@
1
1
  # Yarn #
2
2
 
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
- *
3
+ Yarn is a multi-process webserver written in Ruby 1.9 by Jesper Kjeldgaard.
8
4
 
9
5
 
10
6
  ## Installation ##
@@ -12,16 +8,21 @@ Supports:
12
8
 
13
9
 
14
10
  ## 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
11
 
22
12
 
23
- yarn [options]
13
+ Usage: yarn [options]
24
14
  where [options] are:
25
15
  --host, -h <s>: Hostname or IP address of the server (default: 127.0.0.1)
26
16
  --port, -p <i>: Port number to listen on for incomming requests (default: 3000)
27
- --workers, -w <s>: Number of worker threads (default: 32)
17
+ --workers, -w <i>: Number of worker threads (default: 4)
18
+ --rack, -r <s>: Rackup file <config.ru> (default: off)
19
+ --log, -l: Enable logging
20
+ --debug, -d: Output debug messages
21
+ --version, -v: Print version and exit
22
+ --help, -e: Show this message
23
+
24
+
25
+ ## Todo list ##
26
+
27
+
28
+ * Support persistent connections.
data/bin/yarn CHANGED
@@ -9,7 +9,7 @@ require 'yarn'
9
9
  opts = Trollop::options do
10
10
  version "Yarn v#{Yarn::VERSION} 2011 Jesper Kjeldgaard"
11
11
  banner <<-EOS
12
- Yarn v#{Yarn::VERSION} is a multiprocess webserver written in Ruby 1.9.2
12
+ Yarn v#{Yarn::VERSION} is a multiprocess webserver written in Ruby 1.9
13
13
 
14
14
  Usage: yarn [options]
15
15
  where [options] are:
@@ -19,7 +19,8 @@ EOS
19
19
  opt :port, "Port number to listen on for incomming requests", :default => 3000
20
20
  opt :workers, "Number of worker threads", :default => 4
21
21
  opt :rack, "Rackup file <config.ru>", :default => "off"
22
- opt :debug, "Output debug messages"
22
+ opt :log, "Enable logging", :default => false
23
+ opt :debug, "Output debug messages", :default => false
23
24
  end
24
25
 
25
26
  server = Yarn::Server.new(opts)
@@ -1,8 +1,8 @@
1
1
  Feature: Server control
2
2
 
3
3
  As a developer
4
- I want to be able to command the server
5
- So that I can control the server
4
+ I want to be able to start and stop
5
+ So that I can serve files to the web
6
6
 
7
7
  Scenario: Start server
8
8
  When I start the server on port 3000
@@ -12,7 +12,3 @@ Feature: Server control
12
12
  Given the server is running
13
13
  When I stop the server
14
14
  Then I should see "Server stopped"
15
-
16
- Scenario: Supply port number when starting the server
17
- When I start the server on port 4000
18
- Then I should see "Server started on port 4000"
@@ -1,4 +1,3 @@
1
- require 'pry'
2
1
  require 'rack/handler/yarn'
3
2
  require 'rack/handler'
4
3
 
@@ -6,16 +5,15 @@ module Yarn
6
5
 
7
6
  autoload :Server, "yarn/server"
8
7
  autoload :VERSION, "yarn/version"
8
+ autoload :AbstractHandler, "yarn/abstract_handler"
9
9
  autoload :RequestHandler, "yarn/request_handler"
10
10
  autoload :RackHandler, "yarn/rack_handler"
11
11
  autoload :DirectoryLister, "yarn/directory_lister"
12
12
  autoload :ErrorPage, "yarn/error_page"
13
13
  autoload :Logging, "yarn/logging"
14
- autoload :ParsletParser, "yarn/parslet_parser"
14
+ autoload :Parser, "yarn/parser"
15
15
  autoload :Response, "yarn/response"
16
16
  autoload :STATUS_CODES, "yarn/statuses"
17
- autoload :Worker, "yarn/worker"
18
- autoload :WorkerPool, "yarn/worker_pool"
19
17
 
20
18
  Rack::Handler.register 'yarn', 'Yarn'
21
19
 
@@ -0,0 +1,116 @@
1
+ require 'date'
2
+ require 'rubygems'
3
+ require 'parslet'
4
+
5
+ module Yarn
6
+
7
+ class EmptyRequestError < StandardError; end
8
+ class ProcessingError < StandardError; end
9
+
10
+ class AbstractHandler
11
+
12
+ include Logging
13
+ include ErrorPage
14
+
15
+ attr_accessor :session, :parser, :request, :response
16
+
17
+ def initialize
18
+ @parser = Parser.new
19
+ @response = Response.new
20
+ end
21
+
22
+ def run(session)
23
+ set_common_headers
24
+ @session = session
25
+ begin
26
+ parse_request
27
+ debug "Request parsed, path: #{request_path}"
28
+ prepare_response
29
+ debug "Response prepared: #{@response.status}"
30
+ return_response
31
+ log "#{STATUS_CODES[@response.status]} #{client_address} #{request_path}"
32
+ rescue EmptyRequestError
33
+ log "Empty request from #{client_address}"
34
+ ensure
35
+ @session.close
36
+ debug "Connection closed"
37
+ end
38
+ end
39
+
40
+ def parse_request
41
+ raw_request = read_request
42
+ raise EmptyRequestError if raw_request.empty?
43
+
44
+ begin
45
+ @request = @parser.run raw_request
46
+ rescue Parslet::ParseFailed => e
47
+ @response.status = 400
48
+ debug "Parse failed: #{@request}"
49
+ end
50
+ end
51
+
52
+ def prepare_response
53
+ end
54
+
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
62
+ end
63
+ end
64
+
65
+ def read_request
66
+ input = []
67
+ while (line = @session.gets) do
68
+ length = line.gsub(/\D/,"") if line =~ /Content-Length/
69
+ if line == "\r\n"
70
+ input << line
71
+ input << @session.read(length.to_i) if length
72
+ break
73
+ else
74
+ input << line
75
+ end
76
+ end
77
+
78
+ debug "Done reading request"
79
+ input.join
80
+ end
81
+
82
+ def persistent?
83
+ return @request[:headers]["Connection"] == "keep-alive"
84
+ end
85
+
86
+ def set_common_headers
87
+ @response.headers[:Server] = "Yarn webserver v#{VERSION}"
88
+
89
+ # HTTP date format: Fri, 31 Dec 1999 23:59:59 GMT
90
+ time ||= DateTime.now.new_offset(0)
91
+ @response.headers[:Date] = time.strftime("%a, %d %b %Y %H:%M:%S GMT")
92
+ # Close connection header ( until support for persistent connections )
93
+ @response.headers[:Connection] = "Close"
94
+ end
95
+
96
+ def extract_path
97
+ path = @request[:uri][:path].to_s
98
+ if path[0] == "/" && path != "/"
99
+ path = path[1..-1]
100
+ end
101
+ path.gsub(/%20/, " ").strip
102
+ end
103
+
104
+ def post_body
105
+ @request ? @request[:body].to_s : ""
106
+ end
107
+
108
+ def request_path
109
+ @request[:uri][:path] if @request
110
+ end
111
+
112
+ def client_address
113
+ @session.peeraddr(:numeric)[2] if @session
114
+ end
115
+ end
116
+ end
@@ -1,10 +1,10 @@
1
- require 'logger'
2
1
  require 'date'
3
2
 
4
3
  module Yarn
5
4
  module Logging
6
5
 
7
6
  def log(msg)
7
+ return nil unless $log
8
8
  if msg.respond_to?(:each)
9
9
  msg.each do |line|
10
10
  output.puts "#{timestamp} #{line}"
@@ -19,7 +19,8 @@ module Yarn
19
19
  end
20
20
 
21
21
  def output
22
- $output || $stdout
22
+ out ||= $output || $stdout
23
+ out
23
24
  end
24
25
 
25
26
  def timestamp
@@ -1,10 +1,8 @@
1
1
  require 'rubygems'
2
2
  require 'parslet'
3
3
 
4
- module Parslet::Atoms::DSL; def once; repeat(1); end; end
5
-
6
4
  module Yarn
7
- class ParsletParser < Parslet::Parser
5
+ class Parser < Parslet::Parser
8
6
 
9
7
  # general rules
10
8
 
@@ -15,9 +13,9 @@ module Yarn
15
13
  rule(:spaces) { match('\s+') }
16
14
 
17
15
  # header rules
18
- rule(:header_value) { match['^\r\n'].once }
16
+ rule(:header_value) { match['^\r\n'].repeat(1) }
19
17
 
20
- rule(:header_name) { match['a-zA-Z\-'].once }
18
+ rule(:header_name) { match['a-zA-Z\-'].repeat(1) }
21
19
 
22
20
  rule(:header) do
23
21
  header_name.as(:name) >>
@@ -28,7 +26,7 @@ module Yarn
28
26
  end
29
27
 
30
28
  # request-line rules
31
- rule(:http_version) { match['HTTP\/\d\.\d'].once }
29
+ rule(:http_version) { match['HTTP\/\d\.\d'].repeat(1) }
32
30
 
33
31
  rule(:query) do
34
32
  match['\S+'].repeat(1)
@@ -37,12 +35,12 @@ module Yarn
37
35
  rule(:path) do
38
36
  match['^\?'].repeat(1).as(:path) >>
39
37
  str("?") >>
40
- query.as(:query) | match['^\s'].once.as(:path)
38
+ query.as(:query) | match['^\s'].repeat(1).as(:path)
41
39
  end
42
40
 
43
41
  rule(:port) { match['\d+'].repeat(1) }
44
42
 
45
- rule(:host) { match['^\/:'].once }
43
+ rule(:host) { match['^\/:'].repeat(1) }
46
44
 
47
45
  rule(:absolute_uri) do
48
46
  str("http://") >>
@@ -56,7 +54,7 @@ module Yarn
56
54
 
57
55
  rule(:spaces) { match('\s+') }
58
56
 
59
- rule(:method) { match['OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT'].once }
57
+ rule(:method) { match['OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT'].repeat(1) }
60
58
 
61
59
  # RFC2616: Method SP Request-URI SP HTTP-Version CRLF
62
60
  rule(:request_line) do
@@ -69,7 +67,7 @@ module Yarn
69
67
  end
70
68
 
71
69
  # body rule
72
- rule(:body) { match['\S'].once }
70
+ rule(:body) { match['\S'].repeat(1) }
73
71
 
74
72
  # RFC2616: Request-Line *(( header ) CRLF) CRLF [ message-body ]
75
73
  rule(:request) do
@@ -1,15 +1,16 @@
1
1
  require 'rack'
2
- require 'pry'
3
2
 
4
3
  module Yarn
5
- class RackHandler < RequestHandler
4
+ class RackHandler < AbstractHandler
6
5
 
7
- attr_accessor :env
6
+ attr_accessor :env, :opts
8
7
 
9
- def initialize(app)
10
- @parser = ParsletParser.new
8
+ def initialize(app,opts={})
9
+ @parser = Parser.new
11
10
  @response = Response.new
12
11
  @app = app
12
+ @host = opts[:host]
13
+ @port = opts[:port].to_s
13
14
  end
14
15
 
15
16
  def prepare_response
@@ -23,14 +24,18 @@ module Yarn
23
24
  end
24
25
 
25
26
  def make_env
27
+ input = StringIO.new("").set_encoding(Encoding::ASCII_8BIT)
28
+ if has_body?
29
+ input.string = @request[:body]
30
+ end
26
31
  @env = {
27
32
  "REQUEST_METHOD" => @request[:method].to_s,
28
33
  "PATH_INFO" => @request[:uri][:path].to_s,
29
34
  "QUERY_STRING" => @request[:uri][:query].to_s,
30
- "SERVER_NAME" => @request[:uri][:host].to_s,
31
- "SERVER_PORT" => @request[:uri][:port].to_s,
35
+ "SERVER_NAME" => @host,
36
+ "SERVER_PORT" => @port,
32
37
  "SCRIPT_NAME" => "",
33
- "rack.input" => StringIO.new("").set_encoding(Encoding::ASCII_8BIT),
38
+ "rack.input" => input,
34
39
  "rack.version" => Rack::VERSION,
35
40
  "rack.errors" => $output,
36
41
  "rack.multithread" => true,
@@ -38,6 +43,14 @@ module Yarn
38
43
  "rack.run_once" => false,
39
44
  "rack.url_scheme" => "http"
40
45
  }
46
+ @env["CONTENT_LENGTH"] = @request[:body].size.to_i if has_body?
47
+
48
+ return @env
49
+ end
50
+
51
+ def has_body?
52
+ value ||= !! @request[:body]
53
+ value
41
54
  end
42
55
 
43
56
  end
@@ -1,69 +1,15 @@
1
- require 'date'
2
- require 'rubygems'
3
- require 'parslet'
4
-
5
1
  module Yarn
6
-
7
- class EmptyRequestError < StandardError; end
8
- class ProcessingError < StandardError; end
9
-
10
- class RequestHandler
11
-
12
- include Logging
13
- include ErrorPage
14
-
15
- attr_accessor :session, :parser, :request, :response
16
-
17
- def initialize
18
- @parser = ParsletParser.new
19
- @response = Response.new
20
- end
21
-
22
- def run(session)
23
- set_common_headers
24
- @session = session
25
- begin
26
- parse_request
27
- debug "Request parsed, path: #{request_path}"
28
- prepare_response
29
- debug "Response prepared: #{@response.status}"
30
- return_response
31
- log "#{STATUS_CODES[@response.status]} #{client_address} #{request_path}"
32
- rescue EmptyRequestError
33
- log "Empty request from #{client_address}"
34
- ensure
35
- close_connection
36
- debug "Connection closed"
37
- end
38
- end
39
-
40
- def parse_request
41
- raw_request = read_request
42
- raise EmptyRequestError if raw_request.empty?
43
-
44
- begin
45
- @request = @parser.run raw_request
46
- rescue Parslet::ParseFailed => e
47
- @response.status = 400
48
- debug "Parse failed: #{@request}"
49
- end
50
- end
51
-
2
+ class RequestHandler < AbstractHandler
52
3
  def prepare_response
53
4
  path = extract_path
54
5
 
55
6
  @response.headers["Content-Type"] = "text/html"
56
7
 
57
8
  begin
58
- if File.directory? path
59
- serve_directory path
9
+ if File.directory?(path)
10
+ serve_directory(path)
60
11
  elsif File.exists?(path)
61
- if path =~ /.*\.rb$/
62
- @response.body << execute_script(path)
63
- @response.status = 200
64
- else
65
- serve_file(path)
66
- end
12
+ path =~ /.*\.rb$/ ? serve_ruby_file(path) : serve_file(path)
67
13
  else
68
14
  serve_404_page
69
15
  end
@@ -73,72 +19,15 @@ module Yarn
73
19
  end
74
20
  end
75
21
 
76
- def execute_script(path)
77
- response = `ruby #{path} #{post_body}`
78
- if !! ($?.to_s =~ /1$/)
79
- raise ProcessingError
80
- else
81
- response
82
- end
83
- end
84
-
85
- def post_body
86
- @request ? @request[:body].to_s : ""
87
- end
88
-
89
- def return_response
90
- @session.puts "HTTP/1.1 #{@response.status} #{STATUS_CODES[@response.status]}"
91
- @session.puts @response.headers.map { |k,v| "#{k}: #{v}" }
92
- @session.puts ""
93
-
94
- @response.body.each do |line|
95
- @session.puts line
96
- end
97
- end
98
-
99
- def close_connection
100
- if @session #&& !persistent?
101
- @session.close
102
- else
103
- # TODO: start some kind of timeout
104
- end
105
- end
106
-
107
- def read_request
108
- input = []
109
- while (line = @session.gets) do
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
118
- end
119
- @session.close_read
120
- debug "Done reading request"
121
- input.join
122
- end
123
-
124
- def persistent?
125
- return @request[:headers]["Connection"] == "keep-alive"
126
- end
127
-
128
- def set_common_headers
129
- @response.headers[:Server] = "Yarn webserver v#{VERSION}"
130
-
131
- # HTTP date format: Fri, 31 Dec 1999 23:59:59 GMT
132
- time = DateTime.now.new_offset(0)
133
- @response.headers[:Date] = time.strftime("%a, %d %b %Y %H:%M:%S GMT")
134
- # Close connection header ( until support for persistent connections )
135
- @response.headers[:Connection] = "Close"
136
- end
137
-
138
22
  def serve_file(path)
139
- @response.status = 200
140
23
  @response.body << read_file(path)
141
24
  @response.headers["Content-Type"] = get_mime_type path
25
+ @response.status = 200
26
+ end
27
+
28
+ def serve_ruby_file(path)
29
+ @response.body << execute_script(path)
30
+ @response.status = 200
142
31
  end
143
32
 
144
33
  def serve_directory(path)
@@ -148,8 +37,7 @@ module Yarn
148
37
  @response.headers["Content-Type"] = "text/html"
149
38
  else
150
39
  @response.headers["Content-Type"] = "text/html"
151
- directory_lister = DirectoryLister.new
152
- @response.body << directory_lister.list(path)
40
+ @response.body << DirectoryLister.list(path)
153
41
  end
154
42
  end
155
43
 
@@ -160,22 +48,12 @@ module Yarn
160
48
  file_contents
161
49
  end
162
50
 
163
- def extract_path
164
- path = @request[:uri][:path].to_s
165
- if path[0] == "/" && path != "/"
166
- path = path[1..-1]
167
- end
168
- path.gsub(/%20/, " ").strip
169
- end
170
-
171
- def serve_directory(path)
172
- @response.status = 200
173
- if File.exists?("index.html")# || File.exists?("/index.html")
174
- @response.body = read_file "index.html"
175
- @response.headers["Content-Type"] = "text/html"
51
+ def execute_script(path)
52
+ response = `ruby #{path} #{post_body}`
53
+ if !! ($?.to_s =~ /1$/)
54
+ raise ProcessingError
176
55
  else
177
- @response.headers["Content-Type"] = "text/html"
178
- @response.body << DirectoryLister.list(path)
56
+ response
179
57
  end
180
58
  end
181
59
 
@@ -184,28 +62,20 @@ module Yarn
184
62
  filetype = path.split('.').last
185
63
 
186
64
  return case
187
- when ["html", "htm"].include?(filetype)
188
- "text/html"
189
- when "txt" == filetype
190
- "text/plain"
191
- when "css" == filetype
192
- "text/css"
193
- when "js" == filetype
194
- "text/javascript"
195
- when ["png", "jpg", "jpeg", "gif", "tiff"].include?(filetype)
196
- "image/#{filetype}"
197
- when ["zip","pdf","postscript","x-tar","x-dvi"].include?(filetype)
198
- "application/#{filetype}"
199
- else false
65
+ when ["html", "htm"].include?(filetype)
66
+ "text/html"
67
+ when "txt" == filetype
68
+ "text/plain"
69
+ when "css" == filetype
70
+ "text/css"
71
+ when "js" == filetype
72
+ "text/javascript"
73
+ when ["png", "jpg", "jpeg", "gif", "tiff"].include?(filetype)
74
+ "image/#{filetype}"
75
+ when ["zip","pdf","postscript","x-tar","x-dvi"].include?(filetype)
76
+ "application/#{filetype}"
77
+ else false
78
+ end
200
79
  end
201
80
  end
202
-
203
- def request_path
204
- @request[:uri][:path] if @request
205
- end
206
-
207
- def client_address
208
- @session.peeraddr(:numeric)[2] if @session
209
- end
210
- end
211
81
  end
@@ -14,15 +14,18 @@ module Yarn
14
14
  host: '127.0.0.1',
15
15
  port: 3000,
16
16
  workers: 4,
17
+ log: true,
17
18
  rack: "off"
18
19
  }.merge(options)
19
20
 
20
21
  @app = nil
21
22
  @app = load_rack_app(opts[:rack]) unless opts[:rack] == "off"
23
+ @opts = opts
22
24
 
23
25
  @host, @port, @num_workers = opts[:host], opts[:port], opts[:workers]
24
26
  @workers = []
25
27
  $output, $debug = opts[:output], opts[:debug]
28
+ $log = opts[:log] || opts[:debug]
26
29
  end
27
30
 
28
31
  def load_rack_app(app_path)
@@ -48,14 +51,20 @@ module Yarn
48
51
 
49
52
  def init_workers
50
53
  @num_workers.times do
51
- @workers << fork do
52
- trap("INT") { exit }
53
- loop do
54
- handler ||= @app ? RackHandler.new(@app) : RequestHandler.new
55
- session = @socket.accept
56
- handler.run session
57
- end
58
- end
54
+ @workers << fork_worker
55
+ end
56
+ end
57
+
58
+ def fork_worker
59
+ fork { worker }
60
+ end
61
+
62
+ def worker
63
+ trap("INT") { exit }
64
+ loop do
65
+ @handler ||= @app ? RackHandler.new(@app,@opts) : RequestHandler.new
66
+ session = @socket.accept
67
+ @handler.run session
59
68
  end
60
69
  end
61
70
 
@@ -1,3 +1,3 @@
1
1
  module Yarn
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.9"
3
3
  end
@@ -2,18 +2,15 @@ $LOAD_PATH << File.expand_path('../../../lib', __FILE__)
2
2
 
3
3
  require 'rubygems'
4
4
  require 'bundler/setup'
5
+ require 'simplecov'
5
6
 
6
- # unless /jruby/ =~ `ruby -v`
7
- require 'simplecov'
8
- SimpleCov.start do
9
- add_filter "/spec/"
10
- add_filter "lib/yarn/http*"
11
- end
12
- # end
7
+ SimpleCov.start do
8
+ add_filter "/spec/"
9
+ add_filter "lib/rack/handler/"
10
+ end
13
11
 
14
12
  require 'yarn'
15
13
  require 'helpers'
16
- require 'pry'
17
14
 
18
15
  RSpec.configure do |config|
19
16
  config.include Helpers
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ module Yarn
4
+ describe AbstractHandler do
5
+ describe "Common behaviour" do
6
+
7
+ before(:each) do
8
+ @dummy_request = "GET /resource/1 HTTP/1.1\r\n "
9
+
10
+ @session = mock('TCPSocket')
11
+ @session.stub(:gets)
12
+ @session.stub(:close)
13
+
14
+ @handler = AbstractHandler.new
15
+ @handler.session = @session
16
+ @handler.stub(:read_request).and_return(@dummy_request)
17
+ end
18
+
19
+ describe "#read_request" do
20
+ it "should return a string from a feed" do
21
+ @handler.unstub!(:read_request)
22
+ @handler.session = StringIO.new
23
+ @handler.session.string = "line1\nline2\nline3"
24
+
25
+ @handler.read_request.should == "line1\nline2\nline3"
26
+ end
27
+
28
+ it "should handle POST body" do
29
+ @req = "POST /app HTTP/1.1\nCookie: $key=value;\nContent-Length: 17;\n\r\nfield1=1&field2=2"
30
+ @handler.unstub!(:read_request)
31
+ @handler.session = StringIO.new
32
+ @handler.session.string = @req
33
+
34
+ @handler.read_request.should == @req
35
+ end
36
+ end
37
+
38
+ describe "#parse_request" do
39
+ it "should invoke the Parser" do
40
+ @handler.parser.should_receive(:run)
41
+
42
+ @handler.parse_request
43
+ end
44
+
45
+ it "should set the bad-request header if parsing fails" do
46
+ bad_request = "BAD Warble warble request"
47
+ @handler.response.status.should be_nil
48
+
49
+ @session.stub(:gets).and_return(bad_request)
50
+ @handler.parse_request
51
+
52
+ @handler.response.status.should == 400
53
+ end
54
+ end
55
+
56
+ describe "#return_response" do
57
+ it "should write the response to the socket" do
58
+ @handler.session.should_receive(:puts).at_least(1).times
59
+ @handler.stub(:response).and_return("HTTP/1.1 201 OK")
60
+ @handler.return_response
61
+ end
62
+ end
63
+
64
+ describe "#persistent?" do
65
+ it "should return true if the Connection header is set to keep-alive" do
66
+ @handler.request = { headers: { "Connection" => "keep-alive" } }
67
+
68
+ @handler.persistent?.should be_true
69
+ end
70
+
71
+ it "should return false if the Connection header is set to close" do
72
+ @handler.request = { headers: { "Connection" => "close" } }
73
+
74
+ @handler.persistent?.should be_false
75
+ end
76
+ end
77
+
78
+ describe "#run" do
79
+ it "should call all relevant template methods" do
80
+ @handler.stub(:client_address)
81
+ @handler.should_receive(:parse_request).once
82
+ @handler.should_receive(:prepare_response).once
83
+ @handler.should_receive(:return_response).once
84
+
85
+ @handler.run(@session)
86
+ end
87
+ end
88
+ end
89
+
90
+
91
+ end
92
+ end
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Yarn
4
- describe ParsletParser do
4
+ describe Parser do
5
5
  before do
6
- @parser = ParsletParser.new
6
+ @parser = Parser.new
7
7
  end
8
8
 
9
9
  describe "#parse request_line" do
@@ -53,13 +53,11 @@ module Yarn
53
53
  describe "#parse parameters" do
54
54
  it "parses query in the url" do
55
55
  result = @parser.run("GET /page?param1=1&param2=2 HTTP/1.1\n")
56
- puts result
57
56
  result[:uri][:query].should == "param1=1&param2=2"
58
57
  end
59
58
 
60
59
  it "should parse query on rails assets" do
61
60
  result = @parser.run("GET assets/application.js?body=0 HTTP/1.1\n")
62
- puts result
63
61
  result[:uri][:query].should == "body=0"
64
62
  result[:uri][:path].should == "assets/application.js"
65
63
  end
@@ -116,7 +114,6 @@ module Yarn
116
114
  body = "attr=test&attr2=some_other_value&attr3=1231682368125361823"
117
115
  request = "POST /form HTTP/1.1\r\n#{header}\r\n\r\n#{body}"
118
116
  result = @parser.run(request)
119
- puts result
120
117
  result[:body].should == body
121
118
  end
122
119
  end
@@ -4,7 +4,8 @@ module Yarn
4
4
  describe RackHandler do
5
5
 
6
6
  before(:each) do
7
- @handler = RackHandler.new(nil)
7
+ @app = mock('app')
8
+ @handler = RackHandler.new(@app, { host: "www.hostname.com", port: 8888 })
8
9
  @handler.request = @handler.parser.run "GET http://www.hostname.com:8888/some_controller/some_action?param1=1&param2=2 HTTP/1.1"
9
10
  end
10
11
 
@@ -3,92 +3,6 @@ require 'spec_helper'
3
3
  module Yarn
4
4
  describe RequestHandler do
5
5
 
6
- describe "Common behaviour" do
7
-
8
- before(:each) do
9
- @dummy_request = "GET /resource/1 HTTP/1.1\r\n "
10
-
11
- @session = mock('TCPSocket')
12
- @session.stub(:gets).and_return(@dummy_request)
13
-
14
- @handler = RequestHandler.new
15
- @handler.session = @session
16
- @handler.stub(:debug,:log).and_return(true) #silence output
17
- @handler.stub(:read_request).and_return(@dummy_request)
18
- end
19
-
20
- describe "#read_request" do
21
- it "should return a string from a feed" do
22
- @handler.unstub!(:read_request)
23
- @handler.session = StringIO.new
24
- @handler.session.string = "line1\nline2\nline3"
25
-
26
- @handler.read_request.should == "line1\nline2\nline3"
27
- end
28
-
29
- it "should handle POST body" do
30
- @req = "POST /app HTTP/1.1\nCookie: $key=value;\nContent-Length: 17;\n\r\nfield1=1&field2=2"
31
- @handler.unstub!(:read_request)
32
- @handler.session = StringIO.new
33
- @handler.session.string = @req
34
-
35
- @handler.read_request.should == @req
36
- end
37
- end
38
-
39
- describe "#parse_request" do
40
- it "should invoke the Parser" do
41
- @handler.parser.should_receive(:run)
42
-
43
- @handler.parse_request
44
- end
45
-
46
- it "should set the bad-request header if parsing fails" do
47
- bad_request = "BAD Warble warble request"
48
- @handler.response.status.should be_nil
49
-
50
- @session.stub(:gets).and_return(bad_request)
51
- @handler.parse_request
52
-
53
- @handler.response.status.should == 400
54
- end
55
- end
56
-
57
- describe "#return_response" do
58
- it "should write the response to the socket" do
59
- @handler.session.should_receive(:puts).at_least(1).times
60
- @handler.stub(:response).and_return("HTTP/1.1 201 OK")
61
- @handler.return_response
62
- end
63
- end
64
-
65
- describe "#persistent?" do
66
- it "should return true if the Connection header is set to keep-alive" do
67
- @handler.request = { headers: { "Connection" => "keep-alive" } }
68
-
69
- @handler.persistent?.should be_true
70
- end
71
-
72
- it "should return false if the Connection header is set to close" do
73
- @handler.request = { headers: { "Connection" => "close" } }
74
-
75
- @handler.persistent?.should be_false
76
- end
77
- end
78
-
79
- describe "#run" do
80
- it "should call all relevant template methods" do
81
- @handler.stub(:client_address)
82
- @handler.should_receive(:parse_request).once
83
- @handler.should_receive(:prepare_response).once
84
- @handler.should_receive(:return_response).once
85
- @handler.should_receive(:close_connection).once
86
-
87
- @handler.run(@dummy_request)
88
- end
89
- end
90
- end
91
-
92
6
  describe "Static requests" do
93
7
 
94
8
  before(:each) do
@@ -103,7 +17,6 @@ module Yarn
103
17
  end
104
18
 
105
19
  after(:each) do
106
- @handler.close_connection
107
20
  File.delete("index.html") if File.exists?("index.html")
108
21
  File.delete("testdir/index.html") if File.exists?("testdir/index.html")
109
22
  Dir.delete("testdir") if Dir.exists?("testdir")
@@ -216,6 +129,7 @@ module Yarn
216
129
  end
217
130
  end
218
131
  end
132
+
219
133
  describe "Dynamic requests" do
220
134
  before(:each) do
221
135
  @file_content = "!#/bin/ruby\nputs 'Success!'"
@@ -47,18 +47,6 @@ module Yarn
47
47
  end
48
48
  end
49
49
 
50
- # describe "#start" do
51
- # it "should start the socket_listener" do
52
- # @thread = Thread.new do
53
- # @server = Server.new({ output: $console })
54
- # @server.start
55
- # end
56
- # sleep 2
57
- # get("/").should be_true
58
- # @thread.kill
59
- # end
60
- # end
61
-
62
50
  describe "#stop" do
63
51
  it "should notify the server is stopped" do
64
52
  $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'
9
+ gem 'yarn', '~> 0.1'
10
10
  gem 'pry'
11
11
 
12
12
  # Gems used only for assets and not required
@@ -8,9 +8,8 @@ Gem::Specification.new do |s|
8
8
  s.authors = ["Jesper Kjeldgaard"]
9
9
  s.email = ["jkjeldgaard@gmail.com"]
10
10
  s.homepage = "https://github.com/thejspr/yarn"
11
- s.summary = %q{Multi-threaded webserver for static and dynamic (*.rb) files as well as rack applications.}
12
- s.description = %q{A multi-threaded web-server written in Ruby 1.9.}
13
-
11
+ s.summary = %q{Multi-process webserver for static, dynamic (*.rb) files and Rack applications.}
12
+ s.description = %q{A multi-process web-server written in Ruby 1.9.}
14
13
  s.rubyforge_project = "yarn"
15
14
 
16
15
  s.files = `git ls-files`.split("\n")
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.2
4
+ version: 0.0.9
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-02 00:00:00.000000000 Z
12
+ date: 2011-10-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parslet
16
- requirement: &19662180 !ruby/object:Gem::Requirement
16
+ requirement: &12100240 !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: *19662180
24
+ version_requirements: *12100240
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: trollop
27
- requirement: &20080020 !ruby/object:Gem::Requirement
27
+ requirement: &12099740 !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: *20080020
35
+ version_requirements: *12099740
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rack
38
- requirement: &20077620 !ruby/object:Gem::Requirement
38
+ requirement: &12099280 !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: *20077620
46
+ version_requirements: *12099280
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: cucumber
49
- requirement: &20075460 !ruby/object:Gem::Requirement
49
+ requirement: &12098900 !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: *20075460
57
+ version_requirements: *12098900
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: faraday
60
- requirement: &20069600 !ruby/object:Gem::Requirement
60
+ requirement: &12098440 !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: *20069600
68
+ version_requirements: *12098440
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: nokogiri
71
- requirement: &20066540 !ruby/object:Gem::Requirement
71
+ requirement: &12098020 !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: *20066540
79
+ version_requirements: *12098020
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &20064440 !ruby/object:Gem::Requirement
82
+ requirement: &12097600 !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: *20064440
90
+ version_requirements: *12097600
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: simplecov
93
- requirement: &20057640 !ruby/object:Gem::Requirement
93
+ requirement: &12097180 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,8 +98,8 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *20057640
102
- description: A multi-threaded web-server written in Ruby 1.9.
101
+ version_requirements: *12097180
102
+ description: A multi-process web-server written in Ruby 1.9.
103
103
  email:
104
104
  - jkjeldgaard@gmail.com
105
105
  executables:
@@ -113,6 +113,7 @@ files:
113
113
  - .rvmrc
114
114
  - Gemfile
115
115
  - Gemfile.lock
116
+ - LICENCE
116
117
  - README.md
117
118
  - Rakefile
118
119
  - bin/yarn
@@ -132,10 +133,11 @@ files:
132
133
  - features/support/hooks.rb
133
134
  - lib/rack/handler/yarn.rb
134
135
  - lib/yarn.rb
136
+ - lib/yarn/abstract_handler.rb
135
137
  - lib/yarn/directory_lister.rb
136
138
  - lib/yarn/error_page.rb
137
139
  - lib/yarn/logging.rb
138
- - lib/yarn/parslet_parser.rb
140
+ - lib/yarn/parser.rb
139
141
  - lib/yarn/rack_handler.rb
140
142
  - lib/yarn/request_handler.rb
141
143
  - lib/yarn/response.rb
@@ -145,10 +147,11 @@ files:
145
147
  - spec/helpers.rb
146
148
  - spec/rack/handler/yarn_spec.rb
147
149
  - spec/spec_helper.rb
150
+ - spec/yarn/abstract_handler_spec.rb
148
151
  - spec/yarn/directory_lister_spec.rb
149
152
  - spec/yarn/error_page_spec.rb
150
153
  - spec/yarn/logging_spec.rb
151
- - spec/yarn/parslet_parser_spec.rb
154
+ - spec/yarn/parser_spec.rb
152
155
  - spec/yarn/rack_handler_spec.rb
153
156
  - spec/yarn/request_handler_spec.rb
154
157
  - spec/yarn/response_spec.rb
@@ -219,9 +222,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
222
  version: '0'
220
223
  requirements: []
221
224
  rubyforge_project: yarn
222
- rubygems_version: 1.8.10
225
+ rubygems_version: 1.8.11
223
226
  signing_key:
224
227
  specification_version: 3
225
- summary: Multi-threaded webserver for static and dynamic (*.rb) files as well as rack
226
- applications.
228
+ summary: Multi-process webserver for static, dynamic (*.rb) files and Rack applications.
227
229
  test_files: []