rack 1.4.0 → 1.4.5
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/COPYING +1 -1
- data/KNOWN-ISSUES +9 -0
- data/README.rdoc +118 -4
- data/Rakefile +18 -11
- data/SPEC +3 -1
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +150 -0
- data/contrib/rdoc.css +412 -0
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/backports/uri/common_18.rb +14 -28
- data/lib/rack/backports/uri/common_192.rb +14 -17
- data/lib/rack/backports/uri/common_193.rb +29 -0
- data/lib/rack/body_proxy.rb +15 -2
- data/lib/rack/builder.rb +1 -1
- data/lib/rack/cascade.rb +12 -1
- data/lib/rack/commonlogger.rb +18 -5
- data/lib/rack/deflater.rb +5 -1
- data/lib/rack/directory.rb +1 -1
- data/lib/rack/etag.rb +6 -3
- data/lib/rack/file.rb +20 -16
- data/lib/rack/head.rb +1 -0
- data/lib/rack/lint.rb +3 -1
- data/lib/rack/lock.rb +3 -4
- data/lib/rack/mime.rb +1 -1
- data/lib/rack/mock.rb +3 -2
- data/lib/rack/multipart/parser.rb +22 -20
- data/lib/rack/multipart.rb +2 -2
- data/lib/rack/reloader.rb +1 -1
- data/lib/rack/request.rb +4 -6
- data/lib/rack/response.rb +19 -17
- data/lib/rack/server.rb +28 -2
- data/lib/rack/session/abstract/id.rb +8 -3
- data/lib/rack/session/cookie.rb +20 -7
- data/lib/rack/showstatus.rb +2 -2
- data/lib/rack/static.rb +91 -9
- data/lib/rack/utils.rb +74 -36
- data/lib/rack.rb +13 -1
- data/rack.gemspec +3 -3
- data/test/builder/line.ru +1 -0
- data/test/cgi/assets/folder/test.js +1 -0
- data/test/cgi/assets/fonts/font.eot +1 -0
- data/test/cgi/assets/images/image.png +1 -0
- data/test/cgi/assets/index.html +1 -0
- data/test/cgi/assets/javascripts/app.js +1 -0
- data/test/cgi/assets/stylesheets/app.css +1 -0
- data/test/multipart/filename_with_unescaped_percentages +6 -0
- data/test/multipart/filename_with_unescaped_percentages2 +6 -0
- data/test/multipart/filename_with_unescaped_percentages3 +6 -0
- data/test/spec_auth.rb +57 -0
- data/test/spec_auth_basic.rb +8 -0
- data/test/spec_auth_digest.rb +14 -0
- data/test/spec_body_proxy.rb +21 -0
- data/test/spec_builder.rb +7 -1
- data/test/spec_cascade.rb +14 -3
- data/test/spec_chunked.rb +6 -6
- data/test/spec_config.rb +0 -1
- data/test/spec_content_length.rb +26 -13
- data/test/spec_content_type.rb +15 -5
- data/test/spec_deflater.rb +35 -17
- data/test/spec_directory.rb +20 -1
- data/test/spec_etag.rb +29 -13
- data/test/spec_file.rb +58 -30
- data/test/spec_head.rb +25 -7
- data/test/spec_lobster.rb +20 -5
- data/test/spec_lock.rb +46 -21
- data/test/spec_logger.rb +2 -7
- data/test/spec_methodoverride.rb +21 -22
- data/test/spec_mock.rb +12 -7
- data/test/spec_multipart.rb +129 -2
- data/test/spec_nulllogger.rb +13 -2
- data/test/spec_recursive.rb +12 -9
- data/test/spec_request.rb +15 -2
- data/test/spec_response.rb +41 -3
- data/test/spec_runtime.rb +15 -5
- data/test/spec_sendfile.rb +13 -9
- data/test/spec_server.rb +47 -0
- data/test/spec_session_cookie.rb +93 -3
- data/test/spec_session_memcache.rb +10 -8
- data/test/spec_session_pool.rb +13 -10
- data/test/spec_showexceptions.rb +9 -4
- data/test/spec_showstatus.rb +10 -5
- data/test/spec_static.rb +89 -8
- data/test/spec_urlmap.rb +10 -10
- data/test/spec_utils.rb +34 -7
- data/test/static/another/index.html +1 -0
- metadata +26 -8
data/lib/rack/deflater.rb
CHANGED
|
@@ -45,6 +45,7 @@ module Rack
|
|
|
45
45
|
when "identity"
|
|
46
46
|
[status, headers, body]
|
|
47
47
|
when nil
|
|
48
|
+
body.close if body.respond_to?(:close)
|
|
48
49
|
message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
|
|
49
50
|
[406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]]
|
|
50
51
|
end
|
|
@@ -64,6 +65,7 @@ module Rack
|
|
|
64
65
|
gzip.write(part)
|
|
65
66
|
gzip.flush
|
|
66
67
|
}
|
|
68
|
+
ensure
|
|
67
69
|
@body.close if @body.respond_to?(:close)
|
|
68
70
|
gzip.close
|
|
69
71
|
@writer = nil
|
|
@@ -90,9 +92,11 @@ module Rack
|
|
|
90
92
|
def each
|
|
91
93
|
deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
|
|
92
94
|
@body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) }
|
|
93
|
-
@body.close if @body.respond_to?(:close)
|
|
94
95
|
yield deflater.finish
|
|
95
96
|
nil
|
|
97
|
+
ensure
|
|
98
|
+
@body.close if @body.respond_to?(:close)
|
|
99
|
+
deflater.close
|
|
96
100
|
end
|
|
97
101
|
end
|
|
98
102
|
end
|
data/lib/rack/directory.rb
CHANGED
|
@@ -80,7 +80,7 @@ table { width:100%%; }
|
|
|
80
80
|
@files = [['../','Parent Directory','','','']]
|
|
81
81
|
glob = F.join(@path, '*')
|
|
82
82
|
|
|
83
|
-
url_head = (
|
|
83
|
+
url_head = (@script_name.split('/') + @path_info.split('/')).map do |part|
|
|
84
84
|
Rack::Utils.escape part
|
|
85
85
|
end
|
|
86
86
|
|
data/lib/rack/etag.rb
CHANGED
|
@@ -28,8 +28,11 @@ module Rack
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
unless headers['Cache-Control']
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
if digest
|
|
32
|
+
headers['Cache-Control'] = @cache_control if @cache_control
|
|
33
|
+
else
|
|
34
|
+
headers['Cache-Control'] = @no_cache_control if @no_cache_control
|
|
35
|
+
end
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
[status, headers, body]
|
|
@@ -46,7 +49,7 @@ module Rack
|
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def skip_caching?(headers)
|
|
49
|
-
headers['Cache-Control']
|
|
52
|
+
(headers['Cache-Control'] && headers['Cache-Control'].include?('no-cache')) ||
|
|
50
53
|
headers.key?('ETag') || headers.key?('Last-Modified')
|
|
51
54
|
end
|
|
52
55
|
|
data/lib/rack/file.rb
CHANGED
|
@@ -21,9 +21,16 @@ module Rack
|
|
|
21
21
|
|
|
22
22
|
alias :to_path :path
|
|
23
23
|
|
|
24
|
-
def initialize(root,
|
|
24
|
+
def initialize(root, headers={})
|
|
25
25
|
@root = root
|
|
26
|
-
|
|
26
|
+
# Allow a cache_control string for backwards compatibility
|
|
27
|
+
if headers.instance_of? String
|
|
28
|
+
warn \
|
|
29
|
+
"Rack::File headers parameter replaces cache_control after Rack 1.5."
|
|
30
|
+
@headers = { 'Cache-Control' => headers }
|
|
31
|
+
else
|
|
32
|
+
@headers = headers
|
|
33
|
+
end
|
|
27
34
|
end
|
|
28
35
|
|
|
29
36
|
def call(env)
|
|
@@ -34,25 +41,20 @@ module Rack
|
|
|
34
41
|
|
|
35
42
|
def _call(env)
|
|
36
43
|
unless ALLOWED_VERBS.include? env["REQUEST_METHOD"]
|
|
37
|
-
return fail(
|
|
44
|
+
return fail(405, "Method Not Allowed")
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
@path_info = Utils.unescape(env["PATH_INFO"])
|
|
41
48
|
parts = @path_info.split SEPS
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return fail(403, "Forbidden") if depth - 1 < 0
|
|
49
|
-
depth - 1
|
|
50
|
-
else
|
|
51
|
-
depth + 1
|
|
52
|
-
end
|
|
50
|
+
clean = []
|
|
51
|
+
|
|
52
|
+
parts.each do |part|
|
|
53
|
+
next if part.empty? || part == '.'
|
|
54
|
+
part == '..' ? clean.pop : clean << part
|
|
53
55
|
end
|
|
54
56
|
|
|
55
|
-
@path = F.join(@root, *
|
|
57
|
+
@path = F.join(@root, *clean)
|
|
56
58
|
|
|
57
59
|
available = begin
|
|
58
60
|
F.file?(@path) && F.readable?(@path)
|
|
@@ -78,7 +80,9 @@ module Rack
|
|
|
78
80
|
},
|
|
79
81
|
env["REQUEST_METHOD"] == "HEAD" ? [] : self
|
|
80
82
|
]
|
|
81
|
-
|
|
83
|
+
|
|
84
|
+
# Set custom headers
|
|
85
|
+
@headers.each { |field, content| response[1][field] = content } if @headers
|
|
82
86
|
|
|
83
87
|
# NOTE:
|
|
84
88
|
# We check via File::size? whether this file provides size info
|
|
@@ -101,7 +105,7 @@ module Rack
|
|
|
101
105
|
# Partial content:
|
|
102
106
|
@range = ranges[0]
|
|
103
107
|
response[0] = 206
|
|
104
|
-
response[1]["Content-Range"]
|
|
108
|
+
response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
|
|
105
109
|
size = @range.end - @range.begin + 1
|
|
106
110
|
end
|
|
107
111
|
|
data/lib/rack/head.rb
CHANGED
data/lib/rack/lint.rb
CHANGED
|
@@ -528,7 +528,9 @@ module Rack
|
|
|
528
528
|
## The Body itself should not be an instance of String, as this will
|
|
529
529
|
## break in Ruby 1.9.
|
|
530
530
|
##
|
|
531
|
-
## If the Body responds to +close+, it will be called after iteration.
|
|
531
|
+
## If the Body responds to +close+, it will be called after iteration. If
|
|
532
|
+
## the body is replaced by a middleware after action, the original body
|
|
533
|
+
## must be closed first, if it repsonds to close.
|
|
532
534
|
# XXX howto: assert("Body has not been closed") { @closed }
|
|
533
535
|
|
|
534
536
|
|
data/lib/rack/lock.rb
CHANGED
|
@@ -13,12 +13,11 @@ module Rack
|
|
|
13
13
|
old, env[FLAG] = env[FLAG], false
|
|
14
14
|
@mutex.lock
|
|
15
15
|
response = @app.call(env)
|
|
16
|
-
|
|
16
|
+
body = BodyProxy.new(response[2]) { @mutex.unlock }
|
|
17
|
+
response[2] = body
|
|
17
18
|
response
|
|
18
|
-
rescue Exception
|
|
19
|
-
@mutex.unlock
|
|
20
|
-
raise
|
|
21
19
|
ensure
|
|
20
|
+
@mutex.unlock unless body
|
|
22
21
|
env[FLAG] = old
|
|
23
22
|
end
|
|
24
23
|
end
|
data/lib/rack/mime.rb
CHANGED
|
@@ -598,7 +598,7 @@ module Rack
|
|
|
598
598
|
".wmv" => "video/x-ms-wmv",
|
|
599
599
|
".wmx" => "video/x-ms-wmx",
|
|
600
600
|
".wmz" => "application/x-ms-wmz",
|
|
601
|
-
".woff" => "application/
|
|
601
|
+
".woff" => "application/font-woff",
|
|
602
602
|
".wpd" => "application/vnd.wordperfect",
|
|
603
603
|
".wpl" => "application/vnd.ms-wpl",
|
|
604
604
|
".wps" => "application/vnd.ms-works",
|
data/lib/rack/mock.rb
CHANGED
|
@@ -9,12 +9,12 @@ module Rack
|
|
|
9
9
|
# Rack::MockRequest helps testing your Rack application without
|
|
10
10
|
# actually using HTTP.
|
|
11
11
|
#
|
|
12
|
-
# After performing a request on a URL with get/post/put/delete, it
|
|
12
|
+
# After performing a request on a URL with get/post/put/patch/delete, it
|
|
13
13
|
# returns a MockResponse with useful helper methods for effective
|
|
14
14
|
# testing.
|
|
15
15
|
#
|
|
16
16
|
# You can pass a hash with additional configuration to the
|
|
17
|
-
# get/post/put/delete.
|
|
17
|
+
# get/post/put/patch/delete.
|
|
18
18
|
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
|
|
19
19
|
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
|
|
20
20
|
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
|
|
@@ -56,6 +56,7 @@ module Rack
|
|
|
56
56
|
def get(uri, opts={}) request("GET", uri, opts) end
|
|
57
57
|
def post(uri, opts={}) request("POST", uri, opts) end
|
|
58
58
|
def put(uri, opts={}) request("PUT", uri, opts) end
|
|
59
|
+
def patch(uri, opts={}) request("PATCH", uri, opts) end
|
|
59
60
|
def delete(uri, opts={}) request("DELETE", uri, opts) end
|
|
60
61
|
def head(uri, opts={}) request("HEAD", uri, opts) end
|
|
61
62
|
|
|
@@ -14,9 +14,6 @@ module Rack
|
|
|
14
14
|
|
|
15
15
|
fast_forward_to_first_boundary
|
|
16
16
|
|
|
17
|
-
max_key_space = Utils.key_space_limit
|
|
18
|
-
bytes = 0
|
|
19
|
-
|
|
20
17
|
loop do
|
|
21
18
|
head, filename, content_type, name, body =
|
|
22
19
|
get_current_head_and_filename_and_content_type_and_name_and_body
|
|
@@ -31,13 +28,6 @@ module Rack
|
|
|
31
28
|
|
|
32
29
|
filename, data = get_data(filename, body, content_type, name, head)
|
|
33
30
|
|
|
34
|
-
if name
|
|
35
|
-
bytes += name.size
|
|
36
|
-
if bytes > max_key_space
|
|
37
|
-
raise RangeError, "exceeded available parameter key space"
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
31
|
Utils.normalize_params(@params, name, data) unless data.nil?
|
|
42
32
|
|
|
43
33
|
# break if we're at the end of a buffer, but not if it is the end of a field
|
|
@@ -46,7 +36,7 @@ module Rack
|
|
|
46
36
|
|
|
47
37
|
@io.rewind
|
|
48
38
|
|
|
49
|
-
@params
|
|
39
|
+
@params.to_params_hash
|
|
50
40
|
end
|
|
51
41
|
|
|
52
42
|
private
|
|
@@ -56,15 +46,17 @@ module Rack
|
|
|
56
46
|
@boundary = "--#{$1}"
|
|
57
47
|
|
|
58
48
|
@buf = ""
|
|
59
|
-
@params =
|
|
49
|
+
@params = Utils::KeySpaceConstrainedParams.new
|
|
60
50
|
|
|
61
|
-
@content_length = @env['CONTENT_LENGTH'].to_i
|
|
62
51
|
@io = @env['rack.input']
|
|
63
52
|
@io.rewind
|
|
64
53
|
|
|
65
54
|
@boundary_size = Utils.bytesize(@boundary) + EOL.size
|
|
66
55
|
|
|
67
|
-
@content_length
|
|
56
|
+
if @content_length = @env['CONTENT_LENGTH']
|
|
57
|
+
@content_length = @content_length.to_i
|
|
58
|
+
@content_length -= @boundary_size
|
|
59
|
+
end
|
|
68
60
|
true
|
|
69
61
|
end
|
|
70
62
|
|
|
@@ -78,9 +70,16 @@ module Rack
|
|
|
78
70
|
|
|
79
71
|
def fast_forward_to_first_boundary
|
|
80
72
|
loop do
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
content = @io.read(BUFSIZE)
|
|
74
|
+
raise EOFError, "bad content body" unless content
|
|
75
|
+
@buf << content
|
|
76
|
+
|
|
77
|
+
while @buf.gsub!(/\A([^\n]*\n)/, '')
|
|
78
|
+
read_buffer = $1
|
|
79
|
+
return if read_buffer == full_boundary
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
raise EOFError, "bad content body" if Utils.bytesize(@buf) >= BUFSIZE
|
|
84
83
|
end
|
|
85
84
|
end
|
|
86
85
|
|
|
@@ -114,11 +113,11 @@ module Rack
|
|
|
114
113
|
body << @buf.slice!(0, @buf.size - (@boundary_size+4))
|
|
115
114
|
end
|
|
116
115
|
|
|
117
|
-
content = @io.read(BUFSIZE
|
|
116
|
+
content = @io.read(@content_length && BUFSIZE >= @content_length ? @content_length : BUFSIZE)
|
|
118
117
|
raise EOFError, "bad content body" if content.nil? || content.empty?
|
|
119
118
|
|
|
120
119
|
@buf << content
|
|
121
|
-
@content_length -= content.size
|
|
120
|
+
@content_length -= content.size if @content_length
|
|
122
121
|
end
|
|
123
122
|
|
|
124
123
|
[head, filename, content_type, name, body]
|
|
@@ -135,8 +134,11 @@ module Rack
|
|
|
135
134
|
filename = $1
|
|
136
135
|
end
|
|
137
136
|
|
|
137
|
+
if filename && filename.scan(/%.?.?/).all? { |s| s =~ /%[0-9a-fA-F]{2}/ }
|
|
138
|
+
filename = Utils.unescape(filename)
|
|
139
|
+
end
|
|
138
140
|
if filename && filename !~ /\\[^\\"]/
|
|
139
|
-
filename =
|
|
141
|
+
filename = filename.gsub(/\\(.)/, '\1')
|
|
140
142
|
end
|
|
141
143
|
filename
|
|
142
144
|
end
|
data/lib/rack/multipart.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Rack
|
|
|
12
12
|
MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
|
|
13
13
|
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
|
|
14
14
|
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
|
|
15
|
-
DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})
|
|
15
|
+
DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})/
|
|
16
16
|
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
|
|
17
17
|
BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
|
|
18
18
|
BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
|
|
@@ -31,4 +31,4 @@ module Rack
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
end
|
|
34
|
-
end
|
|
34
|
+
end
|
data/lib/rack/reloader.rb
CHANGED
data/lib/rack/request.rb
CHANGED
|
@@ -177,7 +177,7 @@ module Rack
|
|
|
177
177
|
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
|
|
178
178
|
end
|
|
179
179
|
|
|
180
|
-
# Returns the data
|
|
180
|
+
# Returns the data received in the query string.
|
|
181
181
|
def GET
|
|
182
182
|
if @env["rack.request.query_string"] == query_string
|
|
183
183
|
@env["rack.request.query_hash"]
|
|
@@ -187,7 +187,7 @@ module Rack
|
|
|
187
187
|
end
|
|
188
188
|
end
|
|
189
189
|
|
|
190
|
-
# Returns the data
|
|
190
|
+
# Returns the data received in the request body.
|
|
191
191
|
#
|
|
192
192
|
# This method support both application/x-www-form-urlencoded and
|
|
193
193
|
# multipart/form-data.
|
|
@@ -260,12 +260,10 @@ module Rack
|
|
|
260
260
|
# the Cookie header such that those with more specific Path attributes
|
|
261
261
|
# precede those with less specific. Ordering with respect to other
|
|
262
262
|
# attributes (e.g., Domain) is unspecified.
|
|
263
|
-
Utils.parse_query(string, ';,')
|
|
263
|
+
cookies = Utils.parse_query(string, ';,') { |s| Rack::Utils.unescape(s) rescue s }
|
|
264
|
+
cookies.each { |k,v| hash[k] = Array === v ? v.first : v }
|
|
264
265
|
@env["rack.request.cookie_string"] = string
|
|
265
266
|
hash
|
|
266
|
-
rescue => error
|
|
267
|
-
error.message.replace "cannot parse Cookie header: #{error.message}"
|
|
268
|
-
raise
|
|
269
267
|
end
|
|
270
268
|
|
|
271
269
|
def xhr?
|
data/lib/rack/response.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Rack
|
|
|
12
12
|
# You can use Response#write to iteratively generate your response,
|
|
13
13
|
# but note that this is buffered by Rack::Response until you call
|
|
14
14
|
# +finish+. +finish+ however can take a block inside which calls to
|
|
15
|
-
# +write+ are
|
|
15
|
+
# +write+ are synchronous with the Rack response.
|
|
16
16
|
#
|
|
17
17
|
# Your application's +call+ should end returning Response#finish.
|
|
18
18
|
|
|
@@ -74,9 +74,10 @@ module Rack
|
|
|
74
74
|
if [204, 205, 304].include?(status.to_i)
|
|
75
75
|
header.delete "Content-Type"
|
|
76
76
|
header.delete "Content-Length"
|
|
77
|
+
close
|
|
77
78
|
[status.to_i, header, []]
|
|
78
79
|
else
|
|
79
|
-
[status.to_i, header, self]
|
|
80
|
+
[status.to_i, header, BodyProxy.new(self){}]
|
|
80
81
|
end
|
|
81
82
|
end
|
|
82
83
|
alias to_a finish # For *response
|
|
@@ -112,21 +113,22 @@ module Rack
|
|
|
112
113
|
alias headers header
|
|
113
114
|
|
|
114
115
|
module Helpers
|
|
115
|
-
def invalid?;
|
|
116
|
-
|
|
117
|
-
def informational?;
|
|
118
|
-
def successful?;
|
|
119
|
-
def redirection?;
|
|
120
|
-
def client_error?;
|
|
121
|
-
def server_error?;
|
|
122
|
-
|
|
123
|
-
def ok?;
|
|
124
|
-
def bad_request?;
|
|
125
|
-
def forbidden?;
|
|
126
|
-
def not_found?;
|
|
127
|
-
def
|
|
128
|
-
|
|
129
|
-
|
|
116
|
+
def invalid?; status < 100 || status >= 600; end
|
|
117
|
+
|
|
118
|
+
def informational?; status >= 100 && status < 200; end
|
|
119
|
+
def successful?; status >= 200 && status < 300; end
|
|
120
|
+
def redirection?; status >= 300 && status < 400; end
|
|
121
|
+
def client_error?; status >= 400 && status < 500; end
|
|
122
|
+
def server_error?; status >= 500 && status < 600; end
|
|
123
|
+
|
|
124
|
+
def ok?; status == 200; end
|
|
125
|
+
def bad_request?; status == 400; end
|
|
126
|
+
def forbidden?; status == 403; end
|
|
127
|
+
def not_found?; status == 404; end
|
|
128
|
+
def method_not_allowed?; status == 405; end
|
|
129
|
+
def unprocessable?; status == 422; end
|
|
130
|
+
|
|
131
|
+
def redirect?; [301, 302, 303, 307].include? status; end
|
|
130
132
|
|
|
131
133
|
# Headers
|
|
132
134
|
attr_reader :headers, :original_headers
|
data/lib/rack/server.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Rack
|
|
|
26
26
|
|
|
27
27
|
opts.on("-I", "--include PATH",
|
|
28
28
|
"specify $LOAD_PATH (may be used more than once)") { |path|
|
|
29
|
-
options[:include]
|
|
29
|
+
(options[:include] ||= []).concat(path.split(":"))
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
opts.on("-r", "--require LIBRARY",
|
|
@@ -247,11 +247,14 @@ module Rack
|
|
|
247
247
|
pp app
|
|
248
248
|
end
|
|
249
249
|
|
|
250
|
+
check_pid! if options[:pid]
|
|
251
|
+
|
|
250
252
|
# Touch the wrapped app, so that the config.ru is loaded before
|
|
251
253
|
# daemonization (i.e. before chdir, etc).
|
|
252
254
|
wrapped_app
|
|
253
255
|
|
|
254
256
|
daemonize_app if options[:daemonize]
|
|
257
|
+
|
|
255
258
|
write_pid if options[:pid]
|
|
256
259
|
|
|
257
260
|
trap(:INT) do
|
|
@@ -274,7 +277,7 @@ module Rack
|
|
|
274
277
|
options = default_options
|
|
275
278
|
|
|
276
279
|
# Don't evaluate CGI ISINDEX parameters.
|
|
277
|
-
# http://
|
|
280
|
+
# http://www.meb.uni-bonn.de/docs/cgi/cl.html
|
|
278
281
|
args.clear if ENV.include?("REQUEST_METHOD")
|
|
279
282
|
|
|
280
283
|
options.merge! opt_parser.parse!(args)
|
|
@@ -319,5 +322,28 @@ module Rack
|
|
|
319
322
|
::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") }
|
|
320
323
|
at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) }
|
|
321
324
|
end
|
|
325
|
+
|
|
326
|
+
def check_pid!
|
|
327
|
+
case pidfile_process_status
|
|
328
|
+
when :running, :not_owned
|
|
329
|
+
$stderr.puts "A server is already running. Check #{options[:pid]}."
|
|
330
|
+
exit(1)
|
|
331
|
+
when :dead
|
|
332
|
+
::File.delete(options[:pid])
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def pidfile_process_status
|
|
337
|
+
return :exited unless ::File.exist?(options[:pid])
|
|
338
|
+
|
|
339
|
+
pid = ::File.read(options[:pid]).to_i
|
|
340
|
+
Process.kill(0, pid)
|
|
341
|
+
:running
|
|
342
|
+
rescue Errno::ESRCH
|
|
343
|
+
:dead
|
|
344
|
+
rescue Errno::EPERM
|
|
345
|
+
:not_owned
|
|
346
|
+
end
|
|
347
|
+
|
|
322
348
|
end
|
|
323
349
|
end
|
|
@@ -36,7 +36,7 @@ module Rack
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
38
|
def session_id_not_loaded?
|
|
39
|
-
!key?(:id)
|
|
39
|
+
!(@session_id_loaded || key?(:id))
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def load_session_id!
|
|
@@ -116,6 +116,11 @@ module Rack
|
|
|
116
116
|
super
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
+
def merge!(hash)
|
|
120
|
+
load_for_write!
|
|
121
|
+
super
|
|
122
|
+
end
|
|
123
|
+
|
|
119
124
|
private
|
|
120
125
|
|
|
121
126
|
def load_for_read!
|
|
@@ -183,7 +188,7 @@ module Rack
|
|
|
183
188
|
:renew => false,
|
|
184
189
|
:sidbits => 128,
|
|
185
190
|
:cookie_only => true,
|
|
186
|
-
:secure_random =>
|
|
191
|
+
:secure_random => (::SecureRandom rescue false)
|
|
187
192
|
}
|
|
188
193
|
|
|
189
194
|
attr_reader :key, :default_options
|
|
@@ -191,7 +196,7 @@ module Rack
|
|
|
191
196
|
def initialize(app, options={})
|
|
192
197
|
@app = app
|
|
193
198
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
|
194
|
-
@key =
|
|
199
|
+
@key = @default_options.delete(:key)
|
|
195
200
|
@cookie_only = @default_options.delete(:cookie_only)
|
|
196
201
|
initialize_sid
|
|
197
202
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
|
@@ -81,8 +81,16 @@ module Rack
|
|
|
81
81
|
attr_reader :coder
|
|
82
82
|
|
|
83
83
|
def initialize(app, options={})
|
|
84
|
-
@
|
|
85
|
-
@
|
|
84
|
+
@secrets = options.values_at(:secret, :old_secret).compact
|
|
85
|
+
warn <<-MSG unless @secrets.size >= 1
|
|
86
|
+
SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
|
|
87
|
+
This poses a security threat. It is strongly recommended that you
|
|
88
|
+
provide a secret to prevent exploits that may be possible from crafted
|
|
89
|
+
cookies. This will not be supported in future versions of Rack, and
|
|
90
|
+
future versions will even invalidate your existing user cookies.
|
|
91
|
+
|
|
92
|
+
Called from: #{caller[0]}.
|
|
93
|
+
MSG
|
|
86
94
|
@coder = options[:coder] ||= Base64::Marshal.new
|
|
87
95
|
super(app, options.merge!(:cookie_only => true))
|
|
88
96
|
end
|
|
@@ -104,11 +112,16 @@ module Rack
|
|
|
104
112
|
request = Rack::Request.new(env)
|
|
105
113
|
session_data = request.cookies[@key]
|
|
106
114
|
|
|
107
|
-
if
|
|
115
|
+
if @secrets.size > 0 && session_data
|
|
108
116
|
session_data, digest = session_data.split("--")
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
|
|
118
|
+
if session_data && digest
|
|
119
|
+
ok = @secrets.any? do |secret|
|
|
120
|
+
secret && Rack::Utils.secure_compare(digest, generate_hmac(session_data, secret))
|
|
121
|
+
end
|
|
111
122
|
end
|
|
123
|
+
|
|
124
|
+
session_data = nil unless ok
|
|
112
125
|
end
|
|
113
126
|
|
|
114
127
|
coder.decode(session_data) || {}
|
|
@@ -131,8 +144,8 @@ module Rack
|
|
|
131
144
|
session = session.merge("session_id" => session_id)
|
|
132
145
|
session_data = coder.encode(session)
|
|
133
146
|
|
|
134
|
-
if @
|
|
135
|
-
session_data = "#{session_data}--#{generate_hmac(session_data, @
|
|
147
|
+
if @secrets.first
|
|
148
|
+
session_data = "#{session_data}--#{generate_hmac(session_data, @secrets.first)}"
|
|
136
149
|
end
|
|
137
150
|
|
|
138
151
|
if session_data.size > (4096 - @key.size)
|
data/lib/rack/showstatus.rb
CHANGED
|
@@ -3,8 +3,8 @@ require 'rack/request'
|
|
|
3
3
|
require 'rack/utils'
|
|
4
4
|
|
|
5
5
|
module Rack
|
|
6
|
-
# Rack::ShowStatus catches all empty responses
|
|
7
|
-
#
|
|
6
|
+
# Rack::ShowStatus catches all empty responses and replaces them
|
|
7
|
+
# with a site explaining the error.
|
|
8
8
|
#
|
|
9
9
|
# Additional details can be put into <tt>rack.showstatus.detail</tt>
|
|
10
10
|
# and will be shown as HTML. If such details exist, the error page
|