rack 2.0.9.3 → 3.0.0

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +808 -0
  3. data/CONTRIBUTING.md +142 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.md +293 -0
  6. data/SPEC.rdoc +340 -0
  7. data/lib/rack/auth/abstract/handler.rb +6 -2
  8. data/lib/rack/auth/abstract/request.rb +4 -2
  9. data/lib/rack/auth/basic.rb +7 -4
  10. data/lib/rack/auth/digest/md5.rb +1 -129
  11. data/lib/rack/auth/digest/nonce.rb +1 -51
  12. data/lib/rack/auth/digest/params.rb +1 -52
  13. data/lib/rack/auth/digest/request.rb +1 -41
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +18 -15
  16. data/lib/rack/builder.rb +151 -40
  17. data/lib/rack/cascade.rb +30 -12
  18. data/lib/rack/chunked.rb +74 -23
  19. data/lib/rack/common_logger.rb +49 -36
  20. data/lib/rack/conditional_get.rb +33 -26
  21. data/lib/rack/config.rb +2 -0
  22. data/lib/rack/constants.rb +63 -0
  23. data/lib/rack/content_length.rb +13 -16
  24. data/lib/rack/content_type.rb +12 -8
  25. data/lib/rack/deflater.rb +84 -45
  26. data/lib/rack/directory.rb +90 -64
  27. data/lib/rack/etag.rb +17 -23
  28. data/lib/rack/events.rb +23 -20
  29. data/lib/rack/file.rb +5 -172
  30. data/lib/rack/files.rb +216 -0
  31. data/lib/rack/head.rb +10 -9
  32. data/lib/rack/headers.rb +154 -0
  33. data/lib/rack/lint.rb +786 -645
  34. data/lib/rack/lock.rb +4 -6
  35. data/lib/rack/logger.rb +4 -0
  36. data/lib/rack/media_type.rb +10 -5
  37. data/lib/rack/method_override.rb +8 -2
  38. data/lib/rack/mime.rb +17 -1
  39. data/lib/rack/mock.rb +2 -195
  40. data/lib/rack/mock_request.rb +166 -0
  41. data/lib/rack/mock_response.rb +126 -0
  42. data/lib/rack/multipart/generator.rb +21 -15
  43. data/lib/rack/multipart/parser.rb +161 -118
  44. data/lib/rack/multipart/uploaded_file.rb +19 -7
  45. data/lib/rack/multipart.rb +23 -41
  46. data/lib/rack/null_logger.rb +11 -0
  47. data/lib/rack/query_parser.rb +126 -65
  48. data/lib/rack/recursive.rb +9 -5
  49. data/lib/rack/reloader.rb +6 -4
  50. data/lib/rack/request.rb +331 -74
  51. data/lib/rack/response.rb +223 -70
  52. data/lib/rack/rewindable_input.rb +28 -8
  53. data/lib/rack/runtime.rb +11 -8
  54. data/lib/rack/sendfile.rb +42 -33
  55. data/lib/rack/show_exceptions.rb +35 -18
  56. data/lib/rack/show_status.rb +25 -15
  57. data/lib/rack/static.rb +30 -18
  58. data/lib/rack/tempfile_reaper.rb +16 -5
  59. data/lib/rack/urlmap.rb +14 -6
  60. data/lib/rack/utils.rb +268 -260
  61. data/lib/rack/version.rb +34 -0
  62. data/lib/rack.rb +15 -92
  63. metadata +44 -207
  64. data/HISTORY.md +0 -520
  65. data/README.rdoc +0 -316
  66. data/Rakefile +0 -116
  67. data/SPEC +0 -263
  68. data/bin/rackup +0 -4
  69. data/contrib/rack.png +0 -0
  70. data/contrib/rack.svg +0 -150
  71. data/contrib/rack_logo.svg +0 -164
  72. data/contrib/rdoc.css +0 -412
  73. data/example/lobster.ru +0 -4
  74. data/example/protectedlobster.rb +0 -14
  75. data/example/protectedlobster.ru +0 -8
  76. data/lib/rack/handler/cgi.rb +0 -60
  77. data/lib/rack/handler/fastcgi.rb +0 -100
  78. data/lib/rack/handler/lsws.rb +0 -61
  79. data/lib/rack/handler/scgi.rb +0 -70
  80. data/lib/rack/handler/thin.rb +0 -36
  81. data/lib/rack/handler/webrick.rb +0 -120
  82. data/lib/rack/handler.rb +0 -99
  83. data/lib/rack/lobster.rb +0 -70
  84. data/lib/rack/server.rb +0 -395
  85. data/lib/rack/session/abstract/id.rb +0 -510
  86. data/lib/rack/session/cookie.rb +0 -204
  87. data/lib/rack/session/memcache.rb +0 -99
  88. data/lib/rack/session/pool.rb +0 -83
  89. data/rack.gemspec +0 -34
  90. data/test/builder/an_underscore_app.rb +0 -5
  91. data/test/builder/anything.rb +0 -5
  92. data/test/builder/comment.ru +0 -4
  93. data/test/builder/end.ru +0 -5
  94. data/test/builder/line.ru +0 -1
  95. data/test/builder/options.ru +0 -2
  96. data/test/cgi/assets/folder/test.js +0 -1
  97. data/test/cgi/assets/fonts/font.eot +0 -1
  98. data/test/cgi/assets/images/image.png +0 -1
  99. data/test/cgi/assets/index.html +0 -1
  100. data/test/cgi/assets/javascripts/app.js +0 -1
  101. data/test/cgi/assets/stylesheets/app.css +0 -1
  102. data/test/cgi/lighttpd.conf +0 -26
  103. data/test/cgi/rackup_stub.rb +0 -6
  104. data/test/cgi/sample_rackup.ru +0 -5
  105. data/test/cgi/test +0 -9
  106. data/test/cgi/test+directory/test+file +0 -1
  107. data/test/cgi/test.fcgi +0 -9
  108. data/test/cgi/test.gz +0 -0
  109. data/test/cgi/test.ru +0 -5
  110. data/test/gemloader.rb +0 -10
  111. data/test/helper.rb +0 -34
  112. data/test/multipart/bad_robots +0 -259
  113. data/test/multipart/binary +0 -0
  114. data/test/multipart/content_type_and_no_filename +0 -6
  115. data/test/multipart/empty +0 -10
  116. data/test/multipart/fail_16384_nofile +0 -814
  117. data/test/multipart/file1.txt +0 -1
  118. data/test/multipart/filename_and_modification_param +0 -7
  119. data/test/multipart/filename_and_no_name +0 -6
  120. data/test/multipart/filename_with_encoded_words +0 -7
  121. data/test/multipart/filename_with_escaped_quotes +0 -6
  122. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  123. data/test/multipart/filename_with_null_byte +0 -7
  124. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  125. data/test/multipart/filename_with_single_quote +0 -7
  126. data/test/multipart/filename_with_unescaped_percentages +0 -6
  127. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  128. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  129. data/test/multipart/filename_with_unescaped_quotes +0 -6
  130. data/test/multipart/ie +0 -6
  131. data/test/multipart/invalid_character +0 -6
  132. data/test/multipart/mixed_files +0 -21
  133. data/test/multipart/nested +0 -10
  134. data/test/multipart/none +0 -9
  135. data/test/multipart/quoted +0 -15
  136. data/test/multipart/rack-logo.png +0 -0
  137. data/test/multipart/semicolon +0 -6
  138. data/test/multipart/text +0 -15
  139. data/test/multipart/three_files_three_fields +0 -31
  140. data/test/multipart/unity3d_wwwform +0 -11
  141. data/test/multipart/webkit +0 -32
  142. data/test/rackup/config.ru +0 -31
  143. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  144. data/test/spec_auth_basic.rb +0 -89
  145. data/test/spec_auth_digest.rb +0 -260
  146. data/test/spec_body_proxy.rb +0 -85
  147. data/test/spec_builder.rb +0 -233
  148. data/test/spec_cascade.rb +0 -63
  149. data/test/spec_cgi.rb +0 -84
  150. data/test/spec_chunked.rb +0 -103
  151. data/test/spec_common_logger.rb +0 -107
  152. data/test/spec_conditional_get.rb +0 -103
  153. data/test/spec_config.rb +0 -23
  154. data/test/spec_content_length.rb +0 -86
  155. data/test/spec_content_type.rb +0 -46
  156. data/test/spec_deflater.rb +0 -375
  157. data/test/spec_directory.rb +0 -148
  158. data/test/spec_etag.rb +0 -108
  159. data/test/spec_events.rb +0 -133
  160. data/test/spec_fastcgi.rb +0 -85
  161. data/test/spec_file.rb +0 -264
  162. data/test/spec_handler.rb +0 -57
  163. data/test/spec_head.rb +0 -46
  164. data/test/spec_lint.rb +0 -520
  165. data/test/spec_lobster.rb +0 -59
  166. data/test/spec_lock.rb +0 -204
  167. data/test/spec_logger.rb +0 -24
  168. data/test/spec_media_type.rb +0 -42
  169. data/test/spec_method_override.rb +0 -110
  170. data/test/spec_mime.rb +0 -51
  171. data/test/spec_mock.rb +0 -359
  172. data/test/spec_multipart.rb +0 -721
  173. data/test/spec_null_logger.rb +0 -21
  174. data/test/spec_recursive.rb +0 -75
  175. data/test/spec_request.rb +0 -1423
  176. data/test/spec_response.rb +0 -528
  177. data/test/spec_rewindable_input.rb +0 -128
  178. data/test/spec_runtime.rb +0 -50
  179. data/test/spec_sendfile.rb +0 -125
  180. data/test/spec_server.rb +0 -193
  181. data/test/spec_session_abstract_id.rb +0 -31
  182. data/test/spec_session_abstract_session_hash.rb +0 -45
  183. data/test/spec_session_cookie.rb +0 -442
  184. data/test/spec_session_memcache.rb +0 -357
  185. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  186. data/test/spec_session_pool.rb +0 -247
  187. data/test/spec_show_exceptions.rb +0 -93
  188. data/test/spec_show_status.rb +0 -104
  189. data/test/spec_static.rb +0 -184
  190. data/test/spec_tempfile_reaper.rb +0 -64
  191. data/test/spec_thin.rb +0 -96
  192. data/test/spec_urlmap.rb +0 -237
  193. data/test/spec_utils.rb +0 -742
  194. data/test/spec_version.rb +0 -11
  195. data/test/spec_webrick.rb +0 -206
  196. data/test/static/another/index.html +0 -1
  197. data/test/static/foo.html +0 -1
  198. data/test/static/index.html +0 -1
  199. data/test/testrequest.rb +0 -78
  200. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  201. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/chunked.rb CHANGED
@@ -1,69 +1,120 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'utils'
2
5
 
3
6
  module Rack
7
+ warn "Rack::Chunked is deprecated and will be removed in Rack 3.1", uplevel: 1
4
8
 
5
9
  # Middleware that applies chunked transfer encoding to response bodies
6
- # when the response does not include a Content-Length header.
10
+ # when the response does not include a content-length header.
11
+ #
12
+ # This supports the trailer response header to allow the use of trailing
13
+ # headers in the chunked encoding. However, using this requires you manually
14
+ # specify a response body that supports a +trailers+ method. Example:
15
+ #
16
+ # [200, { 'trailer' => 'expires'}, ["Hello", "World"]]
17
+ # # error raised
18
+ #
19
+ # body = ["Hello", "World"]
20
+ # def body.trailers
21
+ # { 'expires' => Time.now.to_s }
22
+ # end
23
+ # [200, { 'trailer' => 'expires'}, body]
24
+ # # No exception raised
7
25
  class Chunked
8
26
  include Rack::Utils
9
27
 
10
- # A body wrapper that emits chunked responses
28
+ # A body wrapper that emits chunked responses.
11
29
  class Body
12
30
  TERM = "\r\n"
13
- TAIL = "0#{TERM}#{TERM}"
14
-
15
- include Rack::Utils
31
+ TAIL = "0#{TERM}"
16
32
 
33
+ # Store the response body to be chunked.
17
34
  def initialize(body)
18
35
  @body = body
19
36
  end
20
37
 
21
- def each
38
+ # For each element yielded by the response body, yield
39
+ # the element in chunked encoding.
40
+ def each(&block)
22
41
  term = TERM
23
42
  @body.each do |chunk|
24
43
  size = chunk.bytesize
25
44
  next if size == 0
26
45
 
27
- chunk = chunk.b
28
- yield [size.to_s(16), term, chunk, term].join
46
+ yield [size.to_s(16), term, chunk.b, term].join
29
47
  end
30
48
  yield TAIL
49
+ yield_trailers(&block)
50
+ yield term
31
51
  end
32
52
 
53
+ # Close the response body if the response body supports it.
33
54
  def close
34
55
  @body.close if @body.respond_to?(:close)
35
56
  end
57
+
58
+ private
59
+
60
+ # Do nothing as this class does not support trailer headers.
61
+ def yield_trailers
62
+ end
63
+ end
64
+
65
+ # A body wrapper that emits chunked responses and also supports
66
+ # sending Trailer headers. Note that the response body provided to
67
+ # initialize must have a +trailers+ method that returns a hash
68
+ # of trailer headers, and the rack response itself should have a
69
+ # Trailer header listing the headers that the +trailers+ method
70
+ # will return.
71
+ class TrailerBody < Body
72
+ private
73
+
74
+ # Yield strings for each trailer header.
75
+ def yield_trailers
76
+ @body.trailers.each_pair do |k, v|
77
+ yield "#{k}: #{v}\r\n"
78
+ end
79
+ end
36
80
  end
37
81
 
38
82
  def initialize(app)
39
83
  @app = app
40
84
  end
41
85
 
42
- # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
43
- # a version (nor response headers)
86
+ # Whether the HTTP version supports chunked encoding (HTTP 1.1 does).
44
87
  def chunkable_version?(ver)
45
88
  case ver
46
- when "HTTP/1.0", nil, "HTTP/0.9"
89
+ # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
90
+ # a version (nor response headers)
91
+ when 'HTTP/1.0', nil, 'HTTP/0.9'
47
92
  false
48
93
  else
49
94
  true
50
95
  end
51
96
  end
52
97
 
98
+ # If the rack app returns a response that should have a body,
99
+ # but does not have content-length or transfer-encoding headers,
100
+ # modify the response to use chunked transfer-encoding.
53
101
  def call(env)
54
- status, headers, body = @app.call(env)
55
- headers = HeaderHash.new(headers)
56
-
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)
102
+ status, headers, body = response = @app.call(env)
103
+
104
+ if chunkable_version?(env[SERVER_PROTOCOL]) &&
105
+ !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
106
+ !headers[CONTENT_LENGTH] &&
107
+ !headers[TRANSFER_ENCODING]
108
+
64
109
  headers[TRANSFER_ENCODING] = 'chunked'
65
- [status, headers, Body.new(body)]
110
+ if headers['trailer']
111
+ response[2] = TrailerBody.new(body)
112
+ else
113
+ response[2] = Body.new(body)
114
+ end
66
115
  end
116
+
117
+ response
67
118
  end
68
119
  end
69
120
  end
@@ -1,63 +1,74 @@
1
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+ require_relative 'body_proxy'
6
+ require_relative 'request'
2
7
 
3
8
  module Rack
4
9
  # Rack::CommonLogger forwards every request to the given +app+, and
5
10
  # logs a line in the
6
11
  # {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)
12
+ # to the configured logger.
18
13
  class CommonLogger
19
14
  # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
20
15
  #
21
16
  # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
17
  #
23
18
  # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
24
- FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
19
+ #
20
+ # The actual format is slightly different than the above due to the
21
+ # separation of SCRIPT_NAME and PATH_INFO, and because the elapsed
22
+ # time in seconds is included at the end.
23
+ FORMAT = %{%s - %s [%s] "%s %s%s%s %s" %d %s %0.4f\n}
25
24
 
26
- def initialize(app, logger=nil)
25
+ # +logger+ can be any object that supports the +write+ or +<<+ methods,
26
+ # which includes the standard library Logger. These methods are called
27
+ # with a single string argument, the log message.
28
+ # If +logger+ is nil, CommonLogger will fall back <tt>env['rack.errors']</tt>.
29
+ def initialize(app, logger = nil)
27
30
  @app = app
28
31
  @logger = logger
29
32
  end
30
33
 
34
+ # Log all requests in common_log format after a response has been
35
+ # returned. Note that if the app raises an exception, the request
36
+ # will not be logged, so if exception handling middleware are used,
37
+ # they should be loaded after this middleware. Additionally, because
38
+ # the logging happens after the request body has been fully sent, any
39
+ # exceptions raised during the sending of the response body will
40
+ # cause the request not to be logged.
31
41
  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]
42
+ began_at = Utils.clock_time
43
+ status, headers, body = response = @app.call(env)
44
+
45
+ response[2] = BodyProxy.new(body) { log(env, status, headers, began_at) }
46
+ response
37
47
  end
38
48
 
39
49
  private
40
50
 
41
- def log(env, status, header, began_at)
42
- now = Time.now
43
- length = extract_content_length(header)
51
+ # Log the request to the configured logger.
52
+ def log(env, status, response_headers, began_at)
53
+ request = Rack::Request.new(env)
54
+ length = extract_content_length(response_headers)
44
55
 
45
- msg = FORMAT % [
46
- env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
47
- env["REMOTE_USER"] || "-",
48
- now.strftime("%d/%b/%Y:%H:%M:%S %z"),
49
- env[REQUEST_METHOD],
50
- env[PATH_INFO],
51
- env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
52
- env[HTTP_VERSION],
56
+ msg = sprintf(FORMAT,
57
+ request.ip || "-",
58
+ request.get_header("REMOTE_USER") || "-",
59
+ Time.now.strftime("%d/%b/%Y:%H:%M:%S %z"),
60
+ request.request_method,
61
+ request.script_name,
62
+ request.path_info,
63
+ request.query_string.empty? ? "" : "?#{request.query_string}",
64
+ request.get_header(SERVER_PROTOCOL),
53
65
  status.to_s[0..3],
54
66
  length,
55
- now - began_at ]
56
-
57
- msg.gsub!(/[^[:print:]\n]/) { |c| "\\x#{c.ord}" }
67
+ Utils.clock_time - began_at)
58
68
 
59
- logger = @logger || env[RACK_ERRORS]
69
+ msg.gsub!(/[^[:print:]\n]/) { |c| sprintf("\\x%x", c.ord) }
60
70
 
71
+ logger = @logger || request.get_header(RACK_ERRORS)
61
72
  # Standard library logger doesn't support write but it supports << which actually
62
73
  # calls to write on the log device without formatting
63
74
  if logger.respond_to?(:write)
@@ -67,9 +78,11 @@ module Rack
67
78
  end
68
79
  end
69
80
 
81
+ # Attempt to determine the content length for the response to
82
+ # include it in the logged data.
70
83
  def extract_content_length(headers)
71
- value = headers[CONTENT_LENGTH] or return '-'
72
- value.to_s == '0' ? '-' : value
84
+ value = headers[CONTENT_LENGTH]
85
+ !value || value.to_s == '0' ? '-' : value
73
86
  end
74
87
  end
75
88
  end
@@ -1,10 +1,14 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+ require_relative 'body_proxy'
2
6
 
3
7
  module Rack
4
8
 
5
- # Middleware that enables conditional GET using If-None-Match and
6
- # If-Modified-Since. The application should set either or both of the
7
- # Last-Modified or Etag response headers according to RFC 2616. When
9
+ # Middleware that enables conditional GET using if-none-match and
10
+ # if-modified-since. The application should set either or both of the
11
+ # last-modified or etag response headers according to RFC 2616. When
8
12
  # either of the conditions is met, the response body is set to be zero
9
13
  # length and the response status is set to 304 Not Modified.
10
14
  #
@@ -19,21 +23,22 @@ module Rack
19
23
  @app = app
20
24
  end
21
25
 
26
+ # Return empty 304 response if the response has not been
27
+ # modified since the last request.
22
28
  def call(env)
23
29
  case env[REQUEST_METHOD]
24
30
  when "GET", "HEAD"
25
- status, headers, body = @app.call(env)
26
- headers = Utils::HeaderHash.new(headers)
31
+ status, headers, body = response = @app.call(env)
32
+
27
33
  if status == 200 && fresh?(env, headers)
28
- status = 304
34
+ response[0] = 304
29
35
  headers.delete(CONTENT_TYPE)
30
36
  headers.delete(CONTENT_LENGTH)
31
- original_body = body
32
- body = Rack::BodyProxy.new([]) do
33
- original_body.close if original_body.respond_to?(:close)
37
+ response[2] = Rack::BodyProxy.new([]) do
38
+ body.close if body.respond_to?(:close)
34
39
  end
35
40
  end
36
- [status, headers, body]
41
+ response
37
42
  else
38
43
  @app.call(env)
39
44
  end
@@ -41,38 +46,40 @@ module Rack
41
46
 
42
47
  private
43
48
 
49
+ # Return whether the response has not been modified since the
50
+ # last request.
44
51
  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
52
+ # if-none-match has priority over if-modified-since per RFC 7232
53
+ if none_match = env['HTTP_IF_NONE_MATCH']
54
+ etag_matches?(none_match, headers)
55
+ elsif (modified_since = env['HTTP_IF_MODIFIED_SINCE']) && (modified_since = to_rfc2822(modified_since))
56
+ modified_since?(modified_since, headers)
57
+ end
54
58
  end
55
59
 
60
+ # Whether the etag response header matches the if-none-match request header.
61
+ # If so, the request has not been modified.
56
62
  def etag_matches?(none_match, headers)
57
- etag = headers['ETag'] and etag == none_match
63
+ headers[ETAG] == none_match
58
64
  end
59
65
 
66
+ # Whether the last-modified response header matches the if-modified-since
67
+ # request header. If so, the request has not been modified.
60
68
  def modified_since?(modified_since, headers)
61
- last_modified = to_rfc2822(headers['Last-Modified']) and
62
- modified_since and
69
+ last_modified = to_rfc2822(headers['last-modified']) and
63
70
  modified_since >= last_modified
64
71
  end
65
72
 
73
+ # Return a Time object for the given string (which should be in RFC2822
74
+ # format), or nil if the string cannot be parsed.
66
75
  def to_rfc2822(since)
67
76
  # shortest possible valid date is the obsolete: 1 Nov 97 09:55 A
68
77
  # anything shorter is invalid, this avoids exceptions for common cases
69
78
  # most common being the empty string
70
79
  if since && since.length >= 16
71
- # NOTE: there is no trivial way to write this in a non execption way
80
+ # NOTE: there is no trivial way to write this in a non exception way
72
81
  # _rfc2822 returns a hash but is not that usable
73
82
  Time.rfc2822(since) rescue nil
74
- else
75
- nil
76
83
  end
77
84
  end
78
85
  end
data/lib/rack/config.rb CHANGED
@@ -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.
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ # Request env keys
5
+ HTTP_HOST = 'HTTP_HOST'
6
+ HTTP_PORT = 'HTTP_PORT'
7
+ HTTPS = 'HTTPS'
8
+ PATH_INFO = 'PATH_INFO'
9
+ REQUEST_METHOD = 'REQUEST_METHOD'
10
+ REQUEST_PATH = 'REQUEST_PATH'
11
+ SCRIPT_NAME = 'SCRIPT_NAME'
12
+ QUERY_STRING = 'QUERY_STRING'
13
+ SERVER_PROTOCOL = 'SERVER_PROTOCOL'
14
+ SERVER_NAME = 'SERVER_NAME'
15
+ SERVER_PORT = 'SERVER_PORT'
16
+ HTTP_COOKIE = 'HTTP_COOKIE'
17
+
18
+ # Response Header Keys
19
+ CACHE_CONTROL = 'cache-control'
20
+ CONTENT_LENGTH = 'content-length'
21
+ CONTENT_TYPE = 'content-type'
22
+ ETAG = 'etag'
23
+ EXPIRES = 'expires'
24
+ SET_COOKIE = 'set-cookie'
25
+ TRANSFER_ENCODING = 'transfer-encoding'
26
+
27
+ # HTTP method verbs
28
+ GET = 'GET'
29
+ POST = 'POST'
30
+ PUT = 'PUT'
31
+ PATCH = 'PATCH'
32
+ DELETE = 'DELETE'
33
+ HEAD = 'HEAD'
34
+ OPTIONS = 'OPTIONS'
35
+ LINK = 'LINK'
36
+ UNLINK = 'UNLINK'
37
+ TRACE = 'TRACE'
38
+
39
+ # Rack environment variables
40
+ RACK_VERSION = 'rack.version'
41
+ RACK_TEMPFILES = 'rack.tempfiles'
42
+ RACK_ERRORS = 'rack.errors'
43
+ RACK_LOGGER = 'rack.logger'
44
+ RACK_INPUT = 'rack.input'
45
+ RACK_SESSION = 'rack.session'
46
+ RACK_SESSION_OPTIONS = 'rack.session.options'
47
+ RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'
48
+ RACK_URL_SCHEME = 'rack.url_scheme'
49
+ RACK_HIJACK = 'rack.hijack'
50
+ RACK_IS_HIJACK = 'rack.hijack?'
51
+ RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
52
+ RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
53
+ RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
54
+ RACK_RESPONSE_FINISHED = 'rack.response_finished'
55
+ RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
56
+ RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
57
+ RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
58
+ RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
59
+ RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
60
+ RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
61
+ RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
62
+ RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
63
+ end
@@ -1,9 +1,14 @@
1
- require 'rack/utils'
2
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'utils'
3
5
 
4
6
  module Rack
5
7
 
6
- # Sets the Content-Length header on responses with fixed-length bodies.
8
+ # Sets the content-length header on responses that do not specify
9
+ # a content-length or transfer-encoding header. Note that this
10
+ # does not fix responses that have an invalid content-length
11
+ # header specified.
7
12
  class ContentLength
8
13
  include Rack::Utils
9
14
 
@@ -12,26 +17,18 @@ module Rack
12
17
  end
13
18
 
14
19
  def call(env)
15
- status, headers, body = @app.call(env)
16
- headers = HeaderHash.new(headers)
20
+ status, headers, body = response = @app.call(env)
17
21
 
18
- if !STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
22
+ if !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
19
23
  !headers[CONTENT_LENGTH] &&
20
24
  !headers[TRANSFER_ENCODING] &&
21
25
  body.respond_to?(:to_ary)
22
26
 
23
- obody = body
24
- body, length = [], 0
25
- obody.each { |part| body << part; length += part.bytesize }
26
-
27
- body = BodyProxy.new(body) do
28
- obody.close if obody.respond_to?(:close)
29
- end
30
-
31
- headers[CONTENT_LENGTH] = length.to_s
27
+ response[2] = body = body.to_ary
28
+ headers[CONTENT_LENGTH] = body.sum(&:bytesize).to_s
32
29
  end
33
30
 
34
- [status, headers, body]
31
+ response
35
32
  end
36
33
  end
37
34
  end
@@ -1,29 +1,33 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'utils'
2
5
 
3
6
  module Rack
4
7
 
5
- # Sets the Content-Type header on responses which don't have one.
8
+ # Sets the content-type header on responses which don't have one.
6
9
  #
7
10
  # Builder Usage:
8
11
  # use Rack::ContentType, "text/plain"
9
12
  #
10
- # When no content type argument is provided, "text/html" is assumed.
13
+ # When no content type argument is provided, "text/html" is the
14
+ # default.
11
15
  class ContentType
12
16
  include Rack::Utils
13
17
 
14
18
  def initialize(app, content_type = "text/html")
15
- @app, @content_type = app, content_type
19
+ @app = app
20
+ @content_type = content_type
16
21
  end
17
22
 
18
23
  def call(env)
19
- status, headers, body = @app.call(env)
20
- headers = Utils::HeaderHash.new(headers)
24
+ status, headers, _ = response = @app.call(env)
21
25
 
22
- unless STATUS_WITH_NO_ENTITY_BODY.include?(status)
26
+ unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
23
27
  headers[CONTENT_TYPE] ||= @content_type
24
28
  end
25
29
 
26
- [status, headers, body]
30
+ response
27
31
  end
28
32
  end
29
33
  end