-
Notifications
You must be signed in to change notification settings - Fork 561
Fix multipart forms without explicit Conent-Length header #2150
Description
Uploading multipart forms without setting the Content-Length header fails for Passenger on Rack 2.x releases. This is because Rack itself does not conform to it's Input Stream SPEC and calls an #eof? on an input stream in the case of no Content-Length header:
def self.parse(io, content_length, content_type, tmpfile, bufsize, qp)
return EMPTY if 0 == content_length
boundary = parse_boundary content_type
return EMPTY unless boundary
io = BoundedIO.new(io, content_length) if content_length
parser = new(boundary, tmpfile, bufsize, qp)
parser.on_read io.read(bufsize), io.eof?
loop do
break if parser.state == :DONE
parser.on_read io.read(bufsize), io.eof?
end
io.rewind
parser.result
endAt this point, the io is of type PhusionPassenger::Utils::TeeInput, which does not implement the #eof?method. This is pretty legit, as the Rack SPEC does not require it. However, in the case of no content length, no BoundedIO wrapping is happening, which leaves a raw call to the #eof? method of PhusionPassenger::Utils::TeeInput, which leads to:
NoMethodError: undefined method `eof?' for #<PhusionPassenger::Utils::TeeInput:0x0000563c49844cf8>
This is where it gets interesting. This problem was found out and fixed in 2017 by rack/rack#1201. The commit sat in master and wasn't ported to rack's 2-0-stable branch, where the 2.* are built from. Yesterday the rack maintainers backported the change into the 2-0-stable branch, but no release have been made yet.
While you are completely complying to the spec, it may be a good idea to put the #eof? method on the TeeInput class for the folks who cannot run on 2-0-stable or are locked to a specific rack version, but can fix the issue by updating Passenger.