rack 1.6.11 → 2.0.7

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 (141) hide show
  1. checksums.yaml +5 -5
  2. data/COPYING +1 -1
  3. data/HISTORY.md +138 -8
  4. data/README.rdoc +18 -28
  5. data/Rakefile +6 -14
  6. data/SPEC +10 -11
  7. data/contrib/rack_logo.svg +164 -111
  8. data/example/protectedlobster.rb +1 -1
  9. data/example/protectedlobster.ru +1 -1
  10. data/lib/rack.rb +70 -21
  11. data/lib/rack/auth/abstract/request.rb +5 -1
  12. data/lib/rack/auth/digest/params.rb +2 -3
  13. data/lib/rack/auth/digest/request.rb +1 -1
  14. data/lib/rack/body_proxy.rb +14 -9
  15. data/lib/rack/builder.rb +3 -3
  16. data/lib/rack/chunked.rb +5 -5
  17. data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
  18. data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
  19. data/lib/rack/content_length.rb +2 -2
  20. data/lib/rack/deflater.rb +4 -39
  21. data/lib/rack/directory.rb +66 -54
  22. data/lib/rack/etag.rb +5 -4
  23. data/lib/rack/events.rb +154 -0
  24. data/lib/rack/file.rb +64 -40
  25. data/lib/rack/handler.rb +3 -25
  26. data/lib/rack/handler/cgi.rb +15 -16
  27. data/lib/rack/handler/fastcgi.rb +13 -14
  28. data/lib/rack/handler/lsws.rb +11 -11
  29. data/lib/rack/handler/scgi.rb +15 -15
  30. data/lib/rack/handler/thin.rb +3 -0
  31. data/lib/rack/handler/webrick.rb +24 -26
  32. data/lib/rack/head.rb +15 -17
  33. data/lib/rack/lint.rb +40 -40
  34. data/lib/rack/lobster.rb +1 -1
  35. data/lib/rack/lock.rb +15 -10
  36. data/lib/rack/logger.rb +2 -2
  37. data/lib/rack/media_type.rb +38 -0
  38. data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
  39. data/lib/rack/mime.rb +18 -5
  40. data/lib/rack/mock.rb +36 -54
  41. data/lib/rack/multipart.rb +35 -6
  42. data/lib/rack/multipart/generator.rb +5 -5
  43. data/lib/rack/multipart/parser.rb +270 -157
  44. data/lib/rack/multipart/uploaded_file.rb +1 -2
  45. data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
  46. data/lib/rack/query_parser.rb +192 -0
  47. data/lib/rack/recursive.rb +8 -8
  48. data/lib/rack/request.rb +394 -305
  49. data/lib/rack/response.rb +130 -57
  50. data/lib/rack/rewindable_input.rb +1 -12
  51. data/lib/rack/runtime.rb +10 -18
  52. data/lib/rack/sendfile.rb +5 -7
  53. data/lib/rack/server.rb +30 -23
  54. data/lib/rack/session/abstract/id.rb +121 -75
  55. data/lib/rack/session/cookie.rb +25 -18
  56. data/lib/rack/session/memcache.rb +2 -2
  57. data/lib/rack/session/pool.rb +9 -9
  58. data/lib/rack/show_exceptions.rb +386 -0
  59. data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
  60. data/lib/rack/static.rb +30 -5
  61. data/lib/rack/tempfile_reaper.rb +2 -2
  62. data/lib/rack/urlmap.rb +15 -14
  63. data/lib/rack/utils.rb +136 -211
  64. data/rack.gemspec +10 -9
  65. data/test/builder/an_underscore_app.rb +5 -0
  66. data/test/builder/options.ru +1 -1
  67. data/test/cgi/test.fcgi +1 -0
  68. data/test/cgi/test.gz +0 -0
  69. data/test/helper.rb +34 -0
  70. data/test/multipart/filename_with_encoded_words +7 -0
  71. data/test/multipart/filename_with_single_quote +7 -0
  72. data/test/multipart/quoted +15 -0
  73. data/test/multipart/rack-logo.png +0 -0
  74. data/test/multipart/unity3d_wwwform +11 -0
  75. data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
  76. data/test/spec_auth_basic.rb +27 -19
  77. data/test/spec_auth_digest.rb +47 -46
  78. data/test/spec_body_proxy.rb +27 -27
  79. data/test/spec_builder.rb +51 -41
  80. data/test/spec_cascade.rb +24 -22
  81. data/test/spec_cgi.rb +49 -67
  82. data/test/spec_chunked.rb +37 -35
  83. data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
  84. data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
  85. data/test/spec_config.rb +3 -2
  86. data/test/spec_content_length.rb +18 -17
  87. data/test/spec_content_type.rb +13 -12
  88. data/test/spec_deflater.rb +85 -49
  89. data/test/spec_directory.rb +87 -27
  90. data/test/spec_etag.rb +32 -31
  91. data/test/spec_events.rb +133 -0
  92. data/test/spec_fastcgi.rb +50 -72
  93. data/test/spec_file.rb +120 -77
  94. data/test/spec_handler.rb +19 -34
  95. data/test/spec_head.rb +15 -14
  96. data/test/spec_lint.rb +164 -199
  97. data/test/spec_lobster.rb +24 -23
  98. data/test/spec_lock.rb +79 -39
  99. data/test/spec_logger.rb +4 -3
  100. data/test/spec_media_type.rb +42 -0
  101. data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
  102. data/test/spec_mime.rb +19 -19
  103. data/test/spec_mock.rb +206 -144
  104. data/test/spec_multipart.rb +322 -200
  105. data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
  106. data/test/spec_recursive.rb +17 -14
  107. data/test/spec_request.rb +780 -605
  108. data/test/spec_response.rb +215 -112
  109. data/test/spec_rewindable_input.rb +50 -40
  110. data/test/spec_runtime.rb +11 -10
  111. data/test/spec_sendfile.rb +30 -35
  112. data/test/spec_server.rb +78 -52
  113. data/test/spec_session_abstract_id.rb +11 -33
  114. data/test/spec_session_abstract_session_hash.rb +45 -0
  115. data/test/spec_session_cookie.rb +99 -67
  116. data/test/spec_session_memcache.rb +60 -61
  117. data/test/spec_session_pool.rb +45 -44
  118. data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +22 -27
  119. data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
  120. data/test/spec_static.rb +71 -32
  121. data/test/spec_tempfile_reaper.rb +11 -10
  122. data/test/spec_thin.rb +55 -50
  123. data/test/spec_urlmap.rb +79 -78
  124. data/test/spec_utils.rb +441 -346
  125. data/test/spec_version.rb +2 -8
  126. data/test/spec_webrick.rb +93 -71
  127. data/test/static/foo.html +1 -0
  128. data/test/testrequest.rb +1 -1
  129. data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
  130. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
  131. metadata +92 -71
  132. data/KNOWN-ISSUES +0 -44
  133. data/lib/rack/backports/uri/common_18.rb +0 -56
  134. data/lib/rack/backports/uri/common_192.rb +0 -52
  135. data/lib/rack/backports/uri/common_193.rb +0 -29
  136. data/lib/rack/handler/evented_mongrel.rb +0 -8
  137. data/lib/rack/handler/mongrel.rb +0 -106
  138. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  139. data/lib/rack/showexceptions.rb +0 -387
  140. data/lib/rack/utils/okjson.rb +0 -600
  141. data/test/spec_mongrel.rb +0 -182
@@ -63,7 +63,7 @@ end
63
63
 
64
64
  if $0 == __FILE__
65
65
  require 'rack'
66
- require 'rack/showexceptions'
66
+ require 'rack/show_exceptions'
67
67
  Rack::Server.start(
68
68
  :app => Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), :Port => 9292
69
69
  )
@@ -5,22 +5,27 @@ module Rack
5
5
  # Rack::Lock locks every request inside a mutex, so that every request
6
6
  # will effectively be executed synchronously.
7
7
  class Lock
8
- FLAG = 'rack.multithread'.freeze
9
-
10
8
  def initialize(app, mutex = Mutex.new)
11
9
  @app, @mutex = app, mutex
12
10
  end
13
11
 
14
12
  def call(env)
15
- old, env[FLAG] = env[FLAG], false
16
13
  @mutex.lock
17
- response = @app.call(env)
18
- body = BodyProxy.new(response[2]) { @mutex.unlock }
19
- response[2] = body
20
- response
21
- ensure
22
- @mutex.unlock unless body
23
- env[FLAG] = old
14
+ @env = env
15
+ @old_rack_multithread = env[RACK_MULTITHREAD]
16
+ begin
17
+ response = @app.call(env.merge!(RACK_MULTITHREAD => false))
18
+ returned = response << BodyProxy.new(response.pop) { unlock }
19
+ ensure
20
+ unlock unless returned
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def unlock
27
+ @mutex.unlock
28
+ @env[RACK_MULTITHREAD] = @old_rack_multithread
24
29
  end
25
30
  end
26
31
  end
@@ -8,10 +8,10 @@ module Rack
8
8
  end
9
9
 
10
10
  def call(env)
11
- logger = ::Logger.new(env['rack.errors'])
11
+ logger = ::Logger.new(env[RACK_ERRORS])
12
12
  logger.level = @level
13
13
 
14
- env['rack.logger'] = logger
14
+ env[RACK_LOGGER] = logger
15
15
  @app.call(env)
16
16
  end
17
17
  end
@@ -0,0 +1,38 @@
1
+ module Rack
2
+ # Rack::MediaType parse media type and parameters out of content_type string
3
+
4
+ class MediaType
5
+ SPLIT_PATTERN = %r{\s*[;,]\s*}
6
+
7
+ class << self
8
+ # The media type (type/subtype) portion of the CONTENT_TYPE header
9
+ # without any media type parameters. e.g., when CONTENT_TYPE is
10
+ # "text/plain;charset=utf-8", the media-type is "text/plain".
11
+ #
12
+ # For more information on the use of media types in HTTP, see:
13
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
14
+ def type(content_type)
15
+ return nil unless content_type
16
+ content_type.split(SPLIT_PATTERN, 2).first.downcase
17
+ end
18
+
19
+ # The media type parameters provided in CONTENT_TYPE as a Hash, or
20
+ # an empty Hash if no CONTENT_TYPE or media-type parameters were
21
+ # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
22
+ # this method responds with the following Hash:
23
+ # { 'charset' => 'utf-8' }
24
+ def params(content_type)
25
+ 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]
29
+ end
30
+
31
+ private
32
+
33
+ def strip_doublequotes(str)
34
+ (str[0] == ?" && str[-1] == ?") ? str[1..-2] : str
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,10 +1,10 @@
1
1
  module Rack
2
2
  class MethodOverride
3
- HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK)
3
+ HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK]
4
4
 
5
5
  METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
6
6
  HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
7
- ALLOWED_METHODS = ["POST"]
7
+ ALLOWED_METHODS = %w[POST]
8
8
 
9
9
  def initialize(app)
10
10
  @app = app
@@ -14,7 +14,7 @@ module Rack
14
14
  if allowed_methods.include?(env[REQUEST_METHOD])
15
15
  method = method_override(env)
16
16
  if HTTP_METHODS.include?(method)
17
- env["rack.methodoverride.original_method"] = env[REQUEST_METHOD]
17
+ env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] = env[REQUEST_METHOD]
18
18
  env[REQUEST_METHOD] = method
19
19
  end
20
20
  end
@@ -29,7 +29,7 @@ module Rack
29
29
  begin
30
30
  method.to_s.upcase
31
31
  rescue ArgumentError
32
- env["rack.errors"].puts "Invalid string for method"
32
+ env[RACK_ERRORS].puts "Invalid string for method"
33
33
  end
34
34
  end
35
35
 
@@ -42,9 +42,9 @@ module Rack
42
42
  def method_override_param(req)
43
43
  req.POST[METHOD_OVERRIDE_PARAM_KEY]
44
44
  rescue Utils::InvalidParameterError, Utils::ParameterTypeError
45
- req.env["rack.errors"].puts "Invalid or incomplete POST params"
45
+ req.get_header(RACK_ERRORS).puts "Invalid or incomplete POST params"
46
46
  rescue EOFError
47
- req.env["rack.errors"].puts "Bad request content body"
47
+ req.get_header(RACK_ERRORS).puts "Bad request content body"
48
48
  end
49
49
  end
50
50
  end
@@ -45,11 +45,6 @@ module Rack
45
45
  #
46
46
  # N.B. On Ubuntu the mime.types file does not include the leading period, so
47
47
  # users may need to modify the data before merging into the hash.
48
- #
49
- # To add the list mongrel provides, use:
50
- #
51
- # require 'mongrel/handlers'
52
- # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
53
48
 
54
49
  MIME_TYPES = {
55
50
  ".123" => "application/vnd.lotus-1-2-3",
@@ -154,8 +149,11 @@ module Rack
154
149
  ".dmg" => "application/octet-stream",
155
150
  ".dna" => "application/vnd.dna",
156
151
  ".doc" => "application/msword",
152
+ ".docm" => "application/vnd.ms-word.document.macroEnabled.12",
157
153
  ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
158
154
  ".dot" => "application/msword",
155
+ ".dotm" => "application/vnd.ms-word.template.macroEnabled.12",
156
+ ".dotx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
159
157
  ".dp" => "application/vnd.osgi.dp",
160
158
  ".dpg" => "application/vnd.dpgraph",
161
159
  ".dsc" => "text/prs.lines.tag",
@@ -444,10 +442,19 @@ module Rack
444
442
  ".pnm" => "image/x-portable-anymap",
445
443
  ".pntg" => "image/x-macpaint",
446
444
  ".portpkg" => "application/vnd.macports.portpkg",
445
+ ".pot" => "application/vnd.ms-powerpoint",
446
+ ".potm" => "application/vnd.ms-powerpoint.template.macroEnabled.12",
447
+ ".potx" => "application/vnd.openxmlformats-officedocument.presentationml.template",
448
+ ".ppa" => "application/vnd.ms-powerpoint",
449
+ ".ppam" => "application/vnd.ms-powerpoint.addin.macroEnabled.12",
447
450
  ".ppd" => "application/vnd.cups-ppd",
448
451
  ".ppm" => "image/x-portable-pixmap",
449
452
  ".pps" => "application/vnd.ms-powerpoint",
453
+ ".ppsm" => "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
454
+ ".ppsx" => "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
450
455
  ".ppt" => "application/vnd.ms-powerpoint",
456
+ ".pptm" => "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
457
+ ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
451
458
  ".prc" => "application/vnd.palm",
452
459
  ".pre" => "application/vnd.lotus-freelance",
453
460
  ".prf" => "application/pics-rules",
@@ -638,8 +645,14 @@ module Rack
638
645
  ".xfdl" => "application/vnd.xfdl",
639
646
  ".xhtml" => "application/xhtml+xml",
640
647
  ".xif" => "image/vnd.xiff",
648
+ ".xla" => "application/vnd.ms-excel",
649
+ ".xlam" => "application/vnd.ms-excel.addin.macroEnabled.12",
641
650
  ".xls" => "application/vnd.ms-excel",
651
+ ".xlsb" => "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
642
652
  ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
653
+ ".xlsm" => "application/vnd.ms-excel.sheet.macroEnabled.12",
654
+ ".xlt" => "application/vnd.ms-excel",
655
+ ".xltx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
643
656
  ".xml" => "application/xml",
644
657
  ".xo" => "application/vnd.olpc-sugar",
645
658
  ".xop" => "application/xop+xml",
@@ -41,27 +41,27 @@ module Rack
41
41
  end
42
42
 
43
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.run_once" => false,
50
- }
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
51
 
52
52
  def initialize(app)
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
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
63
 
64
- def request(method="GET", uri="", opts={})
64
+ def request(method=GET, uri="", opts={})
65
65
  env = self.class.env_for(uri, opts.merge(:method => method))
66
66
 
67
67
  if opts[:lint]
@@ -70,17 +70,17 @@ module Rack
70
70
  app = @app
71
71
  end
72
72
 
73
- errors = env["rack.errors"]
73
+ errors = env[RACK_ERRORS]
74
74
  status, headers, body = app.call(env)
75
75
  MockResponse.new(status, headers, body, errors)
76
76
  ensure
77
77
  body.close if body.respond_to?(:close)
78
78
  end
79
79
 
80
- # For historical reasons, we're pinning to RFC 2396. It's easier for users
81
- # and we get support from ruby 1.8 to 2.2 using this method.
80
+ # For historical reasons, we're pinning to RFC 2396.
81
+ # URI::Parser = URI::RFC2396_Parser
82
82
  def self.parse_uri_rfc2396(uri)
83
- @parser ||= defined?(URI::RFC2396_Parser) ? URI::RFC2396_Parser.new : URI
83
+ @parser ||= URI::Parser.new
84
84
  @parser.parse(uri)
85
85
  end
86
86
 
@@ -91,28 +91,34 @@ module Rack
91
91
 
92
92
  env = DEFAULT_ENV.dup
93
93
 
94
- env_with_encoding(env, opts, uri)
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
95
101
 
96
102
  env[SCRIPT_NAME] = opts[:script_name] || ""
97
103
 
98
104
  if opts[:fatal]
99
- env["rack.errors"] = FatalWarner.new
105
+ env[RACK_ERRORS] = FatalWarner.new
100
106
  else
101
- env["rack.errors"] = StringIO.new
107
+ env[RACK_ERRORS] = StringIO.new
102
108
  end
103
109
 
104
110
  if params = opts[:params]
105
- if env[REQUEST_METHOD] == "GET"
111
+ if env[REQUEST_METHOD] == GET
106
112
  params = Utils.parse_nested_query(params) if params.is_a?(String)
107
113
  params.update(Utils.parse_nested_query(env[QUERY_STRING]))
108
114
  env[QUERY_STRING] = Utils.build_nested_query(params)
109
115
  elsif !opts.has_key?(:input)
110
116
  opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
111
117
  if params.is_a?(Hash)
112
- if data = Utils::Multipart.build_multipart(params)
118
+ if data = Rack::Multipart.build_multipart(params)
113
119
  opts[:input] = data
114
120
  opts["CONTENT_LENGTH"] ||= data.length.to_s
115
- opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
121
+ opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
116
122
  else
117
123
  opts[:input] = Utils.build_nested_query(params)
118
124
  end
@@ -122,8 +128,7 @@ module Rack
122
128
  end
123
129
  end
124
130
 
125
- empty_str = ""
126
- empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding
131
+ empty_str = String.new
127
132
  opts[:input] ||= empty_str
128
133
  if String === opts[:input]
129
134
  rack_input = StringIO.new(opts[:input])
@@ -131,10 +136,10 @@ module Rack
131
136
  rack_input = opts[:input]
132
137
  end
133
138
 
134
- rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
135
- env['rack.input'] = rack_input
139
+ rack_input.set_encoding(Encoding::BINARY)
140
+ env[RACK_INPUT] = rack_input
136
141
 
137
- env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
142
+ env["CONTENT_LENGTH"] ||= env[RACK_INPUT].length.to_s
138
143
 
139
144
  opts.each { |field, value|
140
145
  env[field] = value if String === field
@@ -142,28 +147,6 @@ module Rack
142
147
 
143
148
  env
144
149
  end
145
-
146
- if "<3".respond_to? :b
147
- def self.env_with_encoding(env, opts, uri)
148
- env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : "GET").b
149
- env["SERVER_NAME"] = (uri.host || "example.org").b
150
- env["SERVER_PORT"] = (uri.port ? uri.port.to_s : "80").b
151
- env[QUERY_STRING] = (uri.query.to_s).b
152
- env[PATH_INFO] = ((!uri.path || uri.path.empty?) ? "/" : uri.path).b
153
- env["rack.url_scheme"] = (uri.scheme || "http").b
154
- env["HTTPS"] = (env["rack.url_scheme"] == "https" ? "on" : "off").b
155
- end
156
- else
157
- def self.env_with_encoding(env, opts, uri)
158
- env[REQUEST_METHOD] = opts[:method] ? opts[:method].to_s.upcase : "GET"
159
- env["SERVER_NAME"] = uri.host || "example.org"
160
- env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
161
- env[QUERY_STRING] = uri.query.to_s
162
- env[PATH_INFO] = (!uri.path || uri.path.empty?) ? "/" : uri.path
163
- env["rack.url_scheme"] = uri.scheme || "http"
164
- env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
165
- end
166
- end
167
150
  end
168
151
 
169
152
  # Rack::MockResponse provides useful helpers for testing your apps.
@@ -180,7 +163,6 @@ module Rack
180
163
  def initialize(status, headers, body, errors=StringIO.new(""))
181
164
  @original_headers = headers
182
165
  @errors = errors.string if errors.respond_to?(:string)
183
- @body_string = nil
184
166
 
185
167
  super(body, status, headers)
186
168
  end
@@ -208,7 +190,7 @@ module Rack
208
190
  end
209
191
 
210
192
  def empty?
211
- [201, 204, 205, 304].include? status
193
+ [201, 204, 304].include? status
212
194
  end
213
195
  end
214
196
  end
@@ -1,10 +1,11 @@
1
+ require 'rack/multipart/parser'
2
+
1
3
  module Rack
2
4
  # A multipart form data parser, adapted from IOWA.
3
5
  #
4
6
  # Usually, Rack::Request#POST takes care of calling this.
5
7
  module Multipart
6
8
  autoload :UploadedFile, 'rack/multipart/uploaded_file'
7
- autoload :Parser, 'rack/multipart/parser'
8
9
  autoload :Generator, 'rack/multipart/generator'
9
10
 
10
11
  EOL = "\r\n"
@@ -12,17 +13,45 @@ module Rack
12
13
  MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
13
14
  TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
14
15
  CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
15
- DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})/
16
- RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
16
+ VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
17
17
  BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
18
18
  BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
19
19
  MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
20
- MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^\";]*)"?/ni
20
+ MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name=(#{VALUE})/ni
21
21
  MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
22
+ # Updated definitions from RFC 2231
23
+ ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}
24
+ ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/
25
+ SECTION = /\*[0-9]+/
26
+ REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/
27
+ REGULAR_PARAMETER = /(#{REGULAR_PARAMETER_NAME})=(#{VALUE})/
28
+ EXTENDED_OTHER_NAME = /#{ATTRIBUTE}\*[1-9][0-9]*\*/
29
+ EXTENDED_OTHER_VALUE = /%[0-9a-fA-F]{2}|#{ATTRIBUTE_CHAR}/
30
+ EXTENDED_OTHER_PARAMETER = /(#{EXTENDED_OTHER_NAME})=(#{EXTENDED_OTHER_VALUE}*)/
31
+ EXTENDED_INITIAL_NAME = /#{ATTRIBUTE}(?:\*0)?\*/
32
+ EXTENDED_INITIAL_VALUE = /[a-zA-Z0-9\-]*'[a-zA-Z0-9\-]*'#{EXTENDED_OTHER_VALUE}*/
33
+ EXTENDED_INITIAL_PARAMETER = /(#{EXTENDED_INITIAL_NAME})=(#{EXTENDED_INITIAL_VALUE})/
34
+ EXTENDED_PARAMETER = /#{EXTENDED_INITIAL_PARAMETER}|#{EXTENDED_OTHER_PARAMETER}/
35
+ DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})\s*/
36
+ RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
22
37
 
23
38
  class << self
24
- def parse_multipart(env)
25
- Parser.create(env).parse
39
+ def parse_multipart(env, params = Rack::Utils.default_query_parser)
40
+ extract_multipart Rack::Request.new(env), params
41
+ end
42
+
43
+ def extract_multipart(req, params = Rack::Utils.default_query_parser)
44
+ io = req.get_header(RACK_INPUT)
45
+ io.rewind
46
+ content_length = req.content_length
47
+ content_length = content_length.to_i if content_length
48
+
49
+ tempfile = req.get_header(RACK_MULTIPART_TEMPFILE_FACTORY) || Parser::TEMPFILE_FACTORY
50
+ bufsize = req.get_header(RACK_MULTIPART_BUFFER_SIZE) || Parser::BUFSIZE
51
+
52
+ info = Parser.parse io, content_length, req.get_header('CONTENT_TYPE'), tempfile, bufsize, params
53
+ req.set_header(RACK_TEMPFILES, info.tmp_files)
54
+ info.params
26
55
  end
27
56
 
28
57
  def build_multipart(params, first = true)
@@ -11,18 +11,18 @@ module Rack
11
11
 
12
12
  def dump
13
13
  return nil if @first && !multipart?
14
- return flattened_params if !@first
14
+ return flattened_params unless @first
15
15
 
16
16
  flattened_params.map do |name, file|
17
17
  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)
18
+ ::File.open(file.path, 'rb') do |f|
19
+ f.set_encoding(Encoding::BINARY)
20
20
  content_for_tempfile(f, file, name)
21
21
  end
22
22
  else
23
23
  content_for_other(file, name)
24
24
  end
25
- end.join + "--#{MULTIPART_BOUNDARY}--\r"
25
+ end.join << "--#{MULTIPART_BOUNDARY}--\r"
26
26
  end
27
27
 
28
28
  private
@@ -90,4 +90,4 @@ EOF
90
90
  end
91
91
  end
92
92
  end
93
- end
93
+ end