class Apache::UploadMerger

Constants

CREATE_MODE
DEFAULT_MERGE_THRESHOLD
VERSION

Public Class Methods

new(map = {}, strategy = DEFAULT_MERGE_THRESHOLD) click to toggle source

Creates a new RubyHandler instance for the Apache web server. It is to be installed as a custom 404 ErrorDocument handler.

The argument map contains key/value pairs of URL prefixes and upload base directories.

strategy determines how merging happens:

:symlink

Files are symlinked

:copy

Files are copied

Integer

Files whose size is below that threshold are copied, others are symlinked (default)

# File lib/apache/upload_merger.rb, line 55
def initialize(map = {}, strategy = DEFAULT_MERGE_THRESHOLD)
  @map, @strategy = {}, strategy

  define_merger

  map.each { |prefix, dir|
    @map[prefix] = [%r{\A#{prefix}/(.*)}, dir]
  }
end

Public Instance Methods

handler(request) click to toggle source

If the current request asked for a resource that's not there, it will be merged from one of the appropriate upload directories, determined by its URL prefix. If no matching resource could be found, the original error will be thrown.

# File lib/apache/upload_merger.rb, line 69
def handler(request)
  request.add_common_vars  # REDIRECT_URL

  if url    = request.subprocess_env['REDIRECT_URL'] and
     prefix = request.path_info.untaint              and
     map    = @map[prefix]                           and
     path   = url[map[0], 1].untaint                 and
     src    = find(map[1], path)

    merge(src, File.join(request.server.document_root, prefix, path))

    request.status = HTTP_OK
    request.internal_redirect(url)

    OK
  else
    DECLINED
  end
end

Private Instance Methods

copy(src, dest, stat = File.stat(src)) click to toggle source

TODO: optimize?

# File lib/apache/upload_merger.rb, line 118
def copy(src, dest, stat = File.stat(src))
  File.open(src) { |src_|
    File.open(dest, CREATE_MODE, stat.mode) { |dest_|
      FileUtils.copy_stream(src_, dest_)
    }
  }
rescue Errno::EEXIST
end
define_merger() click to toggle source
# File lib/apache/upload_merger.rb, line 99
def define_merger
  class << self; self; end.send :alias_method, :merge, case @strategy
    when :symlink, :copy then @strategy
    when Integer         then :copy_or_symlink
    else raise ArgumentError, "illegal strategy #{@strategy.inspect}"
  end
end
find(dir, path) click to toggle source

TODO: make it fast and secure

# File lib/apache/upload_merger.rb, line 92
def find(dir, path)
  Dir["#{dir}/*/"].find { |subdir|
    file = File.join(subdir, path).untaint
    return file if File.exists?(file)
  }
end