71 lines
1.6 KiB
Ruby
71 lines
1.6 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
module Gitlab
|
||
|
module WebHooks
|
||
|
class RateLimiter
|
||
|
include Gitlab::Utils::StrongMemoize
|
||
|
|
||
|
LIMIT_NAME = :web_hook_calls
|
||
|
NO_LIMIT = 0
|
||
|
# SystemHooks (instance admin hooks) and ServiceHooks (integration hooks)
|
||
|
# are not rate-limited.
|
||
|
EXCLUDED_HOOK_TYPES = %w(SystemHook ServiceHook).freeze
|
||
|
|
||
|
def initialize(hook)
|
||
|
@hook = hook
|
||
|
@parent = hook.parent
|
||
|
end
|
||
|
|
||
|
# Increments the rate-limit counter.
|
||
|
# Returns true if the hook should be rate-limited.
|
||
|
def rate_limit!
|
||
|
return false if no_limit?
|
||
|
|
||
|
::Gitlab::ApplicationRateLimiter.throttled?(
|
||
|
limit_name,
|
||
|
scope: [root_namespace],
|
||
|
threshold: limit
|
||
|
)
|
||
|
end
|
||
|
|
||
|
# Returns true if the hook is currently over its rate-limit.
|
||
|
# It does not increment the rate-limit counter.
|
||
|
def rate_limited?
|
||
|
return false if no_limit?
|
||
|
|
||
|
Gitlab::ApplicationRateLimiter.peek(
|
||
|
limit_name,
|
||
|
scope: [root_namespace],
|
||
|
threshold: limit
|
||
|
)
|
||
|
end
|
||
|
|
||
|
def limit
|
||
|
strong_memoize(:limit) do
|
||
|
next NO_LIMIT if hook.class.name.in?(EXCLUDED_HOOK_TYPES)
|
||
|
|
||
|
root_namespace.actual_limits.limit_for(limit_name) || NO_LIMIT
|
||
|
end
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
attr_reader :hook, :parent
|
||
|
|
||
|
def no_limit?
|
||
|
limit == NO_LIMIT
|
||
|
end
|
||
|
|
||
|
def root_namespace
|
||
|
@root_namespace ||= parent.root_ancestor
|
||
|
end
|
||
|
|
||
|
def limit_name
|
||
|
LIMIT_NAME
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
Gitlab::WebHooks::RateLimiter.prepend_mod
|