Commit 97ad79d7 authored by Kamil Trzciński | OoO till 14th of Aug's avatar Kamil Trzciński | OoO till 14th of Aug Committed by Bob Van Landuyt

Add send_file logic for image resizing

This will create a workhorse instruction to trigger
an image download + resize on-the-fly.
parent 9d8801cf
...@@ -18,7 +18,11 @@ module SendFileUpload ...@@ -18,7 +18,11 @@ module SendFileUpload
send_params.merge!(filename: attachment, disposition: disposition) send_params.merge!(filename: attachment, disposition: disposition)
end end
if file_upload.file_storage? if image_scaling_request?(file_upload)
location = file_upload.file_storage? ? file_upload.path : file_upload.url
headers.store(*Gitlab::Workhorse.send_scaled_image(location, params[:width].to_i))
head :ok
elsif file_upload.file_storage?
send_file file_upload.path, send_params send_file file_upload.path, send_params
elsif file_upload.class.proxy_download_enabled? || proxy elsif file_upload.class.proxy_download_enabled? || proxy
headers.store(*Gitlab::Workhorse.send_url(file_upload.url(**redirect_params))) headers.store(*Gitlab::Workhorse.send_url(file_upload.url(**redirect_params)))
...@@ -37,4 +41,19 @@ module SendFileUpload ...@@ -37,4 +41,19 @@ module SendFileUpload
"application/octet-stream" "application/octet-stream"
end end
end end
private
def image_scaling_request?(file_upload)
avatar_image_upload?(file_upload) && valid_image_scaling_width? && current_user &&
Feature.enabled?(:dynamic_image_resizing, current_user)
end
def avatar_image_upload?(file_upload)
file_upload.try(:image?) && file_upload.try(:mounted_as)&.to_sym == :avatar
end
def valid_image_scaling_width?
Avatarable::ALLOWED_IMAGE_SCALER_WIDTHS.include?(params[:width]&.to_i)
end
end end
...@@ -3,6 +3,17 @@ ...@@ -3,6 +3,17 @@
module Avatarable module Avatarable
extend ActiveSupport::Concern extend ActiveSupport::Concern
ALLOWED_IMAGE_SCALER_WIDTHS = [
400,
200,
64,
48,
40,
26,
20,
16
].freeze
included do included do
prepend ShadowMethods prepend ShadowMethods
include ObjectStorage::BackgroundMove include ObjectStorage::BackgroundMove
......
---
name: dynamic_image_resizing
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37342
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/233704
group: group::memory
type: development
default_enabled: false
\ No newline at end of file
...@@ -156,6 +156,18 @@ module Gitlab ...@@ -156,6 +156,18 @@ module Gitlab
] ]
end end
def send_scaled_image(location, width)
params = {
'Location' => location,
'Width' => width
}
[
SEND_DATA_HEADER,
"send-scaled-img:#{encode(params)}"
]
end
def channel_websocket(channel) def channel_websocket(channel)
details = { details = {
'Channel' => { 'Channel' => {
......
...@@ -21,6 +21,12 @@ RSpec.describe SendFileUpload do ...@@ -21,6 +21,12 @@ RSpec.describe SendFileUpload do
let(:controller_class) do let(:controller_class) do
Class.new do Class.new do
include SendFileUpload include SendFileUpload
def params
{}
end
def current_user; end
end end
end end
...@@ -42,6 +48,89 @@ RSpec.describe SendFileUpload do ...@@ -42,6 +48,89 @@ RSpec.describe SendFileUpload do
FileUtils.rm_f(temp_file) FileUtils.rm_f(temp_file)
end end
shared_examples 'handles image resize requests' do
let(:headers) { double }
before do
allow(uploader).to receive(:image?).and_return(true)
allow(uploader).to receive(:mounted_as).and_return(:avatar)
allow(controller).to receive(:headers).and_return(headers)
# both of these are valid cases, depending on whether we are dealing with
# local or remote files
allow(controller).to receive(:send_file)
allow(controller).to receive(:redirect_to)
end
context 'when feature is enabled for current user' do
let(:user) { build(:user) }
before do
stub_feature_flags(dynamic_image_resizing: user)
allow(controller).to receive(:current_user).and_return(user)
end
context 'with valid width parameter' do
it 'renders OK with workhorse command header' do
expect(controller).not_to receive(:send_file)
expect(controller).to receive(:params).at_least(:once).and_return(width: '64')
expect(controller).to receive(:head).with(:ok)
expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/)
subject
end
end
context 'with missing width parameter' do
it 'does not write workhorse command header' do
expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/)
subject
end
end
context 'with invalid width parameter' do
it 'does not write workhorse command header' do
expect(controller).to receive(:params).at_least(:once).and_return(width: 'not a number')
expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/)
subject
end
end
context 'with width that is not allowed' do
it 'does not write workhorse command header' do
expect(controller).to receive(:params).at_least(:once).and_return(width: '63')
expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/)
subject
end
end
context 'when image file is not an avatar' do
it 'does not write workhorse command header' do
expect(uploader).to receive(:mounted_as).and_return(nil) # FileUploader is not mounted
expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/)
subject
end
end
end
context 'when feature is disabled' do
before do
stub_feature_flags(dynamic_image_resizing: false)
end
it 'does not write workhorse command header' do
expect(controller).to receive(:params).at_least(:once).and_return(width: '64')
expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/)
subject
end
end
end
context 'when local file is used' do context 'when local file is used' do
before do before do
uploader.store!(temp_file) uploader.store!(temp_file)
...@@ -52,6 +141,8 @@ RSpec.describe SendFileUpload do ...@@ -52,6 +141,8 @@ RSpec.describe SendFileUpload do
subject subject
end end
it_behaves_like 'handles image resize requests'
end end
context 'with inline image' do context 'with inline image' do
...@@ -155,6 +246,8 @@ RSpec.describe SendFileUpload do ...@@ -155,6 +246,8 @@ RSpec.describe SendFileUpload do
it_behaves_like 'proxied file' it_behaves_like 'proxied file'
end end
end end
it_behaves_like 'handles image resize requests'
end end
end end
end end
...@@ -421,6 +421,24 @@ RSpec.describe Gitlab::Workhorse do ...@@ -421,6 +421,24 @@ RSpec.describe Gitlab::Workhorse do
end end
end end
describe '.send_scaled_image' do
let(:location) { 'http://example.com/avatar.png' }
let(:width) { '150' }
subject { described_class.send_scaled_image(location, width) }
it 'sets the header correctly' do
key, command, params = decode_workhorse_header(subject)
expect(key).to eq("Gitlab-Workhorse-Send-Data")
expect(command).to eq("send-scaled-img")
expect(params).to eq({
'Location' => location,
'Width' => width
}.deep_stringify_keys)
end
end
describe '.send_git_snapshot' do describe '.send_git_snapshot' do
let(:url) { 'http://example.com' } let(:url) { 'http://example.com' }
......
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