• Stars
    star
    274
  • Rank 150,274 (Top 3 %)
  • Language
    Ruby
  • Created about 12 years ago
  • Updated about 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Rack middleware for rate-limiting incoming HTTP requests configured to be used with Redis.

Redis Throttle Middleware

Build Status

This is a fork of the Rack Throttle middleware that provides logic for rate-limiting incoming HTTP requests to Rack applications using Redis as storage system. You can use Rack::RedisThrottle with any Ruby web framework based on Rack, including Ruby on Rails 3.0 and Sinatra. This gem was designed to experiment rate limit with Rails 3.x and Doorkeeper.

Thanks to Open Source

Redis Throttle Middleware come to life thanks to the work I've made in Lelylan, an open source microservices architecture for the Internet of Things. If this project helped you in any way, think about giving us a star on Github.

Features

  • Works only with Redis.
  • Automatically deploy by setting ENV['REDIS_RATE_LIMIT_URL'].
  • When the Redis connection is not available redis throttle skips the rate limit check (it does not blow up).
  • Automatically adds X-RateLimit-Limit and X-RateLimit-Remaining headers.
  • Set MockRedis while running your tests

Requirements

Redis Throttle is tested against MRI 1.9.3, 2.0, and 2.1.x.

Installation

Update your gem file and run bundle

gem 'redis-throttle', git: 'git://github.com/lelylan/redis-throttle.git'

Rails Example

# At the top of config/application.rb
require 'rack/redis_throttle'

# Inside the class of config/application.rb
class Application < Rails::Application
  # Limit the daily number of requests to 2500
  config.middleware.use Rack::RedisThrottle::Daily, max: 2500
end

Sinatra example

#!/usr/bin/env ruby -rubygems
require 'sinatra'
require 'rack/throttle'
use Rack::Throttle::Daily, max: 2500

Rack app example

#!/usr/bin/env rackup
require 'rack/throttle'
use Rack::Throttle::Daily max: 2500

run lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, world!\n"] }

Customizations

You can fully customize the implementation details of any of these strategies by simply subclassing one of the default implementations.

In our example we want to reach these goals:

  • We want to use Doorkeper as authorization system (OAuth2)
  • The number of daily requests are based on the user id and not the IP address (default in Rack::RedisThrottle)
  • The number of daily requests is dynamically set per user by the user#rate_limit field.

Now subclass Rack::RedisThrottle::Daily, create your own rules and use it in your Rails app

# /lib/middlewares/daily_rate_limit
require 'rack/redis_throttle'

class DailyRateLimit < Rack::RedisThrottle::Daily

  def call(env)
    @user_rate_limit = user_rate_limit(env)
    super
  end

  def client_identifier(request)
    @user_rate_limit.respond_to?(:_id) ? @user_rate_limit._id : 'user-unknown'
  end

  def max_per_window(request)
    @user_rate_limit.respond_to?(:rate_limit) ? @user_rate_limit.rate_limit : 1000
  end

  # Rate limit only requests sending the access token
  def need_protection?(request)
    request.env.has_key?('HTTP_AUTHORIZATION')
  end

  private

  def user_rate_limit(env)
    request      = Rack::Request.new(env)
    token        = request.env['HTTP_AUTHORIZATION'].split(' ')[-1]
    access_token = Doorkeeper::AccessToken.where(token: token).first
    access_token ? User.find(access_token.resource_owner_id) : nil
  end
end

Now you can use it in your Rails App.

# config/application.rb
module App
  class Application < Rails::Application

    # Puts your rate limit middleware as high as you can in your middleware stack
    config.middleware.insert_after Rack::Lock, 'DailyRateLimit'

Rate limit headers

Rack::RedisThrottle automatically sets two rate limits headers to let the client know the max number of requests and the one availables.

HTTP/1.1 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4999

When you exceed the API calls limit your request is forbidden.

HTTP/1.1 403 Forbidden
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 0

Testing your apps

While testing your Rack app Mock the redis connection by requiring this file

  # Rate limit fake redis connection
  require 'rack/redis_throttle/testing/connection'

HTTP client identification

The rate-limiting counters stored and maintained by Rack::RedisThrottle are keyed to unique HTTP clients. By default, HTTP clients are uniquely identified by their IP address as returned by Rack::Request#ip. If you wish to instead use a more granular, application-specific identifier such as a session key or a user account name, you need only subclass a throttling strategy implementation and override the #client_identifier method.

HTTP Response Codes and Headers

When a client exceeds their rate limit, Rack::RedisThrottle by default returns a "403 Forbidden" response with an associated "Rate Limit Exceeded" message in the response body. If you need personalize it, for example with a JSON message.

def http_error(request, code, message = nil, headers = {})
  [ code, { 'Content-Type' => 'application/json' }.merge(headers), [body(request).to_json] ]
end

def body(request)
  {
    status: 403,
    method: request.env['REQUEST_METHOD'],
    request: "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['PATH_INFO']}",
    description: 'Rate limit exceeded',
    daily_rate_limit: max_per_window(request)
  }
end

Notes

Testing coverage

Only Rack::RedisThrottle::Daily has a test suite. We will cover all the gem whenever I'll find more time and I'll see it being used widely.

Contributing

Fork the repo on github and send a pull requests with topic branches. Do not forget to provide specs to your contribution.

Running specs

  • Fork and clone the repository.
  • Run gem install bundler to get the latest for the gemset.
  • Run bundle install for dependencies.
  • Run bundle exec guard and press enter to execute all specs.

Spec guidelines

Follow betterspecs.org guidelines.

Coding guidelines

Follow github guidelines.

Feedback

Use the issue tracker for bugs. Mail or Tweet us for any idea that can improve the project.

Links

Authors

Andrea Reginato Thanks to Lelylan for letting me share the code.

Contributors

Special thanks to the following people for submitting patches.

Changelog

See CHANGELOG

Copyright

Redis Throttle is free and unencumbered public domain software. See LICENCE

More Repositories

1

simple-oauth2

A simple Node.js client library for Oauth2
JavaScript
1,591
star
2

lelylan

Open Source Lightweight Microservices Architecture for the Internet of Things. For developers.
1,529
star
3

rest-oauth2-server

Rest OAuth2 Server is a Rails 3 app that let you open up your API and manage end-user authentication and client application authorization implementing the OAuth 2.0 Specifications (draft 13).
Ruby
281
star
4

haproxy-mqtt

MQTT HAProxy
Shell
84
star
5

mqtt

Lelylan MQTT Server/Broker
JavaScript
42
star
6

device-directive-ng

AngularJS Web Component <device>
JavaScript
24
star
7

devices-dashboard-ng

Lelylan Devices Dashboard
JavaScript
14
star
8

api-proxy

Lelylan API proxy
JavaScript
13
star
9

lab-projects

Lelylan Lab Projects
HTML
12
star
10

dev

Lelylan Dev Center
HTML
9
star
11

devices

Device monitoring and control API
Ruby
6
star
12

lelylan-ng

AngularJS Client for Lelylan API
JavaScript
6
star
13

people

OAuth 2.0 user authentication and authorization.
Ruby
3
star
14

lelylan-rb

Ruby SDK for Lelylan
Ruby
3
star
15

physicals

Forward API requests to the physical world
CoffeeScript
3
star
16

nodes

two way communication between the cloud and the physical world
JavaScript
2
star
17

site

Lelylan Site
HTML
2
star
18

subscriptions

Lelylan realtime subscriptions API
Ruby
2
star
19

profiles

Lelylan Profile API
Ruby
2
star
20

websockets

Full-duplex communication over TCP
HTML
2
star
21

webhooks

Realtime HTTP notification
CoffeeScript
2
star
22

types

Lelylan type API (properties, functions ans statuses).
Ruby
1
star
23

type-directive-ng

AngularJS Web Component <lelylan-type>
JavaScript
1
star
24

types-dashboard-ng

Source code for Lelylan Types Dashboard
JavaScript
1
star