70 lines
1.6 KiB
Ruby
70 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
|