mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Depend on unreleased rack 1.1
This commit is contained in:
parent
6ae839bf48
commit
d54604c352
5 changed files with 2 additions and 334 deletions
|
@ -32,9 +32,8 @@ rescue LoadError
|
|||
end
|
||||
require 'active_support/core/all'
|
||||
|
||||
gem 'rack', '~> 1.0.0'
|
||||
gem 'rack', '~> 1.1.0'
|
||||
require 'rack'
|
||||
require 'action_dispatch/extensions/rack'
|
||||
|
||||
module ActionDispatch
|
||||
autoload :Request, 'action_dispatch/http/request'
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# Monkey patch in new test helper methods
|
||||
unless Rack::Utils.respond_to?(:build_nested_query)
|
||||
require 'action_dispatch/extensions/rack/mock'
|
||||
require 'action_dispatch/extensions/rack/utils'
|
||||
end
|
|
@ -1,65 +0,0 @@
|
|||
require 'rack/mock'
|
||||
|
||||
module Rack
|
||||
class MockRequest
|
||||
# Return the Rack environment used for a request to +uri+.
|
||||
def self.env_for(uri="", opts={})
|
||||
uri = URI(uri)
|
||||
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
||||
|
||||
env = DEFAULT_ENV.dup
|
||||
|
||||
env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
|
||||
env["SERVER_NAME"] = uri.host || "example.org"
|
||||
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
|
||||
env["QUERY_STRING"] = uri.query.to_s
|
||||
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
|
||||
env["rack.url_scheme"] = uri.scheme || "http"
|
||||
env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
|
||||
|
||||
env["SCRIPT_NAME"] = opts[:script_name] || ""
|
||||
|
||||
if opts[:fatal]
|
||||
env["rack.errors"] = FatalWarner.new
|
||||
else
|
||||
env["rack.errors"] = StringIO.new
|
||||
end
|
||||
|
||||
if params = opts[:params]
|
||||
if env["REQUEST_METHOD"] == "GET"
|
||||
params = Utils.parse_nested_query(params) if params.is_a?(String)
|
||||
params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
|
||||
env["QUERY_STRING"] = Utils.build_nested_query(params)
|
||||
elsif !opts.has_key?(:input)
|
||||
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
||||
if params.is_a?(Hash)
|
||||
if data = Utils::Multipart.build_multipart(params)
|
||||
opts[:input] = data
|
||||
opts["CONTENT_LENGTH"] ||= data.length.to_s
|
||||
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
|
||||
else
|
||||
opts[:input] = Utils.build_nested_query(params)
|
||||
end
|
||||
else
|
||||
opts[:input] = params
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
opts[:input] ||= ""
|
||||
if String === opts[:input]
|
||||
env["rack.input"] = StringIO.new(opts[:input])
|
||||
else
|
||||
env["rack.input"] = opts[:input]
|
||||
end
|
||||
|
||||
env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
|
||||
|
||||
opts.each { |field, value|
|
||||
env[field] = value if String === field
|
||||
}
|
||||
|
||||
env
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,261 +0,0 @@
|
|||
# -*- encoding: binary -*-
|
||||
|
||||
require 'rack/utils'
|
||||
|
||||
module Rack
|
||||
module Utils
|
||||
def normalize_params(params, name, v = nil)
|
||||
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
||||
k = $1 || ''
|
||||
after = $' || ''
|
||||
|
||||
return if k.empty?
|
||||
|
||||
if after == ""
|
||||
params[k] = v
|
||||
elsif after == "[]"
|
||||
params[k] ||= []
|
||||
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
||||
params[k] << v
|
||||
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
|
||||
child_key = $1
|
||||
params[k] ||= []
|
||||
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
||||
if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
|
||||
normalize_params(params[k].last, child_key, v)
|
||||
else
|
||||
params[k] << normalize_params({}, child_key, v)
|
||||
end
|
||||
else
|
||||
params[k] ||= {}
|
||||
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
|
||||
params[k] = normalize_params(params[k], after, v)
|
||||
end
|
||||
|
||||
return params
|
||||
end
|
||||
module_function :normalize_params
|
||||
|
||||
def build_nested_query(value, prefix = nil)
|
||||
case value
|
||||
when Array
|
||||
value.map { |v|
|
||||
build_nested_query(v, "#{prefix}[]")
|
||||
}.join("&")
|
||||
when Hash
|
||||
value.map { |k, v|
|
||||
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
||||
}.join("&")
|
||||
when String
|
||||
raise ArgumentError, "value must be a Hash" if prefix.nil?
|
||||
"#{prefix}=#{escape(value)}"
|
||||
else
|
||||
prefix
|
||||
end
|
||||
end
|
||||
module_function :build_nested_query
|
||||
|
||||
module Multipart
|
||||
class UploadedFile
|
||||
# The filename, *not* including the path, of the "uploaded" file
|
||||
attr_reader :original_filename
|
||||
|
||||
# The content type of the "uploaded" file
|
||||
attr_accessor :content_type
|
||||
|
||||
def initialize(path, content_type = "text/plain", binary = false)
|
||||
raise "#{path} file does not exist" unless ::File.exist?(path)
|
||||
@content_type = content_type
|
||||
@original_filename = ::File.basename(path)
|
||||
@tempfile = Tempfile.new(@original_filename)
|
||||
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
||||
@tempfile.binmode if binary
|
||||
FileUtils.copy_file(path, @tempfile.path)
|
||||
end
|
||||
|
||||
def path
|
||||
@tempfile.path
|
||||
end
|
||||
alias_method :local_path, :path
|
||||
|
||||
def method_missing(method_name, *args, &block) #:nodoc:
|
||||
@tempfile.__send__(method_name, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
MULTIPART_BOUNDARY = "AaB03x"
|
||||
|
||||
def self.parse_multipart(env)
|
||||
unless env['CONTENT_TYPE'] =~
|
||||
%r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
|
||||
nil
|
||||
else
|
||||
boundary = "--#{$1}"
|
||||
|
||||
params = {}
|
||||
buf = ""
|
||||
content_length = env['CONTENT_LENGTH'].to_i
|
||||
input = env['rack.input']
|
||||
input.rewind
|
||||
|
||||
boundary_size = Utils.bytesize(boundary) + EOL.size
|
||||
bufsize = 16384
|
||||
|
||||
content_length -= boundary_size
|
||||
|
||||
read_buffer = ''
|
||||
|
||||
status = input.read(boundary_size, read_buffer)
|
||||
raise EOFError, "bad content body" unless status == boundary + EOL
|
||||
|
||||
rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
|
||||
|
||||
loop {
|
||||
head = nil
|
||||
body = ''
|
||||
filename = content_type = name = nil
|
||||
|
||||
until head && buf =~ rx
|
||||
if !head && i = buf.index(EOL+EOL)
|
||||
head = buf.slice!(0, i+2) # First \r\n
|
||||
buf.slice!(0, 2) # Second \r\n
|
||||
|
||||
filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
|
||||
content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
|
||||
name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
|
||||
|
||||
if content_type || filename
|
||||
body = Tempfile.new("RackMultipart")
|
||||
body.binmode if body.respond_to?(:binmode)
|
||||
end
|
||||
|
||||
next
|
||||
end
|
||||
|
||||
# Save the read body part.
|
||||
if head && (boundary_size+4 < buf.size)
|
||||
body << buf.slice!(0, buf.size - (boundary_size+4))
|
||||
end
|
||||
|
||||
c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
|
||||
raise EOFError, "bad content body" if c.nil? || c.empty?
|
||||
buf << c
|
||||
content_length -= c.size
|
||||
end
|
||||
|
||||
# Save the rest.
|
||||
if i = buf.index(rx)
|
||||
body << buf.slice!(0, i)
|
||||
buf.slice!(0, boundary_size+2)
|
||||
|
||||
content_length = -1 if $1 == "--"
|
||||
end
|
||||
|
||||
if filename == ""
|
||||
# filename is blank which means no file has been selected
|
||||
data = nil
|
||||
elsif filename
|
||||
body.rewind
|
||||
|
||||
# Take the basename of the upload's original filename.
|
||||
# This handles the full Windows paths given by Internet Explorer
|
||||
# (and perhaps other broken user agents) without affecting
|
||||
# those which give the lone filename.
|
||||
filename =~ /^(?:.*[:\\\/])?(.*)/m
|
||||
filename = $1
|
||||
|
||||
data = {:filename => filename, :type => content_type,
|
||||
:name => name, :tempfile => body, :head => head}
|
||||
elsif !filename && content_type
|
||||
body.rewind
|
||||
|
||||
# Generic multipart cases, not coming from a form
|
||||
data = {:type => content_type,
|
||||
:name => name, :tempfile => body, :head => head}
|
||||
else
|
||||
data = body
|
||||
end
|
||||
|
||||
Utils.normalize_params(params, name, data) unless data.nil?
|
||||
|
||||
break if buf.empty? || content_length == -1
|
||||
}
|
||||
|
||||
input.rewind
|
||||
|
||||
params
|
||||
end
|
||||
end
|
||||
|
||||
def self.build_multipart(params, first = true)
|
||||
if first
|
||||
unless params.is_a?(Hash)
|
||||
raise ArgumentError, "value must be a Hash"
|
||||
end
|
||||
|
||||
multipart = false
|
||||
query = lambda { |value|
|
||||
case value
|
||||
when Array
|
||||
value.each(&query)
|
||||
when Hash
|
||||
value.values.each(&query)
|
||||
when UploadedFile
|
||||
multipart = true
|
||||
end
|
||||
}
|
||||
params.values.each(&query)
|
||||
return nil unless multipart
|
||||
end
|
||||
|
||||
flattened_params = Hash.new
|
||||
|
||||
params.each do |key, value|
|
||||
k = first ? key.to_s : "[#{key}]"
|
||||
|
||||
case value
|
||||
when Array
|
||||
value.map { |v|
|
||||
build_multipart(v, false).each { |subkey, subvalue|
|
||||
flattened_params["#{k}[]#{subkey}"] = subvalue
|
||||
}
|
||||
}
|
||||
when Hash
|
||||
build_multipart(value, false).each { |subkey, subvalue|
|
||||
flattened_params[k + subkey] = subvalue
|
||||
}
|
||||
else
|
||||
flattened_params[k] = value
|
||||
end
|
||||
end
|
||||
|
||||
if first
|
||||
flattened_params.map { |name, file|
|
||||
if file.respond_to?(:original_filename)
|
||||
::File.open(file.path, "rb") do |f|
|
||||
f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
|
||||
<<-EOF
|
||||
--#{MULTIPART_BOUNDARY}\r
|
||||
Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r
|
||||
Content-Type: #{file.content_type}\r
|
||||
Content-Length: #{::File.stat(file.path).size}\r
|
||||
\r
|
||||
#{f.read}\r
|
||||
EOF
|
||||
end
|
||||
else
|
||||
<<-EOF
|
||||
--#{MULTIPART_BOUNDARY}\r
|
||||
Content-Disposition: form-data; name="#{name}"\r
|
||||
\r
|
||||
#{file}\r
|
||||
EOF
|
||||
end
|
||||
}.join + "--#{MULTIPART_BOUNDARY}--\r"
|
||||
else
|
||||
flattened_params
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,7 +18,7 @@ class TestRequestTest < ActiveSupport::TestCase
|
|||
assert_equal "0.0.0.0", env.delete("REMOTE_ADDR")
|
||||
assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT")
|
||||
|
||||
assert_equal [0, 1], env.delete("rack.version")
|
||||
assert_equal [1, 0], env.delete("rack.version")
|
||||
assert_equal "", env.delete("rack.input").string
|
||||
assert_kind_of StringIO, env.delete("rack.errors")
|
||||
assert_equal true, env.delete("rack.multithread")
|
||||
|
|
Loading…
Reference in a new issue