debian-mirror-gitlab/app/services/web_hook_service.rb

134 lines
3.6 KiB
Ruby
Raw Normal View History

2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2017-09-10 17:25:29 +05:30
class WebHookService
class InternalErrorResponse
attr_reader :body, :headers, :code
def initialize
2018-03-26 14:24:53 +05:30
@headers = Gitlab::HTTP::Response::Headers.new({})
2017-09-10 17:25:29 +05:30
@body = ''
@code = 'internal error'
end
end
2018-03-26 14:24:53 +05:30
attr_accessor :hook, :data, :hook_name, :request_options
2017-09-10 17:25:29 +05:30
def initialize(hook, data, hook_name)
@hook = hook
@data = data
2018-03-17 18:26:18 +05:30
@hook_name = hook_name.to_s
2018-03-26 14:24:53 +05:30
@request_options = { timeout: Gitlab.config.gitlab.webhook_timeout }
@request_options.merge!(allow_local_requests: true) if @hook.is_a?(SystemHook)
2017-09-10 17:25:29 +05:30
end
def execute
start_time = Time.now
response = if parsed_url.userinfo.blank?
make_request(hook.url)
else
make_request_with_auth
end
log_execution(
trigger: hook_name,
url: hook.url,
request_data: data,
response: response,
execution_duration: Time.now - start_time
)
{
status: :success,
http_status: response.code,
message: response.to_s
}
2018-10-15 14:42:47 +05:30
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError => e
2017-09-10 17:25:29 +05:30
log_execution(
trigger: hook_name,
url: hook.url,
request_data: data,
response: InternalErrorResponse.new,
execution_duration: Time.now - start_time,
error_message: e.to_s
)
Rails.logger.error("WebHook Error => #{e}")
{
status: :error,
message: e.to_s
}
end
def async_execute
2018-03-17 18:26:18 +05:30
WebHookWorker.perform_async(hook.id, data, hook_name)
2017-09-10 17:25:29 +05:30
end
private
def parsed_url
@parsed_url ||= URI.parse(hook.url)
end
def make_request(url, basic_auth = false)
2018-03-26 14:24:53 +05:30
Gitlab::HTTP.post(url,
2017-09-10 17:25:29 +05:30
body: data.to_json,
headers: build_headers(hook_name),
verify: hook.enable_ssl_verification,
2018-03-26 14:24:53 +05:30
basic_auth: basic_auth,
**request_options)
2017-09-10 17:25:29 +05:30
end
def make_request_with_auth
post_url = hook.url.gsub("#{parsed_url.userinfo}@", '')
basic_auth = {
username: CGI.unescape(parsed_url.user),
2018-11-08 19:23:39 +05:30
password: CGI.unescape(parsed_url.password.presence || '')
2017-09-10 17:25:29 +05:30
}
make_request(post_url, basic_auth)
end
def log_execution(trigger:, url:, request_data:, response:, execution_duration:, error_message: nil)
# logging for ServiceHook's is not available
return if hook.is_a?(ServiceHook)
WebHookLog.create(
web_hook: hook,
trigger: trigger,
url: url,
execution_duration: execution_duration,
request_headers: build_headers(hook_name),
request_data: request_data,
response_headers: format_response_headers(response),
response_body: safe_response_body(response),
response_status: response.code,
internal_error_message: error_message
)
end
def build_headers(hook_name)
@headers ||= begin
{
'Content-Type' => 'application/json',
'X-Gitlab-Event' => hook_name.singularize.titleize
}.tap do |hash|
2018-03-17 18:26:18 +05:30
hash['X-Gitlab-Token'] = Gitlab::Utils.remove_line_breaks(hook.token) if hook.token.present?
2017-09-10 17:25:29 +05:30
end
end
end
# Make response headers more stylish
# Net::HTTPHeader has downcased hash with arrays: { 'content-type' => ['text/html; charset=utf-8'] }
# This method format response to capitalized hash with strings: { 'Content-Type' => 'text/html; charset=utf-8' }
def format_response_headers(response)
response.headers.each_capitalized.to_h
end
def safe_response_body(response)
return '' unless response.body
response.body.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
end
end