Skip to content

Commit

Permalink
Update rack to fix multipart uploads with an empty file [#1945 state:…
Browse files Browse the repository at this point in the history
…resolved]
  • Loading branch information
josh committed Mar 13, 2009
1 parent 99d75a7 commit eced3d8
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 43 deletions.
1 change: 1 addition & 0 deletions actionpack/lib/action_controller/vendor/rack-1.0/rack.rb
Expand Up @@ -28,6 +28,7 @@ def self.release

autoload :Builder, "rack/builder"
autoload :Cascade, "rack/cascade"
autoload :Chunked, "rack/chunked"
autoload :CommonLogger, "rack/commonlogger"
autoload :ConditionalGet, "rack/conditionalget"
autoload :ContentLength, "rack/content_length"
Expand Down
49 changes: 49 additions & 0 deletions actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb
@@ -0,0 +1,49 @@
require 'rack/utils'

module Rack

# Middleware that applies chunked transfer encoding to response bodies
# when the response does not include a Content-Length header.
class Chunked
include Rack::Utils

def initialize(app)
@app = app
end

def call(env)
status, headers, body = @app.call(env)
headers = HeaderHash.new(headers)

if env['HTTP_VERSION'] == 'HTTP/1.0' ||
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
headers['Content-Length'] ||
headers['Transfer-Encoding']
[status, headers.to_hash, body]
else
dup.chunk(status, headers, body)
end
end

def chunk(status, headers, body)
@body = body
headers.delete('Content-Length')
headers['Transfer-Encoding'] = 'chunked'
[status, headers.to_hash, self]
end

def each
term = "\r\n"
@body.each do |chunk|
size = bytesize(chunk)
next if size == 0
yield [size.to_s(16), term, chunk, term].join
end
yield ["0", term, "", term].join
end

def close
@body.close if @body.respond_to?(:close)
end
end
end
Expand Up @@ -60,7 +60,7 @@ def serving
body = self
else
body = [F.read(@path)]
size = body.first.size
size = Utils.bytesize(body.first)
end

[200, {
Expand Down
@@ -1,3 +1,5 @@
require 'rack/content_length'

module Rack
module Handler
class CGI
Expand All @@ -6,6 +8,8 @@ def self.run(app, options=nil)
end

def self.serve(app)
app = ContentLength.new(app)

env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"

Expand Down
@@ -1,5 +1,6 @@
require 'fcgi'
require 'socket'
require 'rack/content_length'

module Rack
module Handler
Expand Down Expand Up @@ -29,6 +30,8 @@ def read(*args)
end

def self.serve(request, app)
app = Rack::ContentLength.new(app)

env = request.env
env.delete "HTTP_CONTENT_LENGTH"

Expand Down
@@ -1,5 +1,6 @@
require 'lsapi'
#require 'cgi'
require 'rack/content_length'

module Rack
module Handler
class LSWS
Expand All @@ -9,6 +10,8 @@ def self.run(app, options=nil)
end
end
def self.serve(app)
app = Rack::ContentLength.new(app)

env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
Expand Down
@@ -1,5 +1,7 @@
require 'mongrel'
require 'stringio'
require 'rack/content_length'
require 'rack/chunked'

module Rack
module Handler
Expand Down Expand Up @@ -33,7 +35,7 @@ def self.run(app, options={})
end

def initialize(app)
@app = app
@app = Rack::Chunked.new(Rack::ContentLength.new(app))
end

def process(request, response)
Expand Down
@@ -1,5 +1,7 @@
require 'scgi'
require 'stringio'
require 'rack/content_length'
require 'rack/chunked'

module Rack
module Handler
Expand All @@ -14,7 +16,7 @@ def self.run(app, options=nil)
end

def initialize(settings = {})
@app = settings[:app]
@app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app]))
@log = Object.new
def @log.info(*args); end
def @log.error(*args); end
Expand Down
@@ -1,9 +1,12 @@
require "thin"
require "rack/content_length"
require "rack/chunked"

module Rack
module Handler
class Thin
def self.run(app, options={})
app = Rack::Chunked.new(Rack::ContentLength.new(app))
server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
options[:Port] || 8080,
app)
Expand Down
@@ -1,5 +1,6 @@
require 'webrick'
require 'stringio'
require 'rack/content_length'

module Rack
module Handler
Expand All @@ -14,7 +15,7 @@ def self.run(app, options={})

def initialize(server, app)
super server
@app = app
@app = Rack::ContentLength.new(app)
end

def service(req, res)
Expand Down
54 changes: 19 additions & 35 deletions actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb
Expand Up @@ -374,59 +374,43 @@ def check_content_type(status, headers)

## === The Content-Length
def check_content_length(status, headers, env)
chunked_response = false
headers.each { |key, value|
if key.downcase == 'transfer-encoding'
chunked_response = value.downcase != 'identity'
end
}

headers.each { |key, value|
if key.downcase == 'content-length'
## There must be a <tt>Content-Length</tt>, except when the
## +Status+ is 1xx, 204 or 304, in which case there must be none
## given.
## There must not be a <tt>Content-Length</tt> header when the
## +Status+ is 1xx, 204 or 304.
assert("Content-Length header found in #{status} response, not allowed") {
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}

assert('Content-Length header should not be used if body is chunked') {
not chunked_response
}

bytes = 0
string_body = true

@body.each { |part|
unless part.kind_of?(String)
string_body = false
break
end
if @body.respond_to?(:to_ary)
@body.each { |part|
unless part.kind_of?(String)
string_body = false
break
end

bytes += Rack::Utils.bytesize(part)
}

if env["REQUEST_METHOD"] == "HEAD"
assert("Response body was given for HEAD request, but should be empty") {
bytes == 0
bytes += Rack::Utils.bytesize(part)
}
else
if string_body
assert("Content-Length header was #{value}, but should be #{bytes}") {
value == bytes.to_s

if env["REQUEST_METHOD"] == "HEAD"
assert("Response body was given for HEAD request, but should be empty") {
bytes == 0
}
else
if string_body
assert("Content-Length header was #{value}, but should be #{bytes}") {
value == bytes.to_s
}
end
end
end

return
end
}

if [ String, Array ].include?(@body.class) && !chunked_response
assert('No Content-Length header found') {
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
end
end

## === The Body
Expand Down
Expand Up @@ -12,7 +12,11 @@ module Rack
# first, since they are most specific.

class URLMap
def initialize(map)
def initialize(map = {})
remap(map)
end

def remap(map)
@mapping = map.map { |location, app|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
host, location = $1, $2
Expand Down
Expand Up @@ -372,7 +372,7 @@ def self.parse_multipart(env)
data = body
end

Utils.normalize_params(params, name, data)
Utils.normalize_params(params, name, data) unless data.nil?

break if buf.empty? || content_length == -1
}
Expand Down
Expand Up @@ -103,7 +103,7 @@ def teardown

test "does not create tempfile if no file has been selected" do
params = parse_multipart('none')
assert_equal %w(files submit-name), params.keys.sort
assert_equal %w(submit-name), params.keys.sort
assert_equal 'Larry', params['submit-name']
assert_equal nil, params['files']
end
Expand Down

0 comments on commit eced3d8

Please sign in to comment.