rack 1.6.12 → 2.0.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +18 -28
- data/Rakefile +6 -14
- data/SPEC +10 -11
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack.rb +70 -21
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +24 -26
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +40 -40
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +15 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +270 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +394 -305
- data/lib/rack/response.rb +130 -57
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +30 -23
- data/lib/rack/session/abstract/id.rb +108 -138
- data/lib/rack/session/cookie.rb +26 -28
- data/lib/rack/session/memcache.rb +8 -14
- data/lib/rack/session/pool.rb +14 -21
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +136 -211
- data/rack.gemspec +10 -9
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/filename_with_single_quote +7 -0
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +37 -35
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +85 -49
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +164 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +79 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +780 -605
- data/test/spec_response.rb +215 -112
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +93 -71
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +92 -70
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_mongrel.rb +0 -182
data/example/protectedlobster.rb
CHANGED
data/example/protectedlobster.ru
CHANGED
data/lib/rack.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# modules and classes.
|
8
8
|
#
|
9
9
|
# All modules meant for use in your application are <tt>autoload</tt>ed here,
|
10
|
-
# so it should be enough just to <tt>require rack
|
10
|
+
# so it should be enough just to <tt>require 'rack'</tt> in your code.
|
11
11
|
|
12
12
|
module Rack
|
13
13
|
# The Rack protocol version number implemented.
|
@@ -18,27 +18,80 @@ module Rack
|
|
18
18
|
VERSION.join(".")
|
19
19
|
end
|
20
20
|
|
21
|
+
RELEASE = "2.0.7"
|
22
|
+
|
21
23
|
# Return the Rack release as a dotted string.
|
22
24
|
def self.release
|
23
|
-
|
25
|
+
RELEASE
|
24
26
|
end
|
25
|
-
PATH_INFO = 'PATH_INFO'.freeze
|
26
|
-
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
27
|
-
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
28
|
-
QUERY_STRING = 'QUERY_STRING'.freeze
|
29
|
-
CACHE_CONTROL = 'Cache-Control'.freeze
|
30
|
-
CONTENT_LENGTH = 'Content-Length'.freeze
|
31
|
-
CONTENT_TYPE = 'Content-Type'.freeze
|
32
27
|
|
33
|
-
|
34
|
-
|
28
|
+
HTTP_HOST = 'HTTP_HOST'.freeze
|
29
|
+
HTTP_VERSION = 'HTTP_VERSION'.freeze
|
30
|
+
HTTPS = 'HTTPS'.freeze
|
31
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
32
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
33
|
+
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
34
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
35
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
36
|
+
SERVER_PROTOCOL = 'SERVER_PROTOCOL'.freeze
|
37
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
38
|
+
SERVER_ADDR = 'SERVER_ADDR'.freeze
|
39
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
40
|
+
CACHE_CONTROL = 'Cache-Control'.freeze
|
41
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
42
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
43
|
+
SET_COOKIE = 'Set-Cookie'.freeze
|
44
|
+
TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
|
45
|
+
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
46
|
+
ETAG = 'ETag'.freeze
|
47
|
+
|
48
|
+
# HTTP method verbs
|
49
|
+
GET = 'GET'.freeze
|
50
|
+
POST = 'POST'.freeze
|
51
|
+
PUT = 'PUT'.freeze
|
52
|
+
PATCH = 'PATCH'.freeze
|
53
|
+
DELETE = 'DELETE'.freeze
|
54
|
+
HEAD = 'HEAD'.freeze
|
55
|
+
OPTIONS = 'OPTIONS'.freeze
|
56
|
+
LINK = 'LINK'.freeze
|
57
|
+
UNLINK = 'UNLINK'.freeze
|
58
|
+
TRACE = 'TRACE'.freeze
|
59
|
+
|
60
|
+
# Rack environment variables
|
61
|
+
RACK_VERSION = 'rack.version'.freeze
|
62
|
+
RACK_TEMPFILES = 'rack.tempfiles'.freeze
|
63
|
+
RACK_ERRORS = 'rack.errors'.freeze
|
64
|
+
RACK_LOGGER = 'rack.logger'.freeze
|
65
|
+
RACK_INPUT = 'rack.input'.freeze
|
66
|
+
RACK_SESSION = 'rack.session'.freeze
|
67
|
+
RACK_SESSION_OPTIONS = 'rack.session.options'.freeze
|
68
|
+
RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'.freeze
|
69
|
+
RACK_MULTITHREAD = 'rack.multithread'.freeze
|
70
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
|
71
|
+
RACK_RUNONCE = 'rack.run_once'.freeze
|
72
|
+
RACK_URL_SCHEME = 'rack.url_scheme'.freeze
|
73
|
+
RACK_HIJACK = 'rack.hijack'.freeze
|
74
|
+
RACK_IS_HIJACK = 'rack.hijack?'.freeze
|
75
|
+
RACK_HIJACK_IO = 'rack.hijack_io'.freeze
|
76
|
+
RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'.freeze
|
77
|
+
RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'.freeze
|
78
|
+
RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'.freeze
|
79
|
+
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'.freeze
|
80
|
+
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'.freeze
|
81
|
+
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'.freeze
|
82
|
+
RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'.freeze
|
83
|
+
RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'.freeze
|
84
|
+
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'.freeze
|
85
|
+
RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'.freeze
|
86
|
+
RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'.freeze
|
87
|
+
RACK_SESSION_UNPACKED_COOKIE_DATA = 'rack.session.unpacked_cookie_data'.freeze
|
35
88
|
|
36
89
|
autoload :Builder, "rack/builder"
|
37
90
|
autoload :BodyProxy, "rack/body_proxy"
|
38
91
|
autoload :Cascade, "rack/cascade"
|
39
92
|
autoload :Chunked, "rack/chunked"
|
40
|
-
autoload :CommonLogger, "rack/
|
41
|
-
autoload :ConditionalGet, "rack/
|
93
|
+
autoload :CommonLogger, "rack/common_logger"
|
94
|
+
autoload :ConditionalGet, "rack/conditional_get"
|
42
95
|
autoload :Config, "rack/config"
|
43
96
|
autoload :ContentLength, "rack/content_length"
|
44
97
|
autoload :ContentType, "rack/content_type"
|
@@ -52,16 +105,16 @@ module Rack
|
|
52
105
|
autoload :Lint, "rack/lint"
|
53
106
|
autoload :Lock, "rack/lock"
|
54
107
|
autoload :Logger, "rack/logger"
|
55
|
-
autoload :MethodOverride, "rack/
|
108
|
+
autoload :MethodOverride, "rack/method_override"
|
56
109
|
autoload :Mime, "rack/mime"
|
57
|
-
autoload :NullLogger, "rack/
|
110
|
+
autoload :NullLogger, "rack/null_logger"
|
58
111
|
autoload :Recursive, "rack/recursive"
|
59
112
|
autoload :Reloader, "rack/reloader"
|
60
113
|
autoload :Runtime, "rack/runtime"
|
61
114
|
autoload :Sendfile, "rack/sendfile"
|
62
115
|
autoload :Server, "rack/server"
|
63
|
-
autoload :ShowExceptions, "rack/
|
64
|
-
autoload :ShowStatus, "rack/
|
116
|
+
autoload :ShowExceptions, "rack/show_exceptions"
|
117
|
+
autoload :ShowStatus, "rack/show_status"
|
65
118
|
autoload :Static, "rack/static"
|
66
119
|
autoload :TempfileReaper, "rack/tempfile_reaper"
|
67
120
|
autoload :URLMap, "rack/urlmap"
|
@@ -91,8 +144,4 @@ module Rack
|
|
91
144
|
autoload :Pool, "rack/session/pool"
|
92
145
|
autoload :Memcache, "rack/session/memcache"
|
93
146
|
end
|
94
|
-
|
95
|
-
module Utils
|
96
|
-
autoload :OkJson, "rack/utils/okjson"
|
97
|
-
end
|
98
147
|
end
|
@@ -17,7 +17,7 @@ module Rack
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.split_header_value(str)
|
20
|
-
str.scan(
|
20
|
+
str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)
|
21
21
|
end
|
22
22
|
|
23
23
|
def initialize
|
@@ -38,7 +38,7 @@ module Rack
|
|
38
38
|
|
39
39
|
def to_s
|
40
40
|
map do |k, v|
|
41
|
-
"#{k}="
|
41
|
+
"#{k}=" << (UNQUOTED.include?(k) ? v.to_s : quote(v))
|
42
42
|
end.join(', ')
|
43
43
|
end
|
44
44
|
|
@@ -50,4 +50,3 @@ module Rack
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
53
|
-
|
data/lib/rack/body_proxy.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
module Rack
|
2
2
|
class BodyProxy
|
3
3
|
def initialize(body, &block)
|
4
|
-
@body
|
4
|
+
@body = body
|
5
|
+
@block = block
|
6
|
+
@closed = false
|
5
7
|
end
|
6
8
|
|
7
|
-
def respond_to?(
|
8
|
-
|
9
|
-
|
9
|
+
def respond_to?(method_name, include_all=false)
|
10
|
+
case method_name
|
11
|
+
when :to_ary, 'to_ary'
|
12
|
+
return false
|
13
|
+
end
|
14
|
+
super or @body.respond_to?(method_name, include_all)
|
10
15
|
end
|
11
16
|
|
12
17
|
def close
|
@@ -27,13 +32,13 @@ module Rack
|
|
27
32
|
# We are applying this special case for #each only. Future bugs of this
|
28
33
|
# class will be handled by requesting users to patch their ruby
|
29
34
|
# implementation, to save adding too many methods in this class.
|
30
|
-
def each
|
31
|
-
@body.each
|
35
|
+
def each
|
36
|
+
@body.each { |body| yield body }
|
32
37
|
end
|
33
38
|
|
34
|
-
def method_missing(*args, &block)
|
35
|
-
super if
|
36
|
-
@body.__send__(*args, &block)
|
39
|
+
def method_missing(method_name, *args, &block)
|
40
|
+
super if :to_ary == method_name
|
41
|
+
@body.__send__(method_name, *args, &block)
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
data/lib/rack/builder.rb
CHANGED
@@ -40,7 +40,7 @@ module Rack
|
|
40
40
|
app = new_from_string cfgfile, config
|
41
41
|
else
|
42
42
|
require config
|
43
|
-
app = Object.const_get(::File.basename(config, '.rb').capitalize)
|
43
|
+
app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
|
44
44
|
end
|
45
45
|
return app, options
|
46
46
|
end
|
@@ -50,7 +50,7 @@ module Rack
|
|
50
50
|
TOPLEVEL_BINDING, file, 0
|
51
51
|
end
|
52
52
|
|
53
|
-
def initialize(default_app = nil
|
53
|
+
def initialize(default_app = nil, &block)
|
54
54
|
@use, @map, @run, @warmup = [], nil, default_app, nil
|
55
55
|
instance_eval(&block) if block_given?
|
56
56
|
end
|
@@ -96,7 +96,7 @@ module Rack
|
|
96
96
|
# class Heartbeat
|
97
97
|
# def self.call(env)
|
98
98
|
# [200, { "Content-Type" => "text/plain" }, ["OK"]]
|
99
|
-
#
|
99
|
+
# end
|
100
100
|
# end
|
101
101
|
#
|
102
102
|
# run Heartbeat
|
data/lib/rack/chunked.rb
CHANGED
@@ -21,10 +21,10 @@ module Rack
|
|
21
21
|
def each
|
22
22
|
term = TERM
|
23
23
|
@body.each do |chunk|
|
24
|
-
size = bytesize
|
24
|
+
size = chunk.bytesize
|
25
25
|
next if size == 0
|
26
26
|
|
27
|
-
chunk = chunk.
|
27
|
+
chunk = chunk.b
|
28
28
|
yield [size.to_s(16), term, chunk, term].join
|
29
29
|
end
|
30
30
|
yield TAIL
|
@@ -54,14 +54,14 @@ module Rack
|
|
54
54
|
status, headers, body = @app.call(env)
|
55
55
|
headers = HeaderHash.new(headers)
|
56
56
|
|
57
|
-
if ! chunkable_version?(env[
|
57
|
+
if ! chunkable_version?(env[HTTP_VERSION]) ||
|
58
58
|
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
|
59
59
|
headers[CONTENT_LENGTH] ||
|
60
|
-
headers[
|
60
|
+
headers[TRANSFER_ENCODING]
|
61
61
|
[status, headers, body]
|
62
62
|
else
|
63
63
|
headers.delete(CONTENT_LENGTH)
|
64
|
-
headers[
|
64
|
+
headers[TRANSFER_ENCODING] = 'chunked'
|
65
65
|
[status, headers, Body.new(body)]
|
66
66
|
end
|
67
67
|
end
|
@@ -48,13 +48,13 @@ module Rack
|
|
48
48
|
now.strftime("%d/%b/%Y:%H:%M:%S %z"),
|
49
49
|
env[REQUEST_METHOD],
|
50
50
|
env[PATH_INFO],
|
51
|
-
env[QUERY_STRING].empty? ? "" : "
|
52
|
-
env[
|
51
|
+
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
52
|
+
env[HTTP_VERSION],
|
53
53
|
status.to_s[0..3],
|
54
54
|
length,
|
55
55
|
now - began_at ]
|
56
56
|
|
57
|
-
logger = @logger || env[
|
57
|
+
logger = @logger || env[RACK_ERRORS]
|
58
58
|
# Standard library logger doesn't support write but it supports << which actually
|
59
59
|
# calls to write on the log device without formatting
|
60
60
|
if logger.respond_to?(:write)
|
File without changes
|
data/lib/rack/content_length.rb
CHANGED
@@ -17,12 +17,12 @@ module Rack
|
|
17
17
|
|
18
18
|
if !STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
|
19
19
|
!headers[CONTENT_LENGTH] &&
|
20
|
-
!headers[
|
20
|
+
!headers[TRANSFER_ENCODING] &&
|
21
21
|
body.respond_to?(:to_ary)
|
22
22
|
|
23
23
|
obody = body
|
24
24
|
body, length = [], 0
|
25
|
-
obody.each { |part| body << part; length += bytesize
|
25
|
+
obody.each { |part| body << part; length += part.bytesize }
|
26
26
|
|
27
27
|
body = BodyProxy.new(body) do
|
28
28
|
obody.close if obody.respond_to?(:close)
|
data/lib/rack/deflater.rb
CHANGED
@@ -8,7 +8,6 @@ module Rack
|
|
8
8
|
# Currently supported compression algorithms:
|
9
9
|
#
|
10
10
|
# * gzip
|
11
|
-
# * deflate
|
12
11
|
# * identity (no transformation)
|
13
12
|
#
|
14
13
|
# The middleware automatically detects when compression is supported
|
@@ -22,7 +21,7 @@ module Rack
|
|
22
21
|
# [app] rack app instance
|
23
22
|
# [options] hash of deflater options, i.e.
|
24
23
|
# 'if' - a lambda enabling / disabling deflation based on returned boolean value
|
25
|
-
# e.g use Rack::Deflater, :if => lambda { |env, status, headers, body| body.
|
24
|
+
# e.g use Rack::Deflater, :if => lambda { |env, status, headers, body| body.map(&:bytesize).reduce(0, :+) > 512 }
|
26
25
|
# 'include' - a list of content types that should be compressed
|
27
26
|
def initialize(app, options = {})
|
28
27
|
@app = app
|
@@ -41,11 +40,11 @@ module Rack
|
|
41
40
|
|
42
41
|
request = Request.new(env)
|
43
42
|
|
44
|
-
encoding = Utils.select_best_encoding(%w(gzip
|
43
|
+
encoding = Utils.select_best_encoding(%w(gzip identity),
|
45
44
|
request.accept_encoding)
|
46
45
|
|
47
46
|
# Set the Vary HTTP header.
|
48
|
-
vary = headers["Vary"].to_s.split(",").map
|
47
|
+
vary = headers["Vary"].to_s.split(",").map(&:strip)
|
49
48
|
unless vary.include?("*") || vary.include?("Accept-Encoding")
|
50
49
|
headers["Vary"] = vary.push("Accept-Encoding").join(",")
|
51
50
|
end
|
@@ -57,10 +56,6 @@ module Rack
|
|
57
56
|
mtime = headers.key?("Last-Modified") ?
|
58
57
|
Time.httpdate(headers["Last-Modified"]) : Time.now
|
59
58
|
[status, headers, GzipStream.new(body, mtime)]
|
60
|
-
when "deflate"
|
61
|
-
headers['Content-Encoding'] = "deflate"
|
62
|
-
headers.delete(CONTENT_LENGTH)
|
63
|
-
[status, headers, DeflateStream.new(body)]
|
64
59
|
when "identity"
|
65
60
|
[status, headers, body]
|
66
61
|
when nil
|
@@ -101,36 +96,6 @@ module Rack
|
|
101
96
|
end
|
102
97
|
end
|
103
98
|
|
104
|
-
class DeflateStream
|
105
|
-
DEFLATE_ARGS = [
|
106
|
-
Zlib::DEFAULT_COMPRESSION,
|
107
|
-
# drop the zlib header which causes both Safari and IE to choke
|
108
|
-
-Zlib::MAX_WBITS,
|
109
|
-
Zlib::DEF_MEM_LEVEL,
|
110
|
-
Zlib::DEFAULT_STRATEGY
|
111
|
-
]
|
112
|
-
|
113
|
-
def initialize(body)
|
114
|
-
@body = body
|
115
|
-
@closed = false
|
116
|
-
end
|
117
|
-
|
118
|
-
def each
|
119
|
-
deflator = ::Zlib::Deflate.new(*DEFLATE_ARGS)
|
120
|
-
@body.each { |part| yield deflator.deflate(part, Zlib::SYNC_FLUSH) }
|
121
|
-
yield deflator.finish
|
122
|
-
nil
|
123
|
-
ensure
|
124
|
-
deflator.close
|
125
|
-
end
|
126
|
-
|
127
|
-
def close
|
128
|
-
return if @closed
|
129
|
-
@closed = true
|
130
|
-
@body.close if @body.respond_to?(:close)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
99
|
private
|
135
100
|
|
136
101
|
def should_deflate?(env, status, headers, body)
|
@@ -143,7 +108,7 @@ module Rack
|
|
143
108
|
end
|
144
109
|
|
145
110
|
# Skip if @compressible_types are given and does not include request's content type
|
146
|
-
return false if @compressible_types && !(headers.has_key?(
|
111
|
+
return false if @compressible_types && !(headers.has_key?(CONTENT_TYPE) && @compressible_types.include?(headers[CONTENT_TYPE][/[^;]*/]))
|
147
112
|
|
148
113
|
# Skip if @condition lambda is given and evaluates to false
|
149
114
|
return false if @condition && !@condition.call(env, status, headers, body)
|
data/lib/rack/directory.rb
CHANGED
@@ -39,58 +39,83 @@ table { width:100%%; }
|
|
39
39
|
</body></html>
|
40
40
|
PAGE
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
class DirectoryBody < Struct.new(:root, :path, :files)
|
43
|
+
def each
|
44
|
+
show_path = Rack::Utils.escape_html(path.sub(/^#{root}/,''))
|
45
|
+
listings = files.map{|f| DIR_FILE % DIR_FILE_escape(*f) }*"\n"
|
46
|
+
page = DIR_PAGE % [ show_path, show_path , listings ]
|
47
|
+
page.each_line{|l| yield l }
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
# Assumes url is already escaped.
|
52
|
+
def DIR_FILE_escape url, *html
|
53
|
+
[url, *html.map { |e| Utils.escape_html(e) }]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :root, :path
|
44
58
|
|
45
59
|
def initialize(root, app=nil)
|
46
|
-
@root =
|
60
|
+
@root = ::File.expand_path(root)
|
47
61
|
@app = app || Rack::File.new(@root)
|
62
|
+
@head = Rack::Head.new(lambda { |env| get env })
|
48
63
|
end
|
49
64
|
|
50
65
|
def call(env)
|
51
|
-
|
66
|
+
# strip body if this is a HEAD call
|
67
|
+
@head.call env
|
52
68
|
end
|
53
69
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@env = env
|
58
|
-
@script_name = env[SCRIPT_NAME]
|
59
|
-
@path_info = Utils.unescape(env[PATH_INFO])
|
70
|
+
def get(env)
|
71
|
+
script_name = env[SCRIPT_NAME]
|
72
|
+
path_info = Utils.unescape_path(env[PATH_INFO])
|
60
73
|
|
61
|
-
if
|
74
|
+
if bad_request = check_bad_request(path_info)
|
75
|
+
bad_request
|
76
|
+
elsif forbidden = check_forbidden(path_info)
|
62
77
|
forbidden
|
63
78
|
else
|
64
|
-
|
65
|
-
list_path
|
79
|
+
path = ::File.join(@root, path_info)
|
80
|
+
list_path(env, path, path_info, script_name)
|
66
81
|
end
|
67
82
|
end
|
68
83
|
|
69
|
-
def
|
70
|
-
return
|
84
|
+
def check_bad_request(path_info)
|
85
|
+
return if Utils.valid_path?(path_info)
|
86
|
+
|
87
|
+
body = "Bad Request\n"
|
88
|
+
size = body.bytesize
|
89
|
+
return [400, {CONTENT_TYPE => "text/plain",
|
90
|
+
CONTENT_LENGTH => size.to_s,
|
91
|
+
"X-Cascade" => "pass"}, [body]]
|
92
|
+
end
|
93
|
+
|
94
|
+
def check_forbidden(path_info)
|
95
|
+
return unless path_info.include? ".."
|
71
96
|
|
72
97
|
body = "Forbidden\n"
|
73
|
-
size =
|
74
|
-
return [403, {
|
98
|
+
size = body.bytesize
|
99
|
+
return [403, {CONTENT_TYPE => "text/plain",
|
75
100
|
CONTENT_LENGTH => size.to_s,
|
76
101
|
"X-Cascade" => "pass"}, [body]]
|
77
102
|
end
|
78
103
|
|
79
|
-
def list_directory
|
80
|
-
|
81
|
-
glob =
|
104
|
+
def list_directory(path_info, path, script_name)
|
105
|
+
files = [['../','Parent Directory','','','']]
|
106
|
+
glob = ::File.join(path, '*')
|
82
107
|
|
83
|
-
url_head = (
|
84
|
-
Rack::Utils.
|
108
|
+
url_head = (script_name.split('/') + path_info.split('/')).map do |part|
|
109
|
+
Rack::Utils.escape_path part
|
85
110
|
end
|
86
111
|
|
87
112
|
Dir[glob].sort.each do |node|
|
88
113
|
stat = stat(node)
|
89
|
-
next
|
90
|
-
basename =
|
91
|
-
ext =
|
114
|
+
next unless stat
|
115
|
+
basename = ::File.basename(node)
|
116
|
+
ext = ::File.extname(node)
|
92
117
|
|
93
|
-
url =
|
118
|
+
url = ::File.join(*url_head + [Rack::Utils.escape_path(basename)])
|
94
119
|
size = stat.size
|
95
120
|
type = stat.directory? ? 'directory' : Mime.mime_type(ext)
|
96
121
|
size = stat.directory? ? '-' : filesize_format(size)
|
@@ -98,49 +123,42 @@ table { width:100%%; }
|
|
98
123
|
url << '/' if stat.directory?
|
99
124
|
basename << '/' if stat.directory?
|
100
125
|
|
101
|
-
|
126
|
+
files << [ url, basename, size, type, mtime ]
|
102
127
|
end
|
103
128
|
|
104
|
-
return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'},
|
129
|
+
return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'}, DirectoryBody.new(@root, path, files) ]
|
105
130
|
end
|
106
131
|
|
107
|
-
def stat(node
|
108
|
-
|
132
|
+
def stat(node)
|
133
|
+
::File.stat(node)
|
109
134
|
rescue Errno::ENOENT, Errno::ELOOP
|
110
135
|
return nil
|
111
136
|
end
|
112
137
|
|
113
138
|
# TODO: add correct response if not readable, not sure if 404 is the best
|
114
139
|
# option
|
115
|
-
def list_path
|
116
|
-
|
140
|
+
def list_path(env, path, path_info, script_name)
|
141
|
+
stat = ::File.stat(path)
|
117
142
|
|
118
|
-
if
|
119
|
-
return @app.call(
|
120
|
-
return list_directory if
|
143
|
+
if stat.readable?
|
144
|
+
return @app.call(env) if stat.file?
|
145
|
+
return list_directory(path_info, path, script_name) if stat.directory?
|
121
146
|
else
|
122
147
|
raise Errno::ENOENT, 'No such file or directory'
|
123
148
|
end
|
124
149
|
|
125
150
|
rescue Errno::ENOENT, Errno::ELOOP
|
126
|
-
return entity_not_found
|
151
|
+
return entity_not_found(path_info)
|
127
152
|
end
|
128
153
|
|
129
|
-
def entity_not_found
|
130
|
-
body = "Entity not found: #{
|
131
|
-
size =
|
132
|
-
return [404, {
|
154
|
+
def entity_not_found(path_info)
|
155
|
+
body = "Entity not found: #{path_info}\n"
|
156
|
+
size = body.bytesize
|
157
|
+
return [404, {CONTENT_TYPE => "text/plain",
|
133
158
|
CONTENT_LENGTH => size.to_s,
|
134
159
|
"X-Cascade" => "pass"}, [body]]
|
135
160
|
end
|
136
161
|
|
137
|
-
def each
|
138
|
-
show_path = Rack::Utils.escape_html(@path.sub(/^#{@root}/,''))
|
139
|
-
files = @files.map{|f| DIR_FILE % DIR_FILE_escape(*f) }*"\n"
|
140
|
-
page = DIR_PAGE % [ show_path, show_path , files ]
|
141
|
-
page.each_line{|l| yield l }
|
142
|
-
end
|
143
|
-
|
144
162
|
# Stolen from Ramaze
|
145
163
|
|
146
164
|
FILESIZE_FORMAT = [
|
@@ -155,13 +173,7 @@ table { width:100%%; }
|
|
155
173
|
return format % (int.to_f / size) if int >= size
|
156
174
|
end
|
157
175
|
|
158
|
-
int
|
159
|
-
end
|
160
|
-
|
161
|
-
private
|
162
|
-
# Assumes url is already escaped.
|
163
|
-
def DIR_FILE_escape url, *html
|
164
|
-
[url, *html.map { |e| Utils.escape_html(e) }]
|
176
|
+
"#{int}B"
|
165
177
|
end
|
166
178
|
end
|
167
179
|
end
|