From 0ff6ff47c312ca57189206b6fb88714e15ed2fd8 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 8 Oct 2024 20:18:34 -0400 Subject: [PATCH 1/5] Always add accept header for cbor protocol --- .../lib/aws-sdk-core/rpc_v2/content_type_handler.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/content_type_handler.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/content_type_handler.rb index 77aeb01f255..917e24a2692 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/content_type_handler.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/content_type_handler.rb @@ -13,11 +13,13 @@ def call(context) accept = if eventstream_output?(context) 'application/vnd.amazon.eventstream' + else + 'application/cbor' end headers = context.http_request.headers headers['Content-Type'] ||= content_type if content_type - headers['Accept'] ||= accept if accept + headers['Accept'] ||= accept @handler.call(context) end From 5a58effd732206c9dadfa91e5a31c4d8e16ea788 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 8 Oct 2024 20:20:35 -0400 Subject: [PATCH 2/5] Add changelog --- gems/aws-sdk-core/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gems/aws-sdk-core/CHANGELOG.md b/gems/aws-sdk-core/CHANGELOG.md index 92f50c28c31..d5c740dc477 100644 --- a/gems/aws-sdk-core/CHANGELOG.md +++ b/gems/aws-sdk-core/CHANGELOG.md @@ -1,6 +1,8 @@ Unreleased Changes ------------------ +* Issue - Fix RPCv2 protocol to always send an Accept header for CBOR. + 3.209.1 (2024-09-25) ------------------ From aab4c5fdaa55a311b2a5cf4ff9c0957613e9dc9e Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Fri, 18 Oct 2024 10:05:44 -0400 Subject: [PATCH 3/5] Test with new protocol tests --- .../protocol-tests/input/smithy-rpc-v2-cbor.json | 15 +++++++++++++++ .../spec/protocol_tests_spec_helper.rb | 1 + gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb | 2 ++ 3 files changed, 18 insertions(+) diff --git a/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/smithy-rpc-v2-cbor.json b/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/smithy-rpc-v2-cbor.json index 1ae4671636b..f790704fd34 100644 --- a/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/smithy-rpc-v2-cbor.json +++ b/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/smithy-rpc-v2-cbor.json @@ -35,6 +35,7 @@ "uri": "/service/RpcV2Protocol/operation/EmptyInputOutput", "body": "v/8=", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -76,6 +77,7 @@ "uri": "/service/RpcV2Protocol/operation/NoInputOutput", "body": "", "headers": { + "Accept": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, "forbidHeaders": [ @@ -129,6 +131,7 @@ "uri": "/service/RpcV2Protocol/operation/OptionalInputOutput", "body": "v/8=", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -217,6 +220,7 @@ "uri": "/service/RpcV2Protocol/operation/RecursiveShapes", "body": "v2ZuZXN0ZWS/Y2Zvb2RGb28xZm5lc3RlZL9jYmFyZEJhcjFvcmVjdXJzaXZlTWVtYmVyv2Nmb29kRm9vMmZuZXN0ZWS/Y2JhcmRCYXIy//////8=", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -359,6 +363,7 @@ "uri": "/service/RpcV2Protocol/operation/RpcV2CborDenseMaps", "body": "oW5kZW5zZVN0cnVjdE1hcKJjZm9voWJoaWV0aGVyZWNiYXqhYmhpY2J5ZQ==", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -394,6 +399,7 @@ "uri": "/service/RpcV2Protocol/operation/RpcV2CborDenseMaps", "body": "om5kZW5zZU51bWJlck1hcKFheABvZGVuc2VCb29sZWFuTWFwoWF49A==", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -430,6 +436,7 @@ "uri": "/service/RpcV2Protocol/operation/RpcV2CborDenseMaps", "body": "oWtkZW5zZVNldE1hcKJheIBheYJhYWFi", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -665,6 +672,7 @@ "uri": "/service/RpcV2Protocol/operation/RpcV2CborLists", "body": "v2pzdHJpbmdMaXN0gmNmb29jYmFyaXN0cmluZ1NldIJjZm9vY2JhcmtpbnRlZ2VyTGlzdIIBAmtib29sZWFuTGlzdIL19G10aW1lc3RhbXBMaXN0gsH7QdTX+/OAAADB+0HU1/vzgAAAaGVudW1MaXN0gmNGb29hMGtpbnRFbnVtTGlzdIIBAnBuZXN0ZWRTdHJpbmdMaXN0goJjZm9vY2JhcoJjYmF6Y3F1eG1zdHJ1Y3R1cmVMaXN0gqJhYWExYWJhMqJhYWEzYWJhNGhibG9iTGlzdIJDZm9vQ2Jhcv8=", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -696,6 +704,7 @@ "uri": "/service/RpcV2Protocol/operation/RpcV2CborLists", "body": "v2pzdHJpbmdMaXN0n///", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -727,6 +736,7 @@ "uri": "/service/RpcV2Protocol/operation/RpcV2CborLists", "body": "oWpzdHJpbmdMaXN0gA==", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -841,6 +851,7 @@ "uri": "/service/RpcV2Protocol/operation/SimpleScalarProperties", "body": "v2lieXRlVmFsdWUFa2RvdWJsZVZhbHVl+z/+OVgQYk3TcWZhbHNlQm9vbGVhblZhbHVl9GpmbG9hdFZhbHVl+kD0AABsaW50ZWdlclZhbHVlGQEAaWxvbmdWYWx1ZRkmkWpzaG9ydFZhbHVlGSaqa3N0cmluZ1ZhbHVlZnNpbXBsZXB0cnVlQm9vbGVhblZhbHVl9WlibG9iVmFsdWVDZm9v/w==", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -870,6 +881,7 @@ "uri": "/service/RpcV2Protocol/operation/SimpleScalarProperties", "body": "v/8=", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -900,6 +912,7 @@ "uri": "/service/RpcV2Protocol/operation/SimpleScalarProperties", "body": "v2tkb3VibGVWYWx1Zft/+AAAAAAAAGpmbG9hdFZhbHVl+n/AAAD/", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -930,6 +943,7 @@ "uri": "/service/RpcV2Protocol/operation/SimpleScalarProperties", "body": "v2tkb3VibGVWYWx1Zft/8AAAAAAAAGpmbG9hdFZhbHVl+n+AAAD/", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, @@ -960,6 +974,7 @@ "uri": "/service/RpcV2Protocol/operation/SimpleScalarProperties", "body": "v2tkb3VibGVWYWx1Zfv/8AAAAAAAAGpmbG9hdFZhbHVl+v+AAAD/", "headers": { + "Accept": "application/cbor", "Content-Type": "application/cbor", "smithy-protocol": "rpc-v2-cbor" }, diff --git a/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb b/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb index 84da256b79e..be6a07a23cf 100644 --- a/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb +++ b/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb @@ -114,6 +114,7 @@ def set_engine(context, protocol, engine) when /xml/, /ec2/, /query/ Aws::Xml::Parser when /smithy-rpc-v2-cbor/ + Aws::RpcV2 # autoload Aws::Cbor else raise "unsupported protocol: #{protocol}" diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb index e269c9c5756..db3a16a6f59 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'cbor' require_relative 'rpc_v2/handler' require_relative 'rpc_v2/content_type_handler' From 0d41ad0a6db16a036f3eb2330fa007362c830fb9 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Fri, 18 Oct 2024 10:23:45 -0400 Subject: [PATCH 4/5] Move cbor engine to rpc v2 namespace --- .../spec/protocol_tests_spec_helper.rb | 7 +- gems/aws-sdk-core/lib/aws-sdk-core/cbor.rb | 59 +---------------- gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb | 65 ++++++++++++++++++- .../lib/aws-sdk-core/rpc_v2/builder.rb | 2 +- .../{cbor => rpc_v2}/cbor_engine.rb | 9 ++- .../lib/aws-sdk-core/rpc_v2/error_handler.rb | 2 +- .../lib/aws-sdk-core/rpc_v2/parser.rb | 2 +- .../spec/aws/{cbor_spec.rb => rpc_v2_spec.rb} | 9 ++- 8 files changed, 80 insertions(+), 75 deletions(-) rename gems/aws-sdk-core/lib/aws-sdk-core/{cbor => rpc_v2}/cbor_engine.rb (56%) rename gems/aws-sdk-core/spec/aws/{cbor_spec.rb => rpc_v2_spec.rb} (73%) diff --git a/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb b/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb index be6a07a23cf..f54ed522936 100644 --- a/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb +++ b/build_tools/aws-sdk-code-generator/spec/protocol_tests_spec_helper.rb @@ -114,8 +114,7 @@ def set_engine(context, protocol, engine) when /xml/, /ec2/, /query/ Aws::Xml::Parser when /smithy-rpc-v2-cbor/ - Aws::RpcV2 # autoload - Aws::Cbor + Aws::RpcV2 else raise "unsupported protocol: #{protocol}" end @@ -302,8 +301,8 @@ def match_req_body(suite, test_case, http_req, it) expected_body = Aws::Json.load(expected_body) end when 'smithy-rpc-v2-cbor' - request_body = Aws::Cbor.decode(request_body) - expected_body = Aws::Cbor.decode(Base64.decode64(expected_body)) + request_body = Aws::RpcV2.decode(request_body) + expected_body = Aws::RpcV2.decode(Base64.decode64(expected_body)) else raise "unsupported protocol: `#{protocol}`" end assert(it, request_body, expected_body) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/cbor.rb b/gems/aws-sdk-core/lib/aws-sdk-core/cbor.rb index bba6d660704..e65c5743738 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/cbor.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/cbor.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require_relative 'cbor/encoder' +require_relative 'cbor/decoder' + module Aws # @api private module Cbor @@ -46,61 +49,5 @@ def initialize(add_info) super("Unexpected additional information: #{add_info}") end end - - class << self - # @param [Symbol,Class] engine - # Must be one of the following values: - # - # * :cbor - # - def engine=(engine) - @engine = Class === engine ? engine : load_engine(engine) - end - - # @return [Class] Returns the default engine. - # One of: - # - # * {CborEngine} - # - def engine - set_default_engine unless @engine - @engine - end - - def encode(data) - @engine.encode(data) - end - - def decode(bytes) - bytes.force_encoding(Encoding::BINARY) - @engine.decode(bytes) - end - - def set_default_engine - [:cbor].each do |name| - @engine ||= try_load_engine(name) - end - - unless @engine - raise 'Unable to find a compatible cbor library.' - end - end - - private - - def load_engine(name) - require "aws-sdk-core/cbor/#{name}_engine" - const_name = name[0].upcase + name[1..-1] + 'Engine' - const_get(const_name) - end - - def try_load_engine(name) - load_engine(name) - rescue LoadError - false - end - end - - set_default_engine end end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb index db3a16a6f59..f112c61d908 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2.rb @@ -1,8 +1,69 @@ # frozen_string_literal: true require_relative 'cbor' -require_relative 'rpc_v2/handler' +require_relative 'rpc_v2/builder' require_relative 'rpc_v2/content_type_handler' require_relative 'rpc_v2/error_handler' -require_relative 'rpc_v2/builder' +require_relative 'rpc_v2/handler' require_relative 'rpc_v2/parser' + +module Aws + # @api private + module RpcV2 + class << self + # @param [Symbol,Class] engine + # Must be one of the following values: + # + # * :cbor + # + def engine=(engine) + @engine = Class === engine ? engine : load_engine(engine) + end + + # @return [Class] Returns the default engine. + # One of: + # + # * {CborEngine} + # + def engine + set_default_engine unless @engine + @engine + end + + def encode(data) + @engine.encode(data) + end + + def decode(bytes) + bytes.force_encoding(Encoding::BINARY) + @engine.decode(bytes) + end + + def set_default_engine + [:cbor].each do |name| + @engine ||= try_load_engine(name) + end + + unless @engine + raise 'Unable to find a compatible cbor library.' + end + end + + private + + def load_engine(name) + require "aws-sdk-core/rpc_v2/#{name}_engine" + const_name = name[0].upcase + name[1..-1] + 'Engine' + const_get(const_name) + end + + def try_load_engine(name) + load_engine(name) + rescue LoadError + false + end + end + + set_default_engine + end +end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/builder.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/builder.rb index 8a92270c866..b8656445012 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/builder.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/builder.rb @@ -16,7 +16,7 @@ def serialize(params) # different than if the input shape is a structure with no members. return nil if @rules.shape.struct_class == EmptyStructure - Cbor.encode(format(@rules, params)) + RpcV2.encode(format(@rules, params)) end private diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/cbor/cbor_engine.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/cbor_engine.rb similarity index 56% rename from gems/aws-sdk-core/lib/aws-sdk-core/cbor/cbor_engine.rb rename to gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/cbor_engine.rb index 5f06bd3fdd8..116d16676f1 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/cbor/cbor_engine.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/cbor_engine.rb @@ -1,18 +1,17 @@ # frozen_string_literal: true -require_relative 'encoder' -require_relative 'decoder' +require_relative '../cbor' module Aws - module Cbor + module RpcV2 # Pure Ruby implementation of CBOR encode and decode module CborEngine def self.encode(data) - Encoder.new.add(data).bytes + Cbor::Encoder.new.add(data).bytes end def self.decode(bytes) - Decoder.new(bytes.force_encoding(Encoding::BINARY)).decode + Cbor::Decoder.new(bytes.force_encoding(Encoding::BINARY)).decode end end end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/error_handler.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/error_handler.rb index 3e13b05f0ab..1c0bae56d70 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/error_handler.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/error_handler.rb @@ -27,7 +27,7 @@ def valid_response?(context) end def extract_error(body, context) - data = Cbor.decode(body) + data = RpcV2.decode(body) code = error_code(data, context) message = data['message'] data = parse_error_data(context, body, code) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/parser.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/parser.rb index a4a47312b75..d219e391e2d 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/parser.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rpc_v2/parser.rb @@ -16,7 +16,7 @@ def initialize(rules, query_compatible: false) def parse(cbor, target = nil) return {} if cbor.empty? - parse_ref(@rules, Cbor.decode(cbor), target) + parse_ref(@rules, RpcV2.decode(cbor), target) end private diff --git a/gems/aws-sdk-core/spec/aws/cbor_spec.rb b/gems/aws-sdk-core/spec/aws/rpc_v2_spec.rb similarity index 73% rename from gems/aws-sdk-core/spec/aws/cbor_spec.rb rename to gems/aws-sdk-core/spec/aws/rpc_v2_spec.rb index 6e3a3c445c8..c7716698f74 100644 --- a/gems/aws-sdk-core/spec/aws/cbor_spec.rb +++ b/gems/aws-sdk-core/spec/aws/rpc_v2_spec.rb @@ -1,22 +1,21 @@ # frozen_string_literal: true require_relative '../spec_helper' -require 'aws-sdk-core/cbor' module Aws - describe Cbor do + describe RpcV2 do [:cbor].each do |engine| describe("ENGINE: #{engine}") do begin - Cbor.engine = engine + RpcV2.engine = engine rescue LoadError next end describe '.encode' do it 'encodes an object into bytes' do - expect(Cbor.encode('abc')).to eq("\x63abc") + expect(RpcV2.encode('abc')).to eq("\x63abc") end end @@ -24,7 +23,7 @@ module Aws it 'decodes bytes into an object' do # frozen string bytes = StringIO.new("\x63abc").read - expect(Cbor.decode(bytes)).to eq('abc') + expect(RpcV2.decode(bytes)).to eq('abc') end end end From d2604838afe60f73766e05d77b90c6b4e39cffd1 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Fri, 18 Oct 2024 17:54:48 -0400 Subject: [PATCH 5/5] Fix new protocol tests --- .../spec/protocol-tests-ignore-list.json | 7 +- .../spec/protocol-tests/input/rest-json.json | 41 +++++- .../spec/protocol-tests/input/rest-xml.json | 124 +++++++++++++++++- .../spec/protocol-tests/output/rest-xml.json | 81 ++++++++++++ .../lib/aws-sdk-core/rest/request/headers.rb | 4 +- 5 files changed, 244 insertions(+), 13 deletions(-) diff --git a/build_tools/aws-sdk-code-generator/spec/protocol-tests-ignore-list.json b/build_tools/aws-sdk-code-generator/spec/protocol-tests-ignore-list.json index 06f1010edb6..24ccf9397b4 100644 --- a/build_tools/aws-sdk-code-generator/spec/protocol-tests-ignore-list.json +++ b/build_tools/aws-sdk-code-generator/spec/protocol-tests-ignore-list.json @@ -1,6 +1,11 @@ { "rest-xml" : { - "input" : {}, + "input" : { + "NestedXmlMapWithXmlNameSerializes": { + "description": "Smithy 1.52.0 did not include a fix to this test.", + "engines": ["ox", "nokogiri", "rexml", "libxml", "oga"] + } + }, "output" : { "SimpleScalarPropertiesComplexEscapes": { "description": "OxEngine does not handle all escape cases but other engines supports it", diff --git a/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-json.json b/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-json.json index 4718d3531b9..5325d432279 100644 --- a/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-json.json +++ b/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-json.json @@ -1685,7 +1685,7 @@ }, "documentation": "

This examples adds headers to the input of a request and response by prefix.

" }, - "description": "No prefix headers are serialized because the value is empty", + "description": "No prefix headers are serialized because the value is not present", "params": { "foo": "Foo", "fooMap": {} @@ -1698,6 +1698,35 @@ "X-Foo": "Foo" } } + }, + { + "id": "RestJsonHttpPrefixEmptyHeaders", + "given": { + "name": "HttpPrefixHeaders", + "http": { + "method": "GET", + "requestUri": "/HttpPrefixHeaders", + "responseCode": 200 + }, + "input": { + "shape": "HttpPrefixHeadersInput" + }, + "documentation": "

This examples adds headers to the input of a request and response by prefix.

" + }, + "description": "Serialize prefix headers were the value is present but empty", + "params": { + "fooMap": { + "Abc": "" + } + }, + "serialized": { + "method": "GET", + "uri": "/HttpPrefixHeaders", + "body": "", + "headers": { + "X-Foo-Abc": "" + } + } } ] }, @@ -4362,7 +4391,7 @@ }, "documentation": "

Null and empty headers are not sent over the wire.

" }, - "description": "Do not send null values, empty strings, or empty lists over the wire in headers", + "description": "Do not send null values, but do send empty strings and empty lists over the wire in headers", "params": { "a": null, "b": "", @@ -4372,10 +4401,12 @@ "method": "GET", "uri": "/NullAndEmptyHeadersClient", "body": "", + "headers": { + "X-B": "", + "X-C": "" + }, "forbidHeaders": [ - "X-A", - "X-B", - "X-C" + "X-A" ] } } diff --git a/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-xml.json b/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-xml.json index 9577b3db84e..9fa4ae78288 100644 --- a/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-xml.json +++ b/build_tools/aws-sdk-code-generator/spec/protocol-tests/input/rest-xml.json @@ -1674,7 +1674,7 @@ }, "documentation": "

This examples adds headers to the input of a request and response by prefix.

" }, - "description": "No prefix headers are serialized because the value is empty", + "description": "No prefix headers are serialized because the value is not present", "params": { "foo": "Foo", "fooMap": {} @@ -1687,6 +1687,35 @@ "X-Foo": "Foo" } } + }, + { + "id": "HttpPrefixEmptyHeaders", + "given": { + "name": "HttpPrefixHeaders", + "http": { + "method": "GET", + "requestUri": "/HttpPrefixHeaders", + "responseCode": 200 + }, + "input": { + "shape": "HttpPrefixHeadersInputOutput" + }, + "documentation": "

This examples adds headers to the input of a request and response by prefix.

" + }, + "description": "Serialize prefix headers were the value is present but empty", + "params": { + "fooMap": { + "Abc": "" + } + }, + "serialized": { + "method": "GET", + "uri": "/HttpPrefixHeaders", + "body": "", + "headers": { + "X-Foo-Abc": "" + } + } } ] }, @@ -2750,6 +2779,89 @@ } ] }, + { + "description": "Test cases for NestedXmlMapWithXmlName operation", + "metadata": { + "protocol": "rest-xml", + "protocols": [ + "rest-xml" + ], + "apiVersion": "2019-12-16" + }, + "shapes": { + "NestedXmlMapWithXmlNameInputOutput": { + "type": "structure", + "members": { + "nestedXmlMapWithXmlNameMap": { + "shape": "NestedXmlMapWithXmlNameMap" + } + } + }, + "NestedXmlMapWithXmlNameMap": { + "type": "map", + "key": { + "shape": "String", + "locationName": "OuterKey" + }, + "value": { + "shape": "NestedXmlMapWithXmlNameInnerMap" + } + }, + "NestedXmlMapWithXmlNameInnerMap": { + "type": "map", + "key": { + "shape": "String", + "locationName": "InnerKey" + }, + "value": { + "shape": "String", + "locationName": "InnerValue" + } + }, + "String": { + "type": "string" + } + }, + "cases": [ + { + "id": "NestedXmlMapWithXmlNameSerializes", + "given": { + "name": "NestedXmlMapWithXmlName", + "http": { + "method": "POST", + "requestUri": "/NestedXmlMapWithXmlName", + "responseCode": 200 + }, + "input": { + "shape": "NestedXmlMapWithXmlNameInputOutput", + "locationName": "NestedXmlMapWithXmlNameRequest" + }, + "documentation": "

Nested Xml Maps with key/values with @xmlName

" + }, + "description": "Serializes nested XML Maps in requests that have xmlName on members", + "params": { + "nestedXmlMapWithXmlNameMap": { + "foo": { + "bar": "Baz", + "fizz": "Buzz" + }, + "qux": { + "foobar": "Bar", + "fizzbuzz": "Buzz" + } + } + }, + "serialized": { + "method": "POST", + "uri": "/NestedXmlMapWithXmlName", + "body": " \n \n \n foo\n \n \n bar\n Baz\n \n \n fizz\n Buzz\n \n \n \n \n qux\n \n \n foobar\n Bar\n \n \n fizzbuzz\n Buzz\n \n \n \n \n \n", + "headers": { + "Content-Type": "application/xml" + } + } + } + ] + }, { "description": "Test cases for NoInputAndNoOutput operation", "metadata": { @@ -2869,7 +2981,7 @@ }, "documentation": "

Null and empty headers are not sent over the wire.

" }, - "description": "Do not send null values, empty strings, or empty lists over the wire in headers", + "description": "Do not send null values, but do send empty strings and empty lists over the wire in headers", "params": { "a": null, "b": "", @@ -2879,10 +2991,12 @@ "method": "GET", "uri": "/NullAndEmptyHeadersClient", "body": "", + "headers": { + "X-B": "", + "X-C": "" + }, "forbidHeaders": [ - "X-A", - "X-B", - "X-C" + "X-A" ] } } diff --git a/build_tools/aws-sdk-code-generator/spec/protocol-tests/output/rest-xml.json b/build_tools/aws-sdk-code-generator/spec/protocol-tests/output/rest-xml.json index 9d2efabd011..fd9ea948175 100644 --- a/build_tools/aws-sdk-code-generator/spec/protocol-tests/output/rest-xml.json +++ b/build_tools/aws-sdk-code-generator/spec/protocol-tests/output/rest-xml.json @@ -1999,6 +1999,87 @@ } ] }, + { + "description": "Test cases for NestedXmlMapWithXmlName operation", + "metadata": { + "protocol": "rest-xml", + "protocols": [ + "rest-xml" + ], + "apiVersion": "2019-12-16" + }, + "shapes": { + "NestedXmlMapWithXmlNameInputOutput": { + "type": "structure", + "members": { + "nestedXmlMapWithXmlNameMap": { + "shape": "NestedXmlMapWithXmlNameMap" + } + } + }, + "NestedXmlMapWithXmlNameMap": { + "type": "map", + "key": { + "shape": "String", + "locationName": "OuterKey" + }, + "value": { + "shape": "NestedXmlMapWithXmlNameInnerMap" + } + }, + "NestedXmlMapWithXmlNameInnerMap": { + "type": "map", + "key": { + "shape": "String", + "locationName": "InnerKey" + }, + "value": { + "shape": "String", + "locationName": "InnerValue" + } + }, + "String": { + "type": "string" + } + }, + "cases": [ + { + "id": "NestedXmlMapWithXmlNameDeserializes", + "given": { + "name": "NestedXmlMapWithXmlName", + "http": { + "method": "POST", + "requestUri": "/NestedXmlMapWithXmlName", + "responseCode": 200 + }, + "output": { + "shape": "NestedXmlMapWithXmlNameInputOutput" + }, + "documentation": "

Nested Xml Maps with key/values with @xmlName

" + }, + "description": "Serializes nested XML maps in responses that have xmlName on members", + "result": { + "nestedXmlMapWithXmlNameMap": { + "foo": { + "bar": "Baz", + "fizz": "Buzz" + }, + "qux": { + "foobar": "Bar", + "fizzbuzz": "Buzz" + } + } + }, + "response": { + "status_code": 200, + "headers": { + "Content-Type": "application/xml" + }, + "body": " \n \n \n foo\n \n \n bar\n Baz\n \n \n fizz\n Buzz\n \n \n \n \n qux\n \n \n foobar\n Bar\n \n \n fizzbuzz\n Buzz\n \n \n \n \n \n" + } + } + ] + }, { "description": "Test cases for NoInputAndNoOutput operation", "metadata": { diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/rest/request/headers.rb b/gems/aws-sdk-core/lib/aws-sdk-core/rest/request/headers.rb index 2bff017aaf2..c51bdb4297b 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/rest/request/headers.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/rest/request/headers.rb @@ -20,7 +20,7 @@ def initialize(rules) def apply(http_req, params) @rules.shape.members.each do |name, ref| value = params[name] - next if value.nil? || ((ref.shape).is_a?(StringShape) && value.empty?) + next if value.nil? case ref.location when 'header' then apply_header_value(http_req.headers, ref, value) @@ -51,7 +51,7 @@ def timestamp(ref, value) end def list(headers, ref, values) - return if !values || values.empty? + return if values.nil? member_ref = ref.shape.member values = values.collect do |value|