rack 1.4.7 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (183) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +77 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +122 -456
  5. data/Rakefile +32 -31
  6. data/SPEC +119 -29
  7. data/bin/rackup +1 -0
  8. data/contrib/rack_logo.svg +164 -111
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +4 -2
  11. data/example/protectedlobster.ru +3 -1
  12. data/lib/rack/auth/abstract/handler.rb +7 -5
  13. data/lib/rack/auth/abstract/request.rb +8 -6
  14. data/lib/rack/auth/basic.rb +5 -2
  15. data/lib/rack/auth/digest/md5.rb +10 -8
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +5 -4
  18. data/lib/rack/auth/digest/request.rb +4 -2
  19. data/lib/rack/body_proxy.rb +11 -9
  20. data/lib/rack/builder.rb +63 -20
  21. data/lib/rack/cascade.rb +10 -9
  22. data/lib/rack/chunked.rb +45 -11
  23. data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
  24. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
  25. data/lib/rack/config.rb +7 -0
  26. data/lib/rack/content_length.rb +12 -6
  27. data/lib/rack/content_type.rb +4 -2
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +73 -42
  30. data/lib/rack/directory.rb +77 -56
  31. data/lib/rack/etag.rb +25 -13
  32. data/lib/rack/events.rb +156 -0
  33. data/lib/rack/file.rb +4 -143
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler/cgi.rb +18 -17
  36. data/lib/rack/handler/fastcgi.rb +21 -17
  37. data/lib/rack/handler/lsws.rb +14 -12
  38. data/lib/rack/handler/scgi.rb +27 -21
  39. data/lib/rack/handler/thin.rb +19 -5
  40. data/lib/rack/handler/webrick.rb +66 -24
  41. data/lib/rack/handler.rb +29 -19
  42. data/lib/rack/head.rb +21 -14
  43. data/lib/rack/lint.rb +259 -65
  44. data/lib/rack/lobster.rb +17 -10
  45. data/lib/rack/lock.rb +19 -10
  46. data/lib/rack/logger.rb +4 -2
  47. data/lib/rack/media_type.rb +43 -0
  48. data/lib/rack/method_override.rb +52 -0
  49. data/lib/rack/mime.rb +43 -6
  50. data/lib/rack/mock.rb +109 -44
  51. data/lib/rack/multipart/generator.rb +11 -12
  52. data/lib/rack/multipart/parser.rb +302 -115
  53. data/lib/rack/multipart/uploaded_file.rb +4 -3
  54. data/lib/rack/multipart.rb +40 -9
  55. data/lib/rack/null_logger.rb +39 -0
  56. data/lib/rack/query_parser.rb +218 -0
  57. data/lib/rack/recursive.rb +14 -11
  58. data/lib/rack/reloader.rb +12 -5
  59. data/lib/rack/request.rb +484 -270
  60. data/lib/rack/response.rb +196 -77
  61. data/lib/rack/rewindable_input.rb +5 -14
  62. data/lib/rack/runtime.rb +13 -6
  63. data/lib/rack/sendfile.rb +44 -20
  64. data/lib/rack/server.rb +175 -61
  65. data/lib/rack/session/abstract/id.rb +276 -133
  66. data/lib/rack/session/cookie.rb +75 -40
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +24 -18
  69. data/lib/rack/show_exceptions.rb +392 -0
  70. data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
  71. data/lib/rack/static.rb +65 -38
  72. data/lib/rack/tempfile_reaper.rb +24 -0
  73. data/lib/rack/urlmap.rb +40 -15
  74. data/lib/rack/utils.rb +316 -285
  75. data/lib/rack.rb +78 -23
  76. data/rack.gemspec +26 -19
  77. metadata +44 -209
  78. data/KNOWN-ISSUES +0 -30
  79. data/lib/rack/backports/uri/common_18.rb +0 -56
  80. data/lib/rack/backports/uri/common_192.rb +0 -52
  81. data/lib/rack/backports/uri/common_193.rb +0 -29
  82. data/lib/rack/handler/evented_mongrel.rb +0 -8
  83. data/lib/rack/handler/mongrel.rb +0 -100
  84. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  85. data/lib/rack/methodoverride.rb +0 -33
  86. data/lib/rack/nulllogger.rb +0 -18
  87. data/lib/rack/showexceptions.rb +0 -378
  88. data/test/builder/anything.rb +0 -5
  89. data/test/builder/comment.ru +0 -4
  90. data/test/builder/end.ru +0 -5
  91. data/test/builder/line.ru +0 -1
  92. data/test/builder/options.ru +0 -2
  93. data/test/cgi/assets/folder/test.js +0 -1
  94. data/test/cgi/assets/fonts/font.eot +0 -1
  95. data/test/cgi/assets/images/image.png +0 -1
  96. data/test/cgi/assets/index.html +0 -1
  97. data/test/cgi/assets/javascripts/app.js +0 -1
  98. data/test/cgi/assets/stylesheets/app.css +0 -1
  99. data/test/cgi/lighttpd.conf +0 -26
  100. data/test/cgi/lighttpd.errors +0 -1
  101. data/test/cgi/rackup_stub.rb +0 -6
  102. data/test/cgi/sample_rackup.ru +0 -5
  103. data/test/cgi/test +0 -9
  104. data/test/cgi/test+directory/test+file +0 -1
  105. data/test/cgi/test.fcgi +0 -8
  106. data/test/cgi/test.ru +0 -5
  107. data/test/gemloader.rb +0 -10
  108. data/test/multipart/bad_robots +0 -259
  109. data/test/multipart/binary +0 -0
  110. data/test/multipart/content_type_and_no_filename +0 -6
  111. data/test/multipart/empty +0 -10
  112. data/test/multipart/fail_16384_nofile +0 -814
  113. data/test/multipart/file1.txt +0 -1
  114. data/test/multipart/filename_and_modification_param +0 -7
  115. data/test/multipart/filename_with_escaped_quotes +0 -6
  116. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  117. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages +0 -6
  119. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  120. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  121. data/test/multipart/filename_with_unescaped_quotes +0 -6
  122. data/test/multipart/ie +0 -6
  123. data/test/multipart/mixed_files +0 -21
  124. data/test/multipart/nested +0 -10
  125. data/test/multipart/none +0 -9
  126. data/test/multipart/semicolon +0 -6
  127. data/test/multipart/text +0 -15
  128. data/test/multipart/three_files_three_fields +0 -31
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth.rb +0 -57
  133. data/test/spec_auth_basic.rb +0 -81
  134. data/test/spec_auth_digest.rb +0 -259
  135. data/test/spec_body_proxy.rb +0 -69
  136. data/test/spec_builder.rb +0 -207
  137. data/test/spec_cascade.rb +0 -61
  138. data/test/spec_cgi.rb +0 -102
  139. data/test/spec_chunked.rb +0 -87
  140. data/test/spec_commonlogger.rb +0 -57
  141. data/test/spec_conditionalget.rb +0 -102
  142. data/test/spec_config.rb +0 -22
  143. data/test/spec_content_length.rb +0 -86
  144. data/test/spec_content_type.rb +0 -45
  145. data/test/spec_deflater.rb +0 -187
  146. data/test/spec_directory.rb +0 -88
  147. data/test/spec_etag.rb +0 -98
  148. data/test/spec_fastcgi.rb +0 -107
  149. data/test/spec_file.rb +0 -200
  150. data/test/spec_handler.rb +0 -59
  151. data/test/spec_head.rb +0 -48
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -58
  154. data/test/spec_lock.rb +0 -167
  155. data/test/spec_logger.rb +0 -23
  156. data/test/spec_methodoverride.rb +0 -72
  157. data/test/spec_mock.rb +0 -269
  158. data/test/spec_mongrel.rb +0 -182
  159. data/test/spec_multipart.rb +0 -479
  160. data/test/spec_nulllogger.rb +0 -23
  161. data/test/spec_recursive.rb +0 -72
  162. data/test/spec_request.rb +0 -955
  163. data/test/spec_response.rb +0 -313
  164. data/test/spec_rewindable_input.rb +0 -118
  165. data/test/spec_runtime.rb +0 -49
  166. data/test/spec_sendfile.rb +0 -90
  167. data/test/spec_server.rb +0 -121
  168. data/test/spec_session_abstract_id.rb +0 -43
  169. data/test/spec_session_cookie.rb +0 -361
  170. data/test/spec_session_memcache.rb +0 -321
  171. data/test/spec_session_pool.rb +0 -209
  172. data/test/spec_showexceptions.rb +0 -92
  173. data/test/spec_showstatus.rb +0 -84
  174. data/test/spec_static.rb +0 -145
  175. data/test/spec_thin.rb +0 -86
  176. data/test/spec_urlmap.rb +0 -213
  177. data/test/spec_utils.rb +0 -554
  178. data/test/spec_webrick.rb +0 -143
  179. data/test/static/another/index.html +0 -1
  180. data/test/static/index.html +0 -1
  181. data/test/testrequest.rb +0 -78
  182. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  183. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ class MethodOverride
5
+ HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK]
6
+
7
+ METHOD_OVERRIDE_PARAM_KEY = "_method"
8
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE"
9
+ ALLOWED_METHODS = %w[POST]
10
+
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ if allowed_methods.include?(env[REQUEST_METHOD])
17
+ method = method_override(env)
18
+ if HTTP_METHODS.include?(method)
19
+ env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] = env[REQUEST_METHOD]
20
+ env[REQUEST_METHOD] = method
21
+ end
22
+ end
23
+
24
+ @app.call(env)
25
+ end
26
+
27
+ def method_override(env)
28
+ req = Request.new(env)
29
+ method = method_override_param(req) ||
30
+ env[HTTP_METHOD_OVERRIDE_HEADER]
31
+ begin
32
+ method.to_s.upcase
33
+ rescue ArgumentError
34
+ env[RACK_ERRORS].puts "Invalid string for method"
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def allowed_methods
41
+ ALLOWED_METHODS
42
+ end
43
+
44
+ def method_override_param(req)
45
+ req.POST[METHOD_OVERRIDE_PARAM_KEY]
46
+ rescue Utils::InvalidParameterError, Utils::ParameterTypeError
47
+ req.get_header(RACK_ERRORS).puts "Invalid or incomplete POST params"
48
+ rescue EOFError
49
+ req.get_header(RACK_ERRORS).puts "Bad request content body"
50
+ end
51
+ end
52
+ end
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,11 +15,26 @@ 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
20
22
 
23
+ # Returns true if the given value is a mime match for the given mime match
24
+ # specification, false otherwise.
25
+ #
26
+ # Rack::Mime.match?('text/html', 'text/*') => true
27
+ # Rack::Mime.match?('text/plain', '*') => true
28
+ # Rack::Mime.match?('text/html', 'application/json') => false
29
+
30
+ def match?(value, matcher)
31
+ v1, v2 = value.split('/', 2)
32
+ m1, m2 = matcher.split('/', 2)
33
+
34
+ (m1 == '*' || v1 == m1) && (m2.nil? || m2 == '*' || m2 == v2)
35
+ end
36
+ module_function :match?
37
+
21
38
  # List of most common mime-types, selected various sources
22
39
  # according to their usefulness in a webserving scope for Ruby
23
40
  # users.
@@ -30,11 +47,6 @@ module Rack
30
47
  #
31
48
  # N.B. On Ubuntu the mime.types file does not include the leading period, so
32
49
  # users may need to modify the data before merging into the hash.
33
- #
34
- # To add the list mongrel provides, use:
35
- #
36
- # require 'mongrel/handlers'
37
- # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
38
50
 
39
51
  MIME_TYPES = {
40
52
  ".123" => "application/vnd.lotus-1-2-3",
@@ -139,8 +151,11 @@ module Rack
139
151
  ".dmg" => "application/octet-stream",
140
152
  ".dna" => "application/vnd.dna",
141
153
  ".doc" => "application/msword",
154
+ ".docm" => "application/vnd.ms-word.document.macroEnabled.12",
142
155
  ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
143
156
  ".dot" => "application/msword",
157
+ ".dotm" => "application/vnd.ms-word.template.macroEnabled.12",
158
+ ".dotx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
144
159
  ".dp" => "application/vnd.osgi.dp",
145
160
  ".dpg" => "application/vnd.dpgraph",
146
161
  ".dsc" => "text/prs.lines.tag",
@@ -293,6 +308,7 @@ module Rack
293
308
  ".lvp" => "audio/vnd.lucent.voice",
294
309
  ".lwp" => "application/vnd.lotus-wordpro",
295
310
  ".m3u" => "audio/x-mpegurl",
311
+ ".m3u8" => "application/x-mpegurl",
296
312
  ".m4a" => "audio/mp4a-latm",
297
313
  ".m4v" => "video/mp4",
298
314
  ".ma" => "application/mathematica",
@@ -330,6 +346,7 @@ module Rack
330
346
  ".mp4s" => "application/mp4",
331
347
  ".mp4v" => "video/mp4",
332
348
  ".mpc" => "application/vnd.mophun.certificate",
349
+ ".mpd" => "application/dash+xml",
333
350
  ".mpeg" => "video/mpeg",
334
351
  ".mpg" => "video/mpeg",
335
352
  ".mpga" => "audio/mpeg",
@@ -429,10 +446,19 @@ module Rack
429
446
  ".pnm" => "image/x-portable-anymap",
430
447
  ".pntg" => "image/x-macpaint",
431
448
  ".portpkg" => "application/vnd.macports.portpkg",
449
+ ".pot" => "application/vnd.ms-powerpoint",
450
+ ".potm" => "application/vnd.ms-powerpoint.template.macroEnabled.12",
451
+ ".potx" => "application/vnd.openxmlformats-officedocument.presentationml.template",
452
+ ".ppa" => "application/vnd.ms-powerpoint",
453
+ ".ppam" => "application/vnd.ms-powerpoint.addin.macroEnabled.12",
432
454
  ".ppd" => "application/vnd.cups-ppd",
433
455
  ".ppm" => "image/x-portable-pixmap",
434
456
  ".pps" => "application/vnd.ms-powerpoint",
457
+ ".ppsm" => "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
458
+ ".ppsx" => "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
435
459
  ".ppt" => "application/vnd.ms-powerpoint",
460
+ ".pptm" => "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
461
+ ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
436
462
  ".prc" => "application/vnd.palm",
437
463
  ".pre" => "application/vnd.lotus-freelance",
438
464
  ".prf" => "application/pics-rules",
@@ -520,6 +546,7 @@ module Rack
520
546
  ".spp" => "application/scvp-vp-response",
521
547
  ".spq" => "application/scvp-vp-request",
522
548
  ".src" => "application/x-wais-source",
549
+ ".srt" => "text/srt",
523
550
  ".srx" => "application/sparql-results+xml",
524
551
  ".sse" => "application/vnd.kodak-descriptor",
525
552
  ".ssf" => "application/vnd.epson.ssf",
@@ -554,6 +581,7 @@ module Rack
554
581
  ".tr" => "text/troff",
555
582
  ".tra" => "application/vnd.trueapp",
556
583
  ".trm" => "application/x-msterminal",
584
+ ".ts" => "video/mp2t",
557
585
  ".tsv" => "text/tab-separated-values",
558
586
  ".ttf" => "application/octet-stream",
559
587
  ".twd" => "application/vnd.simtech-mindmapper",
@@ -578,9 +606,11 @@ module Rack
578
606
  ".vrml" => "model/vrml",
579
607
  ".vsd" => "application/vnd.visio",
580
608
  ".vsf" => "application/vnd.vsf",
609
+ ".vtt" => "text/vtt",
581
610
  ".vtu" => "model/vnd.vtu",
582
611
  ".vxml" => "application/voicexml+xml",
583
612
  ".war" => "application/java-archive",
613
+ ".wasm" => "application/wasm",
584
614
  ".wav" => "audio/x-wav",
585
615
  ".wax" => "audio/x-ms-wax",
586
616
  ".wbmp" => "image/vnd.wap.wbmp",
@@ -599,6 +629,7 @@ module Rack
599
629
  ".wmx" => "video/x-ms-wmx",
600
630
  ".wmz" => "application/x-ms-wmz",
601
631
  ".woff" => "application/font-woff",
632
+ ".woff2" => "application/font-woff2",
602
633
  ".wpd" => "application/vnd.wordperfect",
603
634
  ".wpl" => "application/vnd.ms-wpl",
604
635
  ".wps" => "application/vnd.ms-works",
@@ -622,8 +653,14 @@ module Rack
622
653
  ".xfdl" => "application/vnd.xfdl",
623
654
  ".xhtml" => "application/xhtml+xml",
624
655
  ".xif" => "image/vnd.xiff",
656
+ ".xla" => "application/vnd.ms-excel",
657
+ ".xlam" => "application/vnd.ms-excel.addin.macroEnabled.12",
625
658
  ".xls" => "application/vnd.ms-excel",
659
+ ".xlsb" => "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
626
660
  ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
661
+ ".xlsm" => "application/vnd.ms-excel.sheet.macroEnabled.12",
662
+ ".xlt" => "application/vnd.ms-excel",
663
+ ".xltx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
627
664
  ".xml" => "application/xml",
628
665
  ".xo" => "application/vnd.olpc-sugar",
629
666
  ".xop" => "application/xop+xml",
data/lib/rack/mock.rb CHANGED
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'stringio'
3
5
  require 'rack'
4
6
  require 'rack/lint'
5
7
  require 'rack/utils'
6
8
  require 'rack/response'
9
+ require 'cgi/cookie'
7
10
 
8
11
  module Rack
9
12
  # Rack::MockRequest helps testing your Rack application without
@@ -41,27 +44,28 @@ module Rack
41
44
  end
42
45
 
43
46
  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.run_once" => false,
50
- }
47
+ RACK_VERSION => Rack::VERSION,
48
+ RACK_INPUT => StringIO.new,
49
+ RACK_ERRORS => StringIO.new,
50
+ RACK_MULTITHREAD => true,
51
+ RACK_MULTIPROCESS => true,
52
+ RACK_RUNONCE => false,
53
+ }.freeze
51
54
 
52
55
  def initialize(app)
53
56
  @app = app
54
57
  end
55
58
 
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
59
+ def get(uri, opts = {}) request(GET, uri, opts) end
60
+ def post(uri, opts = {}) request(POST, uri, opts) end
61
+ def put(uri, opts = {}) request(PUT, uri, opts) end
62
+ def patch(uri, opts = {}) request(PATCH, uri, opts) end
63
+ def delete(uri, opts = {}) request(DELETE, uri, opts) end
64
+ def head(uri, opts = {}) request(HEAD, uri, opts) end
65
+ def options(uri, opts = {}) request(OPTIONS, uri, opts) end
62
66
 
63
- def request(method="GET", uri="", opts={})
64
- env = self.class.env_for(uri, opts.merge(:method => method))
67
+ def request(method = GET, uri = "", opts = {})
68
+ env = self.class.env_for(uri, opts.merge(method: method))
65
69
 
66
70
  if opts[:lint]
67
71
  app = Rack::Lint.new(@app)
@@ -69,48 +73,55 @@ module Rack
69
73
  app = @app
70
74
  end
71
75
 
72
- errors = env["rack.errors"]
73
- status, headers, body = app.call(env)
76
+ errors = env[RACK_ERRORS]
77
+ status, headers, body = app.call(env)
74
78
  MockResponse.new(status, headers, body, errors)
75
79
  ensure
76
80
  body.close if body.respond_to?(:close)
77
81
  end
78
82
 
83
+ # For historical reasons, we're pinning to RFC 2396.
84
+ # URI::Parser = URI::RFC2396_Parser
85
+ def self.parse_uri_rfc2396(uri)
86
+ @parser ||= URI::Parser.new
87
+ @parser.parse(uri)
88
+ end
89
+
79
90
  # Return the Rack environment used for a request to +uri+.
80
- def self.env_for(uri="", opts={})
81
- uri = URI(uri)
91
+ def self.env_for(uri = "", opts = {})
92
+ uri = parse_uri_rfc2396(uri)
82
93
  uri.path = "/#{uri.path}" unless uri.path[0] == ?/
83
94
 
84
95
  env = DEFAULT_ENV.dup
85
96
 
86
- env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
87
- env["SERVER_NAME"] = uri.host || "example.org"
88
- env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
89
- env["QUERY_STRING"] = uri.query.to_s
90
- env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
91
- env["rack.url_scheme"] = uri.scheme || "http"
92
- env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
97
+ env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
98
+ env[SERVER_NAME] = (uri.host || "example.org").b
99
+ env[SERVER_PORT] = (uri.port ? uri.port.to_s : "80").b
100
+ env[QUERY_STRING] = (uri.query.to_s).b
101
+ env[PATH_INFO] = ((!uri.path || uri.path.empty?) ? "/" : uri.path).b
102
+ env[RACK_URL_SCHEME] = (uri.scheme || "http").b
103
+ env[HTTPS] = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
93
104
 
94
- env["SCRIPT_NAME"] = opts[:script_name] || ""
105
+ env[SCRIPT_NAME] = opts[:script_name] || ""
95
106
 
96
107
  if opts[:fatal]
97
- env["rack.errors"] = FatalWarner.new
108
+ env[RACK_ERRORS] = FatalWarner.new
98
109
  else
99
- env["rack.errors"] = StringIO.new
110
+ env[RACK_ERRORS] = StringIO.new
100
111
  end
101
112
 
102
113
  if params = opts[:params]
103
- if env["REQUEST_METHOD"] == "GET"
114
+ if env[REQUEST_METHOD] == GET
104
115
  params = Utils.parse_nested_query(params) if params.is_a?(String)
105
- params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
106
- env["QUERY_STRING"] = Utils.build_nested_query(params)
116
+ params.update(Utils.parse_nested_query(env[QUERY_STRING]))
117
+ env[QUERY_STRING] = Utils.build_nested_query(params)
107
118
  elsif !opts.has_key?(:input)
108
119
  opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
109
120
  if params.is_a?(Hash)
110
- if data = Utils::Multipart.build_multipart(params)
121
+ if data = Rack::Multipart.build_multipart(params)
111
122
  opts[:input] = data
112
123
  opts["CONTENT_LENGTH"] ||= data.length.to_s
113
- opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
124
+ opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
114
125
  else
115
126
  opts[:input] = Utils.build_nested_query(params)
116
127
  end
@@ -120,8 +131,7 @@ module Rack
120
131
  end
121
132
  end
122
133
 
123
- empty_str = ""
124
- empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding
134
+ empty_str = String.new
125
135
  opts[:input] ||= empty_str
126
136
  if String === opts[:input]
127
137
  rack_input = StringIO.new(opts[:input])
@@ -129,10 +139,10 @@ module Rack
129
139
  rack_input = opts[:input]
130
140
  end
131
141
 
132
- rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
133
- env['rack.input'] = rack_input
142
+ rack_input.set_encoding(Encoding::BINARY)
143
+ env[RACK_INPUT] = rack_input
134
144
 
135
- env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
145
+ env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
136
146
 
137
147
  opts.each { |field, value|
138
148
  env[field] = value if String === field
@@ -148,17 +158,19 @@ module Rack
148
158
 
149
159
  class MockResponse < Rack::Response
150
160
  # Headers
151
- attr_reader :original_headers
161
+ attr_reader :original_headers, :cookies
152
162
 
153
163
  # Errors
154
164
  attr_accessor :errors
155
165
 
156
- def initialize(status, headers, body, errors=StringIO.new(""))
166
+ def initialize(status, headers, body, errors = StringIO.new(""))
157
167
  @original_headers = headers
158
168
  @errors = errors.string if errors.respond_to?(:string)
159
- @body_string = nil
169
+ @cookies = parse_cookies_from_header
160
170
 
161
171
  super(body, status, headers)
172
+
173
+ buffered_body!
162
174
  end
163
175
 
164
176
  def =~(other)
@@ -180,11 +192,64 @@ module Rack
180
192
  # ...
181
193
  # res.body.should == "foo!"
182
194
  # end
183
- super.join
195
+ buffer = String.new
196
+
197
+ super.each do |chunk|
198
+ buffer << chunk
199
+ end
200
+
201
+ return buffer
184
202
  end
185
203
 
186
204
  def empty?
187
- [201, 204, 205, 304].include? status
205
+ [201, 204, 304].include? status
206
+ end
207
+
208
+ def cookie(name)
209
+ cookies.fetch(name, nil)
210
+ end
211
+
212
+ private
213
+
214
+ def parse_cookies_from_header
215
+ cookies = Hash.new
216
+ if original_headers.has_key? 'Set-Cookie'
217
+ set_cookie_header = original_headers.fetch('Set-Cookie')
218
+ set_cookie_header.split("\n").each do |cookie|
219
+ cookie_name, cookie_filling = cookie.split('=', 2)
220
+ cookie_attributes = identify_cookie_attributes cookie_filling
221
+ parsed_cookie = CGI::Cookie.new(
222
+ 'name' => cookie_name.strip,
223
+ 'value' => cookie_attributes.fetch('value'),
224
+ 'path' => cookie_attributes.fetch('path', nil),
225
+ 'domain' => cookie_attributes.fetch('domain', nil),
226
+ 'expires' => cookie_attributes.fetch('expires', nil),
227
+ 'secure' => cookie_attributes.fetch('secure', false)
228
+ )
229
+ cookies.store(cookie_name, parsed_cookie)
230
+ end
231
+ end
232
+ cookies
233
+ end
234
+
235
+ def identify_cookie_attributes(cookie_filling)
236
+ cookie_bits = cookie_filling.split(';')
237
+ cookie_attributes = Hash.new
238
+ cookie_attributes.store('value', cookie_bits[0].strip)
239
+ cookie_bits.each do |bit|
240
+ if bit.include? '='
241
+ cookie_attribute, attribute_value = bit.split('=')
242
+ cookie_attributes.store(cookie_attribute.strip, attribute_value.strip)
243
+ if cookie_attribute.include? 'max-age'
244
+ cookie_attributes.store('expires', Time.now + attribute_value.strip.to_i)
245
+ end
246
+ end
247
+ if bit.include? 'secure'
248
+ cookie_attributes.store('secure', true)
249
+ end
250
+ end
251
+ cookie_attributes
188
252
  end
253
+
189
254
  end
190
255
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Multipart
3
5
  class Generator
@@ -11,37 +13,34 @@ module Rack
11
13
 
12
14
  def dump
13
15
  return nil if @first && !multipart?
14
- return flattened_params if !@first
16
+ return flattened_params unless @first
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) if f.respond_to?(:set_encoding)
20
+ ::File.open(file.path, 'rb') do |f|
21
+ f.set_encoding(Encoding::BINARY)
20
22
  content_for_tempfile(f, file, name)
21
23
  end
22
24
  else
23
25
  content_for_other(file, name)
24
26
  end
25
- end.join + "--#{MULTIPART_BOUNDARY}--\r"
27
+ end.join << "--#{MULTIPART_BOUNDARY}--\r"
26
28
  end
27
29
 
28
30
  private
29
31
  def multipart?
30
- multipart = false
31
-
32
32
  query = lambda { |value|
33
33
  case value
34
34
  when Array
35
- value.each(&query)
35
+ value.any?(&query)
36
36
  when Hash
37
- value.values.each(&query)
37
+ value.values.any?(&query)
38
38
  when Rack::Multipart::UploadedFile
39
- multipart = true
39
+ true
40
40
  end
41
41
  }
42
- @params.values.each(&query)
43
42
 
44
- multipart
43
+ @params.values.any?(&query)
45
44
  end
46
45
 
47
46
  def flattened_params
@@ -90,4 +89,4 @@ EOF
90
89
  end
91
90
  end
92
91
  end
93
- end
92
+ end