mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* lib/cgi/core.rb (CGI::QueryExtension): delete MorphingBody
and replace like as 1.8's in multipart reading. see [ruby-dev:36443], reference from CGIAlt http://cgialt.rubyforge.org/ * test/cgi/test_cgi_multipart.rb : fixed multipart test. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19663 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
bd421aa2d2
commit
10e9b63806
3 changed files with 113 additions and 138 deletions
|
@ -1,3 +1,12 @@
|
||||||
|
Thu Oct 2 21:22:43 2008 Takeyuki FUJIOKA <xibbar@ruby-lang.org>
|
||||||
|
|
||||||
|
* lib/cgi/core.rb (CGI::QueryExtension): delete MorphingBody
|
||||||
|
and replace like as 1.8's in multipart reading.
|
||||||
|
see [ruby-dev:36443],
|
||||||
|
reference from CGIAlt http://cgialt.rubyforge.org/
|
||||||
|
|
||||||
|
* test/cgi/test_cgi_multipart.rb : fixed multipart test.
|
||||||
|
|
||||||
Thu Oct 2 20:46:17 2008 Tanaka Akira <akr@fsij.org>
|
Thu Oct 2 20:46:17 2008 Tanaka Akira <akr@fsij.org>
|
||||||
|
|
||||||
* string.c (rb_str_sub_bang): fix coderange.
|
* string.c (rb_str_sub_bang): fix coderange.
|
||||||
|
|
234
lib/cgi/core.rb
234
lib/cgi/core.rb
|
@ -345,6 +345,15 @@ class CGI
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Maximum content length of post data
|
||||||
|
##MAX_CONTENT_LENGTH = 2 * 1024 * 1024
|
||||||
|
|
||||||
|
# Maximum content length of multipart data
|
||||||
|
MAX_MULTIPART_LENGTH = 128 * 1024 * 1024
|
||||||
|
|
||||||
|
# Maximum number of request parameters when multipart
|
||||||
|
MAX_MULTIPART_COUNT = 128
|
||||||
|
|
||||||
# Mixin module. It provides the follow functionality groups:
|
# Mixin module. It provides the follow functionality groups:
|
||||||
#
|
#
|
||||||
# 1. Access to CGI environment variables as methods. See
|
# 1. Access to CGI environment variables as methods. See
|
||||||
|
@ -404,98 +413,105 @@ class CGI
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_multipart(boundary, content_length)
|
def read_multipart(boundary, content_length)
|
||||||
params = Hash.new([])
|
## read first boundary
|
||||||
boundary = "--" + boundary
|
stdin = $stdin
|
||||||
quoted_boundary = Regexp.quote(boundary)
|
first_line = "--#{boundary}#{EOL}"
|
||||||
buf = ""
|
content_length -= first_line.bytesize
|
||||||
|
status = stdin.read(first_line.bytesize)
|
||||||
|
raise EOFError.new("no content body") unless status
|
||||||
|
raise EOFError.new("bad content body") unless first_line == status
|
||||||
|
## parse and set params
|
||||||
|
params = {}
|
||||||
|
boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/
|
||||||
|
boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize
|
||||||
|
boundary_end = nil
|
||||||
|
buf = ''
|
||||||
bufsize = 10 * 1024
|
bufsize = 10 * 1024
|
||||||
boundary_end=""
|
max_count = MAX_MULTIPART_COUNT
|
||||||
|
n = 0
|
||||||
# start multipart/form-data
|
while true
|
||||||
stdinput.binmode if defined? stdinput.binmode
|
(n += 1) < max_count or raise StandardError.new("too many parameters.")
|
||||||
boundary_size = boundary.bytesize + EOL.bytesize
|
## create body (StringIO or Tempfile)
|
||||||
content_length -= boundary_size
|
body = create_body(bufsize < content_length)
|
||||||
status = stdinput.read(boundary_size)
|
class << body
|
||||||
if nil == status
|
|
||||||
raise EOFError, "no content body"
|
|
||||||
elsif boundary + EOL != status
|
|
||||||
raise EOFError, "bad content body"
|
|
||||||
end
|
|
||||||
|
|
||||||
loop do
|
|
||||||
head = nil
|
|
||||||
body = MorphingBody.new
|
|
||||||
|
|
||||||
until head and /#{quoted_boundary}(?:#{EOL}|--)/.match(buf)
|
|
||||||
if (not head) and /#{EOL}#{EOL}/.match(buf)
|
|
||||||
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/) do
|
|
||||||
head = $1.dup
|
|
||||||
""
|
|
||||||
end
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
if head and ( (EOL + boundary + EOL).bytesize < buf.bytesize )
|
|
||||||
body.print buf[0 ... (buf.bytesize - (EOL + boundary + EOL).bytesize)]
|
|
||||||
buf[0 ... (buf.bytesize - (EOL + boundary + EOL).bytesize)] = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
c = if bufsize < content_length
|
|
||||||
stdinput.read(bufsize)
|
|
||||||
else
|
|
||||||
stdinput.read(content_length)
|
|
||||||
end
|
|
||||||
if c.nil? || c.empty?
|
|
||||||
raise EOFError, "bad content body"
|
|
||||||
end
|
|
||||||
buf.concat(c)
|
|
||||||
content_length -= c.bytesize
|
|
||||||
end
|
|
||||||
|
|
||||||
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/) do
|
|
||||||
body.print $1
|
|
||||||
if "--" == $2
|
|
||||||
content_length = -1
|
|
||||||
end
|
|
||||||
boundary_end = $2.dup
|
|
||||||
""
|
|
||||||
end
|
|
||||||
|
|
||||||
body.rewind
|
|
||||||
|
|
||||||
/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/i.match(head)
|
|
||||||
filename = ($1 or $2 or "")
|
|
||||||
if /Mac/i =~ env_table['HTTP_USER_AGENT'] and
|
|
||||||
/Mozilla/i =~ env_table['HTTP_USER_AGENT'] and
|
|
||||||
/MSIE/i !~ env_table['HTTP_USER_AGENT']
|
|
||||||
filename = CGI::unescape(filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
/Content-Type: ([^\s]*)/i.match(head)
|
|
||||||
content_type = ($1 or "")
|
|
||||||
|
|
||||||
(class << body; self; end).class_eval do
|
|
||||||
alias local_path path
|
alias local_path path
|
||||||
define_method(:original_filename) {filename.dup.taint}
|
attr_reader :original_filename, :content_type
|
||||||
define_method(:content_type) {content_type.dup.taint}
|
|
||||||
end
|
end
|
||||||
|
## find head and boundary
|
||||||
/Content-Disposition:.* name="?([^\";\s]*)"?/i.match(head)
|
head = nil
|
||||||
name = ($1 || "").dup
|
separator = EOL * 2
|
||||||
|
until head && matched = boundary_rexp.match(buf)
|
||||||
if params.has_key?(name)
|
if !head && pos = buf.index(separator)
|
||||||
params[name].push(body)
|
len = pos + EOL.bytesize
|
||||||
else
|
head = buf[0, len]
|
||||||
params[name] = [body]
|
buf = buf[(pos+separator.bytesize)..-1]
|
||||||
|
else
|
||||||
|
if head && buf.size > boundary_size
|
||||||
|
len = buf.size - boundary_size
|
||||||
|
body.print(buf[0, len])
|
||||||
|
buf[0, len] = ''
|
||||||
|
end
|
||||||
|
c = stdin.read(bufsize < content_length ? bufsize : content_length)
|
||||||
|
raise EOFError.new("bad content body") if c.nil? || c.empty?
|
||||||
|
buf << c
|
||||||
|
content_length -= c.bytesize
|
||||||
|
end
|
||||||
end
|
end
|
||||||
break if buf.bytesize == 0
|
## read to end of boundary
|
||||||
|
m = matched
|
||||||
|
len = m.begin(0)
|
||||||
|
s = buf[0, len]
|
||||||
|
if s =~ /(\r?\n)\z/
|
||||||
|
s = buf[0, len - $1.bytesize]
|
||||||
|
end
|
||||||
|
body.print(s)
|
||||||
|
buf = buf[m.end(0)..-1]
|
||||||
|
boundary_end = m[1]
|
||||||
|
content_length = -1 if boundary_end == '--'
|
||||||
|
## reset file cursor position
|
||||||
|
body.rewind
|
||||||
|
## original filename
|
||||||
|
/Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
|
||||||
|
filename = $1 || $2 || ''
|
||||||
|
filename = CGI.unescape(filename) if unescape_filename?()
|
||||||
|
body.instance_variable_set('@original_filename', filename.taint)
|
||||||
|
## content type
|
||||||
|
/Content-Type: (.*)/i.match(head)
|
||||||
|
(content_type = $1 || '').chomp!
|
||||||
|
body.instance_variable_set('@content_type', content_type.taint)
|
||||||
|
## query parameter name
|
||||||
|
/Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
|
||||||
|
name = $1 || $2 || ''
|
||||||
|
(params[name] ||= []) << body
|
||||||
|
## break loop
|
||||||
|
break if buf.size == 0
|
||||||
break if content_length == -1
|
break if content_length == -1
|
||||||
end
|
end
|
||||||
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
|
raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/
|
||||||
|
params.default = []
|
||||||
params
|
params
|
||||||
end # read_multipart
|
end # read_multipart
|
||||||
private :read_multipart
|
private :read_multipart
|
||||||
|
def create_body(is_large) #:nodoc:
|
||||||
|
if is_large
|
||||||
|
require 'tempfile'
|
||||||
|
body = Tempfile.new('CGI')
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
require 'stringio'
|
||||||
|
body = StringIO.new
|
||||||
|
rescue LoadError
|
||||||
|
require 'tempfile'
|
||||||
|
body = Tempfile.new('CGI')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
body.binmode if defined? body.binmode
|
||||||
|
return body
|
||||||
|
end
|
||||||
|
def unescape_filename? #:nodoc:
|
||||||
|
user_agent = $CGI_ENV['HTTP_USER_AGENT']
|
||||||
|
return /Mac/i.match(user_agent) && /Mozilla/i.match(user_agent) && !/MSIE/i.match(user_agent)
|
||||||
|
end
|
||||||
|
|
||||||
# offline mode. read name=value pairs on standard input.
|
# offline mode. read name=value pairs on standard input.
|
||||||
def read_from_cmdline
|
def read_from_cmdline
|
||||||
|
@ -524,57 +540,6 @@ class CGI
|
||||||
|
|
||||||
# A wrapper class to use a StringIO object as the body and switch
|
# A wrapper class to use a StringIO object as the body and switch
|
||||||
# to a TempFile when the passed threshold is passed.
|
# to a TempFile when the passed threshold is passed.
|
||||||
class MorphingBody
|
|
||||||
begin
|
|
||||||
require "stringio"
|
|
||||||
@@small_buffer = lambda{StringIO.new}
|
|
||||||
rescue LoadError
|
|
||||||
require "tempfile"
|
|
||||||
@@small_buffer = lambda{
|
|
||||||
n = Tempfile.new("CGI")
|
|
||||||
n.binmode
|
|
||||||
n
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(morph_threshold = 10240)
|
|
||||||
@threshold = morph_threshold
|
|
||||||
@body = @@small_buffer.call
|
|
||||||
@cur_size = 0
|
|
||||||
@morph_check = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def print(data)
|
|
||||||
if @morph_check && (@cur_size + data.bytesize > @threshold)
|
|
||||||
convert_body
|
|
||||||
end
|
|
||||||
@body.print data
|
|
||||||
end
|
|
||||||
def rewind
|
|
||||||
@body.rewind
|
|
||||||
end
|
|
||||||
def path
|
|
||||||
@body.path
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the true body object.
|
|
||||||
def extract
|
|
||||||
@body
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def convert_body
|
|
||||||
new_body = TempFile.new("CGI")
|
|
||||||
new_body.binmode if defined? @body.binmode
|
|
||||||
new_body.binmode if defined? new_body.binmode
|
|
||||||
|
|
||||||
@body.rewind
|
|
||||||
new_body.print @body.read
|
|
||||||
@body = new_body
|
|
||||||
@morph_check = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Initialize the data from the query.
|
# Initialize the data from the query.
|
||||||
#
|
#
|
||||||
# Handles multipart forms (in particular, forms that involve file uploads).
|
# Handles multipart forms (in particular, forms that involve file uploads).
|
||||||
|
@ -582,6 +547,7 @@ class CGI
|
||||||
def initialize_query()
|
def initialize_query()
|
||||||
if ("POST" == env_table['REQUEST_METHOD']) and
|
if ("POST" == env_table['REQUEST_METHOD']) and
|
||||||
%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|.match(env_table['CONTENT_TYPE'])
|
%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|.match(env_table['CONTENT_TYPE'])
|
||||||
|
raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > MAX_MULTIPART_LENGTH
|
||||||
boundary = $1.dup
|
boundary = $1.dup
|
||||||
@multipart = true
|
@multipart = true
|
||||||
@params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
|
@params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
|
||||||
|
|
|
@ -150,9 +150,9 @@ class CGIMultipartTest < Test::Unit::TestCase
|
||||||
name = hash[:name]
|
name = hash[:name]
|
||||||
expected = hash[:value]
|
expected = hash[:value]
|
||||||
expected_class = @expected_class || (hash[:value].length < threshold ? StringIO : Tempfile)
|
expected_class = @expected_class || (hash[:value].length < threshold ? StringIO : Tempfile)
|
||||||
assert_kind_of(expected_class, cgi[name]) if RUBY_VERSION<"1.9"
|
assert_kind_of(expected_class, cgi[name])
|
||||||
assert_equal(expected, cgi[name].read()) if RUBY_VERSION<"1.9"
|
assert_equal(expected, cgi[name].read())
|
||||||
assert_equal(hash[:filename] || '', cgi[name].original_filename) #if hash[:filename]
|
assert_equal(hash[:filename] || '', cgi[name].original_filename) #if hash[:filename]
|
||||||
assert_equal(hash[:content_type] || '', cgi[name].content_type) #if hash[:content_type]
|
assert_equal(hash[:content_type] || '', cgi[name].content_type) #if hash[:content_type]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -192,7 +192,7 @@ class CGIMultipartTest < Test::Unit::TestCase
|
||||||
]
|
]
|
||||||
@expected_class = Tempfile
|
@expected_class = Tempfile
|
||||||
_test_multipart()
|
_test_multipart()
|
||||||
end
|
end if RUBY_VERSION < "1.9"
|
||||||
|
|
||||||
|
|
||||||
def _set_const(klass, name, value)
|
def _set_const(klass, name, value)
|
||||||
|
|
Loading…
Add table
Reference in a new issue