aws-sdk-core 3.165.0 → 3.201.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +462 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-defaults/default_configuration.rb +4 -4
  5. data/lib/aws-sdk-core/binary/decode_handler.rb +3 -9
  6. data/lib/aws-sdk-core/binary/encode_handler.rb +1 -1
  7. data/lib/aws-sdk-core/binary/event_builder.rb +34 -37
  8. data/lib/aws-sdk-core/binary/event_stream_decoder.rb +1 -0
  9. data/lib/aws-sdk-core/binary/event_stream_encoder.rb +4 -3
  10. data/lib/aws-sdk-core/cbor/cbor_engine.rb +19 -0
  11. data/lib/aws-sdk-core/cbor/decoder.rb +310 -0
  12. data/lib/aws-sdk-core/cbor/encoder.rb +243 -0
  13. data/lib/aws-sdk-core/cbor.rb +106 -0
  14. data/lib/aws-sdk-core/client_stubs.rb +18 -14
  15. data/lib/aws-sdk-core/credential_provider.rb +4 -1
  16. data/lib/aws-sdk-core/credential_provider_chain.rb +8 -5
  17. data/lib/aws-sdk-core/ec2_metadata.rb +1 -1
  18. data/lib/aws-sdk-core/ecs_credentials.rb +178 -53
  19. data/lib/aws-sdk-core/endpoints/condition.rb +5 -0
  20. data/lib/aws-sdk-core/endpoints/endpoint_rule.rb +5 -1
  21. data/lib/aws-sdk-core/endpoints/error_rule.rb +5 -0
  22. data/lib/aws-sdk-core/endpoints/function.rb +5 -0
  23. data/lib/aws-sdk-core/endpoints/matchers.rb +18 -10
  24. data/lib/aws-sdk-core/endpoints/reference.rb +5 -0
  25. data/lib/aws-sdk-core/endpoints/rule.rb +5 -0
  26. data/lib/aws-sdk-core/endpoints/rule_set.rb +5 -0
  27. data/lib/aws-sdk-core/endpoints/rules_provider.rb +5 -0
  28. data/lib/aws-sdk-core/endpoints/templater.rb +6 -0
  29. data/lib/aws-sdk-core/endpoints/tree_rule.rb +5 -0
  30. data/lib/aws-sdk-core/endpoints/url.rb +1 -0
  31. data/lib/aws-sdk-core/endpoints.rb +69 -19
  32. data/lib/aws-sdk-core/error_handler.rb +41 -0
  33. data/lib/aws-sdk-core/errors.rb +12 -3
  34. data/lib/aws-sdk-core/event_emitter.rb +0 -16
  35. data/lib/aws-sdk-core/ini_parser.rb +7 -0
  36. data/lib/aws-sdk-core/instance_profile_credentials.rb +55 -32
  37. data/lib/aws-sdk-core/json/builder.rb +8 -1
  38. data/lib/aws-sdk-core/json/error_handler.rb +29 -14
  39. data/lib/aws-sdk-core/json/handler.rb +12 -6
  40. data/lib/aws-sdk-core/json/json_engine.rb +3 -1
  41. data/lib/aws-sdk-core/json/oj_engine.rb +7 -1
  42. data/lib/aws-sdk-core/json/parser.rb +33 -3
  43. data/lib/aws-sdk-core/json.rb +43 -14
  44. data/lib/aws-sdk-core/log/formatter.rb +6 -0
  45. data/lib/aws-sdk-core/lru_cache.rb +75 -0
  46. data/lib/aws-sdk-core/pageable_response.rb +3 -1
  47. data/lib/aws-sdk-core/param_validator.rb +9 -4
  48. data/lib/aws-sdk-core/plugins/bearer_authorization.rb +2 -0
  49. data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +7 -4
  50. data/lib/aws-sdk-core/plugins/client_metrics_plugin.rb +1 -0
  51. data/lib/aws-sdk-core/plugins/client_metrics_send_plugin.rb +14 -2
  52. data/lib/aws-sdk-core/plugins/credentials_configuration.rb +2 -0
  53. data/lib/aws-sdk-core/plugins/global_configuration.rb +8 -9
  54. data/lib/aws-sdk-core/plugins/http_checksum.rb +2 -1
  55. data/lib/aws-sdk-core/plugins/invocation_id.rb +1 -11
  56. data/lib/aws-sdk-core/plugins/logging.rb +2 -0
  57. data/lib/aws-sdk-core/plugins/protocols/api_gateway.rb +3 -1
  58. data/lib/aws-sdk-core/plugins/protocols/ec2.rb +2 -24
  59. data/lib/aws-sdk-core/plugins/protocols/json_rpc.rb +6 -8
  60. data/lib/aws-sdk-core/plugins/protocols/query.rb +4 -2
  61. data/lib/aws-sdk-core/plugins/protocols/rest_json.rb +3 -15
  62. data/lib/aws-sdk-core/plugins/protocols/rest_xml.rb +3 -0
  63. data/lib/aws-sdk-core/plugins/protocols/rpc_v2.rb +17 -0
  64. data/lib/aws-sdk-core/plugins/regional_endpoint.rb +140 -35
  65. data/lib/aws-sdk-core/plugins/request_compression.rb +226 -0
  66. data/lib/aws-sdk-core/plugins/retries/error_inspector.rb +2 -1
  67. data/lib/aws-sdk-core/plugins/retry_errors.rb +12 -3
  68. data/lib/aws-sdk-core/plugins/sign.rb +43 -19
  69. data/lib/aws-sdk-core/plugins/signature_v2.rb +2 -1
  70. data/lib/aws-sdk-core/plugins/signature_v4.rb +2 -1
  71. data/lib/aws-sdk-core/plugins/stub_responses.rb +1 -0
  72. data/lib/aws-sdk-core/plugins/transfer_encoding.rb +16 -9
  73. data/lib/aws-sdk-core/plugins/user_agent.rb +152 -14
  74. data/lib/aws-sdk-core/process_credentials.rb +45 -27
  75. data/lib/aws-sdk-core/query/ec2_handler.rb +27 -0
  76. data/lib/aws-sdk-core/query/ec2_param_builder.rb +5 -7
  77. data/lib/aws-sdk-core/query/handler.rb +4 -4
  78. data/lib/aws-sdk-core/query/param_builder.rb +2 -2
  79. data/lib/aws-sdk-core/query.rb +2 -1
  80. data/lib/aws-sdk-core/refreshing_credentials.rb +12 -12
  81. data/lib/aws-sdk-core/rest/content_type_handler.rb +60 -0
  82. data/lib/aws-sdk-core/rest/handler.rb +3 -4
  83. data/lib/aws-sdk-core/rest/request/body.rb +32 -5
  84. data/lib/aws-sdk-core/rest/request/endpoint.rb +24 -4
  85. data/lib/aws-sdk-core/rest/request/headers.rb +15 -7
  86. data/lib/aws-sdk-core/rest/request/querystring_builder.rb +62 -36
  87. data/lib/aws-sdk-core/rest/response/body.rb +15 -1
  88. data/lib/aws-sdk-core/rest/response/header_list_parser.rb +79 -0
  89. data/lib/aws-sdk-core/rest/response/headers.rb +8 -3
  90. data/lib/aws-sdk-core/rest.rb +1 -0
  91. data/lib/aws-sdk-core/rpc_v2/builder.rb +62 -0
  92. data/lib/aws-sdk-core/rpc_v2/content_type_handler.rb +45 -0
  93. data/lib/aws-sdk-core/rpc_v2/error_handler.rb +84 -0
  94. data/lib/aws-sdk-core/rpc_v2/handler.rb +74 -0
  95. data/lib/aws-sdk-core/rpc_v2/parser.rb +90 -0
  96. data/lib/aws-sdk-core/rpc_v2.rb +6 -0
  97. data/lib/aws-sdk-core/shared_config.rb +77 -18
  98. data/lib/aws-sdk-core/sso_credentials.rb +80 -45
  99. data/lib/aws-sdk-core/sso_token_provider.rb +3 -2
  100. data/lib/aws-sdk-core/stubbing/protocols/rpc_v2.rb +41 -0
  101. data/lib/aws-sdk-core/stubbing/stub_data.rb +11 -0
  102. data/lib/aws-sdk-core/util.rb +39 -0
  103. data/lib/aws-sdk-core/waiters/poller.rb +4 -2
  104. data/lib/aws-sdk-core/xml/builder.rb +17 -9
  105. data/lib/aws-sdk-core/xml/error_handler.rb +32 -42
  106. data/lib/aws-sdk-core/xml/parser/frame.rb +4 -20
  107. data/lib/aws-sdk-core/xml/parser/{engines/oga.rb → oga_engine.rb} +2 -0
  108. data/lib/aws-sdk-core/xml/parser/stack.rb +2 -0
  109. data/lib/aws-sdk-core/xml/parser.rb +2 -6
  110. data/lib/aws-sdk-core.rb +8 -2
  111. data/lib/aws-sdk-sso/client.rb +103 -46
  112. data/lib/aws-sdk-sso/client_api.rb +6 -0
  113. data/lib/aws-sdk-sso/endpoint_provider.rb +41 -96
  114. data/lib/aws-sdk-sso/endpoints.rb +1 -0
  115. data/lib/aws-sdk-sso/plugins/endpoints.rb +4 -2
  116. data/lib/aws-sdk-sso/types.rb +0 -35
  117. data/lib/aws-sdk-sso.rb +1 -1
  118. data/lib/aws-sdk-ssooidc/client.rb +488 -74
  119. data/lib/aws-sdk-ssooidc/client_api.rb +82 -1
  120. data/lib/aws-sdk-ssooidc/endpoint_provider.rb +41 -95
  121. data/lib/aws-sdk-ssooidc/endpoints.rb +15 -0
  122. data/lib/aws-sdk-ssooidc/errors.rb +52 -0
  123. data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +6 -2
  124. data/lib/aws-sdk-ssooidc/types.rb +372 -83
  125. data/lib/aws-sdk-ssooidc.rb +1 -1
  126. data/lib/aws-sdk-sts/client.rb +361 -288
  127. data/lib/aws-sdk-sts/client_api.rb +24 -11
  128. data/lib/aws-sdk-sts/endpoint_provider.rb +96 -213
  129. data/lib/aws-sdk-sts/endpoints.rb +1 -0
  130. data/lib/aws-sdk-sts/plugins/endpoints.rb +4 -2
  131. data/lib/aws-sdk-sts/presigner.rb +1 -1
  132. data/lib/aws-sdk-sts/types.rb +128 -197
  133. data/lib/aws-sdk-sts.rb +1 -1
  134. data/lib/seahorse/client/async_base.rb +1 -1
  135. data/lib/seahorse/client/async_response.rb +19 -0
  136. data/lib/seahorse/client/base.rb +18 -7
  137. data/lib/seahorse/client/configuration.rb +0 -4
  138. data/lib/seahorse/client/h2/connection.rb +12 -11
  139. data/lib/seahorse/client/h2/handler.rb +1 -0
  140. data/lib/seahorse/client/handler.rb +1 -1
  141. data/lib/seahorse/client/net_http/connection_pool.rb +3 -9
  142. data/lib/seahorse/client/net_http/patches.rb +1 -4
  143. data/lib/seahorse/client/plugin.rb +9 -0
  144. data/lib/seahorse/client/plugins/endpoint.rb +0 -1
  145. data/lib/seahorse/client/plugins/h2.rb +3 -3
  146. data/lib/seahorse/client/plugins/net_http.rb +48 -16
  147. data/lib/seahorse/client/plugins/request_callback.rb +31 -0
  148. data/lib/seahorse/client/response.rb +6 -0
  149. data/lib/seahorse/model/operation.rb +3 -0
  150. data/lib/seahorse/model/shapes.rb +2 -2
  151. data/sig/aws-sdk-core/client_stubs.rbs +10 -0
  152. data/sig/aws-sdk-core/errors.rbs +22 -0
  153. data/sig/aws-sdk-core/resources/collection.rbs +21 -0
  154. data/sig/aws-sdk-core/structure.rbs +4 -0
  155. data/sig/aws-sdk-core/waiters/errors.rbs +20 -0
  156. data/sig/aws-sdk-core.rbs +7 -0
  157. data/sig/seahorse/client/base.rbs +25 -0
  158. data/sig/seahorse/client/handler_builder.rbs +16 -0
  159. data/sig/seahorse/client/response.rbs +61 -0
  160. metadata +44 -17
  161. /data/lib/aws-sdk-core/xml/parser/{engines/libxml.rb → libxml_engine.rb} +0 -0
  162. /data/lib/aws-sdk-core/xml/parser/{engines/nokogiri.rb → nokogiri_engine.rb} +0 -0
  163. /data/lib/aws-sdk-core/xml/parser/{engines/ox.rb → ox_engine.rb} +0 -0
  164. /data/lib/aws-sdk-core/xml/parser/{engines/rexml.rb → rexml_engine.rb} +0 -0
@@ -30,7 +30,9 @@ module Aws
30
30
  private
31
31
 
32
32
  def apply_path_params(uri, params)
33
- path = uri.path.sub(/\/$/, '') + @path_pattern.split('?')[0]
33
+ path = uri.path.sub(%r{/$}, '')
34
+ # handle trailing slash
35
+ path += @path_pattern.split('?')[0] if path.empty? || @path_pattern != '/'
34
36
  uri.path = path.gsub(/{.+?}/) do |placeholder|
35
37
  param_value_for_placeholder(placeholder, params)
36
38
  end
@@ -38,22 +40,40 @@ module Aws
38
40
 
39
41
  def param_value_for_placeholder(placeholder, params)
40
42
  name = param_name(placeholder)
41
- value = params[name].to_s
43
+ param_shape = @rules.shape.member(name).shape
44
+ value =
45
+ case param_shape
46
+ when Seahorse::Model::Shapes::TimestampShape
47
+ timestamp(param_shape, params[name]).to_s
48
+ else
49
+ params[name].to_s
50
+ end
51
+
42
52
  raise ArgumentError, ":#{name} must not be blank" if value.empty?
43
53
 
44
54
  if placeholder.include?('+')
45
- value.gsub(/[^\/]+/) { |v| escape(v) }
55
+ value.gsub(%r{[^/]+}) { |v| escape(v) }
46
56
  else
47
57
  escape(value)
48
58
  end
49
59
  end
50
60
 
51
61
  def param_name(placeholder)
52
- location_name = placeholder.gsub(/[{}+]/,'')
62
+ location_name = placeholder.gsub(/[{}+]/, '')
53
63
  param_name, _ = @rules.shape.member_by_location_name(location_name)
54
64
  param_name
55
65
  end
56
66
 
67
+ def timestamp(ref, value)
68
+ case ref['timestampFormat']
69
+ when 'unixTimestamp' then value.to_i
70
+ when 'rfc822' then value.utc.httpdate
71
+ else
72
+ # serializing as RFC 3399 date-time is the default
73
+ value.utc.iso8601
74
+ end
75
+ end
76
+
57
77
  def apply_querystring_params(uri, params)
58
78
  # collect params that are supposed to be part of the query string
59
79
  parts = @rules.shape.members.inject([]) do |prts, (member_name, member_ref)|
@@ -20,7 +20,8 @@ module Aws
20
20
  def apply(http_req, params)
21
21
  @rules.shape.members.each do |name, ref|
22
22
  value = params[name]
23
- next if value.nil?
23
+ next if value.nil? || ((ref.shape).is_a?(StringShape) && value.empty?)
24
+
24
25
  case ref.location
25
26
  when 'header' then apply_header_value(http_req.headers, ref, value)
26
27
  when 'headers' then apply_header_map(http_req.headers, ref, value)
@@ -49,12 +50,19 @@ module Aws
49
50
  end
50
51
  end
51
52
 
52
- def list(headers, ref, value)
53
- return if !value || value.empty?
54
- headers[ref.location_name] = value
55
- .compact
56
- .map { |s| Seahorse::Util.escape_header_list_string(s.to_s) }
57
- .join(',')
53
+ def list(headers, ref, values)
54
+ return if !values || values.empty?
55
+
56
+ member_ref = ref.shape.member
57
+ values = values.collect do |value|
58
+ case member_ref.shape
59
+ when TimestampShape
60
+ timestamp(member_ref, value).to_s
61
+ else
62
+ Seahorse::Util.escape_header_list_string(value.to_s)
63
+ end
64
+ end
65
+ headers[ref.location_name] = values.compact.join(', ')
58
66
  end
59
67
 
60
68
  def apply_header_map(headers, ref, values)
@@ -4,9 +4,16 @@ module Aws
4
4
  module Rest
5
5
  module Request
6
6
  class QuerystringBuilder
7
-
8
7
  include Seahorse::Model::Shapes
9
8
 
9
+ SUPPORTED_TYPES = [
10
+ BooleanShape,
11
+ FloatShape,
12
+ IntegerShape,
13
+ StringShape,
14
+ TimestampShape
15
+ ].freeze
16
+
10
17
  # Provide shape references and param values:
11
18
  #
12
19
  # [
@@ -23,39 +30,32 @@ module Aws
23
30
  #
24
31
  # @return [String] Returns a built querystring
25
32
  def build(params)
33
+ # keys in query maps must NOT override other keys
34
+ query_keys = query_keys(params)
26
35
  params.map do |(shape_ref, param_value)|
27
- build_part(shape_ref, param_value)
28
- end.join('&')
36
+ build_part(shape_ref, param_value, query_keys)
37
+ end.reject { |p| p.nil? || p.empty? }.join('&')
29
38
  end
30
39
 
31
40
  private
32
41
 
33
- def build_part(shape_ref, param_value)
42
+ def query_keys(params)
43
+ keys = Set.new
44
+ params.each do |(shape_ref, _)|
45
+ keys << shape_ref.location_name unless shape_ref.shape.is_a?(MapShape)
46
+ end
47
+ keys
48
+ end
49
+
50
+ def build_part(shape_ref, param_value, query_keys)
34
51
  case shape_ref.shape
35
52
  # supported scalar types
36
- when StringShape, BooleanShape, FloatShape, IntegerShape, StringShape
37
- param_name = shape_ref.location_name
38
- "#{param_name}=#{escape(param_value.to_s)}"
39
- when TimestampShape
40
- param_name = shape_ref.location_name
41
- "#{param_name}=#{escape(timestamp(shape_ref, param_value))}"
53
+ when *SUPPORTED_TYPES
54
+ "#{shape_ref.location_name}=#{query_value(shape_ref, param_value)}"
42
55
  when MapShape
43
- if StringShape === shape_ref.shape.value.shape
44
- query_map_of_string(param_value)
45
- elsif ListShape === shape_ref.shape.value.shape
46
- query_map_of_string_list(param_value)
47
- else
48
- msg = "only map of string and string list supported"
49
- raise NotImplementedError, msg
50
- end
56
+ generate_query_map(shape_ref, param_value, query_keys)
51
57
  when ListShape
52
- if StringShape === shape_ref.shape.member.shape
53
- list_of_strings(shape_ref.location_name, param_value)
54
- else
55
- msg = "Only list of strings supported, got "\
56
- "#{shape_ref.shape.member.shape.class.name}"
57
- raise NotImplementedError, msg
58
- end
58
+ generate_query_list(shape_ref, param_value)
59
59
  else
60
60
  raise NotImplementedError
61
61
  end
@@ -71,34 +71,60 @@ module Aws
71
71
  end
72
72
  end
73
73
 
74
- def query_map_of_string(hash)
74
+ def query_value(ref, value)
75
+ case ref.shape
76
+ when TimestampShape
77
+ escape(timestamp(ref, value))
78
+ when *SUPPORTED_TYPES
79
+ escape(value.to_s)
80
+ else
81
+ raise NotImplementedError
82
+ end
83
+ end
84
+
85
+ def generate_query_list(ref, values)
86
+ member_ref = ref.shape.member
87
+ values.map do |value|
88
+ value = query_value(member_ref, value)
89
+ "#{ref.location_name}=#{value}"
90
+ end
91
+ end
92
+
93
+ def generate_query_map(ref, value, query_keys)
94
+ case ref.shape.value.shape
95
+ when StringShape
96
+ query_map_of_string(value, query_keys)
97
+ when ListShape
98
+ query_map_of_string_list(value, query_keys)
99
+ else
100
+ msg = 'Only map of string and string list supported'
101
+ raise NotImplementedError, msg
102
+ end
103
+ end
104
+
105
+ def query_map_of_string(hash, query_keys)
75
106
  list = []
76
107
  hash.each_pair do |key, value|
77
- list << "#{escape(key)}=#{escape(value)}"
108
+ key = escape(key)
109
+ list << "#{key}=#{escape(value)}" unless query_keys.include?(key)
78
110
  end
79
111
  list
80
112
  end
81
113
 
82
- def query_map_of_string_list(hash)
114
+ def query_map_of_string_list(hash, query_keys)
83
115
  list = []
84
116
  hash.each_pair do |key, values|
117
+ key = escape(key)
85
118
  values.each do |value|
86
- list << "#{escape(key)}=#{escape(value)}"
119
+ list << "#{key}=#{escape(value)}" unless query_keys.include?(key)
87
120
  end
88
121
  end
89
122
  list
90
123
  end
91
124
 
92
- def list_of_strings(name, values)
93
- values.map do |value|
94
- "#{name}=#{escape(value)}"
95
- end
96
- end
97
-
98
125
  def escape(string)
99
126
  Seahorse::Util.uri_escape(string)
100
127
  end
101
-
102
128
  end
103
129
  end
104
130
  end
@@ -20,7 +20,8 @@ module Aws
20
20
  if event_stream?
21
21
  data[@rules[:payload]] = parse_eventstream(body)
22
22
  elsif streaming?
23
- data[@rules[:payload]] = body
23
+ # empty blob payloads are omitted
24
+ data[@rules[:payload]] = body unless empty_blob_payload?(body)
24
25
  elsif @rules[:payload]
25
26
  data[@rules[:payload]] = parse(body.read, @rules[:payload_member])
26
27
  elsif !@rules.shape.member_names.empty?
@@ -30,6 +31,19 @@ module Aws
30
31
 
31
32
  private
32
33
 
34
+ def empty_blob_payload?(body)
35
+ true if non_streaming_blob_payload? && empty_body?(body)
36
+ end
37
+
38
+ def non_streaming_blob_payload?
39
+ @rules[:payload_member].shape.is_a?(BlobShape) &&
40
+ !@rules[:payload_member]['streaming']
41
+ end
42
+
43
+ def empty_body?(body)
44
+ body.respond_to?(:size) && body.size.zero?
45
+ end
46
+
33
47
  def event_stream?
34
48
  @rules[:payload] && @rules[:payload_member].eventstream
35
49
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ module Aws
6
+ module Rest
7
+ module Response
8
+ # @api private
9
+ module HeaderListParser
10
+
11
+ class << self
12
+ # parse a list of possibly quoted and escaped string values
13
+ # Follows:
14
+ # # [RFC-7230's specification of header values](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6).
15
+ def parse_string_list(value)
16
+ buffer = StringScanner.new(value)
17
+ parsed = []
18
+
19
+ parsed << read_value(buffer) until buffer.eos?
20
+
21
+ parsed
22
+ end
23
+
24
+ def parse_timestamp_list(value, ref)
25
+ # timestamp lists use an http-date by default and are unescaped
26
+ # eg: Mon, 16 Dec 2019 23:48:18 GMT, Mon, 16 Dec 2019 23:48:18 GMT
27
+ case ref['timestampFormat'] || ref.shape['timestampFormat']
28
+ when 'unixTimestamp'
29
+ value.split(', ').map { |v| Time.at(v.to_f) }
30
+ when 'iso8601' then value.split(', ').map { |v| Time.parse(v) }
31
+ else
32
+ # header default to rfc822/http-date, which has a comma after day
33
+ value.split(',').each_slice(2).map { |v| Time.parse(v[0] + v[1])}
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def read_value(buffer)
40
+ until buffer.eos?
41
+ case buffer.peek(1)
42
+ when ' ', "\t"
43
+ # drop leading whitespace
44
+ buffer.getch
45
+ next
46
+ when '"'
47
+ buffer.getch # drop the quote and advance
48
+ return read_quoted_value(buffer)
49
+ else
50
+ return read_unquoted_value(buffer)
51
+ end
52
+ end
53
+ # buffer is only whitespace
54
+ nil
55
+ end
56
+
57
+ def read_unquoted_value(buffer)
58
+ # there cannot be any escaped values
59
+ value = buffer.scan_until(/,|$/)
60
+ # drop the comma if we matched it
61
+ buffer.matched == ',' ? value.chop : value
62
+ end
63
+
64
+ def read_quoted_value(buffer)
65
+ # scan until we have an unescaped double quote
66
+ value = buffer.scan_until(/[^\\]"/)
67
+ raise ArgumentError, 'Invalid String list: No closing quote found' unless value
68
+
69
+ # drop any remaining whitespace/commas
70
+ buffer.scan_until(/[\s,]*/)
71
+ # the last character will always be the closing quote.
72
+ # Add a starting quote and then unescape (undump)
73
+ "\"#{value}".undump
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'time'
4
4
  require 'base64'
5
+ require_relative 'header_list_parser'
5
6
 
6
7
  module Aws
7
8
  module Rest
@@ -36,12 +37,16 @@ module Aws
36
37
  def cast_value(ref, value)
37
38
  value = extract_json_trait(value) if ref['jsonvalue']
38
39
  case ref.shape
39
- when StringShape then value
40
+ when StringShape then value.to_s
40
41
  when IntegerShape then value.to_i
41
- when FloatShape then value.to_f
42
+ when FloatShape then Util.deserialize_number(value)
42
43
  when BooleanShape then value == 'true'
43
44
  when ListShape then
44
- value.split(",").map { |v| cast_value(ref.shape.member, v) }
45
+ case ref.shape.member.shape
46
+ when StringShape then HeaderListParser.parse_string_list(value)
47
+ when TimestampShape then HeaderListParser.parse_timestamp_list(value, ref.shape.member)
48
+ else value.split(', ').map { |v| cast_value(ref.shape.member, v) }
49
+ end
45
50
  when TimestampShape
46
51
  if value =~ /^\d+(\.\d*)/
47
52
  Time.at(value.to_f)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'rest/handler'
4
+ require_relative 'rest/content_type_handler'
4
5
  require_relative 'rest/request/body'
5
6
  require_relative 'rest/request/builder'
6
7
  require_relative 'rest/request/endpoint'
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Aws
6
+ module RpcV2
7
+ class Builder
8
+ include Seahorse::Model::Shapes
9
+
10
+ def initialize(rules, _options = {})
11
+ @rules = rules
12
+ end
13
+
14
+ def serialize(params)
15
+ # If the input shape is empty, do not set a body. This is
16
+ # different than if the input shape is a structure with no members.
17
+ return nil if @rules.shape.struct_class == EmptyStructure
18
+
19
+ Cbor.encode(format(@rules, params))
20
+ end
21
+
22
+ private
23
+
24
+ def structure(ref, values)
25
+ shape = ref.shape
26
+ values.each_pair.with_object({}) do |(key, value), data|
27
+ if shape.member?(key) && !value.nil?
28
+ member_ref = shape.member(key)
29
+ member_name = member_ref.location_name || key
30
+ data[member_name] = format(member_ref, value)
31
+ end
32
+ end
33
+ end
34
+
35
+ def list(ref, values)
36
+ member_ref = ref.shape.member
37
+ values.collect { |value| format(member_ref, value) }
38
+ end
39
+
40
+ def map(ref, values)
41
+ value_ref = ref.shape.value
42
+ values.each.with_object({}) do |(key, value), data|
43
+ data[key] = format(value_ref, value)
44
+ end
45
+ end
46
+
47
+ def blob(value)
48
+ (String === value ? value : value.read).force_encoding(Encoding::BINARY)
49
+ end
50
+
51
+ def format(ref, value)
52
+ case ref.shape
53
+ when StructureShape then structure(ref, value)
54
+ when ListShape then list(ref, value)
55
+ when MapShape then map(ref, value)
56
+ when BlobShape then blob(value)
57
+ else value
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module RpcV2
5
+ class ContentTypeHandler < Seahorse::Client::Handler
6
+ def call(context)
7
+ content_type =
8
+ if eventstream_input?(context)
9
+ 'application/vnd.amazon.eventstream'
10
+ elsif !empty_input_structure?(context)
11
+ 'application/cbor'
12
+ end
13
+ accept =
14
+ if eventstream_output?(context)
15
+ 'application/vnd.amazon.eventstream'
16
+ end
17
+
18
+ headers = context.http_request.headers
19
+ headers['Content-Type'] ||= content_type if content_type
20
+ headers['Accept'] ||= accept if accept
21
+ @handler.call(context)
22
+ end
23
+
24
+ private
25
+
26
+ def eventstream_input?(context)
27
+ context.operation.input.shape.members.each do |_, ref|
28
+ return true if ref.eventstream
29
+ end
30
+ false
31
+ end
32
+
33
+ def eventstream_output?(context)
34
+ context.operation.output.shape.members.each do |_, ref|
35
+ return true if ref.eventstream
36
+ end
37
+ false
38
+ end
39
+
40
+ def empty_input_structure?(context)
41
+ context.operation.input.shape.struct_class == EmptyStructure
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module RpcV2
5
+ class ErrorHandler < Aws::ErrorHandler
6
+
7
+ def call(context)
8
+ # Malformed responses should throw an http based error, so we check
9
+ # 200 range for error handling only for this case.
10
+ @handler.call(context).on(200..599) do |response|
11
+ if !valid_response?(context)
12
+ code, message, data = http_status_error(context)
13
+ response.error = build_error(context, code, message, data)
14
+ elsif (300..599).cover?(context.http_response.status_code)
15
+ response.error = error(context)
16
+ end
17
+ response.data = nil
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def valid_response?(context)
24
+ req_header = context.http_request.headers['smithy-protocol']
25
+ resp_header = context.http_response.headers['smithy-protocol']
26
+ req_header == resp_header
27
+ end
28
+
29
+ def extract_error(body, context)
30
+ data = Cbor.decode(body)
31
+ code = error_code(data, context)
32
+ message = data['message']
33
+ data = parse_error_data(context, body, code)
34
+ [code, message, data]
35
+ rescue Cbor::Error
36
+ [http_status_error_code(context), '', EmptyStructure.new]
37
+ end
38
+
39
+ def error_code(data, context)
40
+ code =
41
+ if aws_query_error?(context)
42
+ error = context.http_response.headers['x-amzn-query-error'].split(';')[0]
43
+ remove_prefix(error, context)
44
+ else
45
+ data['__type']
46
+ end
47
+ if code
48
+ code.split('#').last
49
+ else
50
+ http_status_error_code(context)
51
+ end
52
+ end
53
+
54
+ def parse_error_data(context, body, code)
55
+ data = EmptyStructure.new
56
+ if (error_rules = context.operation.errors)
57
+ error_rules.each do |rule|
58
+ # match modeled shape name with the type(code) only
59
+ # some type(code) might contains invalid characters
60
+ # such as ':' (efs) etc
61
+ match = rule.shape.name == code.gsub(/[^^a-zA-Z0-9]/, '')
62
+ next unless match && rule.shape.members.any?
63
+
64
+ data = Parser.new(rule).parse(body)
65
+ end
66
+ end
67
+ data
68
+ end
69
+
70
+ def aws_query_error?(context)
71
+ context.config.api.metadata['awsQueryCompatible'] &&
72
+ context.http_response.headers['x-amzn-query-error']
73
+ end
74
+
75
+ def remove_prefix(error_code, context)
76
+ if (prefix = context.config.api.metadata['errorPrefix'])
77
+ error_code.sub(/^#{prefix}/, '')
78
+ else
79
+ error_code
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module RpcV2
5
+ class Handler < Seahorse::Client::Handler
6
+ # @param [Seahorse::Client::RequestContext] context
7
+ # @return [Seahorse::Client::Response]
8
+ def call(context)
9
+ build_request(context)
10
+ response = @handler.call(context)
11
+ response.on(200..299) { |resp| resp.data = parse_body(context) }
12
+ response.on(200..599) { |_resp| apply_request_id(context) }
13
+ response
14
+ end
15
+
16
+ private
17
+
18
+ def build_request(context)
19
+ context.http_request.headers['smithy-protocol'] = 'rpc-v2-cbor'
20
+ context.http_request.http_method = 'POST'
21
+ context.http_request.body = build_body(context)
22
+ build_url(context)
23
+ end
24
+
25
+ def build_url(context)
26
+ base = context.http_request.endpoint
27
+ service_name = context.config.api.metadata['targetPrefix']
28
+ base.path += "/service/#{service_name}/operation/#{context.operation.name}"
29
+ end
30
+
31
+ def build_body(context)
32
+ Builder.new(context.operation.input).serialize(context.params)
33
+ end
34
+
35
+ def parse_body(context)
36
+ cbor = context.http_response.body_contents
37
+ if (rules = context.operation.output)
38
+ if cbor.is_a?(Array)
39
+ # an array of emitted events
40
+ if cbor[0].respond_to?(:response)
41
+ # initial response exists
42
+ # it must be the first event arrived
43
+ resp_struct = cbor.shift.response
44
+ else
45
+ resp_struct = context.operation.output.shape.struct_class.new
46
+ end
47
+
48
+ rules.shape.members.each do |name, ref|
49
+ if ref.eventstream
50
+ resp_struct.send("#{name}=", cbor.to_enum)
51
+ end
52
+ end
53
+ resp_struct
54
+ else
55
+ Parser.new(
56
+ rules,
57
+ query_compatible: query_compatible?(context)
58
+ ).parse(cbor)
59
+ end
60
+ else
61
+ EmptyStructure.new
62
+ end
63
+ end
64
+
65
+ def apply_request_id(context)
66
+ context[:request_id] = context.http_response.headers['x-amzn-requestid']
67
+ end
68
+
69
+ def query_compatible?(context)
70
+ context.config.api.metadata.key?('awsQueryCompatible')
71
+ end
72
+ end
73
+ end
74
+ end