diff options
Diffstat (limited to 'app/services/import/validate_remote_git_endpoint_service.rb')
-rw-r--r-- | app/services/import/validate_remote_git_endpoint_service.rb | 79 |
1 files changed, 53 insertions, 26 deletions
diff --git a/app/services/import/validate_remote_git_endpoint_service.rb b/app/services/import/validate_remote_git_endpoint_service.rb index a994072c4aa..8297757997f 100644 --- a/app/services/import/validate_remote_git_endpoint_service.rb +++ b/app/services/import/validate_remote_git_endpoint_service.rb @@ -13,6 +13,8 @@ module Import GIT_PROTOCOL_PKT_LEN = 4 GIT_MINIMUM_RESPONSE_LENGTH = GIT_PROTOCOL_PKT_LEN + GIT_EXPECTED_FIRST_PACKET_LINE.length EXPECTED_CONTENT_TYPE = "application/x-#{GIT_SERVICE_NAME}-advertisement" + INVALID_BODY_MESSAGE = 'Not a git repository: Invalid response body' + INVALID_CONTENT_TYPE_MESSAGE = 'Not a git repository: Invalid content-type' def initialize(params) @params = params @@ -30,32 +32,35 @@ module Import uri.fragment = nil url = Gitlab::Utils.append_path(uri.to_s, "/info/refs?service=#{GIT_SERVICE_NAME}") - response_body = '' - result = nil - Gitlab::HTTP.try_get(url, stream_body: true, follow_redirects: false, basic_auth: auth) do |fragment| - response_body += fragment - next if response_body.length < GIT_MINIMUM_RESPONSE_LENGTH - - result = if status_code_is_valid(fragment) && content_type_is_valid(fragment) && response_body_is_valid(response_body) - :success - else - :error - end - - # We are interested only in the first chunks of the response - # So we're using stream_body: true and breaking when receive enough body - break - end + response, response_body = http_get_and_extract_first_chunks(url) - if result == :success - ServiceResponse.success - else - ServiceResponse.error(message: "#{uri} is not a valid HTTP Git repository") - end + validate(uri, response, response_body) + rescue *Gitlab::HTTP::HTTP_ERRORS => err + error_result("HTTP #{err.class.name.underscore} error: #{err.message}") + rescue StandardError => err + ServiceResponse.error( + message: "Internal #{err.class.name.underscore} error: #{err.message}", + reason: 500 + ) end private + def http_get_and_extract_first_chunks(url) + # We are interested only in the first chunks of the response + # So we're using stream_body: true and breaking when receive enough body + response = nil + response_body = '' + + Gitlab::HTTP.get(url, stream_body: true, follow_redirects: false, basic_auth: auth) do |response_chunk| + response = response_chunk + response_body += response_chunk + break if GIT_MINIMUM_RESPONSE_LENGTH <= response_body.length + end + + [response, response_body] + end + def auth unless @params[:user].to_s.blank? { @@ -65,15 +70,37 @@ module Import end end - def status_code_is_valid(fragment) - fragment.http_response.code == '200' + def validate(uri, response, response_body) + return status_code_error(uri, response) unless status_code_is_valid?(response) + return error_result(INVALID_CONTENT_TYPE_MESSAGE) unless content_type_is_valid?(response) + return error_result(INVALID_BODY_MESSAGE) unless response_body_is_valid?(response_body) + + ServiceResponse.success + end + + def status_code_error(uri, response) + http_code = response.http_response.code.to_i + message = response.http_response.message || Rack::Utils::HTTP_STATUS_CODES[http_code] + + error_result( + "#{uri} endpoint error: #{http_code}#{message.presence&.prepend(' ')}", + http_code + ) + end + + def error_result(message, reason = nil) + ServiceResponse.error(message: message, reason: reason) + end + + def status_code_is_valid?(response) + response.http_response.code == '200' end - def content_type_is_valid(fragment) - fragment.http_response['content-type'] == EXPECTED_CONTENT_TYPE + def content_type_is_valid?(response) + response.http_response['content-type'] == EXPECTED_CONTENT_TYPE end - def response_body_is_valid(response_body) + def response_body_is_valid?(response_body) response_body.match?(GIT_BODY_MESSAGE_REGEXP) end end |