rack 2.0.8 → 2.2.2

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +690 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +152 -148
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +29 -5
  8. data/bin/rackup +1 -0
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +3 -1
  11. data/example/protectedlobster.ru +2 -0
  12. data/lib/rack.rb +67 -73
  13. data/lib/rack/auth/abstract/handler.rb +3 -1
  14. data/lib/rack/auth/abstract/request.rb +1 -1
  15. data/lib/rack/auth/basic.rb +7 -4
  16. data/lib/rack/auth/digest/md5.rb +13 -11
  17. data/lib/rack/auth/digest/nonce.rb +6 -3
  18. data/lib/rack/auth/digest/params.rb +4 -2
  19. data/lib/rack/auth/digest/request.rb +5 -3
  20. data/lib/rack/body_proxy.rb +15 -14
  21. data/lib/rack/builder.rb +116 -23
  22. data/lib/rack/cascade.rb +28 -12
  23. data/lib/rack/chunked.rb +68 -20
  24. data/lib/rack/common_logger.rb +33 -25
  25. data/lib/rack/conditional_get.rb +20 -16
  26. data/lib/rack/config.rb +2 -0
  27. data/lib/rack/content_length.rb +8 -7
  28. data/lib/rack/content_type.rb +5 -4
  29. data/lib/rack/core_ext/regexp.rb +14 -0
  30. data/lib/rack/deflater.rb +59 -34
  31. data/lib/rack/directory.rb +84 -64
  32. data/lib/rack/etag.rb +5 -4
  33. data/lib/rack/events.rb +19 -20
  34. data/lib/rack/file.rb +4 -173
  35. data/lib/rack/files.rb +218 -0
  36. data/lib/rack/handler.rb +7 -2
  37. data/lib/rack/handler/cgi.rb +2 -3
  38. data/lib/rack/handler/fastcgi.rb +4 -4
  39. data/lib/rack/handler/lsws.rb +3 -3
  40. data/lib/rack/handler/scgi.rb +9 -8
  41. data/lib/rack/handler/thin.rb +3 -3
  42. data/lib/rack/handler/webrick.rb +15 -6
  43. data/lib/rack/head.rb +1 -1
  44. data/lib/rack/lint.rb +71 -25
  45. data/lib/rack/lobster.rb +10 -10
  46. data/lib/rack/lock.rb +2 -1
  47. data/lib/rack/logger.rb +2 -0
  48. data/lib/rack/media_type.rb +10 -5
  49. data/lib/rack/method_override.rb +4 -2
  50. data/lib/rack/mime.rb +9 -1
  51. data/lib/rack/mock.rb +97 -20
  52. data/lib/rack/multipart.rb +6 -4
  53. data/lib/rack/multipart/generator.rb +17 -13
  54. data/lib/rack/multipart/parser.rb +54 -56
  55. data/lib/rack/multipart/uploaded_file.rb +15 -7
  56. data/lib/rack/null_logger.rb +2 -0
  57. data/lib/rack/query_parser.rb +53 -28
  58. data/lib/rack/recursive.rb +7 -5
  59. data/lib/rack/reloader.rb +8 -4
  60. data/lib/rack/request.rb +220 -61
  61. data/lib/rack/response.rb +127 -44
  62. data/lib/rack/rewindable_input.rb +4 -3
  63. data/lib/rack/runtime.rb +6 -4
  64. data/lib/rack/sendfile.rb +13 -9
  65. data/lib/rack/server.rb +95 -24
  66. data/lib/rack/session/abstract/id.rb +36 -23
  67. data/lib/rack/session/cookie.rb +11 -12
  68. data/lib/rack/session/memcache.rb +4 -93
  69. data/lib/rack/session/pool.rb +5 -3
  70. data/lib/rack/show_exceptions.rb +21 -17
  71. data/lib/rack/show_status.rb +9 -9
  72. data/lib/rack/static.rb +23 -11
  73. data/lib/rack/tempfile_reaper.rb +1 -1
  74. data/lib/rack/urlmap.rb +12 -6
  75. data/lib/rack/utils.rb +98 -109
  76. data/lib/rack/version.rb +29 -0
  77. data/rack.gemspec +40 -28
  78. metadata +36 -177
  79. data/HISTORY.md +0 -505
  80. data/test/builder/an_underscore_app.rb +0 -5
  81. data/test/builder/anything.rb +0 -5
  82. data/test/builder/comment.ru +0 -4
  83. data/test/builder/end.ru +0 -5
  84. data/test/builder/line.ru +0 -1
  85. data/test/builder/options.ru +0 -2
  86. data/test/cgi/assets/folder/test.js +0 -1
  87. data/test/cgi/assets/fonts/font.eot +0 -1
  88. data/test/cgi/assets/images/image.png +0 -1
  89. data/test/cgi/assets/index.html +0 -1
  90. data/test/cgi/assets/javascripts/app.js +0 -1
  91. data/test/cgi/assets/stylesheets/app.css +0 -1
  92. data/test/cgi/lighttpd.conf +0 -26
  93. data/test/cgi/rackup_stub.rb +0 -6
  94. data/test/cgi/sample_rackup.ru +0 -5
  95. data/test/cgi/test +0 -9
  96. data/test/cgi/test+directory/test+file +0 -1
  97. data/test/cgi/test.fcgi +0 -9
  98. data/test/cgi/test.gz +0 -0
  99. data/test/cgi/test.ru +0 -5
  100. data/test/gemloader.rb +0 -10
  101. data/test/helper.rb +0 -34
  102. data/test/multipart/bad_robots +0 -259
  103. data/test/multipart/binary +0 -0
  104. data/test/multipart/content_type_and_no_filename +0 -6
  105. data/test/multipart/empty +0 -10
  106. data/test/multipart/fail_16384_nofile +0 -814
  107. data/test/multipart/file1.txt +0 -1
  108. data/test/multipart/filename_and_modification_param +0 -7
  109. data/test/multipart/filename_and_no_name +0 -6
  110. data/test/multipart/filename_with_encoded_words +0 -7
  111. data/test/multipart/filename_with_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  113. data/test/multipart/filename_with_null_byte +0 -7
  114. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  115. data/test/multipart/filename_with_single_quote +0 -7
  116. data/test/multipart/filename_with_unescaped_percentages +0 -6
  117. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  119. data/test/multipart/filename_with_unescaped_quotes +0 -6
  120. data/test/multipart/ie +0 -6
  121. data/test/multipart/invalid_character +0 -6
  122. data/test/multipart/mixed_files +0 -21
  123. data/test/multipart/nested +0 -10
  124. data/test/multipart/none +0 -9
  125. data/test/multipart/quoted +0 -15
  126. data/test/multipart/rack-logo.png +0 -0
  127. data/test/multipart/semicolon +0 -6
  128. data/test/multipart/text +0 -15
  129. data/test/multipart/three_files_three_fields +0 -31
  130. data/test/multipart/unity3d_wwwform +0 -11
  131. data/test/multipart/webkit +0 -32
  132. data/test/rackup/config.ru +0 -31
  133. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  134. data/test/spec_auth_basic.rb +0 -89
  135. data/test/spec_auth_digest.rb +0 -260
  136. data/test/spec_body_proxy.rb +0 -85
  137. data/test/spec_builder.rb +0 -233
  138. data/test/spec_cascade.rb +0 -63
  139. data/test/spec_cgi.rb +0 -84
  140. data/test/spec_chunked.rb +0 -103
  141. data/test/spec_common_logger.rb +0 -95
  142. data/test/spec_conditional_get.rb +0 -103
  143. data/test/spec_config.rb +0 -23
  144. data/test/spec_content_length.rb +0 -86
  145. data/test/spec_content_type.rb +0 -46
  146. data/test/spec_deflater.rb +0 -375
  147. data/test/spec_directory.rb +0 -148
  148. data/test/spec_etag.rb +0 -108
  149. data/test/spec_events.rb +0 -133
  150. data/test/spec_fastcgi.rb +0 -85
  151. data/test/spec_file.rb +0 -264
  152. data/test/spec_handler.rb +0 -57
  153. data/test/spec_head.rb +0 -46
  154. data/test/spec_lint.rb +0 -515
  155. data/test/spec_lobster.rb +0 -59
  156. data/test/spec_lock.rb +0 -204
  157. data/test/spec_logger.rb +0 -24
  158. data/test/spec_media_type.rb +0 -42
  159. data/test/spec_method_override.rb +0 -110
  160. data/test/spec_mime.rb +0 -51
  161. data/test/spec_mock.rb +0 -359
  162. data/test/spec_multipart.rb +0 -722
  163. data/test/spec_null_logger.rb +0 -21
  164. data/test/spec_recursive.rb +0 -75
  165. data/test/spec_request.rb +0 -1407
  166. data/test/spec_response.rb +0 -510
  167. data/test/spec_rewindable_input.rb +0 -128
  168. data/test/spec_runtime.rb +0 -50
  169. data/test/spec_sendfile.rb +0 -125
  170. data/test/spec_server.rb +0 -193
  171. data/test/spec_session_abstract_id.rb +0 -31
  172. data/test/spec_session_abstract_session_hash.rb +0 -45
  173. data/test/spec_session_cookie.rb +0 -442
  174. data/test/spec_session_memcache.rb +0 -357
  175. data/test/spec_session_pool.rb +0 -247
  176. data/test/spec_show_exceptions.rb +0 -93
  177. data/test/spec_show_status.rb +0 -104
  178. data/test/spec_static.rb +0 -184
  179. data/test/spec_tempfile_reaper.rb +0 -64
  180. data/test/spec_thin.rb +0 -96
  181. data/test/spec_urlmap.rb +0 -237
  182. data/test/spec_utils.rb +0 -742
  183. data/test/spec_version.rb +0 -11
  184. data/test/spec_webrick.rb +0 -206
  185. data/test/static/another/index.html +0 -1
  186. data/test/static/foo.html +0 -1
  187. data/test/static/index.html +0 -1
  188. data/test/testrequest.rb +0 -78
  189. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  190. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,24 +1,38 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Cascade tries a request on several apps, and returns the
3
- # first response that is not 404 or 405 (or in a list of configurable
4
- # status codes).
5
+ # first response that is not 404 or 405 (or in a list of configured
6
+ # status codes). If all applications tried return one of the configured
7
+ # status codes, return the last response.
5
8
 
6
9
  class Cascade
7
- NotFound = [404, {CONTENT_TYPE => "text/plain"}, []]
10
+ # deprecated, no longer used
11
+ NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
8
12
 
13
+ # An array of applications to try in order.
9
14
  attr_reader :apps
10
15
 
11
- def initialize(apps, catch=[404, 405])
12
- @apps = []; @has_app = {}
16
+ # Set the apps to send requests to, and what statuses result in
17
+ # cascading. Arguments:
18
+ #
19
+ # apps: An enumerable of rack applications.
20
+ # cascade_for: The statuses to use cascading for. If a response is received
21
+ # from an app, the next app is tried.
22
+ def initialize(apps, cascade_for = [404, 405])
23
+ @apps = []
13
24
  apps.each { |app| add app }
14
25
 
15
- @catch = {}
16
- [*catch].each { |status| @catch[status] = true }
26
+ @cascade_for = {}
27
+ [*cascade_for].each { |status| @cascade_for[status] = true }
17
28
  end
18
29
 
30
+ # Call each app in order. If the responses uses a status that requires
31
+ # cascading, try the next app. If all responses require cascading,
32
+ # return the response from the last app.
19
33
  def call(env)
20
- result = NotFound
21
-
34
+ return [404, { CONTENT_TYPE => "text/plain" }, []] if @apps.empty?
35
+ result = nil
22
36
  last_body = nil
23
37
 
24
38
  @apps.each do |app|
@@ -31,20 +45,22 @@ module Rack
31
45
  last_body.close if last_body.respond_to? :close
32
46
 
33
47
  result = app.call(env)
48
+ return result unless @cascade_for.include?(result[0].to_i)
34
49
  last_body = result[2]
35
- break unless @catch.include?(result[0].to_i)
36
50
  end
37
51
 
38
52
  result
39
53
  end
40
54
 
55
+ # Append an app to the list of apps to cascade. This app will
56
+ # be tried last.
41
57
  def add(app)
42
- @has_app[app] = true
43
58
  @apps << app
44
59
  end
45
60
 
61
+ # Whether the given app is one of the apps to cascade to.
46
62
  def include?(app)
47
- @has_app.include? app
63
+ @apps.include?(app)
48
64
  end
49
65
 
50
66
  alias_method :<<, :add
@@ -1,69 +1,117 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Rack
4
4
 
5
5
  # Middleware that applies chunked transfer encoding to response bodies
6
6
  # when the response does not include a Content-Length header.
7
+ #
8
+ # This supports the Trailer response header to allow the use of trailing
9
+ # headers in the chunked encoding. However, using this requires you manually
10
+ # specify a response body that supports a +trailers+ method. Example:
11
+ #
12
+ # [200, { 'Trailer' => 'Expires'}, ["Hello", "World"]]
13
+ # # error raised
14
+ #
15
+ # body = ["Hello", "World"]
16
+ # def body.trailers
17
+ # { 'Expires' => Time.now.to_s }
18
+ # end
19
+ # [200, { 'Trailer' => 'Expires'}, body]
20
+ # # No exception raised
7
21
  class Chunked
8
22
  include Rack::Utils
9
23
 
10
- # A body wrapper that emits chunked responses
24
+ # A body wrapper that emits chunked responses.
11
25
  class Body
12
26
  TERM = "\r\n"
13
- TAIL = "0#{TERM}#{TERM}"
14
-
15
- include Rack::Utils
27
+ TAIL = "0#{TERM}"
16
28
 
29
+ # Store the response body to be chunked.
17
30
  def initialize(body)
18
31
  @body = body
19
32
  end
20
33
 
21
- def each
34
+ # For each element yielded by the response body, yield
35
+ # the element in chunked encoding.
36
+ def each(&block)
22
37
  term = TERM
23
38
  @body.each do |chunk|
24
39
  size = chunk.bytesize
25
40
  next if size == 0
26
41
 
27
- chunk = chunk.b
28
- yield [size.to_s(16), term, chunk, term].join
42
+ yield [size.to_s(16), term, chunk.b, term].join
29
43
  end
30
44
  yield TAIL
45
+ yield_trailers(&block)
46
+ yield term
31
47
  end
32
48
 
49
+ # Close the response body if the response body supports it.
33
50
  def close
34
51
  @body.close if @body.respond_to?(:close)
35
52
  end
53
+
54
+ private
55
+
56
+ # Do nothing as this class does not support trailer headers.
57
+ def yield_trailers
58
+ end
59
+ end
60
+
61
+ # A body wrapper that emits chunked responses and also supports
62
+ # sending Trailer headers. Note that the response body provided to
63
+ # initialize must have a +trailers+ method that returns a hash
64
+ # of trailer headers, and the rack response itself should have a
65
+ # Trailer header listing the headers that the +trailers+ method
66
+ # will return.
67
+ class TrailerBody < Body
68
+ private
69
+
70
+ # Yield strings for each trailer header.
71
+ def yield_trailers
72
+ @body.trailers.each_pair do |k, v|
73
+ yield "#{k}: #{v}\r\n"
74
+ end
75
+ end
36
76
  end
37
77
 
38
78
  def initialize(app)
39
79
  @app = app
40
80
  end
41
81
 
42
- # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
43
- # a version (nor response headers)
82
+ # Whether the HTTP version supports chunked encoding (HTTP 1.1 does).
44
83
  def chunkable_version?(ver)
45
84
  case ver
46
- when "HTTP/1.0", nil, "HTTP/0.9"
85
+ # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
86
+ # a version (nor response headers)
87
+ when 'HTTP/1.0', nil, 'HTTP/0.9'
47
88
  false
48
89
  else
49
90
  true
50
91
  end
51
92
  end
52
93
 
94
+ # If the rack app returns a response that should have a body,
95
+ # but does not have Content-Length or Transfer-Encoding headers,
96
+ # modify the response to use chunked Transfer-Encoding.
53
97
  def call(env)
54
98
  status, headers, body = @app.call(env)
55
- headers = HeaderHash.new(headers)
99
+ headers = HeaderHash[headers]
100
+
101
+ if chunkable_version?(env[SERVER_PROTOCOL]) &&
102
+ !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
103
+ !headers[CONTENT_LENGTH] &&
104
+ !headers[TRANSFER_ENCODING]
56
105
 
57
- if ! chunkable_version?(env[HTTP_VERSION]) ||
58
- STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
59
- headers[CONTENT_LENGTH] ||
60
- headers[TRANSFER_ENCODING]
61
- [status, headers, body]
62
- else
63
- headers.delete(CONTENT_LENGTH)
64
106
  headers[TRANSFER_ENCODING] = 'chunked'
65
- [status, headers, Body.new(body)]
107
+ if headers['Trailer']
108
+ body = TrailerBody.new(body)
109
+ else
110
+ body = Body.new(body)
111
+ end
66
112
  end
113
+
114
+ [status, headers, body]
67
115
  end
68
116
  end
69
117
  end
@@ -1,58 +1,64 @@
1
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Rack
4
4
  # Rack::CommonLogger forwards every request to the given +app+, and
5
5
  # logs a line in the
6
6
  # {Apache common log format}[http://httpd.apache.org/docs/1.3/logs.html#common]
7
- # to the +logger+.
8
- #
9
- # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
10
- # an instance of Rack::NullLogger.
11
- #
12
- # +logger+ can be any class, including the standard library Logger, and is
13
- # expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
14
- # According to the SPEC, the error stream must also respond to +puts+
15
- # (which takes a single argument that responds to +to_s+), and +flush+
16
- # (which is called without arguments in order to make the error appear for
17
- # sure)
7
+ # to the configured logger.
18
8
  class CommonLogger
19
9
  # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
20
10
  #
21
11
  # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
12
  #
23
13
  # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
24
- FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
14
+ #
15
+ # The actual format is slightly different than the above due to the
16
+ # separation of SCRIPT_NAME and PATH_INFO, and because the elapsed
17
+ # time in seconds is included at the end.
18
+ FORMAT = %{%s - %s [%s] "%s %s%s%s %s" %d %s %0.4f\n}
25
19
 
26
- def initialize(app, logger=nil)
20
+ # +logger+ can be any object that supports the +write+ or +<<+ methods,
21
+ # which includes the standard library Logger. These methods are called
22
+ # with a single string argument, the log message.
23
+ # If +logger+ is nil, CommonLogger will fall back <tt>env['rack.errors']</tt>.
24
+ def initialize(app, logger = nil)
27
25
  @app = app
28
26
  @logger = logger
29
27
  end
30
28
 
29
+ # Log all requests in common_log format after a response has been
30
+ # returned. Note that if the app raises an exception, the request
31
+ # will not be logged, so if exception handling middleware are used,
32
+ # they should be loaded after this middleware. Additionally, because
33
+ # the logging happens after the request body has been fully sent, any
34
+ # exceptions raised during the sending of the response body will
35
+ # cause the request not to be logged.
31
36
  def call(env)
32
- began_at = Time.now
33
- status, header, body = @app.call(env)
34
- header = Utils::HeaderHash.new(header)
35
- body = BodyProxy.new(body) { log(env, status, header, began_at) }
36
- [status, header, body]
37
+ began_at = Utils.clock_time
38
+ status, headers, body = @app.call(env)
39
+ headers = Utils::HeaderHash[headers]
40
+ body = BodyProxy.new(body) { log(env, status, headers, began_at) }
41
+ [status, headers, body]
37
42
  end
38
43
 
39
44
  private
40
45
 
46
+ # Log the request to the configured logger.
41
47
  def log(env, status, header, began_at)
42
- now = Time.now
43
48
  length = extract_content_length(header)
44
49
 
45
50
  msg = FORMAT % [
46
51
  env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
47
52
  env["REMOTE_USER"] || "-",
48
- now.strftime("%d/%b/%Y:%H:%M:%S %z"),
53
+ Time.now.strftime("%d/%b/%Y:%H:%M:%S %z"),
49
54
  env[REQUEST_METHOD],
55
+ env[SCRIPT_NAME],
50
56
  env[PATH_INFO],
51
57
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
52
- env[HTTP_VERSION],
58
+ env[SERVER_PROTOCOL],
53
59
  status.to_s[0..3],
54
60
  length,
55
- now - began_at ]
61
+ Utils.clock_time - began_at ]
56
62
 
57
63
  logger = @logger || env[RACK_ERRORS]
58
64
  # Standard library logger doesn't support write but it supports << which actually
@@ -64,9 +70,11 @@ module Rack
64
70
  end
65
71
  end
66
72
 
73
+ # Attempt to determine the content length for the response to
74
+ # include it in the logged data.
67
75
  def extract_content_length(headers)
68
- value = headers[CONTENT_LENGTH] or return '-'
69
- value.to_s == '0' ? '-' : value
76
+ value = headers[CONTENT_LENGTH]
77
+ !value || value.to_s == '0' ? '-' : value
70
78
  end
71
79
  end
72
80
  end
@@ -1,4 +1,4 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Rack
4
4
 
@@ -19,11 +19,13 @@ module Rack
19
19
  @app = app
20
20
  end
21
21
 
22
+ # Return empty 304 response if the response has not been
23
+ # modified since the last request.
22
24
  def call(env)
23
25
  case env[REQUEST_METHOD]
24
26
  when "GET", "HEAD"
25
27
  status, headers, body = @app.call(env)
26
- headers = Utils::HeaderHash.new(headers)
28
+ headers = Utils::HeaderHash[headers]
27
29
  if status == 200 && fresh?(env, headers)
28
30
  status = 304
29
31
  headers.delete(CONTENT_TYPE)
@@ -41,38 +43,40 @@ module Rack
41
43
 
42
44
  private
43
45
 
46
+ # Return whether the response has not been modified since the
47
+ # last request.
44
48
  def fresh?(env, headers)
45
- modified_since = env['HTTP_IF_MODIFIED_SINCE']
46
- none_match = env['HTTP_IF_NONE_MATCH']
47
-
48
- return false unless modified_since || none_match
49
-
50
- success = true
51
- success &&= modified_since?(to_rfc2822(modified_since), headers) if modified_since
52
- success &&= etag_matches?(none_match, headers) if none_match
53
- success
49
+ # If-None-Match has priority over If-Modified-Since per RFC 7232
50
+ if none_match = env['HTTP_IF_NONE_MATCH']
51
+ etag_matches?(none_match, headers)
52
+ elsif (modified_since = env['HTTP_IF_MODIFIED_SINCE']) && (modified_since = to_rfc2822(modified_since))
53
+ modified_since?(modified_since, headers)
54
+ end
54
55
  end
55
56
 
57
+ # Whether the ETag response header matches the If-None-Match request header.
58
+ # If so, the request has not been modified.
56
59
  def etag_matches?(none_match, headers)
57
- etag = headers['ETag'] and etag == none_match
60
+ headers['ETag'] == none_match
58
61
  end
59
62
 
63
+ # Whether the Last-Modified response header matches the If-Modified-Since
64
+ # request header. If so, the request has not been modified.
60
65
  def modified_since?(modified_since, headers)
61
66
  last_modified = to_rfc2822(headers['Last-Modified']) and
62
- modified_since and
63
67
  modified_since >= last_modified
64
68
  end
65
69
 
70
+ # Return a Time object for the given string (which should be in RFC2822
71
+ # format), or nil if the string cannot be parsed.
66
72
  def to_rfc2822(since)
67
73
  # shortest possible valid date is the obsolete: 1 Nov 97 09:55 A
68
74
  # anything shorter is invalid, this avoids exceptions for common cases
69
75
  # most common being the empty string
70
76
  if since && since.length >= 16
71
- # NOTE: there is no trivial way to write this in a non execption way
77
+ # NOTE: there is no trivial way to write this in a non exception way
72
78
  # _rfc2822 returns a hash but is not that usable
73
79
  Time.rfc2822(since) rescue nil
74
- else
75
- nil
76
80
  end
77
81
  end
78
82
  end
@@ -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,9 +1,11 @@
1
- require 'rack/utils'
2
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Rack
5
4
 
6
- # Sets the Content-Length header on responses with fixed-length bodies.
5
+ # Sets the Content-Length header on responses that do not specify
6
+ # a Content-Length or Transfer-Encoding header. Note that this
7
+ # does not fix responses that have an invalid Content-Length
8
+ # header specified.
7
9
  class ContentLength
8
10
  include Rack::Utils
9
11
 
@@ -13,12 +15,11 @@ module Rack
13
15
 
14
16
  def call(env)
15
17
  status, headers, body = @app.call(env)
16
- headers = HeaderHash.new(headers)
18
+ headers = HeaderHash[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
- !headers[TRANSFER_ENCODING] &&
21
- body.respond_to?(:to_ary)
22
+ !headers[TRANSFER_ENCODING]
22
23
 
23
24
  obody = body
24
25
  body, length = [], 0
@@ -1,4 +1,4 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Rack
4
4
 
@@ -7,7 +7,8 @@ module Rack
7
7
  # Builder Usage:
8
8
  # use Rack::ContentType, "text/plain"
9
9
  #
10
- # When no content type argument is provided, "text/html" is assumed.
10
+ # When no content type argument is provided, "text/html" is the
11
+ # default.
11
12
  class ContentType
12
13
  include Rack::Utils
13
14
 
@@ -17,9 +18,9 @@ module Rack
17
18
 
18
19
  def call(env)
19
20
  status, headers, body = @app.call(env)
20
- headers = Utils::HeaderHash.new(headers)
21
+ headers = Utils::HeaderHash[headers]
21
22
 
22
- unless STATUS_WITH_NO_ENTITY_BODY.include?(status)
23
+ unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
23
24
  headers[CONTENT_TYPE] ||= @content_type
24
25
  end
25
26