Class: RightScale::CloudApi::AWS::S3::RequestSigner
- Inherits:
-
CloudApi::Routine
- Object
- CloudApi::Routine
- RightScale::CloudApi::AWS::S3::RequestSigner
- Defined in:
- lib/cloud/aws/s3/routines/request_signer.rb
Overview
S3 Request signer
Direct Known Subclasses
Defined Under Namespace
Classes: Error
Constant Summary collapse
- SUB_RESOURCES =
This guys are used to sign a request
%w{ acl cors delete lifecycle location logging notification policy requestPayment tagging torrent uploads versionId versioning versions website }
- OVERRIDE_RESPONSE_HEADERS =
Using Query String API Amazon allows to override some of response headers:
response-content-type response-content-language response-expires reponse-cache-control response-content-disposition response-content-encoding
/^response-/
- ONE_YEAR_OF_SECONDS =
One year in seconds
365*60*60*24
Instance Method Summary collapse
-
#compute_body(body, content_type) ⇒ Object
Returns response body.
-
#compute_bucket_name_and_object_path(bucket, relative_path) ⇒ Array
Extracts S3 bucket name and escapes relative path.
-
#compute_headers!(headers, body, host) ⇒ Hash
Sets response headers.
-
#compute_host(bucket, uri) ⇒ URI
Figure out if we need to add bucket name into the host name.
-
#compute_path(bucket, object) ⇒ String
Builds request path.
-
#is_dns_bucket?(bucket) ⇒ Boolean
Returns
true
if DNS compatible buckets are enabled (default) and the given bucket is DNS compatible. - #no_dns_buckets? ⇒ Boolean
-
#process ⇒ void
Authenticates an S3 request.
Instance Method Details
#compute_body(body, content_type) ⇒ Object
Returns response body
168 169 170 171 172 173 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 168 def compute_body(body, content_type) return body if body.nil? # Make sure it is a String instance return body unless body.is_a?(Hash) Utils::contentify_body(body, content_type) end |
#compute_bucket_name_and_object_path(bucket, relative_path) ⇒ Array
Extracts S3 bucket name and escapes relative path
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 115 def compute_bucket_name_and_object_path(bucket, relative_path) return [bucket, relative_path] if bucket # This is a very first attempt: relative_path.to_s[/^([^\/]*)\/?(.*)$/] # Escape part of the path that may have UTF-8 chars (in S3 Object name for instance). # Amazon wants them to be escaped before we sign the request. # P.S. but do not escape "/" (signature v4 does not like this) # new_bucket = $1 pre_object = $2.to_s object = pre_object.split('/').map{|i| Utils::AWS::amz_escape(i)}.join('/') # Preserve '/' if it is the last symbol of the object because it is an operation on a folder object += '/' if object.length > 0 && pre_object[/\/$/] [ new_bucket, object ] end |
#compute_headers!(headers, body, host) ⇒ Hash
Sets response headers
184 185 186 187 188 189 190 191 192 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 184 def compute_headers!(headers, body, host) # Make sure 'content-type' is set. # P.S. Ruby 2.1+ sets 'content-type' by default for POST and PUT requests. # So we need to include it into our signature to avoid the error below: # 'The request signature we calculated does not match the signature you provided. # Check your key and signing method.' headers.set_if_blank('content-type', 'binary/octet-stream') headers end |
#compute_host(bucket, uri) ⇒ URI
Figure out if we need to add bucket name into the host name
If there was a redirect and it had ‘location’ header then there is nothing to do with the host, otherwise we have to add the bucket to the host.
P.S. When Amazon returns a redirect (usually 301) with the new host in the message body, the new host does not have the bucket name in it. But if it is 307 and the host is in the location header then that host name already includes the bucket in it. The only thing we can do so far is to check if the host name starts with the bucket and the name is at least 4th level DNS name.
Examples:
* my-bucket.s3-ap-southeast-2.amazonaws.com
* my-bucket.s3.amazonaws.com
* s3.amazonaws.com
153 154 155 156 157 158 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 153 def compute_host(bucket, uri) return uri unless is_dns_bucket?(bucket) return uri if uri.host[/^#{bucket}\..+\.[^.]+\.[^.]+$/] uri.host = "#{bucket}.#{uri.host}" uri end |
#compute_path(bucket, object) ⇒ String
Builds request path
202 203 204 205 206 207 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 202 def compute_path(bucket, object) data = [] data << bucket unless is_dns_bucket?(bucket) data << object Utils::join_urn(*data) end |
#is_dns_bucket?(bucket) ⇒ Boolean
Returns true
if DNS compatible buckets are enabled (default) and the given bucket is DNS compatible
215 216 217 218 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 215 def is_dns_bucket?(bucket) return false if no_dns_buckets? Utils::AWS::is_dns_bucket?(bucket) end |
#no_dns_buckets? ⇒ Boolean
221 222 223 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 221 def no_dns_buckets? !!@data[:options][:cloud][:no_dns_buckets] end |
#process ⇒ void
This method returns an undefined value.
Authenticates an S3 request
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/cloud/aws/s3/routines/request_signer.rb', line 78 def process uri = @data[:connection][:uri] body = @data[:request][:body] bucket = @data[:request][:bucket] object = @data[:request][:relative_path] bucket, object = compute_bucket_name_and_object_path(bucket, object) body = compute_body(body, @data[:request][:headers]['content-type']) uri = compute_host(bucket, uri) compute_headers!(@data[:request][:headers], body, uri.host) @data[:connection][:uri] = uri @data[:request][:bucket] = bucket @data[:request][:relative_path] = object @data[:request][:body] = body @data[:request][:path] = compute_path(bucket, object) Utils::AWS::sign_v4_signature( @data[:credentials][:aws_access_key_id], @data[:credentials][:aws_secret_access_key], @data[:connection][:uri].host, @data[:request] ) end |