class S3Proxy::App

Public Class Methods

new(options={}) click to toggle source
# File lib/s3_proxy/app.rb, line 6
def initialize(options={})
  @options = options
end

Public Instance Methods

call(env) click to toggle source
# File lib/s3_proxy/app.rb, line 10
def call(env)
  return Errors.method_not_allowed unless %w(GET HEAD).include?(env['REQUEST_METHOD'])
  return Errors.not_found if env['PATH_INFO'].empty?

  # When used as a forward proxy
  if env['HTTP_HOST'] =~ /(.+)\.s3\.amazonaws\.com/
    bucket = $1
    _, key = env['PATH_INFO'].split('/', 2)
  else
    _, bucket, key = env['PATH_INFO'].split('/', 3)
  end

  return Errors.not_found unless bucket && key

  req = {bucket: bucket, key: key}

  req[:if_match] = env['HTTP_IF_MATCH'] if env['HTTP_IF_MATCH']
  req[:if_none_match] = env['HTTP_IF_NONE_MATCH'] if env['HTTP_IF_NONE_MATCH']
  req[:if_modified_since] = env['HTTP_IF_MODIFIED_SINCE'] if env['HTTP_IF_MODIFIED_SINCE']
  req[:if_unmodified_since] = env['HTTP_IF_UNMODIFIED_SINCE'] if env['HTTP_UNMODIFIED_SINCE']

  head = s3.head_object(req)

  return Errors.not_found unless head

  case env['REQUEST_METHOD']
  when 'HEAD'
    gentle env, req, head
  when 'GET'
    if env['rack.hijack?']
      hijack env, req, head
    else
      gentle env, req, head
    end
  end

rescue Aws::S3::Errors::NoSuchKey, Aws::S3::Errors::NotFound
  return Errors.not_found

rescue Aws::S3::Errors::NotModified
  return Errors.not_modified

rescue Aws::S3::Errors::PreconditionFailed
  return Errors.precondition_failed

rescue NameError => e
  # https://github.com/aws/aws-sdk-core-ruby/pull/65
  raise e unless e.message == "wrong constant name 412Error"

  return Errors.precondition_failed
end

Private Instance Methods

gentle(env, request, head) click to toggle source
# File lib/s3_proxy/app.rb, line 86
def gentle(env, request, head)
  case env['REQUEST_METHOD']
  when 'GET'
    fiber = Fiber.new do
      s3.get_object(request) do |chunk|
        Fiber.yield(chunk)
      end
      Fiber.yield(nil)
    end

    body = Enumerator.new do |y|
      while n = fiber.resume
        y << n
      end
    end
  when 'HEAD'
    body = ['']
  end

  headers = {
    'Content-Type' => head.content_type,
    'Content-Length' => head.content_length.to_s,
    'Last-Modified' => head.last_modified,
    'ETag' => head.etag,
  }

  [200, headers, body]
end
hijack(env, request, head) click to toggle source
# File lib/s3_proxy/app.rb, line 64
def hijack(env, request, head)
  env['rack.hijack'].call

  io = env['rack.hijack_io']
  begin
    io.write "HTTP/1.1 200 OK\r\n"
    io.write "Status: 200\r\n"
    io.write "Connection: close\r\n"
    io.write "Content-Type: #{head.content_type}\r\n"
    io.write "Content-Length: #{head.content_length}\r\n"
    io.write "ETag: #{head.etag}\r\n"
    io.write "Last-Modified: #{head.last_modified}\r\n"
    io.write "\r\n"
    io.flush

    s3.get_object(request, target: io)
  ensure
    io.close
  end
  return [200, {}, ['']]
end
s3() click to toggle source
# File lib/s3_proxy/app.rb, line 115
def s3
  @s3 ||= Aws::S3.new(@options)
end