Class: WebHookService

Inherits:
Object
  • Object
show all
Includes:
Gitlab::Utils::StrongMemoize
Defined in:
app/services/web_hook_service.rb

Defined Under Namespace

Classes: InternalErrorResponse

Constant Summary collapse

CustomWebHookTemplateError =
Class.new(StandardError)
REQUEST_BODY_SIZE_LIMIT =
25.megabytes
RESPONSE_BODY_SIZE_LIMIT =

Response body is for UI display only. It does not make much sense to save whatever the receivers throw back at us

8.kilobytes
RESPONSE_HEADERS_COUNT_LIMIT =

The headers are for debugging purpose. They are displayed on the UI only.

50
RESPONSE_HEADERS_SIZE_LIMIT =
1.kilobyte
CUSTOM_TEMPLATE_INTERPOLATION_REGEX =
/{{(.+?)}}/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hook, data, hook_name, uniqueness_token = nil, idempotency_key: nil, force: false) ⇒ WebHookService

Returns a new instance of WebHookService.



51
52
53
54
55
56
57
58
59
60
61
62
# File 'app/services/web_hook_service.rb', line 51

def initialize(hook, data, hook_name, uniqueness_token = nil, idempotency_key: nil, force: false)
  @hook = hook
  @data = data.to_h
  @hook_name = hook_name.to_s
  @uniqueness_token = uniqueness_token
  @idempotency_key = idempotency_key || generate_idempotency_key
  @force = force
  @request_options = {
    timeout: Gitlab.config.gitlab.webhook_timeout,
    allow_local_requests: hook.allow_local_requests?
  }
end

Instance Attribute Details

#dataObject

Returns the value of attribute data.



42
43
44
# File 'app/services/web_hook_service.rb', line 42

def data
  @data
end

#hookObject

Returns the value of attribute hook.



42
43
44
# File 'app/services/web_hook_service.rb', line 42

def hook
  @hook
end

#hook_nameObject

Returns the value of attribute hook_name.



42
43
44
# File 'app/services/web_hook_service.rb', line 42

def hook_name
  @hook_name
end

#idempotency_keyObject (readonly)

Returns the value of attribute idempotency_key.



43
44
45
# File 'app/services/web_hook_service.rb', line 43

def idempotency_key
  @idempotency_key
end

#request_optionsObject

Returns the value of attribute request_options.



42
43
44
# File 'app/services/web_hook_service.rb', line 42

def request_options
  @request_options
end

#uniqueness_tokenObject (readonly)

Returns the value of attribute uniqueness_token.



43
44
45
# File 'app/services/web_hook_service.rb', line 43

def uniqueness_token
  @uniqueness_token
end

Class Method Details

.hook_to_event(hook_name, hook = nil) ⇒ Object



45
46
47
48
49
# File 'app/services/web_hook_service.rb', line 45

def self.hook_to_event(hook_name, hook = nil)
  return hook.class.name.titleize if hook.is_a?(SystemHook)

  hook_name.to_s.singularize.titleize
end

Instance Method Details

#async_executeObject



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'app/services/web_hook_service.rb', line 117

def async_execute
  Gitlab::ApplicationContext.with_context(hook.application_context) do
    break log_silent_mode_enabled if Gitlab::SilentMode.enabled?
    break log_rate_limited if rate_limit!
    break log_recursion_blocked if recursion_blocked?

    params = {
      "recursion_detection_request_uuid" => Gitlab::WebHooks::RecursionDetection::UUID.instance.request_uuid,
      "idempotency_key" => idempotency_key
    }.compact

    WebHookWorker.perform_async(hook.id, data.deep_stringify_keys, hook_name.to_s, params)
  end
end

#disabled?Boolean

Returns:

  • (Boolean)


64
65
66
# File 'app/services/web_hook_service.rb', line 64

def disabled?
  !@force && !hook.executable?
end

#executeObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'app/services/web_hook_service.rb', line 68

def execute
  if Gitlab::SilentMode.enabled?
    log_silent_mode_enabled
    return ServiceResponse.error(message: 'Silent mode enabled')
  end

  return ServiceResponse.error(message: 'Hook disabled') if disabled?

  if recursion_blocked?
    log_recursion_blocked
    return ServiceResponse.error(message: 'Recursive webhook blocked')
  end

  Gitlab::WebHooks::RecursionDetection.register!(hook)

  start_time = Gitlab::Metrics::System.monotonic_time

  response = if parsed_url.userinfo.blank?
               make_request(parsed_url.to_s)
             else
               make_request_with_auth
             end

  log_execution(
    response: response,
    execution_duration: ::Gitlab::Metrics::System.monotonic_time - start_time
  )

  ServiceResponse.success(message: response.body, payload: { http_status: response.code })
rescue *Gitlab::HTTP::HTTP_ERRORS, CustomWebHookTemplateError, Zlib::DataError,
  Gitlab::Json::LimitedEncoder::LimitExceeded, URI::InvalidURIError => e
  execution_duration = ::Gitlab::Metrics::System.monotonic_time - start_time
  error_message = e.to_s

  # An exception raised while rendering the custom template prevents us from calling `#request_payload`
  request_data = e.instance_of?(CustomWebHookTemplateError) ? {} : request_payload

  log_execution(
    response: InternalErrorResponse.new,
    execution_duration: execution_duration,
    error_message: error_message,
    request_data: request_data
  )

  Gitlab::AppLogger.error("WebHook Error after #{execution_duration.to_i.seconds}s => #{e}")

  ServiceResponse.error(message: error_message)
end