diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 8e28194a3..91cd7ce41 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -105,7 +105,7 @@ class AttachmentsController < ApplicationController return end - @attachment = Attachment.new(:file => request.body) + @attachment = Attachment.new(:file => raw_request_body) @attachment.author = User.current @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16) @attachment.content_type = params[:content_type].presence @@ -303,4 +303,14 @@ class AttachmentsController < ApplicationController def update_all_params params.permit(:attachments => [:filename, :description]).require(:attachments) end + + # Get an IO-like object for the request body which is usable to create a new + # attachment. We try to avoid having to read the whole body into memory. + def raw_request_body + if request.body.respond_to?(:size) + request.body + else + request.raw_post + end + end end diff --git a/test/integration/api_test/attachments_test.rb b/test/integration/api_test/attachments_test.rb index 538649950..1589e3f72 100644 --- a/test/integration/api_test/attachments_test.rb +++ b/test/integration/api_test/attachments_test.rb @@ -272,4 +272,29 @@ class Redmine::ApiTest::AttachmentsTest < Redmine::ApiTest::Base assert_equal 12, attachment.filesize assert File.exist? attachment.diskfile end + + test "POST /uploads.json should be compatible with a uwsgi's input" do + set_tmp_attachments_directory + assert_difference 'Attachment.count' do + request_body = Rack::RewindableInput.new(StringIO.new('File content')) + # Uwsgi_IO object does not have size method + request_body.instance_eval('undef :size', __FILE__, __LINE__) + post( + '/uploads.json', + :headers => { + "CONTENT_TYPE" => 'application/octet-stream', + "CONTENT_LENGTH" => '12', + "rack.input" => request_body + }.merge(credentials('jsmith')) + ) + assert_response :created + end + json = ActiveSupport::JSON.decode(response.body) + assert_kind_of Hash, json['upload'] + token = json['upload']['token'] + assert token.present? + assert attachment = Attachment.find_by_token(token) + assert_equal 12, attachment.filesize + assert File.exist? attachment.diskfile + end end