cavalle-rack-contrib 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/COPYING +18 -0
  2. data/README.rdoc +76 -0
  3. data/Rakefile +97 -0
  4. data/lib/rack/contrib.rb +33 -0
  5. data/lib/rack/contrib/accept_format.rb +44 -0
  6. data/lib/rack/contrib/backstage.rb +20 -0
  7. data/lib/rack/contrib/bounce_favicon.rb +16 -0
  8. data/lib/rack/contrib/callbacks.rb +37 -0
  9. data/lib/rack/contrib/config.rb +16 -0
  10. data/lib/rack/contrib/csshttprequest.rb +39 -0
  11. data/lib/rack/contrib/deflect.rb +137 -0
  12. data/lib/rack/contrib/etag.rb +20 -0
  13. data/lib/rack/contrib/evil.rb +12 -0
  14. data/lib/rack/contrib/garbagecollector.rb +14 -0
  15. data/lib/rack/contrib/jsonp.rb +41 -0
  16. data/lib/rack/contrib/lighttpd_script_name_fix.rb +16 -0
  17. data/lib/rack/contrib/locale.rb +31 -0
  18. data/lib/rack/contrib/mailexceptions.rb +120 -0
  19. data/lib/rack/contrib/nested_params.rb +143 -0
  20. data/lib/rack/contrib/not_found.rb +18 -0
  21. data/lib/rack/contrib/post_body_content_type_parser.rb +40 -0
  22. data/lib/rack/contrib/proctitle.rb +30 -0
  23. data/lib/rack/contrib/profiler.rb +106 -0
  24. data/lib/rack/contrib/relative_redirect.rb +44 -0
  25. data/lib/rack/contrib/response_cache.rb +59 -0
  26. data/lib/rack/contrib/route_exceptions.rb +49 -0
  27. data/lib/rack/contrib/sendfile.rb +142 -0
  28. data/lib/rack/contrib/signals.rb +63 -0
  29. data/lib/rack/contrib/time_zone.rb +25 -0
  30. data/rack-contrib.gemspec +87 -0
  31. data/test/404.html +1 -0
  32. data/test/Maintenance.html +1 -0
  33. data/test/mail_settings.rb +12 -0
  34. data/test/spec_rack_accept_format.rb +41 -0
  35. data/test/spec_rack_backstage.rb +26 -0
  36. data/test/spec_rack_callbacks.rb +65 -0
  37. data/test/spec_rack_config.rb +22 -0
  38. data/test/spec_rack_contrib.rb +8 -0
  39. data/test/spec_rack_csshttprequest.rb +66 -0
  40. data/test/spec_rack_deflect.rb +107 -0
  41. data/test/spec_rack_etag.rb +23 -0
  42. data/test/spec_rack_evil.rb +19 -0
  43. data/test/spec_rack_garbagecollector.rb +13 -0
  44. data/test/spec_rack_jsonp.rb +34 -0
  45. data/test/spec_rack_lighttpd_script_name_fix.rb +16 -0
  46. data/test/spec_rack_mailexceptions.rb +97 -0
  47. data/test/spec_rack_nested_params.rb +46 -0
  48. data/test/spec_rack_not_found.rb +17 -0
  49. data/test/spec_rack_post_body_content_type_parser.rb +32 -0
  50. data/test/spec_rack_proctitle.rb +26 -0
  51. data/test/spec_rack_profiler.rb +37 -0
  52. data/test/spec_rack_relative_redirect.rb +78 -0
  53. data/test/spec_rack_response_cache.rb +137 -0
  54. data/test/spec_rack_sendfile.rb +86 -0
  55. metadata +171 -0
data/COPYING ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2008 The Committers
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,76 @@
1
+ = Contributed Rack Middleware and Utilities
2
+
3
+ This package includes a variety of add-on components for Rack, a Ruby web server
4
+ interface:
5
+
6
+ * Rack::ETag - Automatically sets the ETag header on all String bodies.
7
+ * Rack::JSONP - Adds JSON-P support by stripping out the callback param
8
+ and padding the response with the appropriate callback format.
9
+ * Rack::LighttpdScriptNameFix - Fixes how lighttpd sets the SCRIPT_NAME
10
+ and PATH_INFO variables in certain configurations.
11
+ * Rack::Locale - Detects the client locale using the Accept-Language request
12
+ header and sets a rack.locale variable in the environment.
13
+ * Rack::MailExceptions - Rescues exceptions raised from the app and
14
+ sends a useful email with the exception, stacktrace, and contents of the
15
+ environment.
16
+ * Rack::NestedParams - parses form params with subscripts (e.g., * "post[title]=Hello")
17
+ into a nested/recursive Hash structure (based on Rails' implementation).
18
+ * Rack::PostBodyContentTypeParser - Adds support for JSON request bodies. The
19
+ Rack parameter hash is populated by deserializing the JSON data provided in
20
+ the request body when the Content-Type is application/json.
21
+ * Rack::ProcTitle - Displays request information in process title ($0) for
22
+ monitoring/inspection with ps(1).
23
+ * Rack::Profiler - Uses ruby-prof to measure request time.
24
+ * Rack::Sendfile - Enables X-Sendfile support for bodies that can be served
25
+ from file.
26
+ * Rack::Signals - Installs signal handlers that are safely processed after
27
+ a request
28
+ * Rack::TimeZone - Detects the clients timezone using JavaScript and sets
29
+ a variable in Rack's environment with the offset from UTC.
30
+ * Rack::Evil - Lets the rack application return a response to the client from any place.
31
+ * Rack::Callbacks - Implements DLS for pure before/after filter like Middlewares.
32
+ * Rack::Config - Shared configuration for cooperative middleware.
33
+ * Rack::NotFound - A default 404 application.
34
+ * Rack::CSSHTTPRequest - Adds CSSHTTPRequest support by encoding responses as
35
+ CSS for cross-site AJAX-style data loading
36
+ * Rack::Deflect - Helps protect against DoS attacks.
37
+ * Rack::ResponseCache - Caches responses to requests without query strings
38
+ to Disk or a user provider Ruby object. Similar to Rails' page caching.
39
+ * Rack::RelativeRedirect - Transforms relative paths in redirects to
40
+ absolute URLs.
41
+ * Rack::Backstage - Returns content of specified file if it exists, which makes
42
+ it convenient for putting up maintenance pages.
43
+ * Rack::Format - Adds a format extension at the end of the URI when there is none, corresponding to the mime-type given in the Accept HTTP header.
44
+ * Rack::HostMeta - Configures /host-meta using a block
45
+ * Rack::Cookies - Adds simple cookie jar hash to env
46
+
47
+ === Use
48
+
49
+ Git is the quickest way to the rack-contrib sources:
50
+
51
+ git clone git://github.com/rack/rack-contrib.git
52
+
53
+ Gems are currently available from GitHub clones:
54
+
55
+ gem install rack-rack-contrib --source=http://gems.github.com/
56
+
57
+ Requiring 'rack/contrib' will add autoloads to the Rack modules for all of the
58
+ components included. The following example shows what a simple rackup
59
+ (+config.ru+) file might look like:
60
+
61
+ require 'rack'
62
+ require 'rack/contrib'
63
+
64
+ use Rack::Profiler if ENV['RACK_ENV'] == 'development'
65
+
66
+ use Rack::ETag
67
+ use Rack::MailExceptions
68
+
69
+ run theapp
70
+
71
+ === Links
72
+
73
+ rack-contrib on GitHub:: <http://github.com/rack/rack-contrib>
74
+ Rack:: <http://rack.rubyforge.org/>
75
+ Rack On GitHub:: <http://github.org/rack/rack>
76
+ rack-devel mailing list:: <http://groups.google.com/group/rack-devel>
@@ -0,0 +1,97 @@
1
+ # Rakefile for Rack::Contrib. -*-ruby-*-
2
+ require 'rake/rdoctask'
3
+ require 'rake/testtask'
4
+
5
+ desc "Run all the tests"
6
+ task :default => [:test]
7
+
8
+ desc "Generate RDox"
9
+ task "RDOX" do
10
+ sh "specrb -Ilib:test -a --rdox >RDOX"
11
+ end
12
+
13
+ desc "Run specs with test/unit style output"
14
+ task :test do
15
+ sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
16
+ end
17
+
18
+ desc "Run specs with specdoc style output"
19
+ task :spec do
20
+ sh "specrb -Ilib:test -s -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
21
+ end
22
+
23
+ desc "Run all the tests"
24
+ task :fulltest do
25
+ sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
26
+ end
27
+
28
+ desc "Generate RDoc documentation"
29
+ Rake::RDocTask.new(:rdoc) do |rdoc|
30
+ rdoc.options << '--line-numbers' << '--inline-source' <<
31
+ '--main' << 'README' <<
32
+ '--title' << 'Rack Contrib Documentation' <<
33
+ '--charset' << 'utf-8'
34
+ rdoc.rdoc_dir = "doc"
35
+ rdoc.rdoc_files.include 'README.rdoc'
36
+ rdoc.rdoc_files.include 'RDOX'
37
+ rdoc.rdoc_files.include('lib/rack/*.rb')
38
+ rdoc.rdoc_files.include('lib/rack/*/*.rb')
39
+ end
40
+ task :rdoc => ["RDOX"]
41
+
42
+
43
+ # PACKAGING =================================================================
44
+
45
+ # load gemspec like github's gem builder to surface any SAFE issues.
46
+ require 'rubygems/specification'
47
+ $spec = eval(File.read('rack-contrib.gemspec'))
48
+
49
+ def package(ext='')
50
+ "pkg/rack-contrib-#{$spec.version}" + ext
51
+ end
52
+
53
+ desc 'Build packages'
54
+ task :package => %w[.gem .tar.gz].map {|e| package(e)}
55
+
56
+ desc 'Build and install as local gem'
57
+ task :install => package('.gem') do
58
+ sh "gem install #{package('.gem')}"
59
+ end
60
+
61
+ directory 'pkg/'
62
+
63
+ file package('.gem') => %w[pkg/ rack-contrib.gemspec] + $spec.files do |f|
64
+ sh "gem build rack-contrib.gemspec"
65
+ mv File.basename(f.name), f.name
66
+ end
67
+
68
+ file package('.tar.gz') => %w[pkg/] + $spec.files do |f|
69
+ sh "git archive --format=tar HEAD | gzip > #{f.name}"
70
+ end
71
+
72
+ desc 'Publish gem and tarball to rubyforge'
73
+ task 'publish:gem' => [package('.gem'), package('.tar.gz')] do |t|
74
+ sh <<-end
75
+ rubyforge add_release rack rack-contrib #{$spec.version} #{package('.gem')} &&
76
+ rubyforge add_file rack rack-contrib #{$spec.version} #{package('.tar.gz')}
77
+ end
78
+ end
79
+
80
+ # GEMSPEC ===================================================================
81
+
82
+ file 'rack-contrib.gemspec' => FileList['{lib,test}/**','Rakefile', 'README.rdoc'] do |f|
83
+ # read spec file and split out manifest section
84
+ spec = File.read(f.name)
85
+ parts = spec.split(" # = MANIFEST =\n")
86
+ fail 'bad spec' if parts.length != 3
87
+ # determine file list from git ls-files
88
+ files = `git ls-files`.
89
+ split("\n").sort.reject{ |file| file =~ /^\./ }.
90
+ map{ |file| " #{file}" }.join("\n")
91
+ # piece file back together and write...
92
+ parts[1] = " s.files = %w[\n#{files}\n ]\n"
93
+ spec = parts.join(" # = MANIFEST =\n")
94
+ spec.sub!(/s.date = '.*'/, "s.date = '#{Time.now.strftime("%Y-%m-%d")}'")
95
+ File.open(f.name, 'w') { |io| io.write(spec) }
96
+ puts "updated #{f.name}"
97
+ end
@@ -0,0 +1,33 @@
1
+ require 'rack'
2
+
3
+ module Rack
4
+ module Contrib
5
+ def self.release
6
+ "0.9.1"
7
+ end
8
+ end
9
+
10
+ autoload :BounceFavicon, "rack/contrib/bounce_favicon"
11
+ autoload :Cookies, "rack/contrib/cookies"
12
+ autoload :CSSHTTPRequest, "rack/contrib/csshttprequest"
13
+ autoload :Deflect, "rack/contrib/deflect"
14
+ autoload :ETag, "rack/contrib/etag"
15
+ autoload :GarbageCollector, "rack/contrib/garbagecollector"
16
+ autoload :JSONP, "rack/contrib/jsonp"
17
+ autoload :LighttpdScriptNameFix, "rack/contrib/lighttpd_script_name_fix"
18
+ autoload :Locale, "rack/contrib/locale"
19
+ autoload :MailExceptions, "rack/contrib/mailexceptions"
20
+ autoload :PostBodyContentTypeParser, "rack/contrib/post_body_content_type_parser"
21
+ autoload :ProcTitle, "rack/contrib/proctitle"
22
+ autoload :Profiler, "rack/contrib/profiler"
23
+ autoload :Sendfile, "rack/contrib/sendfile"
24
+ autoload :Signals, "rack/contrib/signals"
25
+ autoload :TimeZone, "rack/contrib/time_zone"
26
+ autoload :Evil, "rack/contrib/evil"
27
+ autoload :Callbacks, "rack/contrib/callbacks"
28
+ autoload :NestedParams, "rack/contrib/nested_params"
29
+ autoload :Config, "rack/contrib/config"
30
+ autoload :NotFound, "rack/contrib/not_found"
31
+ autoload :ResponseCache, "rack/contrib/response_cache"
32
+ autoload :RelativeRedirect, "rack/contrib/relative_redirect"
33
+ end
@@ -0,0 +1,44 @@
1
+ module Rack
2
+ #
3
+ # A Rack middleware for automatically adding a <tt>format</tt> token at the end of the request path
4
+ # when there is none. It can detect formats passed in the HTTP_ACCEPT header to populate this token.
5
+ #
6
+ # e.g.:
7
+ # GET /some/resource HTTP/1.1
8
+ # Accept: application/json
9
+ # ->
10
+ # GET /some/resource.json HTTP/1.1
11
+ # Accept: application/json
12
+ #
13
+ # You can add custom types with this kind of function (taken from sinatra):
14
+ # def mime(ext, type)
15
+ # ext = ".#{ext}" unless ext.to_s[0] == ?.
16
+ # Rack::Mime::MIME_TYPES[ext.to_s] = type
17
+ # end
18
+ # and then:
19
+ # mime :json, 'application/json'
20
+ #
21
+ # Note: it does not take into account multiple media types in the Accept header.
22
+ # The first media type takes precedence over all the others.
23
+ #
24
+ # MIT-License - Cyril Rohr
25
+ #
26
+ class AcceptFormat
27
+ # Constants
28
+ DEFAULT_EXTENSION = ".html"
29
+
30
+ def initialize(app)
31
+ @app = app
32
+ end
33
+
34
+ def call(env)
35
+ req = Rack::Request.new(env)
36
+ unless req.path_info =~ /(.*)\.(.+)/
37
+ accept = env['HTTP_ACCEPT'].scan(/[^;,\s]*\/[^;,\s]*/)[0] rescue ""
38
+ extension = Rack::Mime::MIME_TYPES.invert[accept] || DEFAULT_EXTENSION
39
+ req.path_info = req.path_info+"#{extension}"
40
+ end
41
+ @app.call(env)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ module Rack
2
+ class Backstage
3
+ File = ::File
4
+
5
+ def initialize(app, path)
6
+ @app = app
7
+ @file = File.expand_path(path)
8
+ end
9
+
10
+ def call(env)
11
+ if File.exists?(@file)
12
+ content = File.read(@file)
13
+ length = "".respond_to?(:bytesize) ? content.bytesize.to_s : content.size.to_s
14
+ [503, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]]
15
+ else
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module Rack
2
+ # Bounce those annoying favicon.ico requests
3
+ class BounceFavicon
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ if env["PATH_INFO"] == "/favicon.ico"
10
+ [404, {"Content-Type" => "text/html", "Content-Length" => "0"}, []]
11
+ else
12
+ @app.call(env)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ module Rack
2
+ class Callbacks
3
+ def initialize(&block)
4
+ @before = []
5
+ @after = []
6
+ instance_eval(&block) if block_given?
7
+ end
8
+
9
+ def before(middleware, *args, &block)
10
+ if block_given?
11
+ @before << middleware.new(*args, &block)
12
+ else
13
+ @before << middleware.new(*args)
14
+ end
15
+ end
16
+
17
+ def after(middleware, *args, &block)
18
+ if block_given?
19
+ @after << middleware.new(*args, &block)
20
+ else
21
+ @after << middleware.new(*args)
22
+ end
23
+ end
24
+
25
+ def run(app)
26
+ @app = app
27
+ end
28
+
29
+ def call(env)
30
+ @before.each {|c| c.call(env) }
31
+
32
+ response = @app.call(env)
33
+
34
+ @after.inject(response) {|r, c| c.call(r) }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ module Rack
2
+
3
+ # Rack::Config modifies the environment using the block given during
4
+ # initialization.
5
+ class Config
6
+ def initialize(app, &block)
7
+ @app = app
8
+ @block = block
9
+ end
10
+
11
+ def call(env)
12
+ @block.call(env)
13
+ @app.call(env)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ require 'csshttprequest'
2
+
3
+ module Rack
4
+
5
+ # A Rack middleware for providing CSSHTTPRequest responses.
6
+ class CSSHTTPRequest
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ # Proxies the request to the application then encodes the response with
13
+ # the CSSHTTPRequest encoder
14
+ def call(env)
15
+ status, headers, response = @app.call(env)
16
+ if chr_request?(env)
17
+ response = encode(response)
18
+ modify_headers!(headers, response)
19
+ end
20
+ [status, headers, response]
21
+ end
22
+
23
+ def chr_request?(env)
24
+ env['csshttprequest.chr'] ||=
25
+ !(/\.chr$/.match(env['PATH_INFO'])).nil? || Rack::Request.new(env).params['_format'] == 'chr'
26
+ end
27
+
28
+ def encode(response, assembled_body="")
29
+ response.each { |s| assembled_body << s } # call down the stack
30
+ return ::CSSHTTPRequest.encode(assembled_body)
31
+ end
32
+
33
+ def modify_headers!(headers, encoded_response)
34
+ headers['Content-Length'] = encoded_response.length.to_s
35
+ headers['Content-Type'] = 'text/css'
36
+ nil
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,137 @@
1
+ require 'thread'
2
+
3
+ # TODO: optional stats
4
+ # TODO: performance
5
+ # TODO: clean up tests
6
+
7
+ module Rack
8
+
9
+ ##
10
+ # Rack middleware for protecting against Denial-of-service attacks
11
+ # http://en.wikipedia.org/wiki/Denial-of-service_attack.
12
+ #
13
+ # This middleware is designed for small deployments, which most likely
14
+ # are not utilizing load balancing from other software or hardware. Deflect
15
+ # current supports the following functionality:
16
+ #
17
+ # * Saturation prevention (small DoS attacks, or request abuse)
18
+ # * Blacklisting of remote addresses
19
+ # * Whitelisting of remote addresses
20
+ # * Logging
21
+ #
22
+ # === Options:
23
+ #
24
+ # :log When false logging will be bypassed, otherwise pass an object responding to #puts
25
+ # :log_format Alter the logging format
26
+ # :log_date_format Alter the logging date format
27
+ # :request_threshold Number of requests allowed within the set :interval. Defaults to 100
28
+ # :interval Duration in seconds until the request counter is reset. Defaults to 5
29
+ # :block_duration Duration in seconds that a remote address will be blocked. Defaults to 900 (15 minutes)
30
+ # :whitelist Array of remote addresses which bypass Deflect. NOTE: this does not block others
31
+ # :blacklist Array of remote addresses immediately considered malicious
32
+ #
33
+ # === Examples:
34
+ #
35
+ # use Rack::Deflect, :log => $stdout, :request_threshold => 20, :interval => 2, :block_duration => 60
36
+ #
37
+ # CREDIT: TJ Holowaychuk <tj@vision-media.ca>
38
+ #
39
+
40
+ class Deflect
41
+
42
+ attr_reader :options
43
+
44
+ def initialize app, options = {}
45
+ @mutex = Mutex.new
46
+ @remote_addr_map = {}
47
+ @app, @options = app, {
48
+ :log => false,
49
+ :log_format => 'deflect(%s): %s',
50
+ :log_date_format => '%m/%d/%Y',
51
+ :request_threshold => 100,
52
+ :interval => 5,
53
+ :block_duration => 900,
54
+ :whitelist => [],
55
+ :blacklist => []
56
+ }.merge(options)
57
+ end
58
+
59
+ def call env
60
+ return deflect! if deflect? env
61
+ status, headers, body = @app.call env
62
+ [status, headers, body]
63
+ end
64
+
65
+ def deflect!
66
+ [403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, '']
67
+ end
68
+
69
+ def deflect? env
70
+ @remote_addr = env['REMOTE_ADDR']
71
+ return false if options[:whitelist].include? @remote_addr
72
+ return true if options[:blacklist].include? @remote_addr
73
+ sync { watch }
74
+ end
75
+
76
+ def log message
77
+ return unless options[:log]
78
+ options[:log].puts(options[:log_format] % [Time.now.strftime(options[:log_date_format]), message])
79
+ end
80
+
81
+ def sync &block
82
+ @mutex.synchronize(&block)
83
+ end
84
+
85
+ def map
86
+ @remote_addr_map[@remote_addr] ||= {
87
+ :expires => Time.now + options[:interval],
88
+ :requests => 0
89
+ }
90
+ end
91
+
92
+ def watch
93
+ increment_requests
94
+ clear! if watch_expired? and not blocked?
95
+ clear! if blocked? and block_expired?
96
+ block! if watching? and exceeded_request_threshold?
97
+ blocked?
98
+ end
99
+
100
+ def block!
101
+ return if blocked?
102
+ log "blocked #{@remote_addr}"
103
+ map[:block_expires] = Time.now + options[:block_duration]
104
+ end
105
+
106
+ def blocked?
107
+ map.has_key? :block_expires
108
+ end
109
+
110
+ def block_expired?
111
+ map[:block_expires] < Time.now rescue false
112
+ end
113
+
114
+ def watching?
115
+ @remote_addr_map.has_key? @remote_addr
116
+ end
117
+
118
+ def clear!
119
+ return unless watching?
120
+ log "released #{@remote_addr}" if blocked?
121
+ @remote_addr_map.delete @remote_addr
122
+ end
123
+
124
+ def increment_requests
125
+ map[:requests] += 1
126
+ end
127
+
128
+ def exceeded_request_threshold?
129
+ map[:requests] > options[:request_threshold]
130
+ end
131
+
132
+ def watch_expired?
133
+ map[:expires] <= Time.now
134
+ end
135
+
136
+ end
137
+ end