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/lock.rb CHANGED
@@ -1,5 +1,6 @@
1
- require 'thread'
2
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'body_proxy'
3
4
 
4
5
  module Rack
5
6
  # Rack::Lock locks every request inside a mutex, so that every request
@@ -11,10 +12,8 @@ module Rack
11
12
 
12
13
  def call(env)
13
14
  @mutex.lock
14
- @env = env
15
- @old_rack_multithread = env[RACK_MULTITHREAD]
16
15
  begin
17
- response = @app.call(env.merge!(RACK_MULTITHREAD => false))
16
+ response = @app.call(env)
18
17
  returned = response << BodyProxy.new(response.pop) { unlock }
19
18
  ensure
20
19
  unlock unless returned
@@ -25,7 +24,6 @@ module Rack
25
24
 
26
25
  def unlock
27
26
  @mutex.unlock
28
- @env[RACK_MULTITHREAD] = @old_rack_multithread
29
27
  end
30
28
  end
31
29
  end
data/lib/rack/logger.rb CHANGED
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
5
+ require_relative 'constants'
6
+
3
7
  module Rack
4
8
  # Sets up rack.logger to write to rack.errors stream
5
9
  class Logger
@@ -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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'request'
5
+ require_relative 'utils'
6
+
1
7
  module Rack
2
8
  class MethodOverride
3
9
  HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK]
4
10
 
5
- METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
6
- HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
11
+ METHOD_OVERRIDE_PARAM_KEY = "_method"
12
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE"
7
13
  ALLOWED_METHODS = %w[POST]
8
14
 
9
15
  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
@@ -61,6 +63,7 @@ module Rack
61
63
  ".aif" => "audio/x-aiff",
62
64
  ".aiff" => "audio/x-aiff",
63
65
  ".ami" => "application/vnd.amiga.ami",
66
+ ".apng" => "image/apng",
64
67
  ".appcache" => "text/cache-manifest",
65
68
  ".apr" => "application/vnd.lotus-approach",
66
69
  ".asc" => "application/pgp-signature",
@@ -75,6 +78,7 @@ module Rack
75
78
  ".atx" => "application/vnd.antix.game-component",
76
79
  ".au" => "audio/basic",
77
80
  ".avi" => "video/x-msvideo",
81
+ ".avif" => "image/avif",
78
82
  ".bat" => "application/x-msdownload",
79
83
  ".bcpio" => "application/x-bcpio",
80
84
  ".bdm" => "application/vnd.syncml.dm+wbxml",
@@ -195,6 +199,7 @@ module Rack
195
199
  ".fe_launch" => "application/vnd.denovo.fcselayout-link",
196
200
  ".fg5" => "application/vnd.fujitsu.oasysgp",
197
201
  ".fli" => "video/x-fli",
202
+ ".flif" => "image/flif",
198
203
  ".flo" => "application/vnd.micrografx.flo",
199
204
  ".flv" => "video/x-flv",
200
205
  ".flw" => "application/vnd.kde.kivio",
@@ -235,6 +240,10 @@ module Rack
235
240
  ".h264" => "video/h264",
236
241
  ".hbci" => "application/vnd.hbci",
237
242
  ".hdf" => "application/x-hdf",
243
+ ".heic" => "image/heic",
244
+ ".heics" => "image/heic-sequence",
245
+ ".heif" => "image/heif",
246
+ ".heifs" => "image/heif-sequence",
238
247
  ".hh" => "text/x-c",
239
248
  ".hlp" => "application/winhlp",
240
249
  ".hpgl" => "application/vnd.hp-hpgl",
@@ -306,6 +315,7 @@ module Rack
306
315
  ".lvp" => "audio/vnd.lucent.voice",
307
316
  ".lwp" => "application/vnd.lotus-wordpro",
308
317
  ".m3u" => "audio/x-mpegurl",
318
+ ".m3u8" => "application/x-mpegurl",
309
319
  ".m4a" => "audio/mp4a-latm",
310
320
  ".m4v" => "video/mp4",
311
321
  ".ma" => "application/mathematica",
@@ -343,6 +353,7 @@ module Rack
343
353
  ".mp4s" => "application/mp4",
344
354
  ".mp4v" => "video/mp4",
345
355
  ".mpc" => "application/vnd.mophun.certificate",
356
+ ".mpd" => "application/dash+xml",
346
357
  ".mpeg" => "video/mpeg",
347
358
  ".mpg" => "video/mpeg",
348
359
  ".mpga" => "audio/mpeg",
@@ -542,6 +553,7 @@ module Rack
542
553
  ".spp" => "application/scvp-vp-response",
543
554
  ".spq" => "application/scvp-vp-request",
544
555
  ".src" => "application/x-wais-source",
556
+ ".srt" => "text/srt",
545
557
  ".srx" => "application/sparql-results+xml",
546
558
  ".sse" => "application/vnd.kodak-descriptor",
547
559
  ".ssf" => "application/vnd.epson.ssf",
@@ -576,6 +588,7 @@ module Rack
576
588
  ".tr" => "text/troff",
577
589
  ".tra" => "application/vnd.trueapp",
578
590
  ".trm" => "application/x-msterminal",
591
+ ".ts" => "video/mp2t",
579
592
  ".tsv" => "text/tab-separated-values",
580
593
  ".ttf" => "application/octet-stream",
581
594
  ".twd" => "application/vnd.simtech-mindmapper",
@@ -600,15 +613,18 @@ module Rack
600
613
  ".vrml" => "model/vrml",
601
614
  ".vsd" => "application/vnd.visio",
602
615
  ".vsf" => "application/vnd.vsf",
616
+ ".vtt" => "text/vtt",
603
617
  ".vtu" => "model/vnd.vtu",
604
618
  ".vxml" => "application/voicexml+xml",
605
619
  ".war" => "application/java-archive",
620
+ ".wasm" => "application/wasm",
606
621
  ".wav" => "audio/x-wav",
607
622
  ".wax" => "audio/x-ms-wax",
608
623
  ".wbmp" => "image/vnd.wap.wbmp",
609
624
  ".wbs" => "application/vnd.criticaltools.wbs+xml",
610
625
  ".wbxml" => "application/vnd.wap.wbxml",
611
626
  ".webm" => "video/webm",
627
+ ".webp" => "image/webp",
612
628
  ".wm" => "video/x-ms-wm",
613
629
  ".wma" => "audio/x-ms-wma",
614
630
  ".wmd" => "application/x-ms-wmd",
data/lib/rack/mock.rb CHANGED
@@ -1,196 +1,3 @@
1
- require 'uri'
2
- require 'stringio'
3
- require 'rack'
4
- require 'rack/lint'
5
- require 'rack/utils'
6
- require 'rack/response'
1
+ # frozen_string_literal: true
7
2
 
8
- module Rack
9
- # Rack::MockRequest helps testing your Rack application without
10
- # actually using HTTP.
11
- #
12
- # After performing a request on a URL with get/post/put/patch/delete, it
13
- # returns a MockResponse with useful helper methods for effective
14
- # testing.
15
- #
16
- # You can pass a hash with additional configuration to the
17
- # get/post/put/patch/delete.
18
- # <tt>:input</tt>:: A String or IO-like to be used as rack.input.
19
- # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
20
- # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
21
-
22
- class MockRequest
23
- class FatalWarning < RuntimeError
24
- end
25
-
26
- class FatalWarner
27
- def puts(warning)
28
- raise FatalWarning, warning
29
- end
30
-
31
- def write(warning)
32
- raise FatalWarning, warning
33
- end
34
-
35
- def flush
36
- end
37
-
38
- def string
39
- ""
40
- end
41
- end
42
-
43
- DEFAULT_ENV = {
44
- RACK_VERSION => Rack::VERSION,
45
- RACK_INPUT => StringIO.new,
46
- RACK_ERRORS => StringIO.new,
47
- RACK_MULTITHREAD => true,
48
- RACK_MULTIPROCESS => true,
49
- RACK_RUNONCE => false,
50
- }.freeze
51
-
52
- def initialize(app)
53
- @app = app
54
- end
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))
66
-
67
- if opts[:lint]
68
- app = Rack::Lint.new(@app)
69
- else
70
- app = @app
71
- end
72
-
73
- errors = env[RACK_ERRORS]
74
- status, headers, body = app.call(env)
75
- MockResponse.new(status, headers, body, errors)
76
- ensure
77
- body.close if body.respond_to?(:close)
78
- end
79
-
80
- # For historical reasons, we're pinning to RFC 2396.
81
- # URI::Parser = URI::RFC2396_Parser
82
- def self.parse_uri_rfc2396(uri)
83
- @parser ||= URI::Parser.new
84
- @parser.parse(uri)
85
- end
86
-
87
- # Return the Rack environment used for a request to +uri+.
88
- def self.env_for(uri="", opts={})
89
- uri = parse_uri_rfc2396(uri)
90
- uri.path = "/#{uri.path}" unless uri.path[0] == ?/
91
-
92
- env = DEFAULT_ENV.dup
93
-
94
- env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
95
- env[SERVER_NAME] = (uri.host || "example.org").b
96
- env[SERVER_PORT] = (uri.port ? uri.port.to_s : "80").b
97
- env[QUERY_STRING] = (uri.query.to_s).b
98
- env[PATH_INFO] = ((!uri.path || uri.path.empty?) ? "/" : uri.path).b
99
- env[RACK_URL_SCHEME] = (uri.scheme || "http").b
100
- env[HTTPS] = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
101
-
102
- env[SCRIPT_NAME] = opts[:script_name] || ""
103
-
104
- if opts[:fatal]
105
- env[RACK_ERRORS] = FatalWarner.new
106
- else
107
- env[RACK_ERRORS] = StringIO.new
108
- end
109
-
110
- if params = opts[:params]
111
- if env[REQUEST_METHOD] == GET
112
- params = Utils.parse_nested_query(params) if params.is_a?(String)
113
- params.update(Utils.parse_nested_query(env[QUERY_STRING]))
114
- env[QUERY_STRING] = Utils.build_nested_query(params)
115
- elsif !opts.has_key?(:input)
116
- opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
117
- if params.is_a?(Hash)
118
- if data = Rack::Multipart.build_multipart(params)
119
- opts[:input] = data
120
- opts["CONTENT_LENGTH"] ||= data.length.to_s
121
- opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
122
- else
123
- opts[:input] = Utils.build_nested_query(params)
124
- end
125
- else
126
- opts[:input] = params
127
- end
128
- end
129
- end
130
-
131
- empty_str = String.new
132
- opts[:input] ||= empty_str
133
- if String === opts[:input]
134
- rack_input = StringIO.new(opts[:input])
135
- else
136
- rack_input = opts[:input]
137
- end
138
-
139
- rack_input.set_encoding(Encoding::BINARY)
140
- env[RACK_INPUT] = rack_input
141
-
142
- env["CONTENT_LENGTH"] ||= env[RACK_INPUT].length.to_s
143
-
144
- opts.each { |field, value|
145
- env[field] = value if String === field
146
- }
147
-
148
- env
149
- end
150
- end
151
-
152
- # Rack::MockResponse provides useful helpers for testing your apps.
153
- # Usually, you don't create the MockResponse on your own, but use
154
- # MockRequest.
155
-
156
- class MockResponse < Rack::Response
157
- # Headers
158
- attr_reader :original_headers
159
-
160
- # Errors
161
- attr_accessor :errors
162
-
163
- def initialize(status, headers, body, errors=StringIO.new(""))
164
- @original_headers = headers
165
- @errors = errors.string if errors.respond_to?(:string)
166
-
167
- super(body, status, headers)
168
- end
169
-
170
- def =~(other)
171
- body =~ other
172
- end
173
-
174
- def match(other)
175
- body.match other
176
- end
177
-
178
- def body
179
- # FIXME: apparently users of MockResponse expect the return value of
180
- # MockResponse#body to be a string. However, the real response object
181
- # returns the body as a list.
182
- #
183
- # See spec_showstatus.rb:
184
- #
185
- # should "not replace existing messages" do
186
- # ...
187
- # res.body.should == "foo!"
188
- # end
189
- super.join
190
- end
191
-
192
- def empty?
193
- [201, 204, 304].include? status
194
- end
195
- end
196
- end
3
+ require_relative 'mock_request'
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'stringio'
5
+
6
+ require_relative 'constants'
7
+ require_relative 'mock_response'
8
+
9
+ module Rack
10
+ # Rack::MockRequest helps testing your Rack application without
11
+ # actually using HTTP.
12
+ #
13
+ # After performing a request on a URL with get/post/put/patch/delete, it
14
+ # returns a MockResponse with useful helper methods for effective
15
+ # testing.
16
+ #
17
+ # You can pass a hash with additional configuration to the
18
+ # get/post/put/patch/delete.
19
+ # <tt>:input</tt>:: A String or IO-like to be used as rack.input.
20
+ # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
21
+ # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
22
+
23
+ class MockRequest
24
+ class FatalWarning < RuntimeError
25
+ end
26
+
27
+ class FatalWarner
28
+ def puts(warning)
29
+ raise FatalWarning, warning
30
+ end
31
+
32
+ def write(warning)
33
+ raise FatalWarning, warning
34
+ end
35
+
36
+ def flush
37
+ end
38
+
39
+ def string
40
+ ""
41
+ end
42
+ end
43
+
44
+ DEFAULT_ENV = {
45
+ RACK_INPUT => StringIO.new,
46
+ RACK_ERRORS => StringIO.new,
47
+ }.freeze
48
+
49
+ def initialize(app)
50
+ @app = app
51
+ end
52
+
53
+ # Make a GET request and return a MockResponse. See #request.
54
+ def get(uri, opts = {}) request(GET, uri, opts) end
55
+ # Make a POST request and return a MockResponse. See #request.
56
+ def post(uri, opts = {}) request(POST, uri, opts) end
57
+ # Make a PUT request and return a MockResponse. See #request.
58
+ def put(uri, opts = {}) request(PUT, uri, opts) end
59
+ # Make a PATCH request and return a MockResponse. See #request.
60
+ def patch(uri, opts = {}) request(PATCH, uri, opts) end
61
+ # Make a DELETE request and return a MockResponse. See #request.
62
+ def delete(uri, opts = {}) request(DELETE, uri, opts) end
63
+ # Make a HEAD request and return a MockResponse. See #request.
64
+ def head(uri, opts = {}) request(HEAD, uri, opts) end
65
+ # Make an OPTIONS request and return a MockResponse. See #request.
66
+ def options(uri, opts = {}) request(OPTIONS, uri, opts) end
67
+
68
+ # Make a request using the given request method for the given
69
+ # uri to the rack application and return a MockResponse.
70
+ # Options given are passed to MockRequest.env_for.
71
+ def request(method = GET, uri = "", opts = {})
72
+ env = self.class.env_for(uri, opts.merge(method: method))
73
+
74
+ if opts[:lint]
75
+ app = Rack::Lint.new(@app)
76
+ else
77
+ app = @app
78
+ end
79
+
80
+ errors = env[RACK_ERRORS]
81
+ status, headers, body = app.call(env)
82
+ MockResponse.new(status, headers, body, errors)
83
+ ensure
84
+ body.close if body.respond_to?(:close)
85
+ end
86
+
87
+ # For historical reasons, we're pinning to RFC 2396.
88
+ # URI::Parser = URI::RFC2396_Parser
89
+ def self.parse_uri_rfc2396(uri)
90
+ @parser ||= URI::Parser.new
91
+ @parser.parse(uri)
92
+ end
93
+
94
+ # Return the Rack environment used for a request to +uri+.
95
+ # All options that are strings are added to the returned environment.
96
+ # Options:
97
+ # :fatal :: Whether to raise an exception if request outputs to rack.errors
98
+ # :input :: The rack.input to set
99
+ # :http_version :: The SERVER_PROTOCOL to set
100
+ # :method :: The HTTP request method to use
101
+ # :params :: The params to use
102
+ # :script_name :: The SCRIPT_NAME to set
103
+ def self.env_for(uri = "", opts = {})
104
+ uri = parse_uri_rfc2396(uri)
105
+ uri.path = "/#{uri.path}" unless uri.path[0] == ?/
106
+
107
+ env = DEFAULT_ENV.dup
108
+
109
+ env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
110
+ env[SERVER_NAME] = (uri.host || "example.org").b
111
+ env[SERVER_PORT] = (uri.port ? uri.port.to_s : "80").b
112
+ env[SERVER_PROTOCOL] = opts[:http_version] || 'HTTP/1.1'
113
+ env[QUERY_STRING] = (uri.query.to_s).b
114
+ env[PATH_INFO] = (uri.path).b
115
+ env[RACK_URL_SCHEME] = (uri.scheme || "http").b
116
+ env[HTTPS] = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
117
+
118
+ env[SCRIPT_NAME] = opts[:script_name] || ""
119
+
120
+ if opts[:fatal]
121
+ env[RACK_ERRORS] = FatalWarner.new
122
+ else
123
+ env[RACK_ERRORS] = StringIO.new
124
+ end
125
+
126
+ if params = opts[:params]
127
+ if env[REQUEST_METHOD] == GET
128
+ params = Utils.parse_nested_query(params) if params.is_a?(String)
129
+ params.update(Utils.parse_nested_query(env[QUERY_STRING]))
130
+ env[QUERY_STRING] = Utils.build_nested_query(params)
131
+ elsif !opts.has_key?(:input)
132
+ opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
133
+ if params.is_a?(Hash)
134
+ if data = Rack::Multipart.build_multipart(params)
135
+ opts[:input] = data
136
+ opts["CONTENT_LENGTH"] ||= data.length.to_s
137
+ opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
138
+ else
139
+ opts[:input] = Utils.build_nested_query(params)
140
+ end
141
+ else
142
+ opts[:input] = params
143
+ end
144
+ end
145
+ end
146
+
147
+ opts[:input] ||= String.new
148
+ if String === opts[:input]
149
+ rack_input = StringIO.new(opts[:input])
150
+ else
151
+ rack_input = opts[:input]
152
+ end
153
+
154
+ rack_input.set_encoding(Encoding::BINARY)
155
+ env[RACK_INPUT] = rack_input
156
+
157
+ env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
158
+
159
+ opts.each { |field, value|
160
+ env[field] = value if String === field
161
+ }
162
+
163
+ env
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi/cookie'
4
+ require 'time'
5
+
6
+ require_relative 'response'
7
+
8
+ module Rack
9
+ # Rack::MockResponse provides useful helpers for testing your apps.
10
+ # Usually, you don't create the MockResponse on your own, but use
11
+ # MockRequest.
12
+
13
+ class MockResponse < Rack::Response
14
+ class << self
15
+ alias [] new
16
+ end
17
+
18
+ # Headers
19
+ attr_reader :original_headers, :cookies
20
+
21
+ # Errors
22
+ attr_accessor :errors
23
+
24
+ def initialize(status, headers, body, errors = nil)
25
+ @original_headers = headers
26
+
27
+ if errors
28
+ @errors = errors.string if errors.respond_to?(:string)
29
+ else
30
+ @errors = ""
31
+ end
32
+
33
+ super(body, status, headers)
34
+
35
+ @cookies = parse_cookies_from_header
36
+ buffered_body!
37
+ end
38
+
39
+ def =~(other)
40
+ body =~ other
41
+ end
42
+
43
+ def match(other)
44
+ body.match other
45
+ end
46
+
47
+ def body
48
+ return @buffered_body if defined?(@buffered_body)
49
+
50
+ # FIXME: apparently users of MockResponse expect the return value of
51
+ # MockResponse#body to be a string. However, the real response object
52
+ # returns the body as a list.
53
+ #
54
+ # See spec_showstatus.rb:
55
+ #
56
+ # should "not replace existing messages" do
57
+ # ...
58
+ # res.body.should == "foo!"
59
+ # end
60
+ buffer = @buffered_body = String.new
61
+
62
+ @body.each do |chunk|
63
+ buffer << chunk
64
+ end
65
+
66
+ return buffer
67
+ end
68
+
69
+ def empty?
70
+ [201, 204, 304].include? status
71
+ end
72
+
73
+ def cookie(name)
74
+ cookies.fetch(name, nil)
75
+ end
76
+
77
+ private
78
+
79
+ def parse_cookies_from_header
80
+ cookies = Hash.new
81
+ if headers.has_key? 'set-cookie'
82
+ set_cookie_header = headers.fetch('set-cookie')
83
+ Array(set_cookie_header).each do |header_value|
84
+ header_value.split("\n").each do |cookie|
85
+ cookie_name, cookie_filling = cookie.split('=', 2)
86
+ cookie_attributes = identify_cookie_attributes cookie_filling
87
+ parsed_cookie = CGI::Cookie.new(
88
+ 'name' => cookie_name.strip,
89
+ 'value' => cookie_attributes.fetch('value'),
90
+ 'path' => cookie_attributes.fetch('path', nil),
91
+ 'domain' => cookie_attributes.fetch('domain', nil),
92
+ 'expires' => cookie_attributes.fetch('expires', nil),
93
+ 'secure' => cookie_attributes.fetch('secure', false)
94
+ )
95
+ cookies.store(cookie_name, parsed_cookie)
96
+ end
97
+ end
98
+ end
99
+ cookies
100
+ end
101
+
102
+ def identify_cookie_attributes(cookie_filling)
103
+ cookie_bits = cookie_filling.split(';')
104
+ cookie_attributes = Hash.new
105
+ cookie_attributes.store('value', cookie_bits[0].strip)
106
+ cookie_bits.drop(1).each do |bit|
107
+ if bit.include? '='
108
+ cookie_attribute, attribute_value = bit.split('=', 2)
109
+ cookie_attributes.store(cookie_attribute.strip.downcase, attribute_value.strip)
110
+ end
111
+ if bit.include? 'secure'
112
+ cookie_attributes.store('secure', true)
113
+ end
114
+ end
115
+
116
+ if cookie_attributes.key? 'max-age'
117
+ cookie_attributes.store('expires', Time.now + cookie_attributes['max-age'].to_i)
118
+ elsif cookie_attributes.key? 'expires'
119
+ cookie_attributes.store('expires', Time.httpdate(cookie_attributes['expires']))
120
+ end
121
+
122
+ cookie_attributes
123
+ end
124
+
125
+ end
126
+ end