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.

Files changed (141) hide show
  1. checksums.yaml +5 -5
  2. data/COPYING +1 -1
  3. data/HISTORY.md +138 -8
  4. data/README.rdoc +18 -28
  5. data/Rakefile +6 -14
  6. data/SPEC +10 -11
  7. data/contrib/rack_logo.svg +164 -111
  8. data/example/protectedlobster.rb +1 -1
  9. data/example/protectedlobster.ru +1 -1
  10. data/lib/rack.rb +70 -21
  11. data/lib/rack/auth/abstract/request.rb +5 -1
  12. data/lib/rack/auth/digest/params.rb +2 -3
  13. data/lib/rack/auth/digest/request.rb +1 -1
  14. data/lib/rack/body_proxy.rb +14 -9
  15. data/lib/rack/builder.rb +3 -3
  16. data/lib/rack/chunked.rb +5 -5
  17. data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
  18. data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
  19. data/lib/rack/content_length.rb +2 -2
  20. data/lib/rack/deflater.rb +4 -39
  21. data/lib/rack/directory.rb +66 -54
  22. data/lib/rack/etag.rb +5 -4
  23. data/lib/rack/events.rb +154 -0
  24. data/lib/rack/file.rb +64 -40
  25. data/lib/rack/handler.rb +3 -25
  26. data/lib/rack/handler/cgi.rb +15 -16
  27. data/lib/rack/handler/fastcgi.rb +13 -14
  28. data/lib/rack/handler/lsws.rb +11 -11
  29. data/lib/rack/handler/scgi.rb +15 -15
  30. data/lib/rack/handler/thin.rb +3 -0
  31. data/lib/rack/handler/webrick.rb +24 -26
  32. data/lib/rack/head.rb +15 -17
  33. data/lib/rack/lint.rb +40 -40
  34. data/lib/rack/lobster.rb +1 -1
  35. data/lib/rack/lock.rb +15 -10
  36. data/lib/rack/logger.rb +2 -2
  37. data/lib/rack/media_type.rb +38 -0
  38. data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
  39. data/lib/rack/mime.rb +18 -5
  40. data/lib/rack/mock.rb +36 -54
  41. data/lib/rack/multipart.rb +35 -6
  42. data/lib/rack/multipart/generator.rb +5 -5
  43. data/lib/rack/multipart/parser.rb +270 -157
  44. data/lib/rack/multipart/uploaded_file.rb +1 -2
  45. data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
  46. data/lib/rack/query_parser.rb +192 -0
  47. data/lib/rack/recursive.rb +8 -8
  48. data/lib/rack/request.rb +394 -305
  49. data/lib/rack/response.rb +130 -57
  50. data/lib/rack/rewindable_input.rb +1 -12
  51. data/lib/rack/runtime.rb +10 -18
  52. data/lib/rack/sendfile.rb +5 -7
  53. data/lib/rack/server.rb +30 -23
  54. data/lib/rack/session/abstract/id.rb +108 -138
  55. data/lib/rack/session/cookie.rb +26 -28
  56. data/lib/rack/session/memcache.rb +8 -14
  57. data/lib/rack/session/pool.rb +14 -21
  58. data/lib/rack/show_exceptions.rb +386 -0
  59. data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
  60. data/lib/rack/static.rb +30 -5
  61. data/lib/rack/tempfile_reaper.rb +2 -2
  62. data/lib/rack/urlmap.rb +15 -14
  63. data/lib/rack/utils.rb +136 -211
  64. data/rack.gemspec +10 -9
  65. data/test/builder/an_underscore_app.rb +5 -0
  66. data/test/builder/options.ru +1 -1
  67. data/test/cgi/test.fcgi +1 -0
  68. data/test/cgi/test.gz +0 -0
  69. data/test/helper.rb +34 -0
  70. data/test/multipart/filename_with_encoded_words +7 -0
  71. data/test/multipart/filename_with_single_quote +7 -0
  72. data/test/multipart/quoted +15 -0
  73. data/test/multipart/rack-logo.png +0 -0
  74. data/test/multipart/unity3d_wwwform +11 -0
  75. data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
  76. data/test/spec_auth_basic.rb +27 -19
  77. data/test/spec_auth_digest.rb +47 -46
  78. data/test/spec_body_proxy.rb +27 -27
  79. data/test/spec_builder.rb +51 -41
  80. data/test/spec_cascade.rb +24 -22
  81. data/test/spec_cgi.rb +49 -67
  82. data/test/spec_chunked.rb +37 -35
  83. data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
  84. data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
  85. data/test/spec_config.rb +3 -2
  86. data/test/spec_content_length.rb +18 -17
  87. data/test/spec_content_type.rb +13 -12
  88. data/test/spec_deflater.rb +85 -49
  89. data/test/spec_directory.rb +87 -27
  90. data/test/spec_etag.rb +32 -31
  91. data/test/spec_events.rb +133 -0
  92. data/test/spec_fastcgi.rb +50 -72
  93. data/test/spec_file.rb +120 -77
  94. data/test/spec_handler.rb +19 -34
  95. data/test/spec_head.rb +15 -14
  96. data/test/spec_lint.rb +164 -199
  97. data/test/spec_lobster.rb +24 -23
  98. data/test/spec_lock.rb +79 -39
  99. data/test/spec_logger.rb +4 -3
  100. data/test/spec_media_type.rb +42 -0
  101. data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
  102. data/test/spec_mime.rb +19 -19
  103. data/test/spec_mock.rb +206 -144
  104. data/test/spec_multipart.rb +322 -200
  105. data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
  106. data/test/spec_recursive.rb +17 -14
  107. data/test/spec_request.rb +780 -605
  108. data/test/spec_response.rb +215 -112
  109. data/test/spec_rewindable_input.rb +50 -40
  110. data/test/spec_runtime.rb +11 -10
  111. data/test/spec_sendfile.rb +30 -35
  112. data/test/spec_server.rb +78 -52
  113. data/test/spec_session_abstract_id.rb +11 -33
  114. data/test/spec_session_abstract_session_hash.rb +45 -0
  115. data/test/spec_session_cookie.rb +99 -67
  116. data/test/spec_session_memcache.rb +63 -101
  117. data/test/spec_session_pool.rb +48 -84
  118. data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
  119. data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
  120. data/test/spec_static.rb +71 -32
  121. data/test/spec_tempfile_reaper.rb +11 -10
  122. data/test/spec_thin.rb +55 -50
  123. data/test/spec_urlmap.rb +79 -78
  124. data/test/spec_utils.rb +441 -346
  125. data/test/spec_version.rb +2 -8
  126. data/test/spec_webrick.rb +93 -71
  127. data/test/static/foo.html +1 -0
  128. data/test/testrequest.rb +1 -1
  129. data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
  130. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
  131. metadata +92 -70
  132. data/KNOWN-ISSUES +0 -44
  133. data/lib/rack/backports/uri/common_18.rb +0 -56
  134. data/lib/rack/backports/uri/common_192.rb +0 -52
  135. data/lib/rack/backports/uri/common_193.rb +0 -29
  136. data/lib/rack/handler/evented_mongrel.rb +0 -8
  137. data/lib/rack/handler/mongrel.rb +0 -106
  138. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  139. data/lib/rack/showexceptions.rb +0 -387
  140. data/lib/rack/utils/okjson.rb +0 -600
  141. data/test/spec_mongrel.rb +0 -182
@@ -4,7 +4,7 @@ require 'rack/lobster'
4
4
  lobster = Rack::Lobster.new
5
5
 
6
6
  protected_lobster = Rack::Auth::Basic.new(lobster) do |username, password|
7
- 'secret' == password
7
+ Rack::Utils.secure_compare('secret', password)
8
8
  end
9
9
 
10
10
  protected_lobster.realm = 'Lobster 2.0'
@@ -2,7 +2,7 @@ require 'rack/lobster'
2
2
 
3
3
  use Rack::ShowExceptions
4
4
  use Rack::Auth::Basic, "Lobster 2.0" do |username, password|
5
- 'secret' == password
5
+ Rack::Utils.secure_compare('secret', password)
6
6
  end
7
7
 
8
8
  run Rack::Lobster.new
@@ -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.rb</tt> in your code.
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
- "1.6.12"
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
- GET = 'GET'.freeze
34
- HEAD = 'HEAD'.freeze
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/commonlogger"
41
- autoload :ConditionalGet, "rack/conditionalget"
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/methodoverride"
108
+ autoload :MethodOverride, "rack/method_override"
56
109
  autoload :Mime, "rack/mime"
57
- autoload :NullLogger, "rack/nulllogger"
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/showexceptions"
64
- autoload :ShowStatus, "rack/showstatus"
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
@@ -13,7 +13,11 @@ module Rack
13
13
  end
14
14
 
15
15
  def provided?
16
- !authorization_key.nil?
16
+ !authorization_key.nil? && valid?
17
+ end
18
+
19
+ def valid?
20
+ !@env[authorization_key].nil?
17
21
  end
18
22
 
19
23
  def parts
@@ -17,7 +17,7 @@ module Rack
17
17
  end
18
18
 
19
19
  def self.split_header_value(str)
20
- str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] }
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}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v))
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
-
@@ -7,7 +7,7 @@ module Rack
7
7
  module Digest
8
8
  class Request < Auth::AbstractRequest
9
9
  def method
10
- @env['rack.methodoverride.original_method'] || @env[REQUEST_METHOD]
10
+ @env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] || @env[REQUEST_METHOD]
11
11
  end
12
12
 
13
13
  def digest?
@@ -1,12 +1,17 @@
1
1
  module Rack
2
2
  class BodyProxy
3
3
  def initialize(body, &block)
4
- @body, @block, @closed = body, block, false
4
+ @body = body
5
+ @block = block
6
+ @closed = false
5
7
  end
6
8
 
7
- def respond_to?(*args)
8
- return false if args.first.to_s =~ /^to_ary$/
9
- super or @body.respond_to?(*args)
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(*args, &block)
31
- @body.each(*args, &block)
35
+ def each
36
+ @body.each { |body| yield body }
32
37
  end
33
38
 
34
- def method_missing(*args, &block)
35
- super if args.first.to_s =~ /^to_ary$/
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
@@ -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,&block)
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
- # end
99
+ # end
100
100
  # end
101
101
  #
102
102
  # run Heartbeat
@@ -21,10 +21,10 @@ module Rack
21
21
  def each
22
22
  term = TERM
23
23
  @body.each do |chunk|
24
- size = bytesize(chunk)
24
+ size = chunk.bytesize
25
25
  next if size == 0
26
26
 
27
- chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding)
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['HTTP_VERSION']) ||
57
+ if ! chunkable_version?(env[HTTP_VERSION]) ||
58
58
  STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
59
59
  headers[CONTENT_LENGTH] ||
60
- headers['Transfer-Encoding']
60
+ headers[TRANSFER_ENCODING]
61
61
  [status, headers, body]
62
62
  else
63
63
  headers.delete(CONTENT_LENGTH)
64
- headers['Transfer-Encoding'] = 'chunked'
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? ? "" : "?"+env[QUERY_STRING],
52
- env["HTTP_VERSION"],
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['rack.errors']
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)
@@ -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['Transfer-Encoding'] &&
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(part) }
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)
@@ -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.length > 512 }
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 deflate identity),
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 { |v| v.strip }
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?('Content-Type') && @compressible_types.include?(headers['Content-Type'][/[^;]*/]))
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)
@@ -39,58 +39,83 @@ table { width:100%%; }
39
39
  </body></html>
40
40
  PAGE
41
41
 
42
- attr_reader :files
43
- attr_accessor :root, :path
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 = F.expand_path(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
- dup._call(env)
66
+ # strip body if this is a HEAD call
67
+ @head.call env
52
68
  end
53
69
 
54
- F = ::File
55
-
56
- def _call(env)
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 forbidden = check_forbidden
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
- @path = F.join(@root, @path_info)
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 check_forbidden
70
- return unless @path_info.include? ".."
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 = Rack::Utils.bytesize(body)
74
- return [403, {"Content-Type" => "text/plain",
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
- @files = [['../','Parent Directory','','','']]
81
- glob = F.join(@path, '*')
104
+ def list_directory(path_info, path, script_name)
105
+ files = [['../','Parent Directory','','','']]
106
+ glob = ::File.join(path, '*')
82
107
 
83
- url_head = (@script_name.split('/') + @path_info.split('/')).map do |part|
84
- Rack::Utils.escape part
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 unless stat
90
- basename = F.basename(node)
91
- ext = F.extname(node)
114
+ next unless stat
115
+ basename = ::File.basename(node)
116
+ ext = ::File.extname(node)
92
117
 
93
- url = F.join(*url_head + [Rack::Utils.escape(basename)])
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
- @files << [ url, basename, size, type, mtime ]
126
+ files << [ url, basename, size, type, mtime ]
102
127
  end
103
128
 
104
- return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'}, self ]
129
+ return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'}, DirectoryBody.new(@root, path, files) ]
105
130
  end
106
131
 
107
- def stat(node, max = 10)
108
- F.stat(node)
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
- @stat = F.stat(@path)
140
+ def list_path(env, path, path_info, script_name)
141
+ stat = ::File.stat(path)
117
142
 
118
- if @stat.readable?
119
- return @app.call(@env) if @stat.file?
120
- return list_directory if @stat.directory?
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: #{@path_info}\n"
131
- size = Rack::Utils.bytesize(body)
132
- return [404, {"Content-Type" => "text/plain",
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.to_s + 'B'
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