mirror of
https://github.com/discourse/discourse.git
synced 2025-05-25 09:57:25 +08:00
FEATURE: set 'Retry-After' header for 429 responses (#5659)
This commit is contained in:
@ -107,7 +107,15 @@ class ApplicationController < ActionController::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_rate_limit_error(e)
|
def render_rate_limit_error(e)
|
||||||
render_json_error e.description, type: :rate_limit, status: 429, extras: { wait_seconds: e&.available_in }
|
retry_time_in_seconds = e&.available_in
|
||||||
|
|
||||||
|
render_json_error(
|
||||||
|
e.description,
|
||||||
|
type: :rate_limit,
|
||||||
|
status: 429,
|
||||||
|
extras: { wait_seconds: retry_time_in_seconds },
|
||||||
|
headers: { 'Retry-After': retry_time_in_seconds },
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# If they hit the rate limiter
|
# If they hit the rate limiter
|
||||||
@ -523,12 +531,15 @@ class ApplicationController < ActionController::Base
|
|||||||
|
|
||||||
# Render action for a JSON error.
|
# Render action for a JSON error.
|
||||||
#
|
#
|
||||||
# obj - a translated string, an ActiveRecord model, or an array of translated strings
|
# obj - a translated string, an ActiveRecord model, or an array of translated strings
|
||||||
# opts:
|
# opts:
|
||||||
# type - a machine-readable description of the error
|
# type - a machine-readable description of the error
|
||||||
# status - HTTP status code to return
|
# status - HTTP status code to return
|
||||||
|
# headers - extra headers for the response
|
||||||
def render_json_error(obj, opts = {})
|
def render_json_error(obj, opts = {})
|
||||||
opts = { status: opts } if opts.is_a?(Integer)
|
opts = { status: opts } if opts.is_a?(Integer)
|
||||||
|
opts.fetch(:headers, {}).each { |name, value| headers[name.to_s] = value }
|
||||||
|
|
||||||
render json: MultiJson.dump(create_errors_json(obj, opts)), status: opts[:status] || 422
|
render json: MultiJson.dump(create_errors_json(obj, opts)), status: opts[:status] || 422
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ MessageBus.on_middleware_error do |env, e|
|
|||||||
if Discourse::InvalidAccess === e
|
if Discourse::InvalidAccess === e
|
||||||
[403, {}, ["Invalid Access"]]
|
[403, {}, ["Invalid Access"]]
|
||||||
elsif RateLimiter::LimitExceeded === e
|
elsif RateLimiter::LimitExceeded === e
|
||||||
[429, {}, [e.description]]
|
[429, { 'Retry-After' => e.available_in }, [e.description]]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ describe 'rate limiter integration' do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can cleanly limit requests' do
|
it 'can cleanly limit requests and sets a Retry-After header' do
|
||||||
freeze_time
|
freeze_time
|
||||||
#request.set_header("action_dispatch.show_exceptions", true)
|
#request.set_header("action_dispatch.show_exceptions", true)
|
||||||
|
|
||||||
@ -50,6 +50,7 @@ describe 'rate limiter integration' do
|
|||||||
|
|
||||||
data = JSON.parse(response.body)
|
data = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(response.headers['Retry-After']).to eq(60)
|
||||||
expect(data["extras"]["wait_seconds"]).to eq(60)
|
expect(data["extras"]["wait_seconds"]).to eq(60)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user