diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b05128453..d29c2c17f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -41,15 +41,18 @@ protected def api_check if request.format.to_s =~ /\/json|\/xml/ || params[:controller] == "iqdb" - if ApiLimiter.throttled?(request.remote_ip, request.request_method) - render :text => "421 User Throttled\n", :layout => false, :status => 421 + if ApiLimiter.throttled?(CurrentUser.id || request.remote_ip, request.request_method) + render :text => "429 Too Many Requests\n", :layout => false, :status => 429 return false end + # elsif request.format.to_s =~ /\/html/ && !ApiLimiter.idempotent?(request.request_method) + # if ApiLimiter.throttled?(CurrentUser.id || request.remote_ip, request.request_method) + # render :template => "static/too_many_requests", :status => 429 + # end end return true end - def rescue_exception(exception) @exception = exception diff --git a/app/logical/api_limiter.rb b/app/logical/api_limiter.rb index 8889ce3b2..e593439e6 100644 --- a/app/logical/api_limiter.rb +++ b/app/logical/api_limiter.rb @@ -1,5 +1,5 @@ module ApiLimiter - def idempotent?(method) + def self.idempotent?(method) case method when "POST", "PUT", "DELETE" false @@ -9,18 +9,18 @@ module ApiLimiter end end - def throttled?(ip_addr, http_method = "GET") - idempotent = idempotent?(http_method) - key = "api/#{ip_addr}/#{Time.now.hour}/#{idempotent}" + def throttled?(user_key, http_method = "GET") + idempotent = ApiLimiter.idempotent?(http_method) + key = "api/#{user_key}/#{Time.now.hour}/#{idempotent}" MEMCACHE.fetch(key, 1.hour, :raw => true) {0} MEMCACHE.incr(key).to_i > CurrentUser.user.api_hourly_limit(idempotent) end - def remaining_hourly_limit(ip_addr, idempotent = true) - key = "api/#{ip_addr}/#{Time.now.hour}/#{idempotent}" + def remaining_hourly_limit(user_key, idempotent = true) + key = "api/#{user_key}/#{Time.now.hour}/#{idempotent}" requests = MEMCACHE.fetch(key, 1.hour, :raw => true) {0}.to_i CurrentUser.user.api_hourly_limit - requests end - module_function :throttled?, :idempotent?, :remaining_hourly_limit + module_function :throttled?, :remaining_hourly_limit end diff --git a/app/views/static/too_many_requests.html.erb b/app/views/static/too_many_requests.html.erb new file mode 100644 index 000000000..9d37d0aa0 --- /dev/null +++ b/app/views/static/too_many_requests.html.erb @@ -0,0 +1,2 @@ +

Too Many Requests

+

You can only make <%= CurrentUser.api_hourly_limit(false) %> updates and <%= CurrentUser.api_hourly_limit(true) %> reads per hour.

diff --git a/test/functional/posts_controller_test.rb b/test/functional/posts_controller_test.rb index 044268177..ae46b490c 100644 --- a/test/functional/posts_controller_test.rb +++ b/test/functional/posts_controller_test.rb @@ -29,7 +29,7 @@ class PostsControllerTest < ActionController::TestCase end get :index, {:format => "json", :login => @user.name, :api_key => @user.api_key.key} - assert_response 421 + assert_response 429 end end