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.
- data/CHANGELOG +15 -0
- data/README +1 -1
- data/example/adapter.rb +2 -5
- data/example/config.ru +2 -5
- data/example/vlad.rake +6 -3
- data/lib/rack/adapter/loader.rb +27 -23
- data/lib/rack/adapter/rails.rb +9 -3
- data/lib/thin/backends/base.rb +13 -3
- data/lib/thin/command.rb +7 -5
- data/lib/thin/connection.rb +68 -28
- data/lib/thin/controllers/controller.rb +2 -1
- data/lib/thin/daemonizing.rb +25 -20
- data/lib/thin/headers.rb +8 -0
- data/lib/thin/logging.rb +28 -23
- data/lib/thin/request.rb +32 -32
- data/lib/thin/response.rb +22 -19
- data/lib/thin/runner.rb +14 -7
- data/lib/thin/server.rb +6 -2
- data/lib/thin/stats.rb +1 -4
- data/lib/thin/version.rb +4 -4
- data/lib/thin.rb +1 -0
- data/spec/backends/tcp_server_spec.rb +11 -0
- data/spec/command_spec.rb +7 -1
- data/spec/daemonizing_spec.rb +17 -0
- data/spec/headers_spec.rb +11 -0
- data/spec/logging_spec.rb +6 -2
- data/spec/rack/rails_adapter_spec.rb +10 -8
- data/spec/request/parser_spec.rb +1 -1
- data/spec/response_spec.rb +7 -0
- data/spec/runner_spec.rb +22 -3
- data/spec/server/pipelining_spec.rb +1 -1
- data/spec/server/robustness_spec.rb +1 -1
- data/spec/server/stopping_spec.rb +1 -1
- data/spec/server/swiftiply_spec.rb +1 -1
- data/spec/server/tcp_spec.rb +18 -7
- data/spec/server/threaded_spec.rb +1 -1
- data/spec/server/unix_socket_spec.rb +1 -1
- data/spec/spec_helper.rb +10 -0
- data/tasks/announce.rake +1 -1
- data/tasks/deploy.rake +2 -2
- data/tasks/email.erb +0 -4
- data/tasks/gem.rake +2 -2
- metadata +6 -4
- 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 =
|
26
|
-
|
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]
|
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.
|
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|
|
118
|
-
opts.on_tail("-D", "--debug", "Set debbuging on") {
|
119
|
-
opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") {
|
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 "
|
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
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 =
|
8
|
-
MINOR =
|
9
|
-
TINY =
|
7
|
+
MAJOR = 1
|
8
|
+
MINOR = 0
|
9
|
+
TINY = 0
|
10
10
|
|
11
11
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
12
12
|
|
13
|
-
CODENAME = '
|
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
@@ -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
|
data/spec/daemonizing_spec.rb
CHANGED
@@ -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
|
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
|
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
|
data/spec/request/parser_spec.rb
CHANGED
data/spec/response_spec.rb
CHANGED
@@ -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
|
-
|
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'
|
9
|
+
[200, { 'Content-Type' => 'text/html' }, body]
|
10
10
|
end
|
11
11
|
@server.maximum_persistent_connections = 1024
|
12
12
|
end
|