lib/S3.rb in moserp-s3sync-1.2.6 vs lib/S3.rb in moserp-s3sync-1.2.6.1

- old
+ new

@@ -31,16 +31,17 @@ # performs the operation, while the second simply outputs urls with the # appropriate authentication query string parameters, which could be used # in another tool (such as your web browser for GETs). module S3 DEFAULT_HOST = 's3.amazonaws.com' + DEFAULT_VIRTUAL_PATH = '' PORTS_BY_SECURITY = { true => 443, false => 80 } METADATA_PREFIX = 'x-amz-meta-' AMAZON_HEADER_PREFIX = 'x-amz-' # builds the canonical string for signing. - def S3.canonical_string(method, bucket="", path="", path_args={}, headers={}, expires=nil) + def S3.canonical_string(method, virtual_path, bucket="", path="", path_args={}, headers={}, expires=nil) interesting_headers = {} headers.each do |key, value| lk = key.downcase if (lk == 'content-md5' or lk == 'content-type' or @@ -72,10 +73,11 @@ else buf << "#{value}\n" end end + buf << "#{virtual_path}" # build the path using the bucket and key if not bucket.empty? buf << "/#{bucket}" end # append the key (it might be empty string) @@ -91,11 +93,10 @@ elsif path_args.has_key?('location') buf << '?location' elsif path_args.has_key?('logging') buf << '?logging' end - return buf end # encodes the given string with the aws_secret_access_key, by taking the # hmac-sha1 sum, and then base64 encoding it. optionally, it will also @@ -135,15 +136,16 @@ # some connection pooling. class AWSAuthConnection attr_accessor :calling_format def initialize(aws_access_key_id, aws_secret_access_key, is_secure=true, - server=DEFAULT_HOST, port=PORTS_BY_SECURITY[is_secure], + server=DEFAULT_HOST, port=PORTS_BY_SECURITY[is_secure], vpath=DEFAULT_VIRTUAL_PATH, calling_format=CallingFormat::REGULAR) @aws_access_key_id = aws_access_key_id @aws_secret_access_key = aws_secret_access_key @server = server + @virtual_path = vpath @is_secure = is_secure @calling_format = calling_format @port = port end @@ -219,11 +221,10 @@ return ListAllMyBucketsResponse.new(make_request('GET', '', '', {}, headers)) end private def make_request(method, bucket='', key='', path_args={}, headers={}, data='', metadata={}) - # build the domain based on the calling format server = '' if bucket.empty? # for a bucketless request (i.e. list all buckets) # revert to regular domain case since this operation @@ -232,15 +233,15 @@ elsif @calling_format == CallingFormat::SUBDOMAIN server = "#{bucket}.#{@server}" elsif @calling_format == CallingFormat::VANITY server = bucket else - server = @server + server = "#{@server}" end # build the path based on the calling format - path = '' + path = "#{@virtual_path}" if (not bucket.empty?) and (@calling_format == CallingFormat::REGULAR) path << "/#{bucket}" end # add the slash after the bucket regardless # the key will be appended if it is non-empty @@ -258,11 +259,11 @@ req = method_to_request_class(method).new("#{path}") set_headers(req, headers) set_headers(req, metadata, METADATA_PREFIX) - set_aws_auth_header(req, @aws_access_key_id, @aws_secret_access_key, bucket, key, path_args) + set_aws_auth_header(req, @aws_access_key_id, @aws_secret_access_key, @virtual_path, bucket, key, path_args) if req.request_body_permitted? return http.request(req, data) else return http.request(req) end @@ -283,22 +284,22 @@ raise "Unsupported method #{method}" end end # set the Authorization header using AWS signed header authentication - def set_aws_auth_header(request, aws_access_key_id, aws_secret_access_key, bucket='', key='', path_args={}) + def set_aws_auth_header(request, aws_access_key_id, aws_secret_access_key, virtual_path='', bucket='', key='', path_args={}) # we want to fix the date here if it's not already been done. request['Date'] ||= Time.now.httpdate # ruby will automatically add a random content-type on some verbs, so # here we add a dummy one to 'supress' it. change this logic if having # an empty content-type header becomes semantically meaningful for any # other verb. request['Content-Type'] ||= '' canonical_string = - S3.canonical_string(request.method, bucket, key, path_args, request.to_hash, nil) + S3.canonical_string(request.method, virtual_path, bucket, key, path_args, request.to_hash, nil) encoded_canonical = S3.encode(aws_secret_access_key, canonical_string) request['Authorization'] = "AWS #{aws_access_key_id}:#{encoded_canonical}" end @@ -323,16 +324,17 @@ # by default, expire in 1 minute DEFAULT_EXPIRES_IN = 60 def initialize(aws_access_key_id, aws_secret_access_key, is_secure=true, - server=DEFAULT_HOST, port=PORTS_BY_SECURITY[is_secure], + server=DEFAULT_HOST, port=PORTS_BY_SECURITY[is_secure], vpath=DEFAULT_VIRTUAL_PATH, format=CallingFormat::REGULAR) @aws_access_key_id = aws_access_key_id @aws_secret_access_key = aws_secret_access_key @protocol = is_secure ? 'https' : 'http' @server = server + @virtual_path = vpath @port = port @calling_format = format # by default expire @expires_in = DEFAULT_EXPIRES_IN end @@ -427,15 +429,15 @@ else raise "invalid expires state" end canonical_string = - S3::canonical_string(method, bucket, key, path_args, headers, expires) + S3::canonical_string(method, @virtual_path, bucket, key, path_args, headers, expires) encoded_canonical = S3::encode(@aws_secret_access_key, canonical_string) - url = CallingFormat.build_url_base(@protocol, @server, @port, bucket, @calling_format) + url = CallingFormat.build_url_base(@protocol, @server, @port, @virtual_path, bucket, @calling_format) path_args["Signature"] = encoded_canonical.to_s path_args["Expires"] = expires.to_s path_args["AWSAccessKeyId"] = @aws_access_key_id.to_s arg_string = S3.path_args_hash_to_string(path_args) @@ -467,20 +469,20 @@ REGULAR = 0 # http://s3.amazonaws.com/bucket/key SUBDOMAIN = 1 # http://bucket.s3.amazonaws.com/key VANITY = 2 # http://<vanity_domain>/key -- vanity_domain resolves to s3.amazonaws.com # build the url based on the calling format, and bucket - def CallingFormat.build_url_base(protocol, server, port, bucket, format) + def CallingFormat.build_url_base(protocol, server, port, vpath, bucket, format) build_url_base = "#{protocol}://" if bucket.empty? - build_url_base << "#{server}:#{port}" + build_url_base << "#{server}:#{port}/${vpath}" elsif format == SUBDOMAIN - build_url_base << "#{bucket}.#{server}:#{port}" + build_url_base << "#{bucket}.#{server}:#{port}/${vpath}" elsif format == VANITY - build_url_base << "#{bucket}:#{port}" + build_url_base << "#{bucket}:#{port}/${vpath}" else - build_url_base << "#{server}:#{port}/#{bucket}" + build_url_base << "#{server}:#{port}/${vpath}/#{bucket}" end return build_url_base end end @@ -564,11 +566,13 @@ elsif name == 'Size' @curr_entry.size = @curr_text.to_i elsif name == 'StorageClass' @curr_entry.storage_class = @curr_text elsif name == 'ID' + @curr_entry.owner = Owner.new if !@curr_entry.owner @curr_entry.owner.id = @curr_text elsif name == 'DisplayName' + @curr_entry.owner = Owner.new if !@curr_entry.owner @curr_entry.owner.display_name = @curr_text elsif name == 'CommonPrefixes' @common_prefixes << @common_prefix_entry elsif name == 'Prefix' # this is the common prefix for keys that match up to the delimiter