summaryrefslogtreecommitdiff
path: root/lib/gitlab/lfs/client.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/lfs/client.rb')
-rw-r--r--lib/gitlab/lfs/client.rb81
1 files changed, 59 insertions, 22 deletions
diff --git a/lib/gitlab/lfs/client.rb b/lib/gitlab/lfs/client.rb
index a05e8107cad..10df9262cca 100644
--- a/lib/gitlab/lfs/client.rb
+++ b/lib/gitlab/lfs/client.rb
@@ -36,7 +36,7 @@ module Gitlab
headers: build_request_headers
)
- raise BatchSubmitError unless rsp.success?
+ raise BatchSubmitError.new(http_response: rsp) unless rsp.success?
# HTTParty provides rsp.parsed_response, but it only kicks in for the
# application/json content type in the response, which we can't rely on
@@ -53,19 +53,13 @@ module Gitlab
params = {
body_stream: file,
- headers: {
- 'Content-Length' => object.size.to_s,
- 'Content-Type' => 'application/octet-stream',
- 'User-Agent' => GIT_LFS_USER_AGENT
- }.merge(upload_action['header'] || {})
+ headers: upload_headers(object, upload_action)
}
- authenticated = true if params[:headers].key?('Authorization')
- params[:basic_auth] = basic_auth unless authenticated
-
- rsp = Gitlab::HTTP.put(upload_action['href'], params)
+ url = set_basic_auth_and_extract_lfs_url!(params, upload_action['href'])
+ rsp = Gitlab::HTTP.put(url, params)
- raise ObjectUploadError unless rsp.success?
+ raise ObjectUploadError.new(http_response: rsp) unless rsp.success?
ensure
file&.close
end
@@ -76,20 +70,51 @@ module Gitlab
headers: build_request_headers(verify_action['header'])
}
- authenticated = true if params[:headers].key?('Authorization')
- params[:basic_auth] = basic_auth unless authenticated
-
- rsp = Gitlab::HTTP.post(verify_action['href'], params)
+ url = set_basic_auth_and_extract_lfs_url!(params, verify_action['href'])
+ rsp = Gitlab::HTTP.post(url, params)
- raise ObjectVerifyError unless rsp.success?
+ raise ObjectVerifyError.new(http_response: rsp) unless rsp.success?
end
private
+ def set_basic_auth_and_extract_lfs_url!(params, raw_url)
+ authenticated = true if params[:headers].key?('Authorization')
+ params[:basic_auth] = basic_auth unless authenticated
+ strip_userinfo = authenticated || params[:basic_auth].present?
+ lfs_url(raw_url, strip_userinfo)
+ end
+
def build_request_headers(extra_headers = nil)
DEFAULT_HEADERS.merge(extra_headers || {})
end
+ def upload_headers(object, upload_action)
+ # This uses the httprb library to handle case-insensitive HTTP headers
+ headers = ::HTTP::Headers.new
+ headers.merge!(upload_action['header'])
+ transfer_encodings = Array(headers['Transfer-Encoding']&.split(',')).map(&:strip)
+
+ headers['Content-Length'] = object.size.to_s unless transfer_encodings.include?('chunked')
+ headers['Content-Type'] = 'application/octet-stream'
+ headers['User-Agent'] = GIT_LFS_USER_AGENT
+
+ headers.to_h
+ end
+
+ def lfs_url(raw_url, strip_userinfo)
+ # HTTParty will give precedence to the username/password
+ # specified in the URL. This causes problems with Azure DevOps,
+ # which includes a username in the URL. Stripping the userinfo
+ # from the URL allows the provided HTTP Basic Authentication
+ # credentials to be used.
+ if strip_userinfo
+ Gitlab::UrlSanitizer.new(raw_url).sanitized_url
+ else
+ raw_url
+ end
+ end
+
attr_reader :credentials
def batch_url
@@ -105,9 +130,21 @@ module Gitlab
{ username: credentials[:user], password: credentials[:password] }
end
- class BatchSubmitError < StandardError
+ class HttpError < StandardError
+ def initialize(http_response:)
+ super
+
+ @http_response = http_response
+ end
+
+ def http_error
+ "HTTP status #{@http_response.code}"
+ end
+ end
+
+ class BatchSubmitError < HttpError
def message
- "Failed to submit batch"
+ "Failed to submit batch: #{http_error}"
end
end
@@ -122,15 +159,15 @@ module Gitlab
end
end
- class ObjectUploadError < StandardError
+ class ObjectUploadError < HttpError
def message
- "Failed to upload object"
+ "Failed to upload object: #{http_error}"
end
end
- class ObjectVerifyError < StandardError
+ class ObjectVerifyError < HttpError
def message
- "Failed to verify object"
+ "Failed to verify object: #{http_error}"
end
end
end