mss-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +9 -0
- data/LICENSE.txt +0 -0
- data/README.md +192 -0
- data/bin/mss-rb +178 -0
- data/ca-bundle.crt +3554 -0
- data/lib/mss/core/async_handle.rb +89 -0
- data/lib/mss/core/cacheable.rb +76 -0
- data/lib/mss/core/client.rb +786 -0
- data/lib/mss/core/collection/simple.rb +81 -0
- data/lib/mss/core/collection/with_limit_and_next_token.rb +70 -0
- data/lib/mss/core/collection/with_next_token.rb +96 -0
- data/lib/mss/core/collection.rb +262 -0
- data/lib/mss/core/configuration.rb +527 -0
- data/lib/mss/core/credential_providers.rb +653 -0
- data/lib/mss/core/data.rb +251 -0
- data/lib/mss/core/deprecations.rb +83 -0
- data/lib/mss/core/endpoints.rb +36 -0
- data/lib/mss/core/http/connection_pool.rb +374 -0
- data/lib/mss/core/http/curb_handler.rb +150 -0
- data/lib/mss/core/http/handler.rb +88 -0
- data/lib/mss/core/http/net_http_handler.rb +144 -0
- data/lib/mss/core/http/patch.rb +98 -0
- data/lib/mss/core/http/request.rb +258 -0
- data/lib/mss/core/http/response.rb +80 -0
- data/lib/mss/core/indifferent_hash.rb +87 -0
- data/lib/mss/core/inflection.rb +55 -0
- data/lib/mss/core/ini_parser.rb +41 -0
- data/lib/mss/core/json_client.rb +46 -0
- data/lib/mss/core/json_parser.rb +75 -0
- data/lib/mss/core/json_request_builder.rb +34 -0
- data/lib/mss/core/json_response_parser.rb +78 -0
- data/lib/mss/core/lazy_error_classes.rb +107 -0
- data/lib/mss/core/log_formatter.rb +426 -0
- data/lib/mss/core/managed_file.rb +31 -0
- data/lib/mss/core/meta_utils.rb +44 -0
- data/lib/mss/core/model.rb +61 -0
- data/lib/mss/core/naming.rb +29 -0
- data/lib/mss/core/option_grammar.rb +737 -0
- data/lib/mss/core/options/json_serializer.rb +81 -0
- data/lib/mss/core/options/validator.rb +154 -0
- data/lib/mss/core/options/xml_serializer.rb +117 -0
- data/lib/mss/core/page_result.rb +74 -0
- data/lib/mss/core/policy.rb +938 -0
- data/lib/mss/core/query_client.rb +40 -0
- data/lib/mss/core/query_error_parser.rb +23 -0
- data/lib/mss/core/query_request_builder.rb +46 -0
- data/lib/mss/core/query_response_parser.rb +34 -0
- data/lib/mss/core/region.rb +84 -0
- data/lib/mss/core/region_collection.rb +79 -0
- data/lib/mss/core/resource.rb +412 -0
- data/lib/mss/core/resource_cache.rb +39 -0
- data/lib/mss/core/response.rb +214 -0
- data/lib/mss/core/response_cache.rb +49 -0
- data/lib/mss/core/rest_error_parser.rb +23 -0
- data/lib/mss/core/rest_json_client.rb +39 -0
- data/lib/mss/core/rest_request_builder.rb +153 -0
- data/lib/mss/core/rest_response_parser.rb +65 -0
- data/lib/mss/core/rest_xml_client.rb +46 -0
- data/lib/mss/core/service_interface.rb +82 -0
- data/lib/mss/core/signers/base.rb +45 -0
- data/lib/mss/core/signers/cloud_front.rb +55 -0
- data/lib/mss/core/signers/s3.rb +158 -0
- data/lib/mss/core/signers/version_2.rb +71 -0
- data/lib/mss/core/signers/version_3.rb +85 -0
- data/lib/mss/core/signers/version_3_https.rb +60 -0
- data/lib/mss/core/signers/version_4/chunk_signed_stream.rb +190 -0
- data/lib/mss/core/signers/version_4.rb +227 -0
- data/lib/mss/core/uri_escape.rb +43 -0
- data/lib/mss/core/xml/frame.rb +245 -0
- data/lib/mss/core/xml/frame_stack.rb +84 -0
- data/lib/mss/core/xml/grammar.rb +306 -0
- data/lib/mss/core/xml/parser.rb +69 -0
- data/lib/mss/core/xml/root_frame.rb +64 -0
- data/lib/mss/core/xml/sax_handlers/libxml.rb +46 -0
- data/lib/mss/core/xml/sax_handlers/nokogiri.rb +55 -0
- data/lib/mss/core/xml/sax_handlers/ox.rb +40 -0
- data/lib/mss/core/xml/sax_handlers/rexml.rb +46 -0
- data/lib/mss/core/xml/stub.rb +122 -0
- data/lib/mss/core.rb +602 -0
- data/lib/mss/errors.rb +161 -0
- data/lib/mss/rails.rb +194 -0
- data/lib/mss/s3/access_control_list.rb +262 -0
- data/lib/mss/s3/acl_object.rb +263 -0
- data/lib/mss/s3/acl_options.rb +200 -0
- data/lib/mss/s3/bucket.rb +757 -0
- data/lib/mss/s3/bucket_collection.rb +161 -0
- data/lib/mss/s3/bucket_lifecycle_configuration.rb +472 -0
- data/lib/mss/s3/bucket_region_cache.rb +51 -0
- data/lib/mss/s3/bucket_tag_collection.rb +110 -0
- data/lib/mss/s3/bucket_version_collection.rb +78 -0
- data/lib/mss/s3/cipher_io.rb +119 -0
- data/lib/mss/s3/client/xml.rb +265 -0
- data/lib/mss/s3/client.rb +2076 -0
- data/lib/mss/s3/config.rb +60 -0
- data/lib/mss/s3/cors_rule.rb +107 -0
- data/lib/mss/s3/cors_rule_collection.rb +193 -0
- data/lib/mss/s3/data_options.rb +190 -0
- data/lib/mss/s3/encryption_utils.rb +145 -0
- data/lib/mss/s3/errors.rb +93 -0
- data/lib/mss/s3/multipart_upload.rb +353 -0
- data/lib/mss/s3/multipart_upload_collection.rb +75 -0
- data/lib/mss/s3/object_collection.rb +355 -0
- data/lib/mss/s3/object_metadata.rb +102 -0
- data/lib/mss/s3/object_upload_collection.rb +76 -0
- data/lib/mss/s3/object_version.rb +153 -0
- data/lib/mss/s3/object_version_collection.rb +88 -0
- data/lib/mss/s3/paginated_collection.rb +74 -0
- data/lib/mss/s3/policy.rb +73 -0
- data/lib/mss/s3/prefix_and_delimiter_collection.rb +46 -0
- data/lib/mss/s3/prefixed_collection.rb +84 -0
- data/lib/mss/s3/presign_v4.rb +135 -0
- data/lib/mss/s3/presigned_post.rb +574 -0
- data/lib/mss/s3/region_detection.rb +75 -0
- data/lib/mss/s3/request.rb +61 -0
- data/lib/mss/s3/s3_object.rb +1795 -0
- data/lib/mss/s3/tree/branch_node.rb +67 -0
- data/lib/mss/s3/tree/child_collection.rb +103 -0
- data/lib/mss/s3/tree/leaf_node.rb +93 -0
- data/lib/mss/s3/tree/node.rb +21 -0
- data/lib/mss/s3/tree/parent.rb +86 -0
- data/lib/mss/s3/tree.rb +115 -0
- data/lib/mss/s3/uploaded_part.rb +81 -0
- data/lib/mss/s3/uploaded_part_collection.rb +83 -0
- data/lib/mss/s3/website_configuration.rb +101 -0
- data/lib/mss/s3.rb +161 -0
- data/lib/mss/version.rb +16 -0
- data/lib/mss-sdk.rb +2 -0
- data/lib/mss.rb +14 -0
- data/rails/init.rb +14 -0
- metadata +201 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
5
|
+
# the License is located at
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# or in the "license" file accompanying this file. This file is
|
9
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
10
|
+
# ANY KIND, either express or implied. See the License for the specific
|
11
|
+
# language governing permissions and limitations under the License.
|
12
|
+
|
13
|
+
module MSS
|
14
|
+
module Core
|
15
|
+
module Http
|
16
|
+
|
17
|
+
# # NetHttpHandler
|
18
|
+
#
|
19
|
+
# This is the default HTTP handler for the mss-sdk gem. It uses
|
20
|
+
# Ruby's Net::HTTP to make requests. It uses persistent connections
|
21
|
+
# and a connection pool.
|
22
|
+
#
|
23
|
+
class NetHttpHandler
|
24
|
+
|
25
|
+
class TruncatedBodyError < IOError; end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
NETWORK_ERRORS = [
|
29
|
+
SocketError, EOFError, IOError, Timeout::Error,
|
30
|
+
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE,
|
31
|
+
Errno::EINVAL, Errno::ETIMEDOUT, Errno::EHOSTUNREACH,
|
32
|
+
OpenSSL::SSL::SSLError
|
33
|
+
]
|
34
|
+
|
35
|
+
# (see ConnectionPool.new)
|
36
|
+
def initialize options = {}
|
37
|
+
@pool = options[:connection_pool] || ConnectionPool.new(options)
|
38
|
+
@verify_content_length = options[:verify_response_body_content_length]
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [ConnectionPool]
|
42
|
+
attr_reader :pool
|
43
|
+
|
44
|
+
# Given a populated request object and an empty response object,
|
45
|
+
# this method will make the request and them populate the
|
46
|
+
# response.
|
47
|
+
# @param [Request] request
|
48
|
+
# @param [Response] response
|
49
|
+
# @return [nil]
|
50
|
+
def handle request, response, &read_block
|
51
|
+
retry_possible = true
|
52
|
+
|
53
|
+
begin
|
54
|
+
|
55
|
+
@pool.session_for(request.endpoint) do |http|
|
56
|
+
|
57
|
+
http.read_timeout = request.read_timeout
|
58
|
+
http.continue_timeout = request.continue_timeout if
|
59
|
+
http.respond_to?(:continue_timeout=)
|
60
|
+
|
61
|
+
exp_length = nil
|
62
|
+
act_length = 0
|
63
|
+
http.request(build_net_http_request(request)) do |net_http_resp|
|
64
|
+
response.status = net_http_resp.code.to_i
|
65
|
+
response.headers = net_http_resp.to_hash
|
66
|
+
exp_length = determine_expected_content_length(response)
|
67
|
+
if block_given? and response.status < 300
|
68
|
+
net_http_resp.read_body do |data|
|
69
|
+
begin
|
70
|
+
act_length += data.bytesize
|
71
|
+
yield data unless data.empty?
|
72
|
+
ensure
|
73
|
+
retry_possible = false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
else
|
77
|
+
response.body = net_http_resp.read_body
|
78
|
+
act_length += response.body.bytesize unless response.body.nil?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
run_check = exp_length && request.http_method != "HEAD" && @verify_content_length
|
82
|
+
if run_check && act_length != exp_length
|
83
|
+
raise TruncatedBodyError, 'content-length does not match'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
rescue *NETWORK_ERRORS => error
|
88
|
+
raise error unless retry_possible
|
89
|
+
response.network_error = error
|
90
|
+
end
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def determine_expected_content_length response
|
97
|
+
if header = response.headers['content-length']
|
98
|
+
if header.is_a?(Array)
|
99
|
+
header.first.to_i
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Given an MSS::Core::HttpRequest, this method translates
|
105
|
+
# it into a Net::HTTPRequest (Get, Put, Post, Head or Delete).
|
106
|
+
# @param [Request] request
|
107
|
+
# @return [Net::HTTPRequest]
|
108
|
+
def build_net_http_request request
|
109
|
+
|
110
|
+
# Net::HTTP adds a content-type (1.8.7+) and accept-encoding (2.0.0+)
|
111
|
+
# to the request if these headers are not set. Setting a default
|
112
|
+
# empty value defeats this.
|
113
|
+
#
|
114
|
+
# Removing these are necessary for most services to no break request
|
115
|
+
# signatures as well as dynamodb crc32 checks (these fail if the
|
116
|
+
# response is gzipped).
|
117
|
+
headers = { 'content-type' => '', 'accept-encoding' => '' }
|
118
|
+
|
119
|
+
request.headers.each_pair do |key,value|
|
120
|
+
headers[key] = value.to_s
|
121
|
+
end
|
122
|
+
|
123
|
+
request_class = case request.http_method
|
124
|
+
when 'GET' then Net::HTTP::Get
|
125
|
+
when 'PUT' then Net::HTTP::Put
|
126
|
+
when 'POST' then Net::HTTP::Post
|
127
|
+
when 'HEAD' then Net::HTTP::Head
|
128
|
+
when 'DELETE' then Net::HTTP::Delete
|
129
|
+
else
|
130
|
+
msg = "unsupported http method: #{request.http_method}"
|
131
|
+
raise ArgumentError, msg
|
132
|
+
end
|
133
|
+
|
134
|
+
net_http_req = request_class.new(request.uri, headers)
|
135
|
+
net_http_req.body_stream = request.body_stream
|
136
|
+
net_http_req
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module MSS
|
4
|
+
module Core
|
5
|
+
module Http
|
6
|
+
# @api private
|
7
|
+
module Patches
|
8
|
+
|
9
|
+
def self.apply!
|
10
|
+
if RUBY_VERSION >= '2.0'
|
11
|
+
Net::HTTP.send(:include, Ruby_2)
|
12
|
+
elsif RUBY_VERSION >= '1.9.3'
|
13
|
+
Net::HTTP.send(:include, Ruby_1_9_3)
|
14
|
+
end
|
15
|
+
if RUBY_VERSION >= '1.9.3'
|
16
|
+
Net::HTTP.send(:alias_method, :old_transport_request, :transport_request)
|
17
|
+
Net::HTTP.send(:alias_method, :transport_request, :new_transport_request)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Ruby_2
|
22
|
+
def new_transport_request(req)
|
23
|
+
count = 0
|
24
|
+
begin
|
25
|
+
begin_transport req
|
26
|
+
res = catch(:response) {
|
27
|
+
req.exec @socket, @curr_http_version, edit_path(req.path)
|
28
|
+
begin
|
29
|
+
res = Net::HTTPResponse.read_new(@socket)
|
30
|
+
res.decode_content = req.decode_content
|
31
|
+
end while res.kind_of?(Net::HTTPContinue)
|
32
|
+
|
33
|
+
res.uri = req.uri
|
34
|
+
|
35
|
+
res
|
36
|
+
}
|
37
|
+
res.reading_body(@socket, req.response_body_permitted?) {
|
38
|
+
yield res if block_given?
|
39
|
+
}
|
40
|
+
rescue Net::OpenTimeout
|
41
|
+
raise
|
42
|
+
rescue Net::ReadTimeout, IOError, EOFError,
|
43
|
+
Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE,
|
44
|
+
# avoid a dependency on OpenSSL
|
45
|
+
defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
|
46
|
+
Timeout::Error => exception
|
47
|
+
if count == 0 && Net::HTTP::IDEMPOTENT_METHODS_.include?(req.method)
|
48
|
+
count += 1
|
49
|
+
@socket.close if @socket and not @socket.closed?
|
50
|
+
D "Conn close because of error #{exception}, and retry"
|
51
|
+
if req.body_stream
|
52
|
+
if req.body_stream.respond_to?(:rewind)
|
53
|
+
req.body_stream.rewind
|
54
|
+
else
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
end
|
58
|
+
retry
|
59
|
+
end
|
60
|
+
D "Conn close because of error #{exception}"
|
61
|
+
@socket.close if @socket and not @socket.closed?
|
62
|
+
raise
|
63
|
+
end
|
64
|
+
|
65
|
+
end_transport req, res
|
66
|
+
res
|
67
|
+
rescue => exception
|
68
|
+
D "Conn close because of error #{exception}"
|
69
|
+
@socket.close if @socket and not @socket.closed?
|
70
|
+
raise exception
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module Ruby_1_9_3
|
75
|
+
def new_transport_request(req)
|
76
|
+
begin_transport req
|
77
|
+
res = catch(:response) {
|
78
|
+
req.exec @socket, @curr_http_version, edit_path(req.path)
|
79
|
+
begin
|
80
|
+
res = Net::HTTPResponse.read_new(@socket)
|
81
|
+
end while res.kind_of?(Net::HTTPContinue)
|
82
|
+
res
|
83
|
+
}
|
84
|
+
res.reading_body(@socket, req.response_body_permitted?) {
|
85
|
+
yield res if block_given?
|
86
|
+
}
|
87
|
+
end_transport req, res
|
88
|
+
res
|
89
|
+
rescue => exception
|
90
|
+
D "Conn close because of error #{exception}"
|
91
|
+
@socket.close if @socket and not @socket.closed?
|
92
|
+
raise exception
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
5
|
+
# the License is located at
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# or in the "license" file accompanying this file. This file is
|
9
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
10
|
+
# ANY KIND, either express or implied. See the License for the specific
|
11
|
+
# language governing permissions and limitations under the License.
|
12
|
+
|
13
|
+
module MSS
|
14
|
+
module Core
|
15
|
+
module Http
|
16
|
+
|
17
|
+
# Base class for all service reqeusts. This class describes
|
18
|
+
# a basic HTTP request, but will not make one. It is consumed
|
19
|
+
# by a HTTP handler class that sends the actual request
|
20
|
+
# and parses the actual response.
|
21
|
+
class Request
|
22
|
+
|
23
|
+
extend Deprecations
|
24
|
+
|
25
|
+
# Returns a new empty http request object.
|
26
|
+
def initialize
|
27
|
+
@http_method = 'POST'
|
28
|
+
@use_ssl = true
|
29
|
+
@headers = CaseInsensitiveHash.new
|
30
|
+
@uri = '/'
|
31
|
+
@params = []
|
32
|
+
@read_timeout = 60
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [String] hostname of the request
|
36
|
+
attr_accessor :host
|
37
|
+
|
38
|
+
# @return [Integer] Returns the port number this request will be
|
39
|
+
# made via (usually 443 or 80).
|
40
|
+
attr_accessor :port
|
41
|
+
|
42
|
+
# @return [String] Returns the HTTP request method (e.g. 'GET', 'PUT',
|
43
|
+
# 'POST', 'HEAD' or 'DELETE'). Defaults to 'POST'.
|
44
|
+
attr_accessor :http_method
|
45
|
+
|
46
|
+
# @return [CaseInsensitiveHash] request headers
|
47
|
+
attr_accessor :headers
|
48
|
+
|
49
|
+
# @return [String] Returns the request URI (path + querystring).
|
50
|
+
attr_accessor :uri
|
51
|
+
|
52
|
+
# @return [String] The region name this request is for. Only needs
|
53
|
+
# to be populated for requests against signature v4 endpoints.
|
54
|
+
attr_accessor :region
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
attr_accessor :service
|
58
|
+
|
59
|
+
# @return [String] Returns the MSS access key ID used to authorize the
|
60
|
+
# request.
|
61
|
+
# @api private
|
62
|
+
attr_accessor :access_key_id
|
63
|
+
|
64
|
+
# @return [Array<Param>] Returns an array of request params. Requests
|
65
|
+
# that use signature version 2 add params to the request and then
|
66
|
+
# sign those before building the {#body}. Normally the {#body}
|
67
|
+
# should be set directly with the HTTP payload.
|
68
|
+
# @api private
|
69
|
+
attr_accessor :params
|
70
|
+
|
71
|
+
# @return [String] The name of the service for Signature v4 signing.
|
72
|
+
# This does not always match the ruby name (e.g.
|
73
|
+
# simple_email_service and ses do not match).
|
74
|
+
attr_accessor :service_ruby_name
|
75
|
+
|
76
|
+
# @return [Integer] The number of seconds the service has to respond
|
77
|
+
# before a timeout error is raised on the request.
|
78
|
+
attr_accessor :read_timeout
|
79
|
+
|
80
|
+
alias_method :default_read_timeout, :read_timeout
|
81
|
+
deprecated :default_read_timeout, :use => :read_timeout
|
82
|
+
|
83
|
+
# @return [Boolean] Returns `true` if this request should be made
|
84
|
+
# with SSL enabled.
|
85
|
+
attr_accessor :use_ssl
|
86
|
+
|
87
|
+
alias_method :use_ssl?, :use_ssl
|
88
|
+
|
89
|
+
# @return [Float] timeout The number of seconds to wait for a
|
90
|
+
# 100-continue response before sending the HTTP request body.
|
91
|
+
# @api private
|
92
|
+
attr_accessor :continue_timeout
|
93
|
+
|
94
|
+
# @api private
|
95
|
+
def endpoint
|
96
|
+
scheme = use_ssl ? 'https' : 'http'
|
97
|
+
port = case scheme
|
98
|
+
when 'https' then self.port == 443 ? '' : ":#{self.port}"
|
99
|
+
when 'http' then self.port == 80 ? '' : ":#{self.port}"
|
100
|
+
end
|
101
|
+
"#{scheme}://#{host}#{port}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [Integer] Returns the port the request will be made over.
|
105
|
+
# Defaults to 443 for SSL requests and 80 for non-SSL requests.
|
106
|
+
def port
|
107
|
+
@port || (use_ssl? ? 443 : 80)
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [String] Returns the HTTP request path.
|
111
|
+
def path
|
112
|
+
uri.split(/\?/)[0]
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [String] Returns the HTTP request querystring.
|
116
|
+
def querystring
|
117
|
+
uri.split(/\?/)[1]
|
118
|
+
end
|
119
|
+
|
120
|
+
# Adds a request param.
|
121
|
+
#
|
122
|
+
# @overload add_param(param_name, param_value = nil)
|
123
|
+
# Add a param (name/value)
|
124
|
+
# @param [String] param_name
|
125
|
+
# @param [String] param_value Leave blank for sub resources
|
126
|
+
#
|
127
|
+
# @overload add_param(param_obj)
|
128
|
+
# Add a param (object)
|
129
|
+
# @param [Param] param_obj
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def add_param name_or_param, value = nil
|
133
|
+
if name_or_param.kind_of?(Param)
|
134
|
+
@params << name_or_param
|
135
|
+
else
|
136
|
+
@params << Param.new(name_or_param, value)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# @api private
|
141
|
+
def remove_param(name)
|
142
|
+
if param = @params.find { |p| p.name == name }
|
143
|
+
@params.delete(param)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# @api private
|
148
|
+
# @return [String,nil] Returns the url encoded request params. If there
|
149
|
+
# are no params, then nil is returned.
|
150
|
+
def url_encoded_params
|
151
|
+
params.empty? ? nil : params.sort.collect(&:encoded).join('&')
|
152
|
+
end
|
153
|
+
|
154
|
+
# @param [String] body
|
155
|
+
def body= body
|
156
|
+
@body = body
|
157
|
+
if body
|
158
|
+
headers['content-length'] = body.bytesize if body
|
159
|
+
else
|
160
|
+
headers.delete('content-length')
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# @note Calling #body on a request with a #body_stream
|
165
|
+
# will cause the entire stream to be read into memory.
|
166
|
+
# @return [String,nil] Returns the request body.
|
167
|
+
def body
|
168
|
+
if @body
|
169
|
+
@body
|
170
|
+
elsif @body_stream
|
171
|
+
@body = @body_stream.read
|
172
|
+
if @body_stream.respond_to?(:rewind)
|
173
|
+
@body_stream.rewind
|
174
|
+
else
|
175
|
+
@body_stream = StringIO.new(@body)
|
176
|
+
end
|
177
|
+
@body
|
178
|
+
else
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Sets the request body as an IO object that will be streamed.
|
184
|
+
# @note You must also set the #headers['content-length']
|
185
|
+
# @param [IO] stream An object that responds to #read and #eof.
|
186
|
+
def body_stream= stream
|
187
|
+
@body_stream = stream
|
188
|
+
end
|
189
|
+
|
190
|
+
# @return [IO,nil]
|
191
|
+
def body_stream
|
192
|
+
if @body_stream
|
193
|
+
@body_stream
|
194
|
+
elsif @body
|
195
|
+
StringIO.new(@body)
|
196
|
+
else
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# @api private
|
202
|
+
class CaseInsensitiveHash < Hash
|
203
|
+
|
204
|
+
def []= key, value
|
205
|
+
super(key.to_s.downcase, value)
|
206
|
+
end
|
207
|
+
|
208
|
+
def [] key
|
209
|
+
super(key.to_s.downcase)
|
210
|
+
end
|
211
|
+
|
212
|
+
def has_key?(key)
|
213
|
+
super(key.to_s.downcase)
|
214
|
+
end
|
215
|
+
alias_method :key?, :has_key?
|
216
|
+
alias_method :include?, :has_key?
|
217
|
+
alias_method :member?, :has_key?
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
# Represents a single request paramater. Some services accept this
|
222
|
+
# in a form encoded body string, others as query parameters.
|
223
|
+
# It is up to each service's Request class to determine how to
|
224
|
+
# consume these params.
|
225
|
+
# @api private
|
226
|
+
class Param
|
227
|
+
|
228
|
+
include UriEscape
|
229
|
+
|
230
|
+
attr_accessor :name, :value
|
231
|
+
|
232
|
+
def initialize name, value = nil
|
233
|
+
@name = name
|
234
|
+
@value = value
|
235
|
+
end
|
236
|
+
|
237
|
+
def <=> other
|
238
|
+
name <=> other.name
|
239
|
+
end
|
240
|
+
|
241
|
+
def to_s
|
242
|
+
value ? "#{name}=#{value}" : name
|
243
|
+
end
|
244
|
+
|
245
|
+
def ==(other)
|
246
|
+
other.kind_of?(Param) and to_s == other.to_s
|
247
|
+
end
|
248
|
+
|
249
|
+
def encoded
|
250
|
+
value ? "#{escape(name)}=#{escape(value)}" : "#{escape(name)}="
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
5
|
+
# the License is located at
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# or in the "license" file accompanying this file. This file is
|
9
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
10
|
+
# ANY KIND, either express or implied. See the License for the specific
|
11
|
+
# language governing permissions and limitations under the License.
|
12
|
+
|
13
|
+
module MSS
|
14
|
+
module Core
|
15
|
+
module Http
|
16
|
+
|
17
|
+
# Represents the http response from a service request.
|
18
|
+
#
|
19
|
+
# Responses have:
|
20
|
+
#
|
21
|
+
# * status (200, 404, 500, etc)
|
22
|
+
# * headers (hash of response headers)
|
23
|
+
# * body (the response body)
|
24
|
+
class Response
|
25
|
+
|
26
|
+
# @return [Integer] Returns the http response status code.
|
27
|
+
attr_accessor :status
|
28
|
+
|
29
|
+
# @return [Hash] ({}) Returns the HTTP response headers.
|
30
|
+
attr_accessor :headers
|
31
|
+
|
32
|
+
# @return [String,nil] Returns the HTTP response body.
|
33
|
+
attr_accessor :body
|
34
|
+
|
35
|
+
# @return [Exception,nil]
|
36
|
+
attr_accessor :network_error
|
37
|
+
|
38
|
+
# @return [Boolean] Returns `true` if the request could not be made
|
39
|
+
# because of a networking issue (including timeouts).
|
40
|
+
def network_error?
|
41
|
+
@network_error ? true : false
|
42
|
+
end
|
43
|
+
|
44
|
+
# The #network_error attribute was previously #timeout, aliasing
|
45
|
+
# for backwards compatability
|
46
|
+
alias_method :timeout=, :network_error=
|
47
|
+
|
48
|
+
# @param [Hash] options
|
49
|
+
# @option options [Integer] :status (200) HTTP status code
|
50
|
+
# @option options [Hash] :headers ({}) HTTP response headers
|
51
|
+
# @option options [String] :body ('') HTTP response body
|
52
|
+
def initialize options = {}, &block
|
53
|
+
@status = options[:status] || 200
|
54
|
+
@headers = options[:headers] || {}
|
55
|
+
@body = options[:body]
|
56
|
+
yield(self) if block_given?
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the header value with the given name.
|
61
|
+
#
|
62
|
+
# The value is matched case-insensitively so if the headers hash
|
63
|
+
# contains a key like 'Date' and you request the value for
|
64
|
+
# 'date' the 'Date' value will be returned.
|
65
|
+
#
|
66
|
+
# @param [String,Symbol] name The name of the header to fetch a value for.
|
67
|
+
# @return [String,nil] The value of the given header
|
68
|
+
def header name
|
69
|
+
headers.each_pair do |header_name, header_value|
|
70
|
+
if header_name.downcase == name.to_s.downcase
|
71
|
+
return header_value.is_a?(Array) ? header_value.first : header_value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
5
|
+
# the License is located at
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# or in the "license" file accompanying this file. This file is
|
9
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
10
|
+
# ANY KIND, either express or implied. See the License for the specific
|
11
|
+
# language governing permissions and limitations under the License.
|
12
|
+
|
13
|
+
module MSS
|
14
|
+
module Core
|
15
|
+
|
16
|
+
# A utility class to provide indifferent access to hash data.
|
17
|
+
#
|
18
|
+
# Inspired by ActiveSupport's HashWithIndifferentAccess, this class
|
19
|
+
# has a few notable differences:
|
20
|
+
#
|
21
|
+
# * ALL keys are converted to strings (via #to_s)
|
22
|
+
# * It does not deep merge/convert hashes indifferently, good for fla
|
23
|
+
# * It will not perserve default value behaviours
|
24
|
+
#
|
25
|
+
# These features were omitted because our primary use for this class is to
|
26
|
+
# wrap a 1-level hash as a return value, but we want the user to access
|
27
|
+
# the values with string or symbol keys.
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
class IndifferentHash < Hash
|
31
|
+
|
32
|
+
def initialize *args
|
33
|
+
if args.first.is_a?(Hash)
|
34
|
+
super()
|
35
|
+
merge!(*args)
|
36
|
+
else
|
37
|
+
super(*args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
alias_method :_getter, :[]
|
42
|
+
alias_method :_setter, :[]=
|
43
|
+
|
44
|
+
def []=(key, value)
|
45
|
+
_setter(_convert_key(key), value)
|
46
|
+
end
|
47
|
+
alias_method :store, :[]=
|
48
|
+
|
49
|
+
def [] key
|
50
|
+
_getter(_convert_key(key))
|
51
|
+
end
|
52
|
+
|
53
|
+
def merge! hash
|
54
|
+
hash.each_pair do |key,value|
|
55
|
+
self[key] = value
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
alias_method :update, :merge!
|
60
|
+
|
61
|
+
def merge hash
|
62
|
+
self.dup.merge!(hash)
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_key? key
|
66
|
+
super(_convert_key(key))
|
67
|
+
end
|
68
|
+
alias_method :key?, :has_key?
|
69
|
+
alias_method :member?, :has_key?
|
70
|
+
alias_method :include?, :has_key?
|
71
|
+
|
72
|
+
def fetch key, *extras, &block
|
73
|
+
super(_convert_key(key), *extras, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete key
|
77
|
+
super(_convert_key(key))
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def _convert_key key
|
82
|
+
key.is_a?(String) ? key : key.to_s
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|