aws-sdk-core 3.191.0 → 3.196.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +109 -1
  3. data/VERSION +1 -1
  4. data/lib/aws-sdk-core/binary/decode_handler.rb +0 -5
  5. data/lib/aws-sdk-core/binary/event_builder.rb +34 -37
  6. data/lib/aws-sdk-core/credential_provider.rb +1 -1
  7. data/lib/aws-sdk-core/ec2_metadata.rb +1 -1
  8. data/lib/aws-sdk-core/ecs_credentials.rb +2 -1
  9. data/lib/aws-sdk-core/event_emitter.rb +0 -16
  10. data/lib/aws-sdk-core/instance_profile_credentials.rb +3 -2
  11. data/lib/aws-sdk-core/json/builder.rb +8 -1
  12. data/lib/aws-sdk-core/json/error_handler.rb +10 -3
  13. data/lib/aws-sdk-core/json/parser.rb +4 -1
  14. data/lib/aws-sdk-core/lru_cache.rb +75 -0
  15. data/lib/aws-sdk-core/param_validator.rb +7 -2
  16. data/lib/aws-sdk-core/plugins/invocation_id.rb +1 -11
  17. data/lib/aws-sdk-core/plugins/protocols/rest_json.rb +3 -16
  18. data/lib/aws-sdk-core/plugins/protocols/rest_xml.rb +1 -2
  19. data/lib/aws-sdk-core/plugins/request_compression.rb +1 -1
  20. data/lib/aws-sdk-core/plugins/sign.rb +8 -3
  21. data/lib/aws-sdk-core/plugins/user_agent.rb +3 -2
  22. data/lib/aws-sdk-core/query/ec2_param_builder.rb +5 -7
  23. data/lib/aws-sdk-core/query/param_builder.rb +2 -2
  24. data/lib/aws-sdk-core/rest/request/body.rb +32 -5
  25. data/lib/aws-sdk-core/rest/request/content_type.rb +60 -0
  26. data/lib/aws-sdk-core/rest/request/endpoint.rb +22 -4
  27. data/lib/aws-sdk-core/rest/request/headers.rb +15 -7
  28. data/lib/aws-sdk-core/rest/request/querystring_builder.rb +23 -11
  29. data/lib/aws-sdk-core/rest/response/body.rb +15 -1
  30. data/lib/aws-sdk-core/rest/response/header_list_parser.rb +79 -0
  31. data/lib/aws-sdk-core/rest/response/headers.rb +8 -3
  32. data/lib/aws-sdk-core/rest.rb +1 -0
  33. data/lib/aws-sdk-core/util.rb +39 -0
  34. data/lib/aws-sdk-core/xml/builder.rb +17 -9
  35. data/lib/aws-sdk-core/xml/error_handler.rb +24 -8
  36. data/lib/aws-sdk-core/xml/parser/frame.rb +4 -20
  37. data/lib/aws-sdk-core/xml/parser/stack.rb +2 -0
  38. data/lib/aws-sdk-core.rb +1 -0
  39. data/lib/aws-sdk-sso/client.rb +73 -48
  40. data/lib/aws-sdk-sso.rb +1 -1
  41. data/lib/aws-sdk-ssooidc/client.rb +123 -50
  42. data/lib/aws-sdk-ssooidc/client_api.rb +22 -0
  43. data/lib/aws-sdk-ssooidc/errors.rb +21 -0
  44. data/lib/aws-sdk-ssooidc/types.rb +77 -9
  45. data/lib/aws-sdk-ssooidc.rb +1 -1
  46. data/lib/aws-sdk-sts/client.rb +73 -48
  47. data/lib/aws-sdk-sts/client_api.rb +8 -8
  48. data/lib/aws-sdk-sts.rb +1 -1
  49. data/lib/seahorse/client/async_base.rb +1 -1
  50. data/lib/seahorse/client/async_response.rb +19 -0
  51. data/lib/seahorse/client/base.rb +1 -0
  52. data/lib/seahorse/client/h2/handler.rb +1 -0
  53. data/lib/seahorse/client/net_http/connection_pool.rb +1 -5
  54. data/lib/seahorse/client/plugin.rb +8 -0
  55. data/lib/seahorse/client/plugins/net_http.rb +48 -16
  56. data/lib/seahorse/model/shapes.rb +2 -2
  57. metadata +5 -2
@@ -36,7 +36,7 @@ module Aws
36
36
  return
37
37
  end
38
38
  if flat?(ref)
39
- if name = query_name(member_ref)
39
+ if (name = query_name(ref))
40
40
  parts = prefix.split('.')
41
41
  parts.pop
42
42
  parts.push(name)
@@ -82,7 +82,7 @@ module Aws
82
82
  end
83
83
 
84
84
  def flat?(ref)
85
- ref.shape.flattened
85
+ ref[:flattened] || ref.shape.flattened
86
86
  end
87
87
 
88
88
  def timestamp(ref, value)
@@ -18,10 +18,13 @@ module Aws
18
18
  # @param [Hash] params
19
19
  def apply(http_req, params)
20
20
  body = build_body(params)
21
+
21
22
  # for rest-json, ensure we send at least an empty object
22
23
  # don't send an empty object for streaming? case.
23
- if body.nil? && @serializer_class == Json::Builder &&
24
- modeled_body? && !streaming?
24
+ if body.nil? &&
25
+ json_builder? &&
26
+ modeled_body? &&
27
+ !streaming?
25
28
  body = '{}'
26
29
  end
27
30
  http_req.body = body
@@ -45,13 +48,29 @@ module Aws
45
48
  params[@rules[:payload]]
46
49
  elsif @rules[:payload]
47
50
  params = params[@rules[:payload]]
48
- serialize(@rules[:payload_member], params) if params
51
+ if params
52
+ if xml_builder? &&
53
+ @rules.shape.member?(@rules[:payload_member].location_name)
54
+ # serializing payload member name for rest-xml is as follows:
55
+ # 1. Use the member locationName if the member value doesn't match the member's name (default)
56
+ # 2. Use the value of the locationName on the member's target if present
57
+ # 3. Use the shape name of the member's target
58
+ serialize(@rules[:payload_member], params, location_name: payload_location_name)
59
+ else
60
+ serialize(@rules[:payload_member], params)
61
+ end
62
+ end
49
63
  else
50
64
  params = body_params(params)
51
65
  serialize(@rules, params) unless params.empty?
52
66
  end
53
67
  end
54
68
 
69
+ def payload_location_name
70
+ @rules[:payload_member].shape['locationName'] ||
71
+ @rules[:payload_member].shape.name
72
+ end
73
+
55
74
  def streaming?
56
75
  @rules[:payload] && (
57
76
  BlobShape === @rules[:payload_member].shape ||
@@ -59,8 +78,16 @@ module Aws
59
78
  )
60
79
  end
61
80
 
62
- def serialize(rules, params)
63
- @serializer_class.new(rules).serialize(params)
81
+ def xml_builder?
82
+ @serializer_class == Xml::Builder
83
+ end
84
+
85
+ def json_builder?
86
+ @serializer_class == Json::Builder
87
+ end
88
+
89
+ def serialize(rules, params, location_name: nil)
90
+ @serializer_class.new(rules, location_name: location_name).serialize(params)
64
91
  end
65
92
 
66
93
  def body_params(params)
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Rest
5
+ # NOTE: headers could be already populated if specified on input shape
6
+ class ContentTypeHandler < Seahorse::Client::Handler
7
+ def call(context)
8
+ if eventstream?(context)
9
+ context.http_request.headers['Content-Type'] ||=
10
+ 'application/vnd.amazon.eventstream'
11
+ elsif (payload = context.operation.input[:payload_member])
12
+ case payload.shape
13
+ when Seahorse::Model::Shapes::BlobShape
14
+ context.http_request.headers['Content-Type'] ||=
15
+ 'application/octet-stream'
16
+ when Seahorse::Model::Shapes::StringShape
17
+ context.http_request.headers['Content-Type'] ||=
18
+ 'text/plain'
19
+ else
20
+ apply_default_content_type(context)
21
+ end
22
+ elsif (body = context.http_request.body) &&
23
+ (!body.respond_to?(:size) || non_empty_body?(body))
24
+ apply_default_content_type(context)
25
+ end
26
+
27
+ @handler.call(context)
28
+ end
29
+
30
+ private
31
+
32
+ def non_empty_body?(body)
33
+ body.respond_to?(:size) && body.size.positive?
34
+ end
35
+
36
+ def eventstream?(context)
37
+ context.operation.input.shape.members.each do |_, ref|
38
+ return ref if ref.eventstream
39
+ end
40
+ false
41
+ end
42
+
43
+ # content-type defaults as noted here:
44
+ # rest-json: https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html#content-type
45
+ # rest-xml: https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html#content-type
46
+ def apply_default_content_type(context)
47
+ protocol = context.config.api.metadata['protocol']
48
+ case protocol
49
+ when 'rest-json'
50
+ context.http_request.headers['Content-Type'] ||=
51
+ 'application/json'
52
+ when 'rest-xml'
53
+ context.http_request.headers['Content-Type'] ||=
54
+ 'application/xml'
55
+ else raise "Unsupported protocol #{protocol}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -30,7 +30,7 @@ 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{/$}, '') + @path_pattern.split('?')[0]
34
34
  uri.path = path.gsub(/{.+?}/) do |placeholder|
35
35
  param_value_for_placeholder(placeholder, params)
36
36
  end
@@ -38,22 +38,40 @@ module Aws
38
38
 
39
39
  def param_value_for_placeholder(placeholder, params)
40
40
  name = param_name(placeholder)
41
- value = params[name].to_s
41
+ param_shape = @rules.shape.member(name).shape
42
+ value =
43
+ case param_shape
44
+ when Seahorse::Model::Shapes::TimestampShape
45
+ timestamp(param_shape, params[name]).to_s
46
+ else
47
+ params[name].to_s
48
+ end
49
+
42
50
  raise ArgumentError, ":#{name} must not be blank" if value.empty?
43
51
 
44
52
  if placeholder.include?('+')
45
- value.gsub(/[^\/]+/) { |v| escape(v) }
53
+ value.gsub(%r{[^/]+}) { |v| escape(v) }
46
54
  else
47
55
  escape(value)
48
56
  end
49
57
  end
50
58
 
51
59
  def param_name(placeholder)
52
- location_name = placeholder.gsub(/[{}+]/,'')
60
+ location_name = placeholder.gsub(/[{}+]/, '')
53
61
  param_name, _ = @rules.shape.member_by_location_name(location_name)
54
62
  param_name
55
63
  end
56
64
 
65
+ def timestamp(ref, value)
66
+ case ref['timestampFormat']
67
+ when 'unixTimestamp' then value.to_i
68
+ when 'rfc822' then value.utc.httpdate
69
+ else
70
+ # serializing as RFC 3399 date-time is the default
71
+ value.utc.iso8601
72
+ end
73
+ end
74
+
57
75
  def apply_querystring_params(uri, params)
58
76
  # collect params that are supposed to be part of the query string
59
77
  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)
@@ -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)
@@ -6,6 +6,7 @@ require_relative 'rest/request/builder'
6
6
  require_relative 'rest/request/endpoint'
7
7
  require_relative 'rest/request/headers'
8
8
  require_relative 'rest/request/querystring_builder'
9
+ require_relative 'rest/request/content_type'
9
10
  require_relative 'rest/response/body'
10
11
  require_relative 'rest/response/headers'
11
12
  require_relative 'rest/response/parser'
@@ -67,6 +67,45 @@ module Aws
67
67
  end
68
68
  end
69
69
 
70
+ # @param [Number] input
71
+ # @return [Number, String] The serialized number
72
+ def serialize_number(input)
73
+ if input == ::Float::INFINITY then 'Infinity'
74
+ elsif input == -::Float::INFINITY then '-Infinity'
75
+ elsif input&.nan? then 'NaN'
76
+ else
77
+ input
78
+ end
79
+ end
80
+
81
+ # @param [String] str
82
+ # @return [Number] The input as a number
83
+ def deserialize_number(str)
84
+ case str
85
+ when 'Infinity' then ::Float::INFINITY
86
+ when '-Infinity' then -::Float::INFINITY
87
+ when 'NaN' then ::Float::NAN
88
+ when nil then nil
89
+ else str.to_f
90
+ end
91
+ end
92
+
93
+ # @param [String, Integer] value
94
+ # @return [Time]
95
+ def deserialize_time(value)
96
+ case value
97
+ when nil then nil
98
+ when /^\d+$/ then Time.at(value.to_i)
99
+ else
100
+ begin
101
+ fractional_time = Time.parse(value).utc.to_f
102
+ Time.at(fractional_time)
103
+ rescue ArgumentError
104
+ raise "unhandled timestamp format `#{value}'"
105
+ end
106
+ end
107
+ end
108
+
70
109
  end
71
110
  end
72
111
  end
@@ -10,6 +10,8 @@ module Aws
10
10
 
11
11
  def initialize(rules, options = {})
12
12
  @rules = rules
13
+ @location_name =
14
+ options[:location_name].nil? ? @rules.location_name : options[:location_name]
13
15
  @xml = options[:target] || []
14
16
  indent = options[:indent] || ''
15
17
  pad = options[:pad] || ''
@@ -17,7 +19,7 @@ module Aws
17
19
  end
18
20
 
19
21
  def to_xml(params)
20
- structure(@rules.location_name, @rules, params)
22
+ structure(@location_name, @rules, params)
21
23
  @xml.join
22
24
  end
23
25
  alias serialize to_xml
@@ -50,7 +52,7 @@ module Aws
50
52
  def list(name, ref, values)
51
53
  if ref[:flattened] || ref.shape.flattened
52
54
  values.each do |value|
53
- member(ref.shape.member.location_name || name, ref.shape.member, value)
55
+ member(name, ref.shape.member, value)
54
56
  end
55
57
  else
56
58
  node(name, ref) do
@@ -65,7 +67,7 @@ module Aws
65
67
  def map(name, ref, hash)
66
68
  key_ref = ref.shape.key
67
69
  value_ref = ref.shape.value
68
- if ref.shape.flattened
70
+ if ref[:flattened] || ref.shape.flattened
69
71
  hash.each do |key, value|
70
72
  node(name, ref) do
71
73
  member(key_ref.location_name || 'key', key_ref, key)
@@ -75,7 +77,8 @@ module Aws
75
77
  else
76
78
  node(name, ref) do
77
79
  hash.each do |key, value|
78
- node('entry', ref) do
80
+ # Pass in a new ShapeRef to create an entry node
81
+ node('entry', ShapeRef.new) do
79
82
  member(key_ref.location_name || 'key', key_ref, key)
80
83
  member(value_ref.location_name || 'value', value_ref, value)
81
84
  end
@@ -129,11 +132,16 @@ module Aws
129
132
  end
130
133
 
131
134
  def shape_attrs(ref)
132
- if xmlns = ref['xmlNamespace']
133
- if prefix = xmlns['prefix']
134
- { 'xmlns:' + prefix => xmlns['uri'] }
135
- else
136
- { 'xmlns' => xmlns['uri'] }
135
+ if (xmlns = ref['xmlNamespace'])
136
+ case xmlns
137
+ when String
138
+ { 'xmlns' => xmlns }
139
+ when Hash
140
+ if (prefix = xmlns['prefix'])
141
+ { "xmlns:#{prefix}" => xmlns['uri'] }
142
+ else
143
+ { 'xmlns' => xmlns['uri'] }
144
+ end
137
145
  end
138
146
  else
139
147
  {}
@@ -26,8 +26,7 @@ module Aws
26
26
  end
27
27
  context[:request_id] = request_id(body)
28
28
  errors_module = context.client.class.errors_module
29
- error_class = errors_module.error_class(code).new(context, message, data)
30
- error_class
29
+ errors_module.error_class(code).new(context, message, data)
31
30
  end
32
31
 
33
32
  def extract_error(body, context)
@@ -43,14 +42,15 @@ module Aws
43
42
  data = EmptyStructure.new
44
43
  if error_rules = context.operation.errors
45
44
  error_rules.each do |rule|
46
- # for modeled shape with error trait
47
- # match `code` in the error trait before
48
- # match modeled shape name
45
+ # query protocol may have custom error code
46
+ # reference: https://smithy.io/2.0/aws/protocols/aws-query-protocol.html#error-code-resolution
49
47
  error_shape_code = rule.shape['error']['code'] if rule.shape['error']
50
48
  match = (code == error_shape_code || code == rule.shape.name)
51
- if match && rule.shape.members.any?
52
- data = Parser.new(rule).parse(context.http_response.body_contents)
53
- end
49
+ next unless match && rule.shape.members.any?
50
+
51
+ data = parse_error_data(rule, context.http_response.body_contents)
52
+ # supporting HTTP bindings
53
+ apply_error_headers(rule, context, data)
54
54
  end
55
55
  end
56
56
  data
@@ -58,6 +58,22 @@ module Aws
58
58
  EmptyStructure.new
59
59
  end
60
60
 
61
+ def parse_error_data(rule, body)
62
+ # errors may nested under <Errors><Error>structure_data</Error></Errors>
63
+ # Or may be flat and under <Error>structure_data</Error>
64
+ body = body.tr("\n", '')
65
+ if matches = body.match(/<Error>(.+?)<\/Error>/)
66
+ Parser.new(rule).parse("<#{rule.shape.name}>#{matches[1]}</#{rule.shape.name}>")
67
+ else
68
+ EmptyStructure.new
69
+ end
70
+ end
71
+
72
+ def apply_error_headers(rule, context, data)
73
+ headers = Aws::Rest::Response::Headers.new(rule)
74
+ headers.apply(context.http_response, data)
75
+ end
76
+
61
77
  def error_code(body, context)
62
78
  if matches = body.match(/<Code>(.+?)<\/Code>/)
63
79
  remove_prefix(unescape(matches[1]), context)
@@ -138,11 +138,7 @@ module Aws
138
138
  end
139
139
 
140
140
  def xml_name(ref)
141
- if flattened_list?(ref)
142
- ref.shape.member.location_name || ref.location_name
143
- else
144
- ref.location_name
145
- end
141
+ ref.location_name
146
142
  end
147
143
 
148
144
  def flattened_list?(ref)
@@ -266,7 +262,7 @@ module Aws
266
262
 
267
263
  class BlobFrame < Frame
268
264
  def result
269
- @text.empty? ? nil : Base64.decode64(@text.join)
265
+ @text.empty? ? '' : Base64.decode64(@text.join)
270
266
  end
271
267
  end
272
268
 
@@ -278,7 +274,7 @@ module Aws
278
274
 
279
275
  class FloatFrame < Frame
280
276
  def result
281
- @text.empty? ? nil : @text.join.to_f
277
+ @text.empty? ? nil : Aws::Util.deserialize_number(@text.join)
282
278
  end
283
279
  end
284
280
 
@@ -296,19 +292,7 @@ module Aws
296
292
 
297
293
  class TimestampFrame < Frame
298
294
  def result
299
- @text.empty? ? nil : parse(@text.join)
300
- end
301
- def parse(value)
302
- case value
303
- when nil then nil
304
- when /^\d+$/ then Time.at(value.to_i)
305
- else
306
- begin
307
- Time.parse(value).utc
308
- rescue ArgumentError
309
- raise "unhandled timestamp format `#{value}'"
310
- end
311
- end
295
+ @text.empty? ? nil : Aws::Util.deserialize_time(@text.join)
312
296
  end
313
297
  end
314
298