thin 0.8.2 → 1.0.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.

Potentially problematic release.


This version of thin might be problematic. Click here for more details.

Files changed (44) hide show
  1. data/CHANGELOG +15 -0
  2. data/README +1 -1
  3. data/example/adapter.rb +2 -5
  4. data/example/config.ru +2 -5
  5. data/example/vlad.rake +6 -3
  6. data/lib/rack/adapter/loader.rb +27 -23
  7. data/lib/rack/adapter/rails.rb +9 -3
  8. data/lib/thin/backends/base.rb +13 -3
  9. data/lib/thin/command.rb +7 -5
  10. data/lib/thin/connection.rb +68 -28
  11. data/lib/thin/controllers/controller.rb +2 -1
  12. data/lib/thin/daemonizing.rb +25 -20
  13. data/lib/thin/headers.rb +8 -0
  14. data/lib/thin/logging.rb +28 -23
  15. data/lib/thin/request.rb +32 -32
  16. data/lib/thin/response.rb +22 -19
  17. data/lib/thin/runner.rb +14 -7
  18. data/lib/thin/server.rb +6 -2
  19. data/lib/thin/stats.rb +1 -4
  20. data/lib/thin/version.rb +4 -4
  21. data/lib/thin.rb +1 -0
  22. data/spec/backends/tcp_server_spec.rb +11 -0
  23. data/spec/command_spec.rb +7 -1
  24. data/spec/daemonizing_spec.rb +17 -0
  25. data/spec/headers_spec.rb +11 -0
  26. data/spec/logging_spec.rb +6 -2
  27. data/spec/rack/rails_adapter_spec.rb +10 -8
  28. data/spec/request/parser_spec.rb +1 -1
  29. data/spec/response_spec.rb +7 -0
  30. data/spec/runner_spec.rb +22 -3
  31. data/spec/server/pipelining_spec.rb +1 -1
  32. data/spec/server/robustness_spec.rb +1 -1
  33. data/spec/server/stopping_spec.rb +1 -1
  34. data/spec/server/swiftiply_spec.rb +1 -1
  35. data/spec/server/tcp_spec.rb +18 -7
  36. data/spec/server/threaded_spec.rb +1 -1
  37. data/spec/server/unix_socket_spec.rb +1 -1
  38. data/spec/spec_helper.rb +10 -0
  39. data/tasks/announce.rake +1 -1
  40. data/tasks/deploy.rake +2 -2
  41. data/tasks/email.erb +0 -4
  42. data/tasks/gem.rake +2 -2
  43. metadata +6 -4
  44. data/lib/thin_parser.bundle +0 -0
data/lib/thin/request.rb CHANGED
@@ -5,7 +5,7 @@ module Thin
5
5
  # Raised when an incoming request is not valid
6
6
  # and the server can not process it.
7
7
  class InvalidRequest < IOError; end
8
-
8
+
9
9
  # A request sent by the client to the server.
10
10
  class Request
11
11
  # Maximum request body size before it is moved out of memory
@@ -13,7 +13,7 @@ module Thin
13
13
  MAX_BODY = 1024 * (80 + 32)
14
14
  BODY_TMPFILE = 'thin-body'.freeze
15
15
  MAX_HEADER = 1024 * (80 + 32)
16
-
16
+
17
17
  # Freeze some HTTP header names & values
18
18
  SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
19
19
  HTTP_VERSION = 'HTTP_VERSION'.freeze
@@ -22,9 +22,9 @@ module Thin
22
22
  FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'.freeze
23
23
  CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
24
24
  CONNECTION = 'HTTP_CONNECTION'.freeze
25
- KEEP_ALIVE_REGEXP = /keep-alive/i
26
- NOT_CLOSE_REGEXP = /[^(close)]/i
27
-
25
+ KEEP_ALIVE_REGEXP = /\bkeep-alive\b/i.freeze
26
+ CLOSE_REGEXP = /\bclose\b/i.freeze
27
+
28
28
  # Freeze some Rack header names
29
29
  RACK_INPUT = 'rack.input'.freeze
30
30
  RACK_VERSION = 'rack.version'.freeze
@@ -32,16 +32,16 @@ module Thin
32
32
  RACK_MULTITHREAD = 'rack.multithread'.freeze
33
33
  RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
34
34
  RACK_RUN_ONCE = 'rack.run_once'.freeze
35
-
35
+
36
36
  # CGI-like request environment variables
37
37
  attr_reader :env
38
-
38
+
39
39
  # Unparsed data of the request
40
40
  attr_reader :data
41
-
41
+
42
42
  # Request body
43
43
  attr_reader :body
44
-
44
+
45
45
  def initialize
46
46
  @parser = HttpParser.new
47
47
  @data = ''
@@ -49,36 +49,36 @@ module Thin
49
49
  @body = StringIO.new
50
50
  @env = {
51
51
  SERVER_SOFTWARE => SERVER,
52
-
52
+
53
53
  # Rack stuff
54
54
  RACK_INPUT => @body,
55
-
55
+
56
56
  RACK_VERSION => VERSION::RACK,
57
57
  RACK_ERRORS => STDERR,
58
-
58
+
59
59
  RACK_MULTITHREAD => false,
60
60
  RACK_MULTIPROCESS => false,
61
61
  RACK_RUN_ONCE => false
62
62
  }
63
63
  end
64
-
64
+
65
65
  # Parse a chunk of data into the request environment
66
66
  # Raises a +InvalidRequest+ if invalid.
67
67
  # Returns +true+ if the parsing is complete.
68
68
  def parse(data)
69
69
  if @parser.finished? # Header finished, can only be some more body
70
- body << data
70
+ body << data
71
71
  else # Parse more header using the super parser
72
72
  @data << data
73
- raise InvalidRequest, 'Header longer than allowed' if @data.size > MAX_HEADER
74
-
73
+ raise InvalidRequest, 'Header longer than allowed' if @data.size > MAX_HEADER
74
+
75
75
  @nparsed = @parser.execute(@env, @data, @nparsed)
76
76
 
77
77
  # Transfert to a tempfile if body is very big
78
78
  move_body_to_tempfile if @parser.finished? && content_length > MAX_BODY
79
79
  end
80
-
81
-
80
+
81
+
82
82
  if finished? # Check if header and body are complete
83
83
  @data = nil
84
84
  @body.rewind
@@ -87,17 +87,17 @@ module Thin
87
87
  false # Not finished, need more data
88
88
  end
89
89
  end
90
-
90
+
91
91
  # +true+ if headers and body are finished parsing
92
92
  def finished?
93
93
  @parser.finished? && @body.size >= content_length
94
94
  end
95
-
95
+
96
96
  # Expected size of the body
97
97
  def content_length
98
98
  @env[CONTENT_LENGTH].to_i
99
99
  end
100
-
100
+
101
101
  # Returns +true+ if the client expect the connection to be persistent.
102
102
  def persistent?
103
103
  # Clients and servers SHOULD NOT assume that a persistent connection
@@ -105,32 +105,32 @@ module Thin
105
105
  # signaled. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html)
106
106
  if @env[HTTP_VERSION] == HTTP_1_0
107
107
  @env[CONNECTION] =~ KEEP_ALIVE_REGEXP
108
-
108
+
109
109
  # HTTP/1.1 client intends to maintain a persistent connection unless
110
110
  # a Connection header including the connection-token "close" was sent
111
111
  # in the request
112
112
  else
113
- @env[CONNECTION].nil? || @env[CONNECTION] =~ NOT_CLOSE_REGEXP
113
+ @env[CONNECTION].nil? || @env[CONNECTION] !~ CLOSE_REGEXP
114
114
  end
115
115
  end
116
-
116
+
117
117
  def remote_address=(address)
118
118
  @env[REMOTE_ADDR] = address
119
119
  end
120
-
120
+
121
121
  def forwarded_for
122
122
  @env[FORWARDED_FOR]
123
123
  end
124
-
124
+
125
125
  def threaded=(value)
126
126
  @env[RACK_MULTITHREAD] = value
127
127
  end
128
-
128
+
129
129
  # Close any resource used by the request
130
130
  def close
131
131
  @body.delete if @body.class == Tempfile
132
- end
133
-
132
+ end
133
+
134
134
  private
135
135
  def move_body_to_tempfile
136
136
  current_body = @body
@@ -138,7 +138,7 @@ module Thin
138
138
  @body = Tempfile.new(BODY_TMPFILE)
139
139
  @body.binmode
140
140
  @body << current_body.read
141
- @env[RACK_INPUT] = @body
141
+ @env[RACK_INPUT] = @body
142
142
  end
143
- end
144
- end
143
+ end
144
+ end
data/lib/thin/response.rb CHANGED
@@ -6,46 +6,48 @@ module Thin
6
6
  KEEP_ALIVE = 'keep-alive'.freeze
7
7
  SERVER = 'Server'.freeze
8
8
  CONTENT_LENGTH = 'Content-Length'.freeze
9
-
9
+
10
10
  # Status code
11
11
  attr_accessor :status
12
-
12
+
13
13
  # Response body, must respond to +each+.
14
14
  attr_accessor :body
15
-
15
+
16
16
  # Headers key-value hash
17
17
  attr_reader :headers
18
-
18
+
19
19
  def initialize
20
20
  @headers = Headers.new
21
21
  @status = 200
22
22
  @persistent = false
23
23
  end
24
-
24
+
25
25
  # String representation of the headers
26
26
  # to be sent in the response.
27
27
  def headers_output
28
28
  # Set default headers
29
29
  @headers[CONNECTION] = persistent? ? KEEP_ALIVE : CLOSE
30
30
  @headers[SERVER] = Thin::SERVER
31
-
31
+
32
32
  @headers.to_s
33
33
  end
34
-
34
+
35
35
  # Top header of the response,
36
36
  # containing the status code and response headers.
37
37
  def head
38
38
  "HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n"
39
39
  end
40
-
40
+
41
41
  if Thin.ruby_18?
42
-
42
+
43
43
  # Ruby 1.8 implementation.
44
44
  # Respects Rack specs.
45
+ #
46
+ # See http://rack.rubyforge.org/doc/files/SPEC.html
45
47
  def headers=(key_value_pairs)
46
48
  key_value_pairs.each do |k, vs|
47
- vs.each { |v| @headers[k] = v.chomp }
48
- end
49
+ vs.each { |v| @headers[k] = v.chomp } if vs
50
+ end if key_value_pairs
49
51
  end
50
52
 
51
53
  else
@@ -57,21 +59,22 @@ module Thin
57
59
  # To be reviewed when a new Rack spec comes out.
58
60
  def headers=(key_value_pairs)
59
61
  key_value_pairs.each do |k, vs|
62
+ next unless vs
60
63
  if vs.is_a?(String)
61
64
  vs.each_line { |v| @headers[k] = v.chomp }
62
65
  else
63
66
  vs.each { |v| @headers[k] = v.chomp }
64
67
  end
65
- end
66
- end
68
+ end if key_value_pairs
69
+ end
67
70
 
68
71
  end
69
-
72
+
70
73
  # Close any resource used by the response
71
74
  def close
72
75
  @body.close if @body.respond_to?(:close)
73
76
  end
74
-
77
+
75
78
  # Yields each chunk of the response.
76
79
  # To control the size of each chunk
77
80
  # define your own +each+ method on +body+.
@@ -81,16 +84,16 @@ module Thin
81
84
  yield chunk
82
85
  end
83
86
  end
84
-
87
+
85
88
  # Tell the client the connection should stay open
86
89
  def persistent!
87
90
  @persistent = true
88
91
  end
89
-
92
+
90
93
  # Persistent connection must be requested as keep-alive
91
94
  # from the server and have a Content-Length.
92
95
  def persistent?
93
96
  @persistent && @headers.has_key?(CONTENT_LENGTH)
94
- end
97
+ end
95
98
  end
96
- end
99
+ end
data/lib/thin/runner.rb CHANGED
@@ -40,7 +40,8 @@ module Thin
40
40
  :log => 'log/thin.log',
41
41
  :pid => 'tmp/pids/thin.pid',
42
42
  :max_conns => Server::DEFAULT_MAXIMUM_CONNECTIONS,
43
- :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
43
+ :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
44
+ :require => []
44
45
  }
45
46
 
46
47
  parse!
@@ -63,7 +64,7 @@ module Thin
63
64
  opts.on("-S", "--socket FILE", "bind to unix domain socket") { |file| @options[:socket] = file }
64
65
  opts.on("-y", "--swiftiply [KEY]", "Run using swiftiply") { |key| @options[:swiftiply] = key }
65
66
  opts.on("-A", "--adapter NAME", "Rack adapter to use (default: autodetect)",
66
- "(#{Rack::ADAPTERS.keys.join(', ')})") { |name| @options[:adapter] = name }
67
+ "(#{Rack::ADAPTERS.map{|(a,b)|a}.join(', ')})") { |name| @options[:adapter] = name }
67
68
  opts.on("-R", "--rackup FILE", "Load a Rack config file instead of " +
68
69
  "Rack adapter") { |file| @options[:rackup] = file }
69
70
  opts.on("-c", "--chdir DIR", "Change to dir before starting") { |dir| @options[:chdir] = File.expand_path(dir) }
@@ -102,6 +103,7 @@ module Thin
102
103
  opts.on("-b", "--backend CLASS", "Backend to use, full classname") { |name| @options[:backend] = name }
103
104
  opts.on("-t", "--timeout SEC", "Request or command timeout in sec " +
104
105
  "(default: #{@options[:timeout]})") { |sec| @options[:timeout] = sec.to_i }
106
+ opts.on("-f", "--force", "Force the execution of the command") { @options[:force] = true }
105
107
  opts.on( "--max-conns NUM", "Maximum number of connections " +
106
108
  "(default: #{@options[:max_conns]})",
107
109
  "Might require sudo to set higher then 1024") { |num| @options[:max_conns] = num.to_i } unless Thin.win?
@@ -110,13 +112,14 @@ module Thin
110
112
  "(default: #{@options[:max_persistent_conns]})") { |num| @options[:max_persistent_conns] = num.to_i }
111
113
  opts.on( "--threaded", "Call the Rack application in threads " +
112
114
  "[experimental]") { @options[:threaded] = true }
115
+ opts.on( "--no-epoll", "Disable the use of epoll") { @options[:no_epoll] = true } if Thin.linux?
113
116
 
114
117
  opts.separator ""
115
118
  opts.separator "Common options:"
116
119
 
117
- opts.on_tail("-r", "--require FILE", "require the library") { |file| ruby_require file }
118
- opts.on_tail("-D", "--debug", "Set debbuging on") { Logging.debug = true }
119
- opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") { Logging.trace = true }
120
+ opts.on_tail("-r", "--require FILE", "require the library") { |file| @options[:require] << file }
121
+ opts.on_tail("-D", "--debug", "Set debbuging on") { @options[:debug] = true }
122
+ opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") { @options[:trace] = true }
120
123
  opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
121
124
  opts.on_tail('-v', '--version', "Show version") { puts Thin::SERVER; exit }
122
125
  end
@@ -139,7 +142,7 @@ module Thin
139
142
  puts @parser
140
143
  exit 1
141
144
  else
142
- abort "Invalid command: #{@command}"
145
+ abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}"
143
146
  end
144
147
  end
145
148
 
@@ -155,6 +158,10 @@ module Thin
155
158
  # relative to this one.
156
159
  Dir.chdir(@options[:chdir]) unless CONFIGLESS_COMMANDS.include?(@command)
157
160
 
161
+ @options[:require].each { |r| ruby_require r }
162
+ Logging.debug = @options[:debug]
163
+ Logging.trace = @options[:trace]
164
+
158
165
  controller = case
159
166
  when cluster? then Controllers::Cluster.new(@options)
160
167
  when service? then Controllers::Service.new(@options)
@@ -198,4 +205,4 @@ module Thin
198
205
  end
199
206
  end
200
207
  end
201
- end
208
+ end
data/lib/thin/server.rb CHANGED
@@ -85,9 +85,14 @@ module Thin
85
85
  # UNIX domain socket on which the server is listening for connections.
86
86
  def_delegator :backend, :socket
87
87
 
88
+ # Disable the use of epoll under Linux
89
+ def_delegators :backend, :no_epoll, :no_epoll=
90
+
88
91
  def initialize(*args, &block)
89
92
  host, port, options = DEFAULT_HOST, DEFAULT_PORT, {}
90
93
 
94
+ # Guess each parameter by its type so they can be
95
+ # received in any order.
91
96
  args.each do |arg|
92
97
  case arg
93
98
  when Fixnum, /^\d+$/ then port = arg.to_i
@@ -102,7 +107,7 @@ module Thin
102
107
  @backend = select_backend(host, port, options)
103
108
 
104
109
  load_cgi_multipart_eof_fix
105
-
110
+
106
111
  @backend.server = self
107
112
 
108
113
  # Set defaults
@@ -139,7 +144,6 @@ module Thin
139
144
  debug ">> Debugging ON"
140
145
  trace ">> Tracing ON"
141
146
 
142
- log ">> Threaded mode #{@backend.threaded? ? 'ON' : 'OFF'}"
143
147
  log ">> Maximum connections set to #{@backend.maximum_connections}"
144
148
  log ">> Listening on #{@backend}, CTRL+C to stop"
145
149
 
data/lib/thin/stats.rb CHANGED
@@ -43,10 +43,7 @@ module Thin
43
43
 
44
44
  [
45
45
  200,
46
- {
47
- 'Content-Type' => 'text/html',
48
- 'Content-Length' => body.size.to_s
49
- },
46
+ { 'Content-Type' => 'text/html' },
50
47
  [body]
51
48
  ]
52
49
  end
data/lib/thin/version.rb CHANGED
@@ -4,13 +4,13 @@ module Thin
4
4
  class PlatformNotSupported < RuntimeError; end
5
5
 
6
6
  module VERSION #:nodoc:
7
- MAJOR = 0
8
- MINOR = 8
9
- TINY = 2
7
+ MAJOR = 1
8
+ MINOR = 0
9
+ TINY = 0
10
10
 
11
11
  STRING = [MAJOR, MINOR, TINY].join('.')
12
12
 
13
- CODENAME = 'Double Margarita'
13
+ CODENAME = "That's What She Said".freeze
14
14
 
15
15
  RACK = [0, 3].freeze # Latest Rack version that was tested
16
16
  end
data/lib/thin.rb CHANGED
@@ -3,6 +3,7 @@ $:.unshift File.expand_path(File.dirname(__FILE__))
3
3
  require 'fileutils'
4
4
  require 'timeout'
5
5
  require 'stringio'
6
+ require 'time'
6
7
 
7
8
  require 'rubygems'
8
9
  require 'eventmachine'
@@ -5,6 +5,17 @@ describe Backends::TcpServer do
5
5
  @backend = Backends::TcpServer.new('0.0.0.0', 3333)
6
6
  end
7
7
 
8
+ it "should not use epoll" do
9
+ @backend.no_epoll = true
10
+ EventMachine.should_not_receive(:epoll)
11
+ @backend.config
12
+ end
13
+
14
+ it "should use epoll" do
15
+ EventMachine.should_receive(:epoll)
16
+ @backend.config
17
+ end
18
+
8
19
  it "should connect" do
9
20
  EventMachine.run do
10
21
  @backend.connect
data/spec/command_spec.rb CHANGED
@@ -3,7 +3,8 @@ require File.dirname(__FILE__) + '/spec_helper'
3
3
  describe Command do
4
4
  before do
5
5
  Command.script = 'thin'
6
- @command = Command.new(:start, :port => 3000, :daemonize => true, :log => 'hi.log')
6
+ @command = Command.new(:start, :port => 3000, :daemonize => true, :log => 'hi.log',
7
+ :require => %w(rubygems thin))
7
8
  end
8
9
 
9
10
  it 'should shellify command' do
@@ -11,4 +12,9 @@ describe Command do
11
12
  out.should include('--port=3000', '--daemonize', '--log="hi.log"', 'thin start --')
12
13
  out.should_not include('--pid')
13
14
  end
15
+
16
+ it 'should shellify Array argument to multiple parameters' do
17
+ out = @command.shellify
18
+ out.should include('--require="rubygems"', '--require="thin"')
19
+ end
14
20
  end
@@ -94,6 +94,23 @@ describe 'Daemonizing' do
94
94
  File.exist?(@server.pid_file).should be_false
95
95
  end
96
96
 
97
+ it 'should force kill process in pid file' do
98
+ @pid = fork do
99
+ @server.daemonize
100
+ loop { sleep 3 }
101
+ end
102
+
103
+ server_should_start_in_less_then 3
104
+
105
+ @pid = @server.pid
106
+
107
+ silence_stream STDOUT do
108
+ TestServer.kill(@server.pid_file, 0)
109
+ end
110
+
111
+ File.exist?(@server.pid_file).should be_false
112
+ end
113
+
97
114
  it 'should send kill signal if timeout' do
98
115
  @pid = fork do
99
116
  @server.should_receive(:stop) # pretend we cannot handle the INT signal
data/spec/headers_spec.rb CHANGED
@@ -26,4 +26,15 @@ describe Headers do
26
26
 
27
27
  @headers.to_s.should == "Host: localhost:3000\r\nSet-Cookie: twice\r\nSet-Cookie: is cooler the once\r\n"
28
28
  end
29
+
30
+ it 'should ignore nil values' do
31
+ @headers['Something'] = nil
32
+ @headers.to_s.should_not include('Something: ')
33
+ end
34
+
35
+ it 'should format Time values correctly' do
36
+ time = Time.now
37
+ @headers['Modified-At'] = time
38
+ @headers.to_s.should include("Modified-At: #{time.httpdate}")
39
+ end
29
40
  end
data/spec/logging_spec.rb CHANGED
@@ -28,14 +28,18 @@ describe Logging do
28
28
  @object.log 'hi'
29
29
  end
30
30
 
31
- it "should not output when silenced [deprecated]" do
32
- @object.should_receive(:warn)
31
+ it "should not output when silenced as instance method" do
33
32
  @object.silent = true
34
33
 
35
34
  @object.should_not_receive(:puts)
36
35
  @object.log 'hi'
37
36
  end
38
37
 
38
+ it "should be usable as module functions" do
39
+ Logging.silent = true
40
+ Logging.log "hi"
41
+ end
42
+
39
43
  after do
40
44
  Logging.silent = true
41
45
  end
@@ -21,7 +21,7 @@ begin
21
21
 
22
22
  it "should handle POST parameters" do
23
23
  data = "foo=bar"
24
- res = @request.post("/simple/post_form", :input => data, 'CONTENT_LENGTH' => data.size)
24
+ res = @request.post("/simple/post_form", :input => data, 'CONTENT_LENGTH' => data.size.to_s, :lint => true)
25
25
 
26
26
  res.should be_ok
27
27
  res["Content-Type"].should include("text/html")
@@ -31,21 +31,21 @@ begin
31
31
  end
32
32
 
33
33
  it "should serve static files" do
34
- res = @request.get("/index.html")
34
+ res = @request.get("/index.html", :lint => true)
35
35
 
36
36
  res.should be_ok
37
37
  res["Content-Type"].should include("text/html")
38
38
  end
39
39
 
40
40
  it "should serve root with index.html if present" do
41
- res = @request.get("/")
41
+ res = @request.get("/", :lint => true)
42
42
 
43
43
  res.should be_ok
44
44
  res["Content-Length"].to_i.should == File.size(@rails_app_path + '/public/index.html')
45
45
  end
46
46
 
47
47
  it "should serve page cache if present" do
48
- res = @request.get("/simple/cached?value=cached")
48
+ res = @request.get("/simple/cached?value=cached", :lint => true)
49
49
 
50
50
  res.should be_ok
51
51
  res.body.should == 'cached'
@@ -57,7 +57,7 @@ begin
57
57
  end
58
58
 
59
59
  it "should not serve page cache on POST request" do
60
- res = @request.get("/simple/cached?value=cached")
60
+ res = @request.get("/simple/cached?value=cached", :lint => true)
61
61
 
62
62
  res.should be_ok
63
63
  res.body.should == 'cached'
@@ -69,10 +69,12 @@ begin
69
69
  end
70
70
 
71
71
  it "handles multiple cookies" do
72
- res = @request.get('/simple/set_cookie?name=a&value=1')
72
+ res = @request.get('/simple/set_cookie?name=a&value=1', :lint => true)
73
73
 
74
- res.should be_ok
75
- res.original_headers['Set-Cookie'].should include('a=1; path=/', '_rails_app_session')
74
+ res.should be_ok
75
+ res.original_headers['Set-Cookie'].size.should == 2
76
+ res.original_headers['Set-Cookie'].first.should include('a=1; path=/')
77
+ res.original_headers['Set-Cookie'].last.should include('_rails_app_session')
76
78
  end
77
79
 
78
80
  after do
@@ -169,7 +169,7 @@ EOS
169
169
 
170
170
  sorta_safe.size.should == nread
171
171
  parser.should be_finished
172
- parser.should_not have_error
172
+ parser.should_not be_error
173
173
  end
174
174
  end
175
175
 
@@ -43,6 +43,13 @@ describe Response do
43
43
 
44
44
  @response.head.should include("Set-Cookie: mium=7", "Set-Cookie: hi=there")
45
45
  end
46
+
47
+ it 'should ignore nil headers' do
48
+ @response.headers = nil
49
+ @response.headers = { 'Host' => 'localhost' }
50
+ @response.headers = { 'Set-Cookie' => nil }
51
+ @response.head.should include('Host: localhost')
52
+ end
46
53
 
47
54
  it 'should output body' do
48
55
  @response.body = '<html></html>'
data/spec/runner_spec.rb CHANGED
@@ -13,7 +13,7 @@ describe Runner do
13
13
  Runner.new(%w(stop)).command.should == 'stop'
14
14
  Runner.new(%w(restart)).command.should == 'restart'
15
15
  end
16
-
16
+
17
17
  it "should abort on unknow command" do
18
18
  runner = Runner.new(%w(poop))
19
19
 
@@ -66,14 +66,33 @@ describe Runner do
66
66
  it "should warn when require a rack config file" do
67
67
  STDERR.stub!(:write)
68
68
  STDERR.should_receive(:write).with(/WARNING:/)
69
-
69
+
70
70
  runner = Runner.new(%w(start -r config.ru))
71
+ runner.run! rescue nil
71
72
 
72
73
  runner.options[:rackup].should == 'config.ru'
73
74
  end
74
75
 
75
76
  it "should require file" do
76
- proc { Runner.new(%w(start -r unexisting)) }.should raise_error(LoadError)
77
+ runner = Runner.new(%w(start -r unexisting))
78
+ proc { runner.run! }.should raise_error(LoadError)
79
+ end
80
+
81
+ it "should remember requires" do
82
+ runner = Runner.new(%w(start -r rubygems -r thin))
83
+ runner.options[:require].should == %w(rubygems thin)
84
+ end
85
+
86
+ it "should remember debug options" do
87
+ runner = Runner.new(%w(start -D -V))
88
+ runner.options[:debug].should be_true
89
+ runner.options[:trace].should be_true
90
+ end
91
+
92
+ it "should default debug and trace to false" do
93
+ runner = Runner.new(%w(start))
94
+ runner.options[:debug].should_not be_true
95
+ runner.options[:trace].should_not be_true
77
96
  end
78
97
  end
79
98
 
@@ -6,7 +6,7 @@ describe Server, "HTTP pipelining" do
6
6
  start_server do |env|
7
7
  calls += 1
8
8
  body = env['PATH_INFO'] + '-' + calls.to_s
9
- [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
9
+ [200, { 'Content-Type' => 'text/html' }, body]
10
10
  end
11
11
  @server.maximum_persistent_connections = 1024
12
12
  end
@@ -4,7 +4,7 @@ describe Server, 'robustness' do
4
4
  before do
5
5
  start_server do |env|
6
6
  body = 'hello!'
7
- [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
7
+ [200, { 'Content-Type' => 'text/html' }, body]
8
8
  end
9
9
  end
10
10