lib/rack/throttle/limiter.rb in rack-throttle-0.3.0 vs lib/rack/throttle/limiter.rb in rack-throttle-0.4.0

- old
+ new

@@ -29,11 +29,11 @@ # @param [Hash{String => String}] env # @return [Array(Integer, Hash, #each)] # @see http://rack.rubyforge.org/doc/SPEC.html def call(env) request = Rack::Request.new(env) - allowed?(request) ? app.call(env) : rate_limit_exceeded + allowed?(request) ? app.call(env) : rate_limit_exceeded(request) end ## # Returns `false` if the rate limit has been exceeded for the given # `request`, or `true` otherwise. @@ -122,11 +122,11 @@ def cache_set(key, value) case when cache.respond_to?(:[]=) begin cache[key] = value - rescue TypeError => e + rescue TypeError # GDBM throws a "TypeError: can't convert Float into String" # exception when trying to store a Float. On the other hand, we # don't want to unnecessarily coerce the value to a String for # any stores that do support other data types (e.g. in-memory # hash objects). So, this is a compromise. @@ -160,23 +160,25 @@ ## # @param [Rack::Request] request # @return [Float] def request_start_time(request) - case - when request.env.has_key?('HTTP_X_REQUEST_START') - request.env['HTTP_X_REQUEST_START'].to_f / 1000 - else - Time.now.to_f + # Check whether HTTP_X_REQUEST_START or HTTP_X_QUEUE_START exist and parse its value (for + # example, when having nginx in your stack, it's going to be in the "t=\d+" format). + if val = (request.env['HTTP_X_REQUEST_START'] || request.env['HTTP_X_QUEUE_START']) + val[/(?:^t=)?(\d+)/, 1].to_f / 1000 + else + Time.now.to_f end end ## # Outputs a `Rate Limit Exceeded` error. # # @return [Array(Integer, Hash, #each)] - def rate_limit_exceeded + def rate_limit_exceeded(request) + options[:rate_limit_exceeded_callback].call(request) if options[:rate_limit_exceeded_callback] headers = respond_to?(:retry_after) ? {'Retry-After' => retry_after.to_f.ceil.to_s} : {} http_error(options[:code] || 403, options[:message] || 'Rate Limit Exceeded', headers) end ## @@ -186,10 +188,10 @@ # @param [String, #to_s] message # @param [Hash{String => String}] headers # @return [Array(Integer, Hash, #each)] def http_error(code, message = nil, headers = {}) [code, {'Content-Type' => 'text/plain; charset=utf-8'}.merge(headers), - http_status(code) + (message.nil? ? "\n" : " (#{message})\n")] + [http_status(code), (message.nil? ? "\n" : " (#{message})\n")]] end ## # Returns the standard HTTP status message for the given status `code`. #