class Attache::Download
Constants
- RESIZE_JOB_POOL
Public Class Methods
new(app)
click to toggle source
# File lib/attache/download.rb, line 6 def initialize(app) @app = app @mutexes = {} end
Public Instance Methods
_call(env, config)
click to toggle source
# File lib/attache/download.rb, line 11 def _call(env, config) case env['PATH_INFO'] when %r{\A/view/} vhosts = {} vhosts[ENV.fetch('REMOTE_GEOMETRY') { 'remote' }] = config.storage && config.bucket && config vhosts[ENV.fetch('BACKUP_GEOMETRY') { 'backup' }] = config.backup parse_path_info(env['PATH_INFO']['/view/'.length..-1]) do |dirname, geometry, basename, relpath| unless config.try(:geometry_whitelist).blank? || config.geometry_whitelist.include?(geometry) return [415, config.download_headers, ["#{geometry} is not supported"]] end if vhost = vhosts[geometry] headers = vhost.download_headers.merge({ 'Location' => vhost.storage_url(relpath: relpath), 'Cache-Control' => 'private, no-cache', }) return [302, headers, []] end thumbnail = case geometry when 'original', *vhosts.keys get_original_file(relpath, vhosts, env) else get_thumbnail_file(geometry, basename, relpath, vhosts, env) end return [404, config.download_headers, []] if thumbnail.try(:size).to_i == 0 headers = { 'Content-Type' => content_type_of(thumbnail.path), }.merge(config.download_headers) [200, headers, rack_response_body_for(thumbnail)] end else @app.call(env) end end
Private Instance Methods
get_first_result_present_async(lambdas)
click to toggle source
Ref gist.github.com/sferik/39831f34eb87686b639c#gistcomment-1652888 a bit more complicated because we want to ignore falsey result
# File lib/attache/download.rb, line 115 def get_first_result_present_async(lambdas) return if lambdas.empty? # queue.pop will never happen queue = Queue.new threads = lambdas.shuffle.collect { |code| Thread.new { queue << [Thread.current, code.call] } } until (item = queue.pop).last do thread, _ = item thread.join # we could be popping `queue` before thread exited break unless threads.any?(&:alive?) || queue.size > 0 end threads.each(&:kill) _, result = item result end
get_original_file(relpath, vhosts, env)
click to toggle source
# File lib/attache/download.rb, line 86 def get_original_file(relpath, vhosts, env) cachekey = File.join(request_hostname(env), relpath) synchronize(cachekey) do Attache.cache.fetch(cachekey) do name_with_vhost_pairs = vhosts.inject({}) { |sum,(k,v)| (v ? sum.merge(k => v) : sum) } get_first_result_present_async(name_with_vhost_pairs.collect {|name, vhost| lambda { Thread.handle_interrupt(BasicObject => :on_blocking) { begin Attache.logger.info "[POOL] looking for #{name} #{relpath}..." vhost.storage_get(relpath: relpath).tap do |v| Attache.logger.info "[POOL] found #{name} #{relpath} = #{v.inspect}" end rescue Exception Attache.logger.error $! Attache.logger.error $@ Attache.logger.info "[POOL] not found #{name} #{relpath}" nil end } } }) end end rescue Exception # Errno::ECONNREFUSED, OpenURI::HTTPError, Excon::Errors, Fog::Errors::Error Attache.logger.error "ERROR REFERER #{env['HTTP_REFERER'].inspect}" nil end
get_thumbnail_file(geometry, basename, relpath, vhosts, env)
click to toggle source
# File lib/attache/download.rb, line 69 def get_thumbnail_file(geometry, basename, relpath, vhosts, env) cachekey = File.join(request_hostname(env), relpath, geometry) synchronize(cachekey) do tempfile = nil Attache.cache.fetch(cachekey) do Attache.logger.info "[POOL] new job" tempfile = RESIZE_JOB_POOL.with do |job| job.perform(geometry, basename, relpath, vhosts, env) do # opens up possibility that job implementation # does not require we download original file prior get_original_file(relpath, vhosts, env) end end end.tap { File.unlink(tempfile.path) if tempfile.try(:path) } end end
parse_path_info(geometrypath) { |dirname, geometry, basename, relpath| ... }
click to toggle source
# File lib/attache/download.rb, line 53 def parse_path_info(geometrypath) parts = geometrypath.split('/') basename = CGI.unescape parts.pop geometry = CGI.unescape parts.pop dirname = parts.join('/') relpath = File.join(dirname, basename) yield dirname, geometry, basename, relpath end
synchronize(key, &block)
click to toggle source
# File lib/attache/download.rb, line 62 def synchronize(key, &block) mutex = @mutexes[key] ||= Mutex.new mutex.synchronize(&block) ensure @mutexes.delete(key) end