Skip to content

Commit

Permalink
Handle Rack errors when too many files are uploaded (#2256)
Browse files Browse the repository at this point in the history
* Capture Rack's "too many multipart files" error with a spec

* Create a custom error class to handle Rack's multipart error

* fixup! Capture Rack's "too many multipart files" error with a spec

* Use our custom error class when Rack raises a multipart limit error

* Use a Payload Too Large status code for TooManyMultipartFiles errors

* Restore Rack's multipart limit after testing the failure

* Upadate CHANGELOG

* Reword CHANGELOG entry for MultipartPartLimitError change

* Backticks around classname in CHANGELOG

* Change next version of Grape from 1.6.3 to 1.7.0

The introduction of the TooManyMultipartFiles changes API behavior, so bump the
minor version.

* Include the system's configured multipart file limit in the error message

The number of allowed multipart files is a configurable value in Rack, pull that
limit and include it in the generated error message.

* Add a note to UPGRADING about the new TooManyMultipartFiles exception
  • Loading branch information
bschmeck authored Apr 26, 2022
1 parent e3451c8 commit 75276be
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 4 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
### 1.6.3 (Next)
### 1.7.0 (Next)

#### Features

Expand All @@ -18,6 +18,7 @@
* [#2227](https://github.com/ruby-grape/grape/pull/2222): Rename `MissingGroupType` and `UnsupportedGroupType` exceptions - [@ericproulx](https://github.com/ericproulx).
* [#2244](https://github.com/ruby-grape/grape/pull/2244): Fix a breaking change in `Grape::Validations` provided in 1.6.1 - [@dm1try](https://github.com/dm1try).
* [#2250](https://github.com/ruby-grape/grape/pull/2250): Add deprecation warning for `UnsupportedGroupTypeError` and `MissingGroupTypeError` - [@ericproulx](https://github.com/ericproulx).
* [#2256](https://github.com/ruby-grape/grape/pull/2256): Raise `Grape::Exceptions::MultipartPartLimitError` from Rack when too many files are uploaded - [@bschmeck](https://github.com/bschmeck).
* Your contribution here.

### 1.6.2 (2021/12/30)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ content negotiation, versioning and much more.

## Stable Release

You're reading the documentation for the next release of Grape, which should be **1.6.3**.
You're reading the documentation for the next release of Grape, which should be **1.7.0**.
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
The current stable release is [1.6.2](https://github.com/ruby-grape/grape/blob/v1.6.2/README.md).

Expand Down
6 changes: 5 additions & 1 deletion UPGRADING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Upgrading Grape
===============

### Upgrading to >= 1.6.3
### Upgrading to >= 1.7.0

#### Exceptions renaming

Expand All @@ -12,6 +12,10 @@ The following exceptions has been renamed for consistency through exceptions nam

See [#2227](https://github.com/ruby-grape/grape/pull/2227) for more information.

#### Handling Multipart Limit Errors

Rack supports a configurable limit on the number of files created from multipart parameters (`Rack::Utils.multipart_part_limit`) and raises an error if params are received that create too many files. If you were handling the Rack error directly, Grape now wraps that error in `Grape::Execeptions::TooManyMultipartFiles`. Additionally, Grape will return a 413 status code if the exception goes unhandled.

### Upgrading to >= 1.6.0

#### Parameter renaming with :as
Expand Down
1 change: 1 addition & 0 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ module Exceptions
autoload :MethodNotAllowed
autoload :InvalidResponse
autoload :EmptyMessageBody
autoload :TooManyMultipartFiles
autoload :MissingGroupTypeError, 'grape/exceptions/missing_group_type'
autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
end
Expand Down
11 changes: 11 additions & 0 deletions lib/grape/exceptions/too_many_multipart_files.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Grape
module Exceptions
class TooManyMultipartFiles < Base
def initialize(limit)
super(message: compose_message(:too_many_multipart_files, limit: limit), status: 413)
end
end
end
end
1 change: 1 addition & 0 deletions lib/grape/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ en:
%{body_format} in the request's 'body'
"
empty_message_body: 'Empty message body supplied with %{body_format} content-type'
too_many_multipart_files: "The number of uploaded files exceeded the system's configured limit (%{limit})"
invalid_accept_header:
problem: 'Invalid accept header'
resolution: '%{message}'
Expand Down
2 changes: 2 additions & 0 deletions lib/grape/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ def params
@params ||= build_params
rescue EOFError
raise Grape::Exceptions::EmptyMessageBody.new(content_type)
rescue Rack::Multipart::MultipartPartLimitError
raise Grape::Exceptions::TooManyMultipartFiles.new(Rack::Utils.multipart_part_limit)
end

def headers
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

module Grape
# The current version of Grape.
VERSION = '1.6.3'
VERSION = '1.7.0'
end
21 changes: 21 additions & 0 deletions spec/grape/endpoint_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,27 @@ def app
end
end

context 'when the limit on multipart files is exceeded' do
around do |example|
limit = Rack::Utils.multipart_part_limit
Rack::Utils.multipart_part_limit = 1
example.run
Rack::Utils.multipart_part_limit = limit
end

it 'returns a 413 if given too many multipart files' do
subject.params do
requires :file, type: Rack::Multipart::UploadedFile
end
subject.post '/upload' do
params[:file][:filename]
end
post '/upload', { file: Rack::Test::UploadedFile.new(__FILE__, 'text/plain'), extra: Rack::Test::UploadedFile.new(__FILE__, 'text/plain') }
expect(last_response.status).to eq(413)
expect(last_response.body).to eq("The number of uploaded files exceeded the system's configured limit (1)")
end
end

it 'responds with a 415 for an unsupported content-type' do
subject.format :json
# subject.content_type :json, "application/json"
Expand Down

0 comments on commit 75276be

Please sign in to comment.