rack 2.0.9 → 2.1.4

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +77 -117
  5. data/Rakefile +25 -18
  6. data/SPEC +3 -4
  7. data/bin/rackup +1 -0
  8. data/example/lobster.ru +2 -0
  9. data/example/protectedlobster.rb +3 -1
  10. data/example/protectedlobster.ru +2 -0
  11. data/lib/rack.rb +63 -60
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +2 -0
  14. data/lib/rack/auth/basic.rb +4 -1
  15. data/lib/rack/auth/digest/md5.rb +9 -7
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +4 -2
  18. data/lib/rack/auth/digest/request.rb +2 -0
  19. data/lib/rack/body_proxy.rb +3 -6
  20. data/lib/rack/builder.rb +39 -15
  21. data/lib/rack/cascade.rb +6 -5
  22. data/lib/rack/chunked.rb +29 -6
  23. data/lib/rack/common_logger.rb +9 -8
  24. data/lib/rack/conditional_get.rb +3 -1
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +3 -1
  27. data/lib/rack/content_type.rb +3 -1
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +32 -17
  30. data/lib/rack/directory.rb +19 -16
  31. data/lib/rack/etag.rb +3 -1
  32. data/lib/rack/events.rb +5 -3
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler.rb +7 -2
  36. data/lib/rack/handler/cgi.rb +3 -1
  37. data/lib/rack/handler/fastcgi.rb +4 -2
  38. data/lib/rack/handler/lsws.rb +3 -1
  39. data/lib/rack/handler/scgi.rb +9 -6
  40. data/lib/rack/handler/thin.rb +3 -1
  41. data/lib/rack/handler/webrick.rb +4 -2
  42. data/lib/rack/head.rb +2 -0
  43. data/lib/rack/lint.rb +14 -11
  44. data/lib/rack/lobster.rb +7 -5
  45. data/lib/rack/lock.rb +2 -0
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +10 -5
  48. data/lib/rack/method_override.rb +4 -2
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +74 -15
  51. data/lib/rack/multipart.rb +5 -3
  52. data/lib/rack/multipart/generator.rb +6 -7
  53. data/lib/rack/multipart/parser.rb +51 -45
  54. data/lib/rack/multipart/uploaded_file.rb +2 -0
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +51 -25
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +10 -4
  59. data/lib/rack/request.rb +79 -26
  60. data/lib/rack/response.rb +71 -31
  61. data/lib/rack/rewindable_input.rb +4 -2
  62. data/lib/rack/runtime.rb +4 -2
  63. data/lib/rack/sendfile.rb +15 -8
  64. data/lib/rack/server.rb +88 -18
  65. data/lib/rack/session/abstract/id.rb +30 -20
  66. data/lib/rack/session/cookie.rb +10 -9
  67. data/lib/rack/session/memcache.rb +4 -93
  68. data/lib/rack/session/pool.rb +4 -2
  69. data/lib/rack/show_exceptions.rb +15 -9
  70. data/lib/rack/show_status.rb +4 -2
  71. data/lib/rack/static.rb +15 -10
  72. data/lib/rack/tempfile_reaper.rb +2 -0
  73. data/lib/rack/urlmap.rb +11 -2
  74. data/lib/rack/utils.rb +59 -72
  75. data/rack.gemspec +17 -7
  76. metadata +33 -175
  77. data/HISTORY.md +0 -505
  78. data/test/builder/an_underscore_app.rb +0 -5
  79. data/test/builder/anything.rb +0 -5
  80. data/test/builder/comment.ru +0 -4
  81. data/test/builder/end.ru +0 -5
  82. data/test/builder/line.ru +0 -1
  83. data/test/builder/options.ru +0 -2
  84. data/test/cgi/assets/folder/test.js +0 -1
  85. data/test/cgi/assets/fonts/font.eot +0 -1
  86. data/test/cgi/assets/images/image.png +0 -1
  87. data/test/cgi/assets/index.html +0 -1
  88. data/test/cgi/assets/javascripts/app.js +0 -1
  89. data/test/cgi/assets/stylesheets/app.css +0 -1
  90. data/test/cgi/lighttpd.conf +0 -26
  91. data/test/cgi/rackup_stub.rb +0 -6
  92. data/test/cgi/sample_rackup.ru +0 -5
  93. data/test/cgi/test +0 -9
  94. data/test/cgi/test+directory/test+file +0 -1
  95. data/test/cgi/test.fcgi +0 -9
  96. data/test/cgi/test.gz +0 -0
  97. data/test/cgi/test.ru +0 -5
  98. data/test/gemloader.rb +0 -10
  99. data/test/helper.rb +0 -34
  100. data/test/multipart/bad_robots +0 -259
  101. data/test/multipart/binary +0 -0
  102. data/test/multipart/content_type_and_no_filename +0 -6
  103. data/test/multipart/empty +0 -10
  104. data/test/multipart/fail_16384_nofile +0 -814
  105. data/test/multipart/file1.txt +0 -1
  106. data/test/multipart/filename_and_modification_param +0 -7
  107. data/test/multipart/filename_and_no_name +0 -6
  108. data/test/multipart/filename_with_encoded_words +0 -7
  109. data/test/multipart/filename_with_escaped_quotes +0 -6
  110. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  111. data/test/multipart/filename_with_null_byte +0 -7
  112. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  113. data/test/multipart/filename_with_single_quote +0 -7
  114. data/test/multipart/filename_with_unescaped_percentages +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  116. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  117. data/test/multipart/filename_with_unescaped_quotes +0 -6
  118. data/test/multipart/ie +0 -6
  119. data/test/multipart/invalid_character +0 -6
  120. data/test/multipart/mixed_files +0 -21
  121. data/test/multipart/nested +0 -10
  122. data/test/multipart/none +0 -9
  123. data/test/multipart/quoted +0 -15
  124. data/test/multipart/rack-logo.png +0 -0
  125. data/test/multipart/semicolon +0 -6
  126. data/test/multipart/text +0 -15
  127. data/test/multipart/three_files_three_fields +0 -31
  128. data/test/multipart/unity3d_wwwform +0 -11
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth_basic.rb +0 -89
  133. data/test/spec_auth_digest.rb +0 -260
  134. data/test/spec_body_proxy.rb +0 -85
  135. data/test/spec_builder.rb +0 -233
  136. data/test/spec_cascade.rb +0 -63
  137. data/test/spec_cgi.rb +0 -84
  138. data/test/spec_chunked.rb +0 -103
  139. data/test/spec_common_logger.rb +0 -95
  140. data/test/spec_conditional_get.rb +0 -103
  141. data/test/spec_config.rb +0 -23
  142. data/test/spec_content_length.rb +0 -86
  143. data/test/spec_content_type.rb +0 -46
  144. data/test/spec_deflater.rb +0 -375
  145. data/test/spec_directory.rb +0 -148
  146. data/test/spec_etag.rb +0 -108
  147. data/test/spec_events.rb +0 -133
  148. data/test/spec_fastcgi.rb +0 -85
  149. data/test/spec_file.rb +0 -264
  150. data/test/spec_handler.rb +0 -57
  151. data/test/spec_head.rb +0 -46
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -59
  154. data/test/spec_lock.rb +0 -204
  155. data/test/spec_logger.rb +0 -24
  156. data/test/spec_media_type.rb +0 -42
  157. data/test/spec_method_override.rb +0 -110
  158. data/test/spec_mime.rb +0 -51
  159. data/test/spec_mock.rb +0 -359
  160. data/test/spec_multipart.rb +0 -722
  161. data/test/spec_null_logger.rb +0 -21
  162. data/test/spec_recursive.rb +0 -75
  163. data/test/spec_request.rb +0 -1407
  164. data/test/spec_response.rb +0 -528
  165. data/test/spec_rewindable_input.rb +0 -128
  166. data/test/spec_runtime.rb +0 -50
  167. data/test/spec_sendfile.rb +0 -125
  168. data/test/spec_server.rb +0 -193
  169. data/test/spec_session_abstract_id.rb +0 -31
  170. data/test/spec_session_abstract_session_hash.rb +0 -45
  171. data/test/spec_session_cookie.rb +0 -442
  172. data/test/spec_session_memcache.rb +0 -357
  173. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  174. data/test/spec_session_pool.rb +0 -247
  175. data/test/spec_show_exceptions.rb +0 -93
  176. data/test/spec_show_status.rb +0 -104
  177. data/test/spec_static.rb +0 -184
  178. data/test/spec_tempfile_reaper.rb +0 -64
  179. data/test/spec_thin.rb +0 -96
  180. data/test/spec_urlmap.rb +0 -237
  181. data/test/spec_utils.rb +0 -742
  182. data/test/spec_version.rb +0 -11
  183. data/test/spec_webrick.rb +0 -206
  184. data/test/static/another/index.html +0 -1
  185. data/test/static/foo.html +0 -1
  186. data/test/static/index.html +0 -1
  187. data/test/testrequest.rb +0 -78
  188. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  189. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Cascade tries a request on several apps, and returns the
3
5
  # first response that is not 404 or 405 (or in a list of configurable
4
6
  # status codes).
5
7
 
6
8
  class Cascade
7
- NotFound = [404, {CONTENT_TYPE => "text/plain"}, []]
9
+ NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
8
10
 
9
11
  attr_reader :apps
10
12
 
11
- def initialize(apps, catch=[404, 405])
12
- @apps = []; @has_app = {}
13
+ def initialize(apps, catch = [404, 405])
14
+ @apps = []
13
15
  apps.each { |app| add app }
14
16
 
15
17
  @catch = {}
@@ -39,12 +41,11 @@ module Rack
39
41
  end
40
42
 
41
43
  def add(app)
42
- @has_app[app] = true
43
44
  @apps << app
44
45
  end
45
46
 
46
47
  def include?(app)
47
- @has_app.include? app
48
+ @apps.include?(app)
48
49
  end
49
50
 
50
51
  alias_method :<<, :add
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  module Rack
@@ -10,7 +12,7 @@ module Rack
10
12
  # A body wrapper that emits chunked responses
11
13
  class Body
12
14
  TERM = "\r\n"
13
- TAIL = "0#{TERM}#{TERM}"
15
+ TAIL = "0#{TERM}"
14
16
 
15
17
  include Rack::Utils
16
18
 
@@ -18,7 +20,7 @@ module Rack
18
20
  @body = body
19
21
  end
20
22
 
21
- def each
23
+ def each(&block)
22
24
  term = TERM
23
25
  @body.each do |chunk|
24
26
  size = chunk.bytesize
@@ -28,11 +30,28 @@ module Rack
28
30
  yield [size.to_s(16), term, chunk, term].join
29
31
  end
30
32
  yield TAIL
33
+ insert_trailers(&block)
34
+ yield TERM
31
35
  end
32
36
 
33
37
  def close
34
38
  @body.close if @body.respond_to?(:close)
35
39
  end
40
+
41
+ private
42
+
43
+ def insert_trailers(&block)
44
+ end
45
+ end
46
+
47
+ class TrailerBody < Body
48
+ private
49
+
50
+ def insert_trailers(&block)
51
+ @body.trailers.each_pair do |k, v|
52
+ yield "#{k}: #{v}\r\n"
53
+ end
54
+ end
36
55
  end
37
56
 
38
57
  def initialize(app)
@@ -43,7 +62,7 @@ module Rack
43
62
  # a version (nor response headers)
44
63
  def chunkable_version?(ver)
45
64
  case ver
46
- when "HTTP/1.0", nil, "HTTP/0.9"
65
+ when 'HTTP/1.0', nil, 'HTTP/0.9'
47
66
  false
48
67
  else
49
68
  true
@@ -54,15 +73,19 @@ module Rack
54
73
  status, headers, body = @app.call(env)
55
74
  headers = HeaderHash.new(headers)
56
75
 
57
- if ! chunkable_version?(env[HTTP_VERSION]) ||
58
- STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
76
+ if ! chunkable_version?(env[SERVER_PROTOCOL]) ||
77
+ STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
59
78
  headers[CONTENT_LENGTH] ||
60
79
  headers[TRANSFER_ENCODING]
61
80
  [status, headers, body]
62
81
  else
63
82
  headers.delete(CONTENT_LENGTH)
64
83
  headers[TRANSFER_ENCODING] = 'chunked'
65
- [status, headers, Body.new(body)]
84
+ if headers['Trailer']
85
+ [status, headers, TrailerBody.new(body)]
86
+ else
87
+ [status, headers, Body.new(body)]
88
+ end
66
89
  end
67
90
  end
68
91
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/body_proxy'
2
4
 
3
5
  module Rack
@@ -23,13 +25,13 @@ module Rack
23
25
  # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
24
26
  FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
25
27
 
26
- def initialize(app, logger=nil)
28
+ def initialize(app, logger = nil)
27
29
  @app = app
28
30
  @logger = logger
29
31
  end
30
32
 
31
33
  def call(env)
32
- began_at = Time.now
34
+ began_at = Utils.clock_time
33
35
  status, header, body = @app.call(env)
34
36
  header = Utils::HeaderHash.new(header)
35
37
  body = BodyProxy.new(body) { log(env, status, header, began_at) }
@@ -39,20 +41,19 @@ module Rack
39
41
  private
40
42
 
41
43
  def log(env, status, header, began_at)
42
- now = Time.now
43
44
  length = extract_content_length(header)
44
45
 
45
46
  msg = FORMAT % [
46
47
  env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
47
48
  env["REMOTE_USER"] || "-",
48
- now.strftime("%d/%b/%Y:%H:%M:%S %z"),
49
+ Time.now.strftime("%d/%b/%Y:%H:%M:%S %z"),
49
50
  env[REQUEST_METHOD],
50
51
  env[PATH_INFO],
51
52
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
52
- env[HTTP_VERSION],
53
+ env[SERVER_PROTOCOL],
53
54
  status.to_s[0..3],
54
55
  length,
55
- now - began_at ]
56
+ Utils.clock_time - began_at ]
56
57
 
57
58
  logger = @logger || env[RACK_ERRORS]
58
59
  # Standard library logger doesn't support write but it supports << which actually
@@ -65,8 +66,8 @@ module Rack
65
66
  end
66
67
 
67
68
  def extract_content_length(headers)
68
- value = headers[CONTENT_LENGTH] or return '-'
69
- value.to_s == '0' ? '-' : value
69
+ value = headers[CONTENT_LENGTH]
70
+ !value || value.to_s == '0' ? '-' : value
70
71
  end
71
72
  end
72
73
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  module Rack
@@ -68,7 +70,7 @@ module Rack
68
70
  # anything shorter is invalid, this avoids exceptions for common cases
69
71
  # most common being the empty string
70
72
  if since && since.length >= 16
71
- # NOTE: there is no trivial way to write this in a non execption way
73
+ # NOTE: there is no trivial way to write this in a non exception way
72
74
  # _rfc2822 returns a hash but is not that usable
73
75
  Time.rfc2822(since) rescue nil
74
76
  else
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Config modifies the environment using the block given during
3
5
  # initialization.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
  require 'rack/body_proxy'
3
5
 
@@ -15,7 +17,7 @@ module Rack
15
17
  status, headers, body = @app.call(env)
16
18
  headers = HeaderHash.new(headers)
17
19
 
18
- if !STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
20
+ if !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
19
21
  !headers[CONTENT_LENGTH] &&
20
22
  !headers[TRANSFER_ENCODING] &&
21
23
  body.respond_to?(:to_ary)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  module Rack
@@ -19,7 +21,7 @@ module Rack
19
21
  status, headers, body = @app.call(env)
20
22
  headers = Utils::HeaderHash.new(headers)
21
23
 
22
- unless STATUS_WITH_NO_ENTITY_BODY.include?(status)
24
+ unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
23
25
  headers[CONTENT_TYPE] ||= @content_type
24
26
  end
25
27
 
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Regexp has `match?` since Ruby 2.4
4
+ # so to support Ruby < 2.4 we need to define this method
5
+
6
+ module Rack
7
+ module RegexpExtensions
8
+ refine Regexp do
9
+ def match?(string, pos = 0)
10
+ !!match(string, pos)
11
+ end
12
+ end unless //.respond_to?(:match?)
13
+ end
14
+ end
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "zlib"
2
4
  require "time" # for Time.httpdate
3
5
  require 'rack/utils'
4
6
 
7
+ require_relative 'core_ext/regexp'
8
+
5
9
  module Rack
6
10
  # This middleware enables compression of http responses.
7
11
  #
@@ -15,19 +19,26 @@ module Rack
15
19
  # directive of 'no-transform' is present, or when the response status
16
20
  # code is one that doesn't allow an entity body.
17
21
  class Deflater
22
+ using ::Rack::RegexpExtensions
23
+
18
24
  ##
19
25
  # Creates Rack::Deflater middleware.
20
26
  #
21
27
  # [app] rack app instance
22
28
  # [options] hash of deflater options, i.e.
23
29
  # 'if' - a lambda enabling / disabling deflation based on returned boolean value
24
- # e.g use Rack::Deflater, :if => lambda { |env, status, headers, body| body.map(&:bytesize).reduce(0, :+) > 512 }
30
+ # e.g use Rack::Deflater, :if => lambda { |*, body| sum=0; body.each { |i| sum += i.length }; sum > 512 }
25
31
  # 'include' - a list of content types that should be compressed
32
+ # 'sync' - determines if the stream is going to be flushed after every chunk.
33
+ # Flushing after every chunk reduces latency for
34
+ # time-sensitive streaming applications, but hurts
35
+ # compression and throughput. Defaults to `true'.
26
36
  def initialize(app, options = {})
27
37
  @app = app
28
38
 
29
39
  @condition = options[:if]
30
40
  @compressible_types = options[:include]
41
+ @sync = options[:sync] == false ? false : true
31
42
  end
32
43
 
33
44
  def call(env)
@@ -52,33 +63,34 @@ module Rack
52
63
  case encoding
53
64
  when "gzip"
54
65
  headers['Content-Encoding'] = "gzip"
55
- headers.delete(CONTENT_LENGTH)
56
- mtime = headers.key?("Last-Modified") ?
57
- Time.httpdate(headers["Last-Modified"]) : Time.now
58
- [status, headers, GzipStream.new(body, mtime)]
66
+ headers.delete('Content-Length')
67
+ mtime = headers["Last-Modified"]
68
+ mtime = Time.httpdate(mtime).to_i if mtime
69
+ [status, headers, GzipStream.new(body, mtime, @sync)]
59
70
  when "identity"
60
71
  [status, headers, body]
61
72
  when nil
62
73
  message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
63
74
  bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
64
- [406, {CONTENT_TYPE => "text/plain", CONTENT_LENGTH => message.length.to_s}, bp]
75
+ [406, { 'Content-Type' => "text/plain", 'Content-Length' => message.length.to_s }, bp]
65
76
  end
66
77
  end
67
78
 
68
79
  class GzipStream
69
- def initialize(body, mtime)
80
+ def initialize(body, mtime, sync)
81
+ @sync = sync
70
82
  @body = body
71
83
  @mtime = mtime
72
- @closed = false
73
84
  end
74
85
 
75
86
  def each(&block)
76
87
  @writer = block
77
- gzip =::Zlib::GzipWriter.new(self)
78
- gzip.mtime = @mtime
88
+ gzip = ::Zlib::GzipWriter.new(self)
89
+ gzip.mtime = @mtime if @mtime
79
90
  @body.each { |part|
80
- gzip.write(part)
81
- gzip.flush
91
+ len = gzip.write(part)
92
+ # Flushing empty parts would raise Zlib::BufError.
93
+ gzip.flush if @sync && len > 0
82
94
  }
83
95
  ensure
84
96
  gzip.close
@@ -90,9 +102,8 @@ module Rack
90
102
  end
91
103
 
92
104
  def close
93
- return if @closed
94
- @closed = true
95
105
  @body.close if @body.respond_to?(:close)
106
+ @body = nil
96
107
  end
97
108
  end
98
109
 
@@ -101,18 +112,22 @@ module Rack
101
112
  def should_deflate?(env, status, headers, body)
102
113
  # Skip compressing empty entity body responses and responses with
103
114
  # no-transform set.
104
- if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
105
- headers[CACHE_CONTROL].to_s =~ /\bno-transform\b/ ||
115
+ if Utils::STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
116
+ /\bno-transform\b/.match?(headers['Cache-Control'].to_s) ||
106
117
  (headers['Content-Encoding'] && headers['Content-Encoding'] !~ /\bidentity\b/)
107
118
  return false
108
119
  end
109
120
 
110
121
  # Skip if @compressible_types are given and does not include request's content type
111
- return false if @compressible_types && !(headers.has_key?(CONTENT_TYPE) && @compressible_types.include?(headers[CONTENT_TYPE][/[^;]*/]))
122
+ return false if @compressible_types && !(headers.has_key?('Content-Type') && @compressible_types.include?(headers['Content-Type'][/[^;]*/]))
112
123
 
113
124
  # Skip if @condition lambda is given and evaluates to false
114
125
  return false if @condition && !@condition.call(env, status, headers, body)
115
126
 
127
+ # No point in compressing empty body, also handles usage with
128
+ # Rack::Sendfile.
129
+ return false if headers[CONTENT_LENGTH] == '0'
130
+
116
131
  true
117
132
  end
118
133
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'time'
2
4
  require 'rack/utils'
3
5
  require 'rack/mime'
6
+ require 'rack/files'
4
7
 
5
8
  module Rack
6
9
  # Rack::Directory serves entries below the +root+ given, according to the
@@ -8,7 +11,7 @@ module Rack
8
11
  # will be presented in an html based index. If a file is found, the env will
9
12
  # be passed to the specified +app+.
10
13
  #
11
- # If +app+ is not specified, a Rack::File of the same +root+ will be used.
14
+ # If +app+ is not specified, a Rack::Files of the same +root+ will be used.
12
15
 
13
16
  class Directory
14
17
  DIR_FILE = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>"
@@ -41,9 +44,9 @@ table { width:100%%; }
41
44
 
42
45
  class DirectoryBody < Struct.new(:root, :path, :files)
43
46
  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
+ show_path = Rack::Utils.escape_html(path.sub(/^#{root}/, ''))
48
+ listings = files.map{|f| DIR_FILE % DIR_FILE_escape(*f) } * "\n"
49
+ page = DIR_PAGE % [ show_path, show_path, listings ]
47
50
  page.each_line{|l| yield l }
48
51
  end
49
52
 
@@ -56,9 +59,9 @@ table { width:100%%; }
56
59
 
57
60
  attr_reader :root, :path
58
61
 
59
- def initialize(root, app=nil)
62
+ def initialize(root, app = nil)
60
63
  @root = ::File.expand_path(root)
61
- @app = app || Rack::File.new(@root)
64
+ @app = app || Rack::Files.new(@root)
62
65
  @head = Rack::Head.new(lambda { |env| get env })
63
66
  end
64
67
 
@@ -86,9 +89,9 @@ table { width:100%%; }
86
89
 
87
90
  body = "Bad Request\n"
88
91
  size = body.bytesize
89
- return [400, {CONTENT_TYPE => "text/plain",
92
+ return [400, { CONTENT_TYPE => "text/plain",
90
93
  CONTENT_LENGTH => size.to_s,
91
- "X-Cascade" => "pass"}, [body]]
94
+ "X-Cascade" => "pass" }, [body]]
92
95
  end
93
96
 
94
97
  def check_forbidden(path_info)
@@ -96,20 +99,20 @@ table { width:100%%; }
96
99
 
97
100
  body = "Forbidden\n"
98
101
  size = body.bytesize
99
- return [403, {CONTENT_TYPE => "text/plain",
102
+ return [403, { CONTENT_TYPE => "text/plain",
100
103
  CONTENT_LENGTH => size.to_s,
101
- "X-Cascade" => "pass"}, [body]]
104
+ "X-Cascade" => "pass" }, [body]]
102
105
  end
103
106
 
104
107
  def list_directory(path_info, path, script_name)
105
- files = [['../','Parent Directory','','','']]
106
- glob = ::File.join(path, '*')
108
+ files = [['../', 'Parent Directory', '', '', '']]
107
109
 
108
110
  url_head = (script_name.split('/') + path_info.split('/')).map do |part|
109
111
  Rack::Utils.escape_path part
110
112
  end
111
113
 
112
- Dir[glob].sort.each do |node|
114
+ Dir.entries(path).reject { |e| e.start_with?('.') }.sort.each do |node|
115
+ node = ::File.join path, node
113
116
  stat = stat(node)
114
117
  next unless stat
115
118
  basename = ::File.basename(node)
@@ -126,7 +129,7 @@ table { width:100%%; }
126
129
  files << [ url, basename, size, type, mtime ]
127
130
  end
128
131
 
129
- return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'}, DirectoryBody.new(@root, path, files) ]
132
+ return [ 200, { CONTENT_TYPE => 'text/html; charset=utf-8' }, DirectoryBody.new(@root, path, files) ]
130
133
  end
131
134
 
132
135
  def stat(node)
@@ -154,9 +157,9 @@ table { width:100%%; }
154
157
  def entity_not_found(path_info)
155
158
  body = "Entity not found: #{path_info}\n"
156
159
  size = body.bytesize
157
- return [404, {CONTENT_TYPE => "text/plain",
160
+ return [404, { CONTENT_TYPE => "text/plain",
158
161
  CONTENT_LENGTH => size.to_s,
159
- "X-Cascade" => "pass"}, [body]]
162
+ "X-Cascade" => "pass" }, [body]]
160
163
  end
161
164
 
162
165
  # Stolen from Ramaze