rack 2.0.9.3 → 2.2.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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +152 -162
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +44 -15
  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/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +1 -1
  14. data/lib/rack/auth/basic.rb +7 -4
  15. data/lib/rack/auth/digest/md5.rb +13 -11
  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 +5 -3
  19. data/lib/rack/body_proxy.rb +15 -14
  20. data/lib/rack/builder.rb +116 -23
  21. data/lib/rack/cascade.rb +28 -12
  22. data/lib/rack/chunked.rb +68 -20
  23. data/lib/rack/common_logger.rb +33 -28
  24. data/lib/rack/conditional_get.rb +20 -16
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +8 -7
  27. data/lib/rack/content_type.rb +5 -4
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +59 -34
  30. data/lib/rack/directory.rb +84 -64
  31. data/lib/rack/etag.rb +5 -4
  32. data/lib/rack/events.rb +19 -20
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +218 -0
  35. data/lib/rack/handler/cgi.rb +2 -3
  36. data/lib/rack/handler/fastcgi.rb +4 -4
  37. data/lib/rack/handler/lsws.rb +3 -3
  38. data/lib/rack/handler/scgi.rb +9 -8
  39. data/lib/rack/handler/thin.rb +17 -11
  40. data/lib/rack/handler/webrick.rb +15 -6
  41. data/lib/rack/handler.rb +7 -2
  42. data/lib/rack/head.rb +1 -1
  43. data/lib/rack/lint.rb +72 -26
  44. data/lib/rack/lobster.rb +10 -10
  45. data/lib/rack/lock.rb +2 -1
  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 +97 -20
  51. data/lib/rack/multipart/generator.rb +17 -13
  52. data/lib/rack/multipart/parser.rb +58 -73
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +7 -4
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +53 -28
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +8 -4
  59. data/lib/rack/request.rb +210 -61
  60. data/lib/rack/response.rb +127 -44
  61. data/lib/rack/rewindable_input.rb +4 -3
  62. data/lib/rack/runtime.rb +6 -4
  63. data/lib/rack/sendfile.rb +13 -9
  64. data/lib/rack/server.rb +95 -24
  65. data/lib/rack/session/abstract/id.rb +33 -21
  66. data/lib/rack/session/cookie.rb +12 -12
  67. data/lib/rack/session/memcache.rb +4 -93
  68. data/lib/rack/session/pool.rb +5 -3
  69. data/lib/rack/show_exceptions.rb +17 -13
  70. data/lib/rack/show_status.rb +5 -5
  71. data/lib/rack/static.rb +23 -11
  72. data/lib/rack/tempfile_reaper.rb +1 -1
  73. data/lib/rack/urlmap.rb +12 -6
  74. data/lib/rack/utils.rb +105 -130
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -28
  78. metadata +39 -182
  79. data/HISTORY.md +0 -520
  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 -107
  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 -520
  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 -721
  163. data/test/spec_null_logger.rb +0 -21
  164. data/test/spec_recursive.rb +0 -75
  165. data/test/spec_request.rb +0 -1423
  166. data/test/spec_response.rb +0 -528
  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_persisted_secure_secure_session_hash.rb +0 -73
  176. data/test/spec_session_pool.rb +0 -247
  177. data/test/spec_show_exceptions.rb +0 -93
  178. data/test/spec_show_status.rb +0 -104
  179. data/test/spec_static.rb +0 -184
  180. data/test/spec_tempfile_reaper.rb +0 -64
  181. data/test/spec_thin.rb +0 -96
  182. data/test/spec_urlmap.rb +0 -237
  183. data/test/spec_utils.rb +0 -742
  184. data/test/spec_version.rb +0 -11
  185. data/test/spec_webrick.rb +0 -206
  186. data/test/static/another/index.html +0 -1
  187. data/test/static/foo.html +0 -1
  188. data/test/static/index.html +0 -1
  189. data/test/testrequest.rb +0 -78
  190. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  191. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/lint.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'forwardable'
3
4
 
4
5
  module Rack
@@ -33,7 +34,7 @@ module Rack
33
34
 
34
35
  ## A Rack application is a Ruby object (not a class) that
35
36
  ## responds to +call+.
36
- def call(env=nil)
37
+ def call(env = nil)
37
38
  dup._call(env)
38
39
  end
39
40
 
@@ -46,13 +47,24 @@ module Rack
46
47
  env[RACK_ERRORS] = ErrorWrapper.new(env[RACK_ERRORS])
47
48
 
48
49
  ## and returns an Array of exactly three values:
49
- status, headers, @body = @app.call(env)
50
+ ary = @app.call(env)
51
+ assert("response #{ary.inspect} is not an Array , but #{ary.class}") {
52
+ ary.kind_of? Array
53
+ }
54
+ assert("response array #{ary.inspect} has #{ary.size} elements instead of 3") {
55
+ ary.size == 3
56
+ }
57
+
58
+ status, headers, @body = ary
50
59
  ## The *status*,
51
60
  check_status status
52
61
  ## the *headers*,
53
62
  check_headers headers
54
63
 
55
- check_hijack_response headers, env
64
+ hijack_proc = check_hijack_response headers, env
65
+ if hijack_proc && headers.is_a?(Hash)
66
+ headers[RACK_HIJACK] = hijack_proc
67
+ end
56
68
 
57
69
  ## and the *body*.
58
70
  check_content_type status, headers
@@ -63,12 +75,15 @@ module Rack
63
75
 
64
76
  ## == The Environment
65
77
  def check_env(env)
66
- ## The environment must be an instance of Hash that includes
78
+ ## The environment must be an unfrozen instance of Hash that includes
67
79
  ## CGI-like headers. The application is free to modify the
68
80
  ## environment.
69
81
  assert("env #{env.inspect} is not a Hash, but #{env.class}") {
70
82
  env.kind_of? Hash
71
83
  }
84
+ assert("env should not be frozen, but is") {
85
+ !env.frozen?
86
+ }
72
87
 
73
88
  ##
74
89
  ## The environment is required to include these variables
@@ -102,17 +117,19 @@ module Rack
102
117
  ## follows the <tt>?</tt>, if any. May be
103
118
  ## empty, but is always required!
104
119
 
105
- ## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>::
106
- ## When combined with <tt>SCRIPT_NAME</tt> and
120
+ ## <tt>SERVER_NAME</tt>:: When combined with <tt>SCRIPT_NAME</tt> and
107
121
  ## <tt>PATH_INFO</tt>, these variables can be
108
122
  ## used to complete the URL. Note, however,
109
123
  ## that <tt>HTTP_HOST</tt>, if present,
110
124
  ## should be used in preference to
111
125
  ## <tt>SERVER_NAME</tt> for reconstructing
112
126
  ## the request URL.
113
- ## <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt>
114
- ## can never be empty strings, and so
115
- ## are always required.
127
+ ## <tt>SERVER_NAME</tt> can never be an empty
128
+ ## string, and so is always required.
129
+
130
+ ## <tt>SERVER_PORT</tt>:: An optional +Integer+ which is the port the
131
+ ## server is running on. Should be specified if
132
+ ## the server is running on a non-standard port.
116
133
 
117
134
  ## <tt>HTTP_</tt> Variables:: Variables corresponding to the
118
135
  ## client-supplied HTTP request
@@ -123,9 +140,8 @@ module Rack
123
140
  ## the presence or absence of the
124
141
  ## appropriate HTTP header in the
125
142
  ## request. See
126
- ## <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
127
- ## RFC3875 section 4.1.18</a> for
128
- ## specific behavior.
143
+ ## {RFC3875 section 4.1.18}[https://tools.ietf.org/html/rfc3875#section-4.1.18]
144
+ ## for specific behavior.
129
145
 
130
146
  ## In addition to this, the Rack environment must include these
131
147
  ## Rack-specific variables:
@@ -197,6 +213,11 @@ module Rack
197
213
  assert("session #{session.inspect} must respond to clear") {
198
214
  session.respond_to?(:clear)
199
215
  }
216
+
217
+ ## to_hash (returning unfrozen Hash instance);
218
+ assert("session #{session.inspect} must respond to to_hash and return unfrozen Hash instance") {
219
+ session.respond_to?(:to_hash) && session.to_hash.kind_of?(Hash) && !session.to_hash.frozen?
220
+ }
200
221
  end
201
222
 
202
223
  ## <tt>rack.logger</tt>:: A common object interface for logging messages.
@@ -252,28 +273,49 @@ module Rack
252
273
  ## accepted specifications and must not be used otherwise.
253
274
  ##
254
275
 
255
- %w[REQUEST_METHOD SERVER_NAME SERVER_PORT
256
- QUERY_STRING
276
+ %w[REQUEST_METHOD SERVER_NAME QUERY_STRING
257
277
  rack.version rack.input rack.errors
258
278
  rack.multithread rack.multiprocess rack.run_once].each { |header|
259
279
  assert("env missing required key #{header}") { env.include? header }
260
280
  }
261
281
 
282
+ ## The <tt>SERVER_PORT</tt> must be an Integer if set.
283
+ assert("env[SERVER_PORT] is not an Integer") do
284
+ server_port = env["SERVER_PORT"]
285
+ server_port.nil? || (Integer(server_port) rescue false)
286
+ end
287
+
288
+ ## The <tt>SERVER_NAME</tt> must be a valid authority as defined by RFC7540.
289
+ assert("#{env[SERVER_NAME]} must be a valid authority") do
290
+ URI.parse("http://#{env[SERVER_NAME]}/") rescue false
291
+ end
292
+
293
+ ## The <tt>HTTP_HOST</tt> must be a valid authority as defined by RFC7540.
294
+ assert("#{env[HTTP_HOST]} must be a valid authority") do
295
+ URI.parse("http://#{env[HTTP_HOST]}/") rescue false
296
+ end
297
+
262
298
  ## The environment must not contain the keys
263
299
  ## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
264
300
  ## (use the versions without <tt>HTTP_</tt>).
265
301
  %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
266
- assert("env contains #{header}, must use #{header[5,-1]}") {
302
+ assert("env contains #{header}, must use #{header[5, -1]}") {
267
303
  not env.include? header
268
304
  }
269
305
  }
270
306
 
271
307
  ## The CGI keys (named without a period) must have String values.
308
+ ## If the string values for CGI keys contain non-ASCII characters,
309
+ ## they should use ASCII-8BIT encoding.
272
310
  env.each { |key, value|
273
311
  next if key.include? "." # Skip extensions
274
312
  assert("env variable #{key} has non-string value #{value.inspect}") {
275
313
  value.kind_of? String
276
314
  }
315
+ next if value.encoding == Encoding::ASCII_8BIT
316
+ assert("env variable #{key} has value containing non-ASCII characters and has non-ASCII-8BIT encoding #{value.inspect} encoding: #{value.encoding}") {
317
+ value.b !~ /[\x80-\xff]/n
318
+ }
277
319
  }
278
320
 
279
321
  ## There are the following restrictions:
@@ -295,7 +337,7 @@ module Rack
295
337
  check_hijack env
296
338
 
297
339
  ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
298
- assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD].dump}") {
340
+ assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]}") {
299
341
  env[REQUEST_METHOD] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
300
342
  }
301
343
 
@@ -336,7 +378,7 @@ module Rack
336
378
  ## When applicable, its external encoding must be "ASCII-8BIT" and it
337
379
  ## must be opened in binary mode, for Ruby 1.9 compatibility.
338
380
  assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
339
- input.external_encoding.name == "ASCII-8BIT"
381
+ input.external_encoding == Encoding::ASCII_8BIT
340
382
  } if input.respond_to?(:external_encoding)
341
383
  assert("rack.input #{input} is not opened in binary mode") {
342
384
  input.binmode?
@@ -568,7 +610,7 @@ module Rack
568
610
 
569
611
  # this check uses headers like a hash, but the spec only requires
570
612
  # headers respond to #each
571
- headers = Rack::Utils::HeaderHash.new(headers)
613
+ headers = Rack::Utils::HeaderHash[headers]
572
614
 
573
615
  ## In order to do this, an application may set the special header
574
616
  ## <tt>rack.hijack</tt> to an object that responds to <tt>call</tt>
@@ -592,7 +634,7 @@ module Rack
592
634
  headers[RACK_HIJACK].respond_to? :call
593
635
  }
594
636
  original_hijack = headers[RACK_HIJACK]
595
- headers[RACK_HIJACK] = proc do |io|
637
+ proc do |io|
596
638
  original_hijack.call HijackWrapper.new(io)
597
639
  end
598
640
  else
@@ -602,6 +644,8 @@ module Rack
602
644
  assert('rack.hijack header must not be present if server does not support hijacking') {
603
645
  headers[RACK_HIJACK].nil?
604
646
  }
647
+
648
+ nil
605
649
  end
606
650
  end
607
651
  ## ==== Conventions
@@ -626,15 +670,17 @@ module Rack
626
670
  assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
627
671
  header.respond_to? :each
628
672
  }
629
- header.each { |key, value|
630
- ## Special headers starting "rack." are for communicating with the
631
- ## server, and must not be sent back to the client.
632
- next if key =~ /^rack\..+$/
633
673
 
674
+ header.each { |key, value|
634
675
  ## The header keys must be Strings.
635
676
  assert("header key must be a string, was #{key.class}") {
636
677
  key.kind_of? String
637
678
  }
679
+
680
+ ## Special headers starting "rack." are for communicating with the
681
+ ## server, and must not be sent back to the client.
682
+ next if key =~ /^rack\..+$/
683
+
638
684
  ## The header must not contain a +Status+ key.
639
685
  assert("header must not contain Status") { key.downcase != "status" }
640
686
  ## The header must conform to RFC7230 token specification, i.e. cannot
@@ -662,7 +708,7 @@ module Rack
662
708
  ## 204 or 304.
663
709
  if key.downcase == "content-type"
664
710
  assert("Content-Type header found in #{status} response, not allowed") {
665
- not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
711
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
666
712
  }
667
713
  return
668
714
  end
@@ -676,7 +722,7 @@ module Rack
676
722
  ## There must not be a <tt>Content-Length</tt> header when the
677
723
  ## +Status+ is 1xx, 204 or 304.
678
724
  assert("Content-Length header found in #{status} response, not allowed") {
679
- not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
725
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
680
726
  }
681
727
  @content_length = value
682
728
  end
data/lib/rack/lobster.rb CHANGED
@@ -1,7 +1,6 @@
1
- require 'zlib'
1
+ # frozen_string_literal: true
2
2
 
3
- require 'rack/request'
4
- require 'rack/response'
3
+ require 'zlib'
5
4
 
6
5
  module Rack
7
6
  # Paste has a Pony, Rack has a Lobster!
@@ -25,8 +24,8 @@ module Rack
25
24
  content = ["<title>Lobstericious!</title>",
26
25
  "<pre>", lobster, "</pre>",
27
26
  "<a href='#{href}'>flip!</a>"]
28
- length = content.inject(0) { |a,e| a+e.size }.to_s
29
- [200, {CONTENT_TYPE => "text/html", CONTENT_LENGTH => length}, content]
27
+ length = content.inject(0) { |a, e| a + e.size }.to_s
28
+ [200, { CONTENT_TYPE => "text/html", CONTENT_LENGTH => length }, content]
30
29
  }
31
30
 
32
31
  def call(env)
@@ -37,8 +36,8 @@ module Rack
37
36
  gsub('\\', 'TEMP').
38
37
  gsub('/', '\\').
39
38
  gsub('TEMP', '/').
40
- gsub('{','}').
41
- gsub('(',')')
39
+ gsub('{', '}').
40
+ gsub('(', ')')
42
41
  end.join("\n")
43
42
  href = "?flip=right"
44
43
  elsif req.GET["flip"] == "crash"
@@ -62,9 +61,10 @@ module Rack
62
61
  end
63
62
 
64
63
  if $0 == __FILE__
65
- require 'rack'
66
- require 'rack/show_exceptions'
64
+ # :nocov:
65
+ require_relative '../rack'
67
66
  Rack::Server.start(
68
- :app => Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), :Port => 9292
67
+ app: Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), Port: 9292
69
68
  )
69
+ # :nocov:
70
70
  end
data/lib/rack/lock.rb CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thread'
2
- require 'rack/body_proxy'
3
4
 
4
5
  module Rack
5
6
  # Rack::Lock locks every request inside a mutex, so that every request
data/lib/rack/logger.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  module Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::MediaType parse media type and parameters out of content_type string
3
5
 
@@ -13,7 +15,7 @@ module Rack
13
15
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
14
16
  def type(content_type)
15
17
  return nil unless content_type
16
- content_type.split(SPLIT_PATTERN, 2).first.downcase
18
+ content_type.split(SPLIT_PATTERN, 2).first.tap &:downcase!
17
19
  end
18
20
 
19
21
  # The media type parameters provided in CONTENT_TYPE as a Hash, or
@@ -23,15 +25,18 @@ module Rack
23
25
  # { 'charset' => 'utf-8' }
24
26
  def params(content_type)
25
27
  return {} if content_type.nil?
26
- Hash[*content_type.split(SPLIT_PATTERN)[1..-1].
27
- collect { |s| s.split('=', 2) }.
28
- map { |k,v| [k.downcase, strip_doublequotes(v)] }.flatten]
28
+
29
+ content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh|
30
+ k, v = s.split('=', 2)
31
+
32
+ hsh[k.tap(&:downcase!)] = strip_doublequotes(v)
33
+ end
29
34
  end
30
35
 
31
36
  private
32
37
 
33
38
  def strip_doublequotes(str)
34
- (str[0] == ?" && str[-1] == ?") ? str[1..-2] : str
39
+ (str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str
35
40
  end
36
41
  end
37
42
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class MethodOverride
3
5
  HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK]
4
6
 
5
- METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
6
- HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
7
+ METHOD_OVERRIDE_PARAM_KEY = "_method"
8
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE"
7
9
  ALLOWED_METHODS = %w[POST]
8
10
 
9
11
  def initialize(app)
data/lib/rack/mime.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Mime
3
5
  # Returns String with mime type if found, otherwise use +fallback+.
@@ -13,7 +15,7 @@ module Rack
13
15
  # This is a shortcut for:
14
16
  # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
15
17
 
16
- def mime_type(ext, fallback='application/octet-stream')
18
+ def mime_type(ext, fallback = 'application/octet-stream')
17
19
  MIME_TYPES.fetch(ext.to_s.downcase, fallback)
18
20
  end
19
21
  module_function :mime_type
@@ -306,6 +308,7 @@ module Rack
306
308
  ".lvp" => "audio/vnd.lucent.voice",
307
309
  ".lwp" => "application/vnd.lotus-wordpro",
308
310
  ".m3u" => "audio/x-mpegurl",
311
+ ".m3u8" => "application/x-mpegurl",
309
312
  ".m4a" => "audio/mp4a-latm",
310
313
  ".m4v" => "video/mp4",
311
314
  ".ma" => "application/mathematica",
@@ -343,6 +346,7 @@ module Rack
343
346
  ".mp4s" => "application/mp4",
344
347
  ".mp4v" => "video/mp4",
345
348
  ".mpc" => "application/vnd.mophun.certificate",
349
+ ".mpd" => "application/dash+xml",
346
350
  ".mpeg" => "video/mpeg",
347
351
  ".mpg" => "video/mpeg",
348
352
  ".mpga" => "audio/mpeg",
@@ -542,6 +546,7 @@ module Rack
542
546
  ".spp" => "application/scvp-vp-response",
543
547
  ".spq" => "application/scvp-vp-request",
544
548
  ".src" => "application/x-wais-source",
549
+ ".srt" => "text/srt",
545
550
  ".srx" => "application/sparql-results+xml",
546
551
  ".sse" => "application/vnd.kodak-descriptor",
547
552
  ".ssf" => "application/vnd.epson.ssf",
@@ -576,6 +581,7 @@ module Rack
576
581
  ".tr" => "text/troff",
577
582
  ".tra" => "application/vnd.trueapp",
578
583
  ".trm" => "application/x-msterminal",
584
+ ".ts" => "video/mp2t",
579
585
  ".tsv" => "text/tab-separated-values",
580
586
  ".ttf" => "application/octet-stream",
581
587
  ".twd" => "application/vnd.simtech-mindmapper",
@@ -600,9 +606,11 @@ module Rack
600
606
  ".vrml" => "model/vrml",
601
607
  ".vsd" => "application/vnd.visio",
602
608
  ".vsf" => "application/vnd.vsf",
609
+ ".vtt" => "text/vtt",
603
610
  ".vtu" => "model/vnd.vtu",
604
611
  ".vxml" => "application/voicexml+xml",
605
612
  ".war" => "application/java-archive",
613
+ ".wasm" => "application/wasm",
606
614
  ".wav" => "audio/x-wav",
607
615
  ".wax" => "audio/x-ms-wax",
608
616
  ".wbmp" => "image/vnd.wap.wbmp",
data/lib/rack/mock.rb CHANGED
@@ -1,9 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'stringio'
3
- require 'rack'
4
- require 'rack/lint'
5
- require 'rack/utils'
6
- require 'rack/response'
5
+ require_relative '../rack'
6
+ require 'cgi/cookie'
7
7
 
8
8
  module Rack
9
9
  # Rack::MockRequest helps testing your Rack application without
@@ -53,16 +53,26 @@ module Rack
53
53
  @app = app
54
54
  end
55
55
 
56
- def get(uri, opts={}) request(GET, uri, opts) end
57
- def post(uri, opts={}) request(POST, uri, opts) end
58
- def put(uri, opts={}) request(PUT, uri, opts) end
59
- def patch(uri, opts={}) request(PATCH, uri, opts) end
60
- def delete(uri, opts={}) request(DELETE, uri, opts) end
61
- def head(uri, opts={}) request(HEAD, uri, opts) end
62
- def options(uri, opts={}) request(OPTIONS, uri, opts) end
63
-
64
- def request(method=GET, uri="", opts={})
65
- env = self.class.env_for(uri, opts.merge(:method => method))
56
+ # Make a GET request and return a MockResponse. See #request.
57
+ def get(uri, opts = {}) request(GET, uri, opts) end
58
+ # Make a POST request and return a MockResponse. See #request.
59
+ def post(uri, opts = {}) request(POST, uri, opts) end
60
+ # Make a PUT request and return a MockResponse. See #request.
61
+ def put(uri, opts = {}) request(PUT, uri, opts) end
62
+ # Make a PATCH request and return a MockResponse. See #request.
63
+ def patch(uri, opts = {}) request(PATCH, uri, opts) end
64
+ # Make a DELETE request and return a MockResponse. See #request.
65
+ def delete(uri, opts = {}) request(DELETE, uri, opts) end
66
+ # Make a HEAD request and return a MockResponse. See #request.
67
+ def head(uri, opts = {}) request(HEAD, uri, opts) end
68
+ # Make an OPTIONS request and return a MockResponse. See #request.
69
+ def options(uri, opts = {}) request(OPTIONS, uri, opts) end
70
+
71
+ # Make a request using the given request method for the given
72
+ # uri to the rack application and return a MockResponse.
73
+ # Options given are passed to MockRequest.env_for.
74
+ def request(method = GET, uri = "", opts = {})
75
+ env = self.class.env_for(uri, opts.merge(method: method))
66
76
 
67
77
  if opts[:lint]
68
78
  app = Rack::Lint.new(@app)
@@ -71,7 +81,7 @@ module Rack
71
81
  end
72
82
 
73
83
  errors = env[RACK_ERRORS]
74
- status, headers, body = app.call(env)
84
+ status, headers, body = app.call(env)
75
85
  MockResponse.new(status, headers, body, errors)
76
86
  ensure
77
87
  body.close if body.respond_to?(:close)
@@ -85,7 +95,14 @@ module Rack
85
95
  end
86
96
 
87
97
  # Return the Rack environment used for a request to +uri+.
88
- def self.env_for(uri="", opts={})
98
+ # All options that are strings are added to the returned environment.
99
+ # Options:
100
+ # :fatal :: Whether to raise an exception if request outputs to rack.errors
101
+ # :input :: The rack.input to set
102
+ # :method :: The HTTP request method to use
103
+ # :params :: The params to use
104
+ # :script_name :: The SCRIPT_NAME to set
105
+ def self.env_for(uri = "", opts = {})
89
106
  uri = parse_uri_rfc2396(uri)
90
107
  uri.path = "/#{uri.path}" unless uri.path[0] == ?/
91
108
 
@@ -139,7 +156,7 @@ module Rack
139
156
  rack_input.set_encoding(Encoding::BINARY)
140
157
  env[RACK_INPUT] = rack_input
141
158
 
142
- env["CONTENT_LENGTH"] ||= env[RACK_INPUT].length.to_s
159
+ env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
143
160
 
144
161
  opts.each { |field, value|
145
162
  env[field] = value if String === field
@@ -154,17 +171,24 @@ module Rack
154
171
  # MockRequest.
155
172
 
156
173
  class MockResponse < Rack::Response
174
+ class << self
175
+ alias [] new
176
+ end
177
+
157
178
  # Headers
158
- attr_reader :original_headers
179
+ attr_reader :original_headers, :cookies
159
180
 
160
181
  # Errors
161
182
  attr_accessor :errors
162
183
 
163
- def initialize(status, headers, body, errors=StringIO.new(""))
184
+ def initialize(status, headers, body, errors = StringIO.new(""))
164
185
  @original_headers = headers
165
186
  @errors = errors.string if errors.respond_to?(:string)
187
+ @cookies = parse_cookies_from_header
166
188
 
167
189
  super(body, status, headers)
190
+
191
+ buffered_body!
168
192
  end
169
193
 
170
194
  def =~(other)
@@ -186,11 +210,64 @@ module Rack
186
210
  # ...
187
211
  # res.body.should == "foo!"
188
212
  # end
189
- super.join
213
+ buffer = String.new
214
+
215
+ super.each do |chunk|
216
+ buffer << chunk
217
+ end
218
+
219
+ return buffer
190
220
  end
191
221
 
192
222
  def empty?
193
223
  [201, 204, 304].include? status
194
224
  end
225
+
226
+ def cookie(name)
227
+ cookies.fetch(name, nil)
228
+ end
229
+
230
+ private
231
+
232
+ def parse_cookies_from_header
233
+ cookies = Hash.new
234
+ if original_headers.has_key? 'Set-Cookie'
235
+ set_cookie_header = original_headers.fetch('Set-Cookie')
236
+ set_cookie_header.split("\n").each do |cookie|
237
+ cookie_name, cookie_filling = cookie.split('=', 2)
238
+ cookie_attributes = identify_cookie_attributes cookie_filling
239
+ parsed_cookie = CGI::Cookie.new(
240
+ 'name' => cookie_name.strip,
241
+ 'value' => cookie_attributes.fetch('value'),
242
+ 'path' => cookie_attributes.fetch('path', nil),
243
+ 'domain' => cookie_attributes.fetch('domain', nil),
244
+ 'expires' => cookie_attributes.fetch('expires', nil),
245
+ 'secure' => cookie_attributes.fetch('secure', false)
246
+ )
247
+ cookies.store(cookie_name, parsed_cookie)
248
+ end
249
+ end
250
+ cookies
251
+ end
252
+
253
+ def identify_cookie_attributes(cookie_filling)
254
+ cookie_bits = cookie_filling.split(';')
255
+ cookie_attributes = Hash.new
256
+ cookie_attributes.store('value', cookie_bits[0].strip)
257
+ cookie_bits.each do |bit|
258
+ if bit.include? '='
259
+ cookie_attribute, attribute_value = bit.split('=')
260
+ cookie_attributes.store(cookie_attribute.strip, attribute_value.strip)
261
+ if cookie_attribute.include? 'max-age'
262
+ cookie_attributes.store('expires', Time.now + attribute_value.strip.to_i)
263
+ end
264
+ end
265
+ if bit.include? 'secure'
266
+ cookie_attributes.store('secure', true)
267
+ end
268
+ end
269
+ cookie_attributes
270
+ end
271
+
195
272
  end
196
273
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Multipart
3
5
  class Generator
@@ -15,9 +17,13 @@ module Rack
15
17
 
16
18
  flattened_params.map do |name, file|
17
19
  if file.respond_to?(:original_filename)
18
- ::File.open(file.path, 'rb') do |f|
19
- f.set_encoding(Encoding::BINARY)
20
- content_for_tempfile(f, file, name)
20
+ if file.path
21
+ ::File.open(file.path, 'rb') do |f|
22
+ f.set_encoding(Encoding::BINARY)
23
+ content_for_tempfile(f, file, name)
24
+ end
25
+ else
26
+ content_for_tempfile(file, file, name)
21
27
  end
22
28
  else
23
29
  content_for_other(file, name)
@@ -27,21 +33,18 @@ module Rack
27
33
 
28
34
  private
29
35
  def multipart?
30
- multipart = false
31
-
32
36
  query = lambda { |value|
33
37
  case value
34
38
  when Array
35
- value.each(&query)
39
+ value.any?(&query)
36
40
  when Hash
37
- value.values.each(&query)
41
+ value.values.any?(&query)
38
42
  when Rack::Multipart::UploadedFile
39
- multipart = true
43
+ true
40
44
  end
41
45
  }
42
- @params.values.each(&query)
43
46
 
44
- multipart
47
+ @params.values.any?(&query)
45
48
  end
46
49
 
47
50
  def flattened_params
@@ -70,12 +73,13 @@ module Rack
70
73
  end
71
74
 
72
75
  def content_for_tempfile(io, file, name)
76
+ length = ::File.stat(file.path).size if file.path
77
+ filename = "; filename=\"#{Utils.escape(file.original_filename)}\"" if file.original_filename
73
78
  <<-EOF
74
79
  --#{MULTIPART_BOUNDARY}\r
75
- Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r
80
+ Content-Disposition: form-data; name="#{name}"#{filename}\r
76
81
  Content-Type: #{file.content_type}\r
77
- Content-Length: #{::File.stat(file.path).size}\r
78
- \r
82
+ #{"Content-Length: #{length}\r\n" if length}\r
79
83
  #{io.read}\r
80
84
  EOF
81
85
  end