Fix remote files not using Content-Type header, streaming (#14184)
parent
65506bac3f
commit
7aaf2b44ec
@ -0,0 +1,10 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class ResponseWithLimit |
||||
def initialize(response, limit) |
||||
@response = response |
||||
@limit = limit |
||||
end |
||||
|
||||
attr_reader :response, :limit |
||||
end |
@ -0,0 +1,27 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
module Paperclip |
||||
module MediaTypeSpoofDetectorExtensions |
||||
def calculated_content_type |
||||
@calculated_content_type ||= type_from_mime_magic || type_from_file_command |
||||
end |
||||
|
||||
def type_from_mime_magic |
||||
@type_from_mime_magic ||= begin |
||||
begin |
||||
File.open(@file.path) do |file| |
||||
MimeMagic.by_magic(file)&.type |
||||
end |
||||
rescue Errno::ENOENT |
||||
'' |
||||
end |
||||
end |
||||
end |
||||
|
||||
def type_from_file_command |
||||
@type_from_file_command ||= FileCommandContentTypeDetector.new(@file.path).detect |
||||
end |
||||
end |
||||
end |
||||
|
||||
Paperclip::MediaTypeSpoofDetector.prepend(Paperclip::MediaTypeSpoofDetectorExtensions) |
@ -0,0 +1,55 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
module Paperclip |
||||
class ResponseWithLimitAdapter < AbstractAdapter |
||||
def self.register |
||||
Paperclip.io_adapters.register self do |target| |
||||
target.is_a?(ResponseWithLimit) |
||||
end |
||||
end |
||||
|
||||
def initialize(target, options = {}) |
||||
super |
||||
cache_current_values |
||||
end |
||||
|
||||
private |
||||
|
||||
def cache_current_values |
||||
@original_filename = filename_from_content_disposition || filename_from_path || 'data' |
||||
@size = @target.response.content_length |
||||
@tempfile = copy_to_tempfile(@target) |
||||
@content_type = @target.response.mime_type || ContentTypeDetector.new(@tempfile.path).detect |
||||
end |
||||
|
||||
def copy_to_tempfile(source) |
||||
bytes_read = 0 |
||||
|
||||
source.response.body.each do |chunk| |
||||
bytes_read += chunk.bytesize |
||||
|
||||
destination.write(chunk) |
||||
chunk.clear |
||||
|
||||
raise Mastodon::LengthValidationError if bytes_read > source.limit |
||||
end |
||||
|
||||
destination.rewind |
||||
destination |
||||
rescue Mastodon::LengthValidationError |
||||
destination.close(true) |
||||
raise |
||||
ensure |
||||
source.response.connection.close |
||||
end |
||||
|
||||
def filename_from_content_disposition |
||||
disposition = @target.response.headers['content-disposition'] |
||||
disposition&.match(/filename="([^"]*)"/)&.captures&.first |
||||
end |
||||
|
||||
def filename_from_path |
||||
@target.response.uri.path.split('/').last |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue