Commit 6dd59a44 authored by Stan Hu's avatar Stan Hu

Enable LFS chunked encoding

LFS currently doesn't use chunked transfers, which causes the whole body
to be read before it is stored. This causes 5+ GB uploads to be blocked
by Cloudflare on GitLab.com.

The LFS client will only enable this mode if the server sends the
`Transfer-Encoding: chunked` HTTP header.

This is guarded by the `lfs_chunked_encoding` feature flag to ensure
this mode doesn't cause issues.

Relates to https://gitlab.com/gitlab-org/gitlab/-/issues/285489
parent 721d1aec
......@@ -92,16 +92,26 @@ module Repositories
{
upload: {
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
header: {
Authorization: authorization_header,
# git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This
# ensures that Workhorse can intercept the request.
'Content-Type': LFS_TRANSFER_CONTENT_TYPE
}.compact
header: upload_headers
}
}
end
def upload_headers
headers = {
Authorization: authorization_header,
# git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This
# ensures that Workhorse can intercept the request.
'Content-Type': LFS_TRANSFER_CONTENT_TYPE
}
if Feature.enabled?(:lfs_chunked_encoding, project)
headers['Transfer-Encoding'] = 'chunked'
end
headers
end
def lfs_check_batch_operation!
if batch_operation_disallowed?
render(
......
---
title: Enable LFS chunked encoding
merge_request: 48269
author:
type: changed
---
name: lfs_chunked_encoding
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/jobs/864043673
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285581
milestone: '13.6'
type: development
group:
default_enabled: false
......@@ -219,10 +219,12 @@ RSpec.describe 'Git LFS API and storage' do
describe 'when handling LFS batch request' do
let(:update_lfs_permissions) { }
let(:update_user_permissions) { }
let(:lfs_chunked_encoding) { true }
before do
update_lfs_permissions
update_user_permissions
stub_feature_flags(lfs_chunked_encoding: lfs_chunked_encoding)
post_lfs_json batch_url(project), body, headers
end
......@@ -524,7 +526,24 @@ RSpec.describe 'Git LFS API and storage' do
expect(json_response['objects']).to be_kind_of(Array)
expect(json_response['objects'].first).to include(sample_object)
expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
expect(json_response['objects'].first['actions']['upload']['header']).to include('Content-Type' => 'application/octet-stream')
headers = json_response['objects'].first['actions']['upload']['header']
expect(headers['Content-Type']).to eq('application/octet-stream')
expect(headers['Transfer-Encoding']).to eq('chunked')
end
context 'when lfs_chunked_encoding feature is disabled' do
let(:lfs_chunked_encoding) { false }
it 'responds with upload hypermedia link' do
expect(json_response['objects']).to be_kind_of(Array)
expect(json_response['objects'].first).to include(sample_object)
expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
headers = json_response['objects'].first['actions']['upload']['header']
expect(headers['Content-Type']).to eq('application/octet-stream')
expect(headers['Transfer-Encoding']).to be_nil
end
end
it_behaves_like 'process authorization header', renew_authorization: renew_authorization
......@@ -548,7 +567,10 @@ RSpec.describe 'Git LFS API and storage' do
expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
expect(lfs_object.projects.pluck(:id)).to include(other_project.id)
expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
expect(json_response['objects'].first['actions']['upload']['header']).to include('Content-Type' => 'application/octet-stream')
headers = json_response['objects'].first['actions']['upload']['header']
expect(headers['Content-Type']).to eq('application/octet-stream')
expect(headers['Transfer-Encoding']).to eq('chunked')
end
it_behaves_like 'process authorization header', renew_authorization: true
......@@ -589,7 +611,10 @@ RSpec.describe 'Git LFS API and storage' do
expect(json_response['objects'].last).to include(non_existing_object)
expect(json_response['objects'].last['actions']['upload']['href']).to eq(objects_url(project, non_existing_object_oid, non_existing_object_size))
expect(json_response['objects'].last['actions']['upload']['header']).to include('Content-Type' => 'application/octet-stream')
headers = json_response['objects'].last['actions']['upload']['header']
expect(headers['Content-Type']).to eq('application/octet-stream')
expect(headers['Transfer-Encoding']).to eq('chunked')
end
it_behaves_like 'process authorization header', renew_authorization: true
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment