class EC2::Common::S3Support

Constants

DEFAULT_REGION
DEFAULT_SIGV

Attributes

pass[RW]
s3_url[RW]
user[RW]

Public Class Methods

bucket_name_s3_safe?(bucket_name) click to toggle source

Return true if the bucket name is S3 safe.

Per the S3 dev guide @ docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?BucketRestrictions.html

  • Be between 3 and 255 characters long

  • Start with a number or letter

  • Contain lowercase letters, numbers, periods (.), underscores (_), and dashes (-)

  • Not be in an IP address style (e.g., “192.168.5.4”)

  • Notes:

  • !!(.…) silliness to force a boolean to be returned

# File lib/ec2/common/s3support.rb, line 40
def self.bucket_name_s3_safe?(bucket_name)
  # Can most probably fold this all into 1 grand regexp but
  # for now opt for semi-clarity.
  !!((3..255).include?(bucket_name.length) and
     (/^[a-z0-9][a-z0-9\._-]+$/ =~ bucket_name) and
     (/^(\d{1,3}\.){3}\d{1,3}$/ !~ bucket_name))
end
bucket_name_s3_v2_safe?(bucket_name) click to toggle source

Return true if the bucket name is S3 (v2) safe.

Per the S3 dev guide @ docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?BucketRestrictions.html

  • Bucket names should not contain underscores (_)

  • Bucket names should be between 3 and 63 characters long

  • Bucket names should not end with a dash

  • Bucket names cannot contain dashes next to periods (e.g., “my-.bucket.com” and “my.-bucket” are invalid)

  • Bucket names must only contain lower case letters

and naturally also fulfills bucket_name_s3_safe?() requirements

  • Notes:

  • !!(.…) silliness to force a boolean to be returned

# File lib/ec2/common/s3support.rb, line 62
def self.bucket_name_s3_v2_safe?(bucket_name)
  # Can most probably fold this all into 1 grand regexp but
  # for now opt for semi-clarity.
  !!(self.bucket_name_s3_safe?(bucket_name) and
     bucket_name.length <= 63 and
     not bucket_name.include?('_') and
     bucket_name[-1] != ?- and
     /(-\.)|(\.-)/ !~ bucket_name)
end
new(s3_url, user, pass, format=nil, debug=nil, sigv=DEFAULT_SIGV, region=DEFAULT_REGION) click to toggle source
# File lib/ec2/common/s3support.rb, line 72
def initialize(s3_url, user, pass, format=nil, debug=nil, sigv=DEFAULT_SIGV, region=DEFAULT_REGION)
  @user = user
  @pass = pass
  @s3_url = fix_s3_url(s3_url)
  @format = format
  @debug = debug
  @sigv = sigv
  @region = region
end

Public Instance Methods

check_bucket_exists(bucket, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 121
def check_bucket_exists(bucket, options={})
  url, bkt = get_bucket_url(bucket)
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::head(url, bkt, options, @user, @pass, @debug, signature, @region)
  end
end
copy(bucket, key, source, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 174
def copy(bucket, key, source, options={})
  url, bkt = get_bucket_url(bucket)
  url << "/#{CGI::escape(key)}"
  options['x-amz-copy-source'] = CGI::escape(source)
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::put(url, bkt, nil, options, @user, @pass, @debug, signature, @region)
  end
end
create_bucket(bucket, location, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 136
def create_bucket(bucket, location, options={})
  url, bkt = get_bucket_url(bucket)
  binary_data = ""
  if (location != nil)
    binary_data = "<CreateBucketConstraint><LocationConstraint>#{location}</LocationConstraint></CreateBucketConstraint>"
  end
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::putdir(url, bkt, binary_data, options, @user, @pass, @debug, signature, @region)
  end
end
delete(bucket, key="", options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 183
def delete(bucket, key="", options={})
  url, bkt = get_bucket_url(bucket)
  url << "/#{CGI::escape(key)}"
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::delete(url, bkt, options, @user, @pass, @debug, signature, @region)
  end
end
fix_s3_url(s3_url) click to toggle source
# File lib/ec2/common/s3support.rb, line 82
def fix_s3_url(s3_url)
  if s3_url !~ %r{://}
    s3_url = "https://#{s3_url}"
  end
  if s3_url[-1..-1] != "/"
    s3_url << "/"
  end
  s3_url
end
get(bucket, key, path=nil, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 158
def get(bucket, key, path=nil, options={})
  url, bkt = get_bucket_url(bucket)
  url << "/#{CGI::escape(key)}"
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::get(url, bkt, path, options, @user, @pass, nil, nil, @debug, signature, @region)
  end
end
get_acl(bucket, key, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 111
def get_acl(bucket, key, options={})
  begin
    url, bkt = get_bucket_url(bucket)
    url << "/#{CGI::escape(key)}?acl"
    http_fallback(url, @sigv) do |url, signature|
      EC2::Common::HTTP::get(url, bkt, nil, options, @user, @pass, nil, nil, @debug, signature, @region)
    end
  end
end
get_bucket_location(bucket, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 128
def get_bucket_location(bucket, options={})
  url, bkt = get_bucket_url(bucket)
  url << "?location"
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::get(url, bkt, nil, options, @user, @pass, nil, nil, @debug, signature, @region)
  end
end
get_bucket_url(bucket) click to toggle source
# File lib/ec2/common/s3support.rb, line 92
def get_bucket_url(bucket)
  # The returned variable should not end with '/', makes constructing the path for sigv4 easier
  if not @format
    if bucket.include? "."
      @format = :path
    else
      @format = :subdomain
    end
  end

  case @format
  when :subdomain
    uri = URI.parse(@s3_url)
    return ["#{uri.scheme}://#{bucket}.#{uri.host}", bucket]
  when :path
    return ["#{@s3_url}#{bucket}", bucket]
  end
end
list_bucket(bucket, prefix=nil, max_keys=nil, path=nil, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 147
def list_bucket(bucket, prefix=nil, max_keys=nil, path=nil, options={})
  url, bkt = get_bucket_url(bucket)
  params = []
  params << "prefix=#{CGI::escape(prefix)}" if prefix
  params << "max-keys=#{CGI::escape(max_keys)}" if max_keys
  url << "?" + params.join("&") unless params.empty?
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::get(url, bkt, path, options, @user, @pass, nil, nil, @debug, signature, @region)
  end
end
put(bucket, key, file, options={}) click to toggle source
# File lib/ec2/common/s3support.rb, line 166
def put(bucket, key, file, options={})
  url, bkt = get_bucket_url(bucket)
  url << "/#{CGI::escape(key)}"
  http_fallback(url, @sigv) do |url, signature|
    EC2::Common::HTTP::put(url, bkt, file, options, @user, @pass, @debug, signature, @region)
  end
end

Private Instance Methods

http_fallback(url, sigv, &block) click to toggle source

This method will cycle through all possible signatures versions, starting with the given the one as first choice

# File lib/ec2/common/s3support.rb, line 194
def http_fallback url, sigv, &block
  all_sigv = [EC2::Common::SIGV2, EC2::Common::SIGV4]
  all_sigv.delete(sigv)
  ordered_sigv = [sigv].concat all_sigv
 
  resp = nil
  access_denied = false
  ordered_sigv.each do |o_sigv|
    redirected = true
    while redirected
      redirected = false
      begin
        resp = block.call(url, o_sigv)
      rescue EC2::Common::HTTP::Error::Redirect => e
        redirected = true
        url = e.endpoint
        STDERR.puts "Redirecting to #{url}, resigning request"
      rescue EC2::Common::HTTP::Error => e
        if e.code == 403
          access_denied = true 
        else
          raise e
        end
      end
    end
    # If the response was 403, try other sigv
    if access_denied == true
      access_denied = false
      STDERR.puts "Signature version #{o_sigv} authentication failed, trying different signature version"
    else
      break
    end
  end
  resp
end