aws-sdk-core 3.191.1 → 3.229.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.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +539 -1
  3. data/VERSION +1 -1
  4. data/lib/aws-defaults/default_configuration.rb +1 -2
  5. data/lib/aws-defaults.rb +4 -1
  6. data/lib/aws-sdk-core/arn.rb +1 -3
  7. data/lib/aws-sdk-core/assume_role_credentials.rb +13 -5
  8. data/lib/aws-sdk-core/assume_role_web_identity_credentials.rb +14 -7
  9. data/lib/aws-sdk-core/binary/decode_handler.rb +3 -9
  10. data/lib/aws-sdk-core/binary/encode_handler.rb +1 -1
  11. data/lib/aws-sdk-core/binary/event_builder.rb +34 -37
  12. data/lib/aws-sdk-core/binary/event_stream_decoder.rb +1 -0
  13. data/lib/aws-sdk-core/binary/event_stream_encoder.rb +4 -3
  14. data/lib/aws-sdk-core/cbor/decoder.rb +308 -0
  15. data/lib/aws-sdk-core/cbor/encoder.rb +243 -0
  16. data/lib/aws-sdk-core/cbor.rb +53 -0
  17. data/lib/aws-sdk-core/client_side_monitoring.rb +9 -0
  18. data/lib/aws-sdk-core/client_stubs.rb +30 -55
  19. data/lib/aws-sdk-core/credential_provider.rb +4 -0
  20. data/lib/aws-sdk-core/credential_provider_chain.rb +38 -11
  21. data/lib/aws-sdk-core/credentials.rb +19 -6
  22. data/lib/aws-sdk-core/ec2_metadata.rb +1 -1
  23. data/lib/aws-sdk-core/ecs_credentials.rb +1 -0
  24. data/lib/aws-sdk-core/endpoints/endpoint.rb +3 -1
  25. data/lib/aws-sdk-core/endpoints/matchers.rb +8 -10
  26. data/lib/aws-sdk-core/endpoints.rb +101 -21
  27. data/lib/aws-sdk-core/error_handler.rb +46 -0
  28. data/lib/aws-sdk-core/errors.rb +11 -2
  29. data/lib/aws-sdk-core/event_emitter.rb +1 -17
  30. data/lib/aws-sdk-core/instance_profile_credentials.rb +148 -158
  31. data/lib/aws-sdk-core/json/builder.rb +8 -1
  32. data/lib/aws-sdk-core/json/error_handler.rb +29 -13
  33. data/lib/aws-sdk-core/json/handler.rb +6 -6
  34. data/lib/aws-sdk-core/json/json_engine.rb +3 -1
  35. data/lib/aws-sdk-core/json/oj_engine.rb +7 -1
  36. data/lib/aws-sdk-core/json/parser.rb +6 -1
  37. data/lib/aws-sdk-core/json.rb +43 -14
  38. data/lib/aws-sdk-core/log/param_filter.rb +2 -2
  39. data/lib/aws-sdk-core/log/param_formatter.rb +7 -3
  40. data/lib/aws-sdk-core/log.rb +10 -0
  41. data/lib/aws-sdk-core/lru_cache.rb +75 -0
  42. data/lib/aws-sdk-core/pageable_response.rb +1 -1
  43. data/lib/aws-sdk-core/param_validator.rb +7 -2
  44. data/lib/aws-sdk-core/plugins/bearer_authorization.rb +2 -0
  45. data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +332 -169
  46. data/lib/aws-sdk-core/plugins/client_metrics_plugin.rb +0 -1
  47. data/lib/aws-sdk-core/plugins/client_metrics_send_plugin.rb +14 -2
  48. data/lib/aws-sdk-core/plugins/credentials_configuration.rb +77 -57
  49. data/lib/aws-sdk-core/plugins/endpoint_pattern.rb +40 -32
  50. data/lib/aws-sdk-core/plugins/global_configuration.rb +8 -9
  51. data/lib/aws-sdk-core/plugins/http_checksum.rb +2 -8
  52. data/lib/aws-sdk-core/plugins/invocation_id.rb +1 -11
  53. data/lib/aws-sdk-core/plugins/protocols/api_gateway.rb +3 -1
  54. data/lib/aws-sdk-core/plugins/protocols/ec2.rb +2 -24
  55. data/lib/aws-sdk-core/plugins/protocols/json_rpc.rb +6 -8
  56. data/lib/aws-sdk-core/plugins/protocols/query.rb +4 -2
  57. data/lib/aws-sdk-core/plugins/protocols/rest_json.rb +3 -15
  58. data/lib/aws-sdk-core/plugins/protocols/rest_xml.rb +3 -0
  59. data/lib/aws-sdk-core/plugins/protocols/rpc_v2.rb +17 -0
  60. data/lib/aws-sdk-core/plugins/regional_endpoint.rb +74 -25
  61. data/lib/aws-sdk-core/plugins/request_compression.rb +11 -2
  62. data/lib/aws-sdk-core/plugins/retry_errors.rb +10 -3
  63. data/lib/aws-sdk-core/plugins/sign.rb +42 -26
  64. data/lib/aws-sdk-core/plugins/signature_v2.rb +2 -1
  65. data/lib/aws-sdk-core/plugins/signature_v4.rb +2 -1
  66. data/lib/aws-sdk-core/plugins/stub_responses.rb +58 -9
  67. data/lib/aws-sdk-core/plugins/telemetry.rb +75 -0
  68. data/lib/aws-sdk-core/plugins/transfer_encoding.rb +16 -9
  69. data/lib/aws-sdk-core/plugins/user_agent.rb +101 -26
  70. data/lib/aws-sdk-core/plugins.rb +39 -0
  71. data/lib/aws-sdk-core/process_credentials.rb +48 -29
  72. data/lib/aws-sdk-core/query/ec2_handler.rb +27 -0
  73. data/lib/aws-sdk-core/query/ec2_param_builder.rb +5 -7
  74. data/lib/aws-sdk-core/query/handler.rb +4 -4
  75. data/lib/aws-sdk-core/query/param_builder.rb +2 -2
  76. data/lib/aws-sdk-core/query.rb +2 -1
  77. data/lib/aws-sdk-core/resources.rb +8 -0
  78. data/lib/aws-sdk-core/rest/content_type_handler.rb +60 -0
  79. data/lib/aws-sdk-core/rest/handler.rb +3 -4
  80. data/lib/aws-sdk-core/rest/request/body.rb +32 -5
  81. data/lib/aws-sdk-core/rest/request/endpoint.rb +24 -4
  82. data/lib/aws-sdk-core/rest/request/headers.rb +15 -7
  83. data/lib/aws-sdk-core/rest/request/querystring_builder.rb +23 -11
  84. data/lib/aws-sdk-core/rest/response/body.rb +15 -1
  85. data/lib/aws-sdk-core/rest/response/header_list_parser.rb +79 -0
  86. data/lib/aws-sdk-core/rest/response/headers.rb +8 -3
  87. data/lib/aws-sdk-core/rest.rb +1 -0
  88. data/lib/aws-sdk-core/rpc_v2/builder.rb +62 -0
  89. data/lib/aws-sdk-core/rpc_v2/cbor_engine.rb +18 -0
  90. data/lib/aws-sdk-core/rpc_v2/content_type_handler.rb +47 -0
  91. data/lib/aws-sdk-core/rpc_v2/error_handler.rb +95 -0
  92. data/lib/aws-sdk-core/rpc_v2/handler.rb +79 -0
  93. data/lib/aws-sdk-core/rpc_v2/parser.rb +98 -0
  94. data/lib/aws-sdk-core/rpc_v2.rb +69 -0
  95. data/lib/aws-sdk-core/shared_config.rb +79 -22
  96. data/lib/aws-sdk-core/shared_credentials.rb +1 -7
  97. data/lib/aws-sdk-core/sso_credentials.rb +4 -1
  98. data/lib/aws-sdk-core/static_token_provider.rb +1 -2
  99. data/lib/aws-sdk-core/stubbing/protocols/ec2.rb +12 -11
  100. data/lib/aws-sdk-core/stubbing/protocols/json.rb +11 -10
  101. data/lib/aws-sdk-core/stubbing/protocols/query.rb +7 -6
  102. data/lib/aws-sdk-core/stubbing/protocols/rest.rb +2 -1
  103. data/lib/aws-sdk-core/stubbing/protocols/rest_json.rb +9 -8
  104. data/lib/aws-sdk-core/stubbing/protocols/rest_xml.rb +6 -5
  105. data/lib/aws-sdk-core/stubbing/protocols/rpc_v2.rb +39 -0
  106. data/lib/aws-sdk-core/stubbing.rb +22 -0
  107. data/lib/aws-sdk-core/telemetry/base.rb +177 -0
  108. data/lib/aws-sdk-core/telemetry/no_op.rb +70 -0
  109. data/lib/aws-sdk-core/telemetry/otel.rb +235 -0
  110. data/lib/aws-sdk-core/telemetry/span_kind.rb +22 -0
  111. data/lib/aws-sdk-core/telemetry/span_status.rb +59 -0
  112. data/lib/aws-sdk-core/telemetry.rb +78 -0
  113. data/lib/aws-sdk-core/token.rb +3 -3
  114. data/lib/aws-sdk-core/token_provider.rb +4 -0
  115. data/lib/aws-sdk-core/token_provider_chain.rb +2 -6
  116. data/lib/aws-sdk-core/util.rb +41 -1
  117. data/lib/aws-sdk-core/waiters/poller.rb +10 -5
  118. data/lib/aws-sdk-core/xml/builder.rb +17 -9
  119. data/lib/aws-sdk-core/xml/error_handler.rb +35 -43
  120. data/lib/aws-sdk-core/xml/parser/frame.rb +4 -20
  121. data/lib/aws-sdk-core/xml/parser/stack.rb +2 -0
  122. data/lib/aws-sdk-core/xml/parser.rb +2 -6
  123. data/lib/aws-sdk-core.rb +82 -107
  124. data/lib/aws-sdk-sso/client.rb +189 -96
  125. data/lib/aws-sdk-sso/client_api.rb +7 -0
  126. data/lib/aws-sdk-sso/endpoint_parameters.rb +9 -6
  127. data/lib/aws-sdk-sso/endpoint_provider.rb +14 -18
  128. data/lib/aws-sdk-sso/endpoints.rb +2 -54
  129. data/lib/aws-sdk-sso/plugins/endpoints.rb +19 -20
  130. data/lib/aws-sdk-sso/types.rb +1 -0
  131. data/lib/aws-sdk-sso.rb +15 -11
  132. data/lib/aws-sdk-ssooidc/client.rb +274 -116
  133. data/lib/aws-sdk-ssooidc/client_api.rb +33 -0
  134. data/lib/aws-sdk-ssooidc/endpoint_parameters.rb +9 -6
  135. data/lib/aws-sdk-ssooidc/endpoint_provider.rb +14 -18
  136. data/lib/aws-sdk-ssooidc/endpoints.rb +2 -54
  137. data/lib/aws-sdk-ssooidc/errors.rb +21 -0
  138. data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +19 -20
  139. data/lib/aws-sdk-ssooidc/types.rb +125 -24
  140. data/lib/aws-sdk-ssooidc.rb +15 -11
  141. data/lib/aws-sdk-sts/client.rb +397 -148
  142. data/lib/aws-sdk-sts/client_api.rb +36 -8
  143. data/lib/aws-sdk-sts/customizations.rb +5 -1
  144. data/lib/aws-sdk-sts/endpoint_parameters.rb +10 -9
  145. data/lib/aws-sdk-sts/endpoint_provider.rb +50 -55
  146. data/lib/aws-sdk-sts/endpoints.rb +2 -118
  147. data/lib/aws-sdk-sts/errors.rb +15 -0
  148. data/lib/aws-sdk-sts/plugins/endpoints.rb +19 -28
  149. data/lib/aws-sdk-sts/presigner.rb +2 -6
  150. data/lib/aws-sdk-sts/types.rb +171 -28
  151. data/lib/aws-sdk-sts.rb +15 -11
  152. data/lib/seahorse/client/async_base.rb +4 -5
  153. data/lib/seahorse/client/async_response.rb +19 -0
  154. data/lib/seahorse/client/base.rb +18 -21
  155. data/lib/seahorse/client/h2/connection.rb +18 -28
  156. data/lib/seahorse/client/h2/handler.rb +14 -3
  157. data/lib/seahorse/client/handler.rb +1 -1
  158. data/lib/seahorse/client/http/response.rb +1 -1
  159. data/lib/seahorse/client/net_http/connection_pool.rb +15 -12
  160. data/lib/seahorse/client/net_http/handler.rb +21 -9
  161. data/lib/seahorse/client/networking_error.rb +1 -1
  162. data/lib/seahorse/client/plugin.rb +8 -0
  163. data/lib/seahorse/client/plugins/endpoint.rb +0 -1
  164. data/lib/seahorse/client/plugins/h2.rb +4 -4
  165. data/lib/seahorse/client/plugins/net_http.rb +57 -16
  166. data/lib/seahorse/client/request_context.rb +9 -2
  167. data/lib/seahorse/client/response.rb +2 -0
  168. data/lib/seahorse/model/shapes.rb +2 -2
  169. data/lib/seahorse/util.rb +2 -1
  170. data/sig/aws-sdk-core/async_client_stubs.rbs +21 -0
  171. data/sig/aws-sdk-core/telemetry/base.rbs +46 -0
  172. data/sig/aws-sdk-core/telemetry/otel.rbs +22 -0
  173. data/sig/aws-sdk-core/telemetry/span_kind.rbs +15 -0
  174. data/sig/aws-sdk-core/telemetry/span_status.rbs +24 -0
  175. data/sig/seahorse/client/async_base.rbs +18 -0
  176. metadata +96 -23
  177. /data/lib/aws-sdk-core/xml/parser/{engines/libxml.rb → libxml_engine.rb} +0 -0
  178. /data/lib/aws-sdk-core/xml/parser/{engines/nokogiri.rb → nokogiri_engine.rb} +0 -0
  179. /data/lib/aws-sdk-core/xml/parser/{engines/oga.rb → oga_engine.rb} +0 -0
  180. /data/lib/aws-sdk-core/xml/parser/{engines/ox.rb → ox_engine.rb} +0 -0
  181. /data/lib/aws-sdk-core/xml/parser/{engines/rexml.rb → rexml_engine.rb} +0 -0
@@ -21,6 +21,7 @@ module Aws
21
21
  @rules.shape.members.each do |name, ref|
22
22
  value = params[name]
23
23
  next if value.nil?
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,18 +50,25 @@ 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.nil?
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)
61
69
  prefix = ref.location_name || ''
62
70
  values.each_pair do |name, value|
63
- headers["#{prefix}#{name}"] = value.to_s
71
+ headers["#{prefix}#{name}"] ||= value.to_s
64
72
  end
65
73
  end
66
74
 
@@ -30,20 +30,30 @@ module Aws
30
30
  #
31
31
  # @return [String] Returns a built querystring
32
32
  def build(params)
33
+ # keys in query maps must NOT override other keys
34
+ query_keys = query_keys(params)
33
35
  params.map do |(shape_ref, param_value)|
34
- build_part(shape_ref, param_value)
35
- end.join('&')
36
+ build_part(shape_ref, param_value, query_keys)
37
+ end.reject { |p| p.nil? || p.empty? }.join('&')
36
38
  end
37
39
 
38
40
  private
39
41
 
40
- 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)
41
51
  case shape_ref.shape
42
52
  # supported scalar types
43
53
  when *SUPPORTED_TYPES
44
54
  "#{shape_ref.location_name}=#{query_value(shape_ref, param_value)}"
45
55
  when MapShape
46
- generate_query_map(shape_ref, param_value)
56
+ generate_query_map(shape_ref, param_value, query_keys)
47
57
  when ListShape
48
58
  generate_query_list(shape_ref, param_value)
49
59
  else
@@ -80,31 +90,33 @@ module Aws
80
90
  end
81
91
  end
82
92
 
83
- def generate_query_map(ref, value)
93
+ def generate_query_map(ref, value, query_keys)
84
94
  case ref.shape.value.shape
85
95
  when StringShape
86
- query_map_of_string(value)
96
+ query_map_of_string(value, query_keys)
87
97
  when ListShape
88
- query_map_of_string_list(value)
98
+ query_map_of_string_list(value, query_keys)
89
99
  else
90
100
  msg = 'Only map of string and string list supported'
91
101
  raise NotImplementedError, msg
92
102
  end
93
103
  end
94
104
 
95
- def query_map_of_string(hash)
105
+ def query_map_of_string(hash, query_keys)
96
106
  list = []
97
107
  hash.each_pair do |key, value|
98
- list << "#{escape(key)}=#{escape(value)}"
108
+ key = escape(key)
109
+ list << "#{key}=#{escape(value)}" unless query_keys.include?(key)
99
110
  end
100
111
  list
101
112
  end
102
113
 
103
- def query_map_of_string_list(hash)
114
+ def query_map_of_string_list(hash, query_keys)
104
115
  list = []
105
116
  hash.each_pair do |key, values|
117
+ key = escape(key)
106
118
  values.each do |value|
107
- list << "#{escape(key)}=#{escape(value)}"
119
+ list << "#{key}=#{escape(value)}" unless query_keys.include?(key)
108
120
  end
109
121
  end
110
122
  list
@@ -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
+ RpcV2.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,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cbor'
4
+
5
+ module Aws
6
+ module RpcV2
7
+ # Pure Ruby implementation of CBOR encode and decode
8
+ module CborEngine
9
+ def self.encode(data)
10
+ Cbor::Encoder.new.add(data).bytes
11
+ end
12
+
13
+ def self.decode(bytes)
14
+ Cbor::Decoder.new(bytes.force_encoding(Encoding::BINARY)).decode
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,47 @@
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
+ else
17
+ 'application/cbor'
18
+ end
19
+
20
+ headers = context.http_request.headers
21
+ headers['Content-Type'] ||= content_type if content_type
22
+ headers['Accept'] ||= accept
23
+ @handler.call(context)
24
+ end
25
+
26
+ private
27
+
28
+ def eventstream_input?(context)
29
+ context.operation.input.shape.members.each do |_, ref|
30
+ return true if ref.eventstream
31
+ end
32
+ false
33
+ end
34
+
35
+ def eventstream_output?(context)
36
+ context.operation.output.shape.members.each do |_, ref|
37
+ return true if ref.eventstream
38
+ end
39
+ false
40
+ end
41
+
42
+ def empty_input_structure?(context)
43
+ context.operation.input.shape.struct_class == EmptyStructure
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module RpcV2
5
+ # @api private
6
+ class ErrorHandler < Aws::ErrorHandler
7
+
8
+ def call(context)
9
+ # Malformed responses should throw an http based error, so we check
10
+ # 200 range for error handling only for this case.
11
+ @handler.call(context).on(200..599) do |response|
12
+ if !valid_response?(context)
13
+ code, message, data = http_status_error(context)
14
+ response.error = build_error(context, code, message, data)
15
+ elsif (300..599).cover?(context.http_response.status_code)
16
+ response.error = error(context)
17
+ end
18
+ response.data = nil
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def valid_response?(context)
25
+ req_header = context.http_request.headers['smithy-protocol']
26
+ resp_header = context.http_response.headers['smithy-protocol']
27
+ req_header == resp_header
28
+ end
29
+
30
+ def extract_error(body, context)
31
+ data = RpcV2.decode(body)
32
+ code = error_code(data, context)
33
+ message = data['message']
34
+ data = parse_error_data(context, body, code)
35
+ [code, message, data]
36
+ rescue Cbor::Error
37
+ [http_status_error_code(context), '', EmptyStructure.new]
38
+ end
39
+
40
+ def error_code(data, context)
41
+ # This is not correct per protocol tests. awsQueryError is intended to populate the
42
+ # error code of the error class. The error class should come from __type. Query and
43
+ # query compatible services currently have dynamic errors raised from error codes instead
44
+ # of the modeled error class. However, changing this in this major version would break
45
+ # existing usage.
46
+ code =
47
+ if aws_query_error?(context)
48
+ aws_query_error_code(context)
49
+ else
50
+ data['__type']
51
+ end
52
+ if code
53
+ code.split('#').last
54
+ else
55
+ http_status_error_code(context)
56
+ end
57
+ end
58
+
59
+ def aws_query_error?(context)
60
+ context.config.api.metadata['awsQueryCompatible'] &&
61
+ context.http_response.headers['x-amzn-query-error']
62
+ end
63
+
64
+ def aws_query_error_code(context)
65
+ query_header = context.http_response.headers['x-amzn-query-error']
66
+ error, _type = query_header.split(';') # type not supported
67
+ remove_prefix(error, context)
68
+ end
69
+
70
+ def remove_prefix(error_code, context)
71
+ if (prefix = context.config.api.metadata['errorPrefix'])
72
+ error_code.sub(/^#{prefix}/, '')
73
+ else
74
+ error_code
75
+ end
76
+ end
77
+
78
+ def parse_error_data(context, body, code)
79
+ data = EmptyStructure.new
80
+ if (error_rules = context.operation.errors)
81
+ error_rules.each do |rule|
82
+ # match modeled shape name with the type(code) only
83
+ # some type(code) might contains invalid characters
84
+ # such as ':' (efs) etc
85
+ match = rule.shape.name == code.gsub(/[^^a-zA-Z0-9]/, '')
86
+ next unless match && rule.shape.members.any?
87
+
88
+ data = Parser.new(rule).parse(body)
89
+ end
90
+ end
91
+ data
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,79 @@
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 = with_metric { @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 with_metric(&block)
19
+ Aws::Plugins::UserAgent.metric('PROTOCOL_RPC_V2_CBOR', &block)
20
+ end
21
+
22
+ def build_request(context)
23
+ context.http_request.headers['Smithy-Protocol'] = 'rpc-v2-cbor'
24
+ context.http_request.headers['X-Amzn-Query-Mode'] = 'true' if query_compatible?(context)
25
+ context.http_request.http_method = 'POST'
26
+ context.http_request.body = build_body(context)
27
+ build_url(context)
28
+ end
29
+
30
+ def build_url(context)
31
+ base = context.http_request.endpoint
32
+ service_name = context.config.api.metadata['targetPrefix']
33
+ base.path += "/service/#{service_name}/operation/#{context.operation.name}"
34
+ end
35
+
36
+ def build_body(context)
37
+ Builder.new(context.operation.input).serialize(context.params)
38
+ end
39
+
40
+ def parse_body(context)
41
+ cbor = context.http_response.body_contents
42
+ if (rules = context.operation.output)
43
+ if cbor.is_a?(Array)
44
+ # an array of emitted events
45
+ if cbor[0].respond_to?(:response)
46
+ # initial response exists
47
+ # it must be the first event arrived
48
+ resp_struct = cbor.shift.response
49
+ else
50
+ resp_struct = context.operation.output.shape.struct_class.new
51
+ end
52
+
53
+ rules.shape.members.each do |name, ref|
54
+ if ref.eventstream
55
+ resp_struct.send("#{name}=", cbor.to_enum)
56
+ end
57
+ end
58
+ resp_struct
59
+ else
60
+ Parser.new(
61
+ rules,
62
+ query_compatible: query_compatible?(context)
63
+ ).parse(cbor)
64
+ end
65
+ else
66
+ EmptyStructure.new
67
+ end
68
+ end
69
+
70
+ def apply_request_id(context)
71
+ context[:request_id] = context.http_response.headers['x-amzn-requestid']
72
+ end
73
+
74
+ def query_compatible?(context)
75
+ context.config.api.metadata.key?('awsQueryCompatible')
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ module Aws
6
+ module RpcV2
7
+ class Parser
8
+ include Seahorse::Model::Shapes
9
+
10
+ # @param [Seahorse::Model::ShapeRef] rules
11
+ def initialize(rules, query_compatible: false)
12
+ @rules = rules
13
+ @query_compatible = query_compatible
14
+ end
15
+
16
+ def parse(cbor, target = nil)
17
+ return {} if cbor.empty?
18
+
19
+ parse_ref(@rules, RpcV2.decode(cbor), target)
20
+ end
21
+
22
+ private
23
+
24
+ def structure(ref, values, target = nil)
25
+ shape = ref.shape
26
+ target = ref.shape.struct_class.new if target.nil?
27
+ values.each do |key, value|
28
+ member_name, member_ref = shape.member_by_location_name(key)
29
+ if member_ref
30
+ target[member_name] = parse_ref(member_ref, value)
31
+ elsif shape.union && key != '__type'
32
+ target[:unknown] = { 'name' => key, 'value' => value }
33
+ end
34
+ end
35
+ # In services that were previously Query/XML, members that were
36
+ # "flattened" defaulted to empty lists. In JSON, these values are nil,
37
+ # which is backwards incompatible. To preserve backwards compatibility,
38
+ # we set a default value of [] for these members.
39
+ if @query_compatible
40
+ ref.shape.members.each do |member_name, member_target|
41
+ next unless target[member_name].nil?
42
+
43
+ if flattened_list?(member_target.shape)
44
+ target[member_name] = []
45
+ elsif flattened_map?(member_target.shape)
46
+ target[member_name] = {}
47
+ end
48
+ end
49
+ end
50
+
51
+ if shape.union
52
+ # convert to subclass
53
+ member_subclass = shape.member_subclass(target.member).new
54
+ member_subclass[target.member] = target.value
55
+ target = member_subclass
56
+ end
57
+ target
58
+ end
59
+
60
+ def list(ref, values, target = nil)
61
+ target = [] if target.nil?
62
+ values.each do |value|
63
+ target << parse_ref(ref.shape.member, value)
64
+ end
65
+ target
66
+ end
67
+
68
+ def map(ref, values, target = nil)
69
+ target = {} if target.nil?
70
+ values.each do |key, value|
71
+ target[key] = parse_ref(ref.shape.value, value) unless value.nil?
72
+ end
73
+ target
74
+ end
75
+
76
+ def parse_ref(ref, value, target = nil)
77
+ if value.nil?
78
+ nil
79
+ else
80
+ case ref.shape
81
+ when StructureShape then structure(ref, value, target)
82
+ when ListShape then list(ref, value, target)
83
+ when MapShape then map(ref, value, target)
84
+ else value
85
+ end
86
+ end
87
+ end
88
+
89
+ def flattened_list?(shape)
90
+ shape.is_a?(ListShape) && shape.flattened
91
+ end
92
+
93
+ def flattened_map?(shape)
94
+ shape.is_a?(MapShape) && shape.flattened
95
+ end
96
+ end
97
+ end
98
+ end