diff options
Diffstat (limited to 'app/services/import')
-rw-r--r-- | app/services/import/bitbucket_server_service.rb | 7 | ||||
-rw-r--r-- | app/services/import/github_service.rb | 1 | ||||
-rw-r--r-- | app/services/import/validate_remote_git_endpoint_service.rb | 81 |
3 files changed, 61 insertions, 28 deletions
diff --git a/app/services/import/bitbucket_server_service.rb b/app/services/import/bitbucket_server_service.rb index 3d961780889..e628e88eaa9 100644 --- a/app/services/import/bitbucket_server_service.rb +++ b/app/services/import/bitbucket_server_service.rb @@ -42,7 +42,8 @@ module Import project_name, target_namespace, current_user, - credentials + credentials, + timeout_strategy ).execute end @@ -74,6 +75,10 @@ module Import @url ||= params[:bitbucket_server_url] end + def timeout_strategy + @timeout_strategy ||= params[:timeout_strategy] || ProjectImportData::PESSIMISTIC_TIMEOUT + end + def allow_local_requests? Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services? end diff --git a/app/services/import/github_service.rb b/app/services/import/github_service.rb index 73e0c229a9c..86c62145a87 100644 --- a/app/services/import/github_service.rb +++ b/app/services/import/github_service.rb @@ -138,6 +138,7 @@ module Import Gitlab::GithubImport::Settings .new(project) .write( + timeout_strategy: params[:timeout_strategy] || ProjectImportData::PESSIMISTIC_TIMEOUT, optional_stages: params[:optional_stages], additional_access_tokens: access_params[:additional_access_tokens] ) diff --git a/app/services/import/validate_remote_git_endpoint_service.rb b/app/services/import/validate_remote_git_endpoint_service.rb index a994072c4aa..2177238fddf 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,16 +70,38 @@ 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) - response_body.match?(GIT_BODY_MESSAGE_REGEXP) + def response_body_is_valid?(response_body) + response_body.length <= GIT_MINIMUM_RESPONSE_LENGTH && response_body.match?(GIT_BODY_MESSAGE_REGEXP) end end end |