rack 1.6.13 → 2.0.0.alpha
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/HISTORY.md +139 -18
- data/README.rdoc +17 -25
- data/Rakefile +6 -14
- data/SPEC +8 -9
- data/contrib/rack_logo.svg +164 -111
- data/lib/rack.rb +70 -21
- 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} +2 -2
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -4
- data/lib/rack/directory.rb +49 -55
- data/lib/rack/etag.rb +2 -1
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +55 -40
- data/lib/rack/handler.rb +2 -24
- 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 +22 -24
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +38 -38
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +6 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +4 -11
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +35 -52
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/multipart/generator.rb +4 -4
- data/lib/rack/multipart/parser.rb +273 -158
- 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 +174 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/reloader.rb +1 -2
- data/lib/rack/request.rb +370 -304
- data/lib/rack/response.rb +129 -56
- 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 +31 -25
- data/lib/rack/session/abstract/id.rb +93 -135
- 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 +13 -14
- data/lib/rack/utils.rb +128 -221
- data/rack.gemspec +9 -5
- 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 +31 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/{filename_with_null_byte → filename_with_single_quote} +1 -1
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +20 -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 +36 -34
- 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 +66 -40
- data/test/spec_directory.rb +72 -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 +96 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +162 -197
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +69 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/spec_method_override.rb +83 -0
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +196 -151
- data/test/spec_multipart.rb +310 -202
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +763 -607
- data/test/spec_response.rb +209 -156
- 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_cookie.rb +97 -65
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/spec_show_exceptions.rb +80 -0
- 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 +417 -345
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +77 -67
- 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 +116 -71
- 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_methodoverride.rb +0 -111
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_showexceptions.rb +0 -98
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.0.alpha"
|
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
|
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.dup.force_encoding(Encoding::BINARY)
|
27
|
+
chunk = chunk.dup.force_encoding(Encoding::BINARY)
|
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
|
@@ -49,12 +49,12 @@ module Rack
|
|
49
49
|
env[REQUEST_METHOD],
|
50
50
|
env[PATH_INFO],
|
51
51
|
env[QUERY_STRING].empty? ? "" : "?"+env[QUERY_STRING],
|
52
|
-
env[
|
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
@@ -45,7 +45,7 @@ module Rack
|
|
45
45
|
request.accept_encoding)
|
46
46
|
|
47
47
|
# Set the Vary HTTP header.
|
48
|
-
vary = headers["Vary"].to_s.split(",").map
|
48
|
+
vary = headers["Vary"].to_s.split(",").map(&:strip)
|
49
49
|
unless vary.include?("*") || vary.include?("Accept-Encoding")
|
50
50
|
headers["Vary"] = vary.push("Accept-Encoding").join(",")
|
51
51
|
end
|
@@ -118,9 +118,9 @@ module Rack
|
|
118
118
|
def each
|
119
119
|
deflator = ::Zlib::Deflate.new(*DEFLATE_ARGS)
|
120
120
|
@body.each { |part| yield deflator.deflate(part, Zlib::SYNC_FLUSH) }
|
121
|
-
yield deflator.finish
|
122
|
-
nil
|
121
|
+
yield fin = deflator.finish
|
123
122
|
ensure
|
123
|
+
deflator.finish unless fin
|
124
124
|
deflator.close
|
125
125
|
end
|
126
126
|
|
@@ -143,7 +143,7 @@ module Rack
|
|
143
143
|
end
|
144
144
|
|
145
145
|
# Skip if @compressible_types are given and does not include request's content type
|
146
|
-
return false if @compressible_types && !(headers.has_key?(
|
146
|
+
return false if @compressible_types && !(headers.has_key?(CONTENT_TYPE) && @compressible_types.include?(headers[CONTENT_TYPE][/[^;]*/]))
|
147
147
|
|
148
148
|
# Skip if @condition lambda is given and evaluates to false
|
149
149
|
return false if @condition && !@condition.call(env, status, headers, body)
|
data/lib/rack/directory.rb
CHANGED
@@ -39,58 +39,65 @@ 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)
|
48
62
|
end
|
49
63
|
|
50
64
|
def call(env)
|
51
|
-
|
52
|
-
|
65
|
+
script_name = env[SCRIPT_NAME]
|
66
|
+
path_info = Utils.unescape_path(env[PATH_INFO])
|
53
67
|
|
54
|
-
|
55
|
-
|
56
|
-
def _call(env)
|
57
|
-
@env = env
|
58
|
-
@script_name = env[SCRIPT_NAME]
|
59
|
-
@path_info = Utils.unescape(env[PATH_INFO])
|
60
|
-
|
61
|
-
if forbidden = check_forbidden
|
68
|
+
if forbidden = check_forbidden(path_info)
|
62
69
|
forbidden
|
63
70
|
else
|
64
|
-
|
65
|
-
list_path
|
71
|
+
path = ::File.join(@root, path_info)
|
72
|
+
list_path(env, path, path_info, script_name)
|
66
73
|
end
|
67
74
|
end
|
68
75
|
|
69
|
-
def check_forbidden
|
70
|
-
return unless
|
76
|
+
def check_forbidden(path_info)
|
77
|
+
return unless path_info.include? ".."
|
71
78
|
|
72
79
|
body = "Forbidden\n"
|
73
|
-
size =
|
74
|
-
return [403, {
|
80
|
+
size = body.bytesize
|
81
|
+
return [403, {CONTENT_TYPE => "text/plain",
|
75
82
|
CONTENT_LENGTH => size.to_s,
|
76
83
|
"X-Cascade" => "pass"}, [body]]
|
77
84
|
end
|
78
85
|
|
79
|
-
def list_directory
|
80
|
-
|
81
|
-
glob =
|
86
|
+
def list_directory(path_info, path, script_name)
|
87
|
+
files = [['../','Parent Directory','','','']]
|
88
|
+
glob = ::File.join(path, '*')
|
82
89
|
|
83
|
-
url_head = (
|
84
|
-
Rack::Utils.
|
90
|
+
url_head = (script_name.split('/') + path_info.split('/')).map do |part|
|
91
|
+
Rack::Utils.escape_path part
|
85
92
|
end
|
86
93
|
|
87
94
|
Dir[glob].sort.each do |node|
|
88
95
|
stat = stat(node)
|
89
|
-
next
|
90
|
-
basename =
|
91
|
-
ext =
|
96
|
+
next unless stat
|
97
|
+
basename = ::File.basename(node)
|
98
|
+
ext = ::File.extname(node)
|
92
99
|
|
93
|
-
url =
|
100
|
+
url = ::File.join(*url_head + [Rack::Utils.escape_path(basename)])
|
94
101
|
size = stat.size
|
95
102
|
type = stat.directory? ? 'directory' : Mime.mime_type(ext)
|
96
103
|
size = stat.directory? ? '-' : filesize_format(size)
|
@@ -98,49 +105,42 @@ table { width:100%%; }
|
|
98
105
|
url << '/' if stat.directory?
|
99
106
|
basename << '/' if stat.directory?
|
100
107
|
|
101
|
-
|
108
|
+
files << [ url, basename, size, type, mtime ]
|
102
109
|
end
|
103
110
|
|
104
|
-
return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'},
|
111
|
+
return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'}, DirectoryBody.new(@root, path, files) ]
|
105
112
|
end
|
106
113
|
|
107
|
-
def stat(node
|
108
|
-
|
114
|
+
def stat(node)
|
115
|
+
::File.stat(node)
|
109
116
|
rescue Errno::ENOENT, Errno::ELOOP
|
110
117
|
return nil
|
111
118
|
end
|
112
119
|
|
113
120
|
# TODO: add correct response if not readable, not sure if 404 is the best
|
114
121
|
# option
|
115
|
-
def list_path
|
116
|
-
|
122
|
+
def list_path(env, path, path_info, script_name)
|
123
|
+
stat = ::File.stat(path)
|
117
124
|
|
118
|
-
if
|
119
|
-
return @app.call(
|
120
|
-
return list_directory if
|
125
|
+
if stat.readable?
|
126
|
+
return @app.call(env) if stat.file?
|
127
|
+
return list_directory(path_info, path, script_name) if stat.directory?
|
121
128
|
else
|
122
129
|
raise Errno::ENOENT, 'No such file or directory'
|
123
130
|
end
|
124
131
|
|
125
132
|
rescue Errno::ENOENT, Errno::ELOOP
|
126
|
-
return entity_not_found
|
133
|
+
return entity_not_found(path_info)
|
127
134
|
end
|
128
135
|
|
129
|
-
def entity_not_found
|
130
|
-
body = "Entity not found: #{
|
131
|
-
size =
|
132
|
-
return [404, {
|
136
|
+
def entity_not_found(path_info)
|
137
|
+
body = "Entity not found: #{path_info}\n"
|
138
|
+
size = body.bytesize
|
139
|
+
return [404, {CONTENT_TYPE => "text/plain",
|
133
140
|
CONTENT_LENGTH => size.to_s,
|
134
141
|
"X-Cascade" => "pass"}, [body]]
|
135
142
|
end
|
136
143
|
|
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
144
|
# Stolen from Ramaze
|
145
145
|
|
146
146
|
FILESIZE_FORMAT = [
|
@@ -157,11 +157,5 @@ table { width:100%%; }
|
|
157
157
|
|
158
158
|
int.to_s + 'B'
|
159
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) }]
|
165
|
-
end
|
166
160
|
end
|
167
161
|
end
|