Class: Deflectable::Watcher

Inherits:
Object
  • Object
show all
Defined in:
lib/deflectable/watcher.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ Watcher

Returns a new instance of Watcher.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/deflectable/watcher.rb', line 7

def initialize(app, options = {})
  @mutex = Mutex.new
  @app = app
  conf = YAML.load_file(Rails.root.join('config/deflect.yml')) rescue {}
  @options = {
    :log => true,
    :log_format => 'deflect(%s): %s',
    :log_date_format => '%m/%d/%Y',
    :request_threshold => 100,
    :interval => 5,
    :block_duration => 900,
    :whitelist => [],
    :blacklist => [],
  }.merge(conf)
end

Instance Attribute Details

#optionsObject

Returns the value of attribute options.



5
6
7
# File 'lib/deflectable/watcher.rb', line 5

def options
  @options
end

Instance Method Details

#block!Object



60
61
62
63
64
# File 'lib/deflectable/watcher.rb', line 60

def block!
  return if blocked?
  log "blocked #{@remote_addr}"
  map[:block_expires] = Time.now + options[:block_duration]
end

#block_expired?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/deflectable/watcher.rb', line 70

def block_expired?
  map[:block_expires] < Time.now rescue false
end

#blocked?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/deflectable/watcher.rb', line 66

def blocked?
  map.has_key? :block_expires
end

#call(env) ⇒ Object



23
24
25
26
27
# File 'lib/deflectable/watcher.rb', line 23

def call(env)
  return reject! if detect?(env)
  status, headers, body = @app.call(env)
  [status, headers, body]
end

#clear!Object



78
79
80
81
82
# File 'lib/deflectable/watcher.rb', line 78

def clear!
  return unless watching?
  log "released #{@remote_addr}" if blocked?
  @remote_addr_map.delete @remote_addr
end

#detect?(env) ⇒ Boolean

Returns:

  • (Boolean)


38
39
40
41
42
43
# File 'lib/deflectable/watcher.rb', line 38

def detect?(env)
  @remote_addr = env['REMOTE_ADDR']
  return false if options[:whitelist].include? @remote_addr
  return true  if options[:blacklist].include? @remote_addr
  @mutex.synchronize { watch }
end

#exceeded_request_threshold?Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/deflectable/watcher.rb', line 88

def exceeded_request_threshold?
  map[:requests] > options[:request_threshold]
end

#increment_requestsObject



84
85
86
# File 'lib/deflectable/watcher.rb', line 84

def increment_requests
  map[:requests] += 1
end

#log(message) ⇒ Object



96
97
98
99
# File 'lib/deflectable/watcher.rb', line 96

def log message
  return unless options[:log]
  options[:log].puts(options[:log_format] % [Time.now.strftime(options[:log_date_format]), message])
end

#mapObject



53
54
55
56
57
58
# File 'lib/deflectable/watcher.rb', line 53

def map
  @remote_addr_map[@remote_addr] ||= {
    :expires => Time.now + options[:interval],
    :requests => 0
  }
end

#reject!Object



29
30
31
32
33
34
35
36
# File 'lib/deflectable/watcher.rb', line 29

def reject!
  res = Rack::Response.new do |r|
    r.status = 403
    r['Content-Type'] = 'text/html;charset=utf-8'
    r.write File.read(Rails.root.join('public/403.html'))
  end
  res.finish
end

#watchObject



45
46
47
48
49
50
51
# File 'lib/deflectable/watcher.rb', line 45

def watch
  increment_requests
  clear! if watch_expired? and not blocked?
  clear! if blocked? and block_expired?
  block! if watching? and exceeded_request_threshold?
  blocked?
end

#watch_expired?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/deflectable/watcher.rb', line 92

def watch_expired?
  map[:expires] <= Time.now
end

#watching?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/deflectable/watcher.rb', line 74

def watching?
  @remote_addr_map.has_key? @remote_addr
end