diff --git a/settings.gradle.kts b/settings.gradle.kts
index ba9479deb25..be6f919cad3 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,6 +1,7 @@
rootProject.name = "smithy"
include(":smithy-aws-traits")
include(":smithy-aws-apigateway-openapi")
+include(":smithy-aws-protocol-tests")
include(":smithy-cli")
include(":smithy-codegen-core")
include(":smithy-codegen-freemarker")
@@ -13,3 +14,10 @@ include(":smithy-jsonschema")
include(":smithy-openapi")
include(":smithy-utils")
include(":smithy-protocol-test-traits")
+
+pluginManagement {
+ repositories {
+ mavenLocal()
+ gradlePluginPortal()
+ }
+}
diff --git a/smithy-aws-protocol-tests/build.gradle.kts b/smithy-aws-protocol-tests/build.gradle.kts
new file mode 100644
index 00000000000..1fcc1d00a2c
--- /dev/null
+++ b/smithy-aws-protocol-tests/build.gradle.kts
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+description = "Defines protocol tests for AWS HTTP protocols."
+extra["displayName"] = "Smithy :: AWS :: Protocol Tests"
+extra["moduleName"] = "software.amazon.smithy.aws.protocoltests"
+
+plugins {
+ id("software.amazon.smithy").version("0.4.2")
+}
+
+dependencies {
+ api(project(":smithy-protocol-test-traits"))
+ api(project(":smithy-aws-traits"))
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/document-lists.smithy b/smithy-aws-protocol-tests/model/rest-xml/document-lists.smithy
new file mode 100644
index 00000000000..8c8cbb7a160
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/document-lists.smithy
@@ -0,0 +1,255 @@
+// This file defines test cases that serialize lists in XML documents.
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// This test case serializes XML lists for the following cases for both
+/// input and output:
+///
+/// 1. Normal XML lists.
+/// 2. Normal XML sets.
+/// 3. XML lists of lists.
+/// 4. XML lists with @xmlName on its members
+/// 5. Flattened XML lists.
+/// 6. Flattened XML lists with @xmlName.
+/// 7. Lists of structures.
+@idempotent
+@http(uri: "/XmlLists", method: "PUT")
+operation XmlLists(XmlListsInputOutput) -> XmlListsInputOutput
+
+apply XmlLists @httpRequestTests([
+ {
+ id: "XmlLists",
+ description: "Serializes XML lists",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/XmlLists",
+ body: """
+
+
+ foo
+ bar
+
+
+ foo
+ bar
+
+
+ 1
+ 2
+
+
+ true
+ false
+
+
+ 2014-04-29T18:30:38Z
+ 2014-04-29T18:30:38Z
+
+
+ Foo
+ 0
+
+
+
+ foo
+ bar
+
+
+ baz
+ qux
+
+
+
+ - foo
+ - bar
+
+ hi
+ bye
+ yep
+ nope
+
+ -
+ 1
+ 2
+
+ -
+ 3
+ 4
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ stringList: ["foo", "bar"],
+ stringSet: ["foo", "bar"],
+ integerList: [1, 2],
+ booleanList: [true, false],
+ timestampList: [1398796238, 1398796238],
+ enumList: ["Foo", "0"],
+ nestedStringList: [["foo", "bar"], ["baz", "qux"]],
+ renamedListMembers: ["foo", "bar"],
+ flattenedList: ["hi", "bye"],
+ flattenedList2: ["yep", "nope"],
+ structureList: [
+ {
+ a: "1",
+ b: "2",
+ },
+ {
+ a: "3",
+ b: "4",
+ }
+ ]
+ }
+ }
+])
+
+apply XmlLists @httpResponseTests([
+ {
+ id: "XmlLists",
+ description: "Serializes XML lists",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+
+ foo
+ bar
+
+
+ foo
+ bar
+
+
+ 1
+ 2
+
+
+ true
+ false
+
+
+ 2014-04-29T18:30:38Z
+ 2014-04-29T18:30:38Z
+
+
+ Foo
+ 0
+
+
+
+ foo
+ bar
+
+
+ baz
+ qux
+
+
+
+ - foo
+ - bar
+
+ hi
+ bye
+ yep
+ nope
+
+ -
+ 1
+ 2
+
+ -
+ 3
+ 4
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ stringList: ["foo", "bar"],
+ stringSet: ["foo", "bar"],
+ integerList: [1, 2],
+ booleanList: [true, false],
+ timestampList: [1398796238, 1398796238],
+ enumList: ["Foo", "0"],
+ nestedStringList: [["foo", "bar"], ["baz", "qux"]],
+ renamedListMembers: ["foo", "bar"],
+ flattenedList: ["hi", "bye"],
+ flattenedList2: ["yep", "nope"],
+ structureList: [
+ {
+ a: "1",
+ b: "2",
+ },
+ {
+ a: "3",
+ b: "4",
+ }
+ ]
+ }
+ }
+])
+
+structure XmlListsInputOutput {
+ stringList: StringList,
+
+ stringSet: StringSet,
+
+ integerList: IntegerList,
+
+ booleanList: BooleanList,
+
+ timestampList: TimestampList,
+
+ enumList: FooEnumList,
+
+ nestedStringList: NestedStringList,
+
+ @xmlName("renamed")
+ renamedListMembers: RenamedListMembers,
+
+ @xmlFlattened
+ // The xmlname on the targeted list is ignored, and the member name is used.
+ flattenedList: RenamedListMembers,
+
+ @xmlName("customName")
+ @xmlFlattened
+ // the xmlName trait on the targeted list's member is ignored when
+ // serializing flattened lists in structures.
+ flattenedList2: RenamedListMembers,
+
+ @xmlName("myStructureList")
+ structureList: StructureList
+}
+
+list RenamedListMembers {
+ @xmlName("item")
+ member: String,
+}
+
+list StructureList {
+ @xmlName("item")
+ member: StructureListMember,
+}
+
+structure StructureListMember {
+ @xmlName("value")
+ a: String,
+
+ @xmlName("other")
+ b: String,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/document-maps.smithy b/smithy-aws-protocol-tests/model/rest-xml/document-maps.smithy
new file mode 100644
index 00000000000..eb395efce8f
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/document-maps.smithy
@@ -0,0 +1,356 @@
+// This file defines test cases that serialize maps in XML payloads.
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// The example tests basic map serialization.
+@http(uri: "/XmlMaps", method: "POST")
+operation XmlMaps(XmlMapsInputOutput) -> XmlMapsInputOutput
+
+apply XmlMaps @httpRequestTests([
+ {
+ id: "XmlMaps",
+ description: "Serializes XML maps",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlMaps",
+ body: """
+
+
+
+ foo
+
+ there
+
+
+
+ baz
+
+ bye
+
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ foo: {
+ hi: "there"
+ },
+ baz: {
+ hi: "bye"
+ }
+ }
+ }
+ }
+])
+
+apply XmlMaps @httpResponseTests([
+ {
+ id: "XmlMaps",
+ description: "Serializes XML maps",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+
+
+ foo
+
+ there
+
+
+
+ baz
+
+ bye
+
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ foo: {
+ hi: "there"
+ },
+ baz: {
+ hi: "bye"
+ }
+ }
+ }
+ }
+])
+
+structure XmlMapsInputOutput {
+ myMap: XmlMapsInputOutputMap,
+}
+
+map XmlMapsInputOutputMap {
+ key: String,
+ value: GreetingStruct
+}
+
+structure GreetingStruct {
+ hi: String
+}
+
+// This example tests maps with @xmlName on members.
+@http(uri: "/XmlMapsXmlName", method: "POST")
+operation XmlMapsXmlName(XmlMapsXmlNameInputOutput) -> XmlMapsXmlNameInputOutput
+
+apply XmlMapsXmlName @httpRequestTests([
+ {
+ id: "XmlMapsXmlName",
+ description: "Serializes XML maps that have xmlName on members",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlMapsXmlName",
+ body: """
+
+
+
+ foo
+
+ there
+
+
+
+ baz
+
+ bye
+
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ foo: {
+ hi: "there"
+ },
+ baz: {
+ hi: "bye"
+ }
+ }
+ }
+ }
+])
+
+apply XmlMapsXmlName @httpResponseTests([
+ {
+ id: "XmlMapsXmlName",
+ description: "Serializes XML lists",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+
+
+ foo
+
+ there
+
+
+
+ baz
+
+ bye
+
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ foo: {
+ hi: "there"
+ },
+ baz: {
+ hi: "bye"
+ }
+ }
+ }
+ }
+])
+
+structure XmlMapsXmlNameInputOutput {
+ myMap: XmlMapsXmlNameInputOutputMap,
+}
+
+map XmlMapsXmlNameInputOutputMap {
+ @xmlName("Attribute")
+ key: String,
+
+ @xmlName("Setting")
+ value: GreetingStruct
+}
+
+/// Flattened maps
+@http(uri: "/FlattenedXmlMap", method: "POST")
+operation FlattenedXmlMap(FlattenedXmlMapInputOutput) -> FlattenedXmlMapInputOutput
+
+apply FlattenedXmlMap @httpRequestTests([
+ {
+ id: "FlattenedXmlMap",
+ description: "Serializes flattened XML maps in requests",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/FlattenedXmlMap",
+ body: """
+
+
+ foo
+ Foo
+
+
+ baz
+ Baz
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ foo: "Foo",
+ baz: "Baz"
+ }
+ }
+ }
+])
+
+apply FlattenedXmlMap @httpResponseTests([
+ {
+ id: "FlattenedXmlMap",
+ description: "Serializes flattened XML maps in responses",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+
+ foo
+ Foo
+
+
+ baz
+ Baz
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ foo: "Foo",
+ baz: "Baz"
+ }
+ }
+ }
+])
+
+structure FlattenedXmlMapInputOutput {
+ @xmlFlattened
+ myMap: FooEnumMap,
+}
+
+/// Flattened maps with @xmlName
+@http(uri: "/FlattenedXmlMapWithXmlName", method: "POST")
+operation FlattenedXmlMapWithXmlName(FlattenedXmlMapWithXmlNameInputOutput) -> FlattenedXmlMapWithXmlNameInputOutput
+
+apply FlattenedXmlMapWithXmlName @httpRequestTests([
+ {
+ id: "FlattenedXmlMapWithXmlName",
+ description: "Serializes flattened XML maps in requests that have xmlName on members",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/FlattenedXmlMapWithXmlName",
+ body: """
+
+
+ a
+ A
+
+
+ b
+ B
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ a: "A",
+ b: "B",
+ }
+ }
+ }
+])
+
+apply FlattenedXmlMapWithXmlName @httpResponseTests([
+ {
+ id: "FlattenedXmlMapWithXmlName",
+ description: "Serializes flattened XML maps in responses that have xmlName on members",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+
+ a
+ A
+
+
+ b
+ B
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ myMap: {
+ a: "A",
+ b: "B",
+ }
+ }
+ }
+])
+
+structure FlattenedXmlMapWithXmlNameInputOutput {
+ @xmlFlattened
+ @xmlName("KVP")
+ myMap: FlattenedXmlMapWithXmlNameInputOutputMap,
+}
+
+map FlattenedXmlMapWithXmlNameInputOutputMap {
+ @xmlName("K")
+ key: String,
+
+ @xmlName("V")
+ value: String,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/document-structs.smithy b/smithy-aws-protocol-tests/model/rest-xml/document-structs.smithy
new file mode 100644
index 00000000000..d7eeb6298c5
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/document-structs.smithy
@@ -0,0 +1,645 @@
+// This file defines test cases that serialize synthesized XML documents
+// in the payload of HTTP requests and responses.
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+// This example serializes simple scalar types in the top level XML document.
+// Note that headers are not serialized in the payload.
+@idempotent
+@http(uri: "/SimpleScalarProperties", method: "PUT")
+operation SimpleScalarProperties(SimpleScalarPropertiesInputOutput) -> SimpleScalarPropertiesInputOutput
+
+apply SimpleScalarProperties @httpRequestTests([
+ {
+ id: "SimpleScalarProperties",
+ description: "Serializes simple scalar properties",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/SimpleScalarProperties",
+ body: """
+
+ string
+ true
+ false
+ 1
+ 2
+ 3
+ 4
+ 5.5
+ 6.5
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml",
+ "X-Foo": "Foo",
+ },
+ params: {
+ foo: "Foo",
+ stringValue: "string",
+ trueBooleanValue: true,
+ falseBooleanValue: false,
+ byteValue: 1,
+ shortValue: 2,
+ integerValue: 3,
+ longValue: 4,
+ floatValue: 5.5,
+ doubleValue: 6.5,
+ }
+ }
+])
+
+apply SimpleScalarProperties @httpResponseTests([
+ {
+ id: "SimpleScalarProperties",
+ description: "Serializes simple scalar properties",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ string
+ true
+ false
+ 1
+ 2
+ 3
+ 4
+ 5.5
+ 6.5
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml",
+ "X-Foo": "Foo",
+ },
+ params: {
+ foo: "Foo",
+ stringValue: "string",
+ trueBooleanValue: true,
+ falseBooleanValue: false,
+ byteValue: 1,
+ shortValue: 2,
+ integerValue: 3,
+ longValue: 4,
+ floatValue: 5.5,
+ doubleValue: 6.5,
+ }
+ }
+])
+
+structure SimpleScalarPropertiesInputOutput {
+ @httpHeader("X-Foo")
+ foo: String,
+
+ stringValue: String,
+ trueBooleanValue: Boolean,
+ falseBooleanValue: Boolean,
+ byteValue: Byte,
+ shortValue: Short,
+ integerValue: Integer,
+ longValue: Long,
+ floatValue: Float,
+
+ @xmlName("DoubleDribble")
+ doubleValue: Double,
+}
+
+/// Blobs are base64 encoded
+@http(uri: "/XmlBlobs", method: "POST")
+operation XmlBlobs(XmlBlobsInputOutput) -> XmlBlobsInputOutput
+
+apply XmlBlobs @httpRequestTests([
+ {
+ id: "XmlBlobs",
+ description: "Blobs are base64 encoded",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlBlobs",
+ body: """
+
+ dmFsdWU=
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ data: "value"
+ }
+ }
+])
+
+apply XmlBlobs @httpResponseTests([
+ {
+ id: "XmlBlobs",
+ description: "Blobs are base64 encoded",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ dmFsdWU=
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ data: "value"
+ }
+ }
+])
+
+structure XmlBlobsInputOutput {
+ data: Blob
+}
+
+/// This tests how timestamps are serialized, including using the
+/// default format of date-time and various @timestampFormat trait
+/// values.
+@http(uri: "/XmlTimestamps", method: "POST")
+operation XmlTimestamps(XmlTimestampsInputOutput) -> XmlTimestampsInputOutput
+
+apply XmlTimestamps @httpRequestTests([
+ {
+ id: "XmlTimestamps",
+ description: "Tests how normal timestamps are serialized",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlTimestamps",
+ body: """
+
+ 2014-04-29T18:30:38Z
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ normal: 1398796238
+ }
+ },
+ {
+ id: "XmlTimestampsWithDateTimeFormat",
+ description: "Ensures that the timestampFormat of date-time works like normal timestamps",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlTimestamps",
+ body: """
+
+ 2014-04-29T18:30:38Z
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ dateTime: 1398796238
+ }
+ },
+ {
+ id: "XmlTimestampsWithEpochSecondsFormat",
+ description: "Ensures that the timestampFormat of epoch-seconds works",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlTimestamps",
+ body: """
+
+ 1398796238
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ epochSeconds: 1398796238
+ }
+ },
+ {
+ id: "XmlTimestampsWithHttpDateFormat",
+ description: "Ensures that the timestampFormat of http-date works",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlTimestamps",
+ body: """
+
+ Tue, 29 Apr 2014 18:30:38 GMT
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ httpDate: 1398796238
+ }
+ },
+])
+
+apply XmlTimestamps @httpResponseTests([
+ {
+ id: "XmlTimestamps",
+ description: "Tests how normal timestamps are serialized",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ 2014-04-29T18:30:38Z
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ normal: 1398796238
+ }
+ },
+ {
+ id: "XmlTimestampsWithDateTimeFormat",
+ description: "Ensures that the timestampFormat of date-time works like normal timestamps",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ 2014-04-29T18:30:38Z
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ dateTime: 1398796238
+ }
+ },
+ {
+ id: "XmlTimestampsWithEpochSecondsFormat",
+ description: "Ensures that the timestampFormat of epoch-seconds works",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ 1398796238
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ epochSeconds: 1398796238
+ }
+ },
+ {
+ id: "XmlTimestampsWithHttpDateFormat",
+ description: "Ensures that the timestampFormat of http-date works",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ Tue, 29 Apr 2014 18:30:38 GMT
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ httpDate: 1398796238
+ }
+ },
+])
+
+structure XmlTimestampsInputOutput {
+ normal: Timestamp,
+
+ @timestampFormat("date-time")
+ dateTime: Timestamp,
+
+ @timestampFormat("epoch-seconds")
+ epochSeconds: Timestamp,
+
+ @timestampFormat("http-date")
+ httpDate: Timestamp,
+}
+
+/// This example serializes enums as top level properties, in lists, sets, and maps.
+@idempotent
+@http(uri: "/XmlEnums", method: "PUT")
+operation XmlEnums(XmlEnumsInputOutput) -> XmlEnumsInputOutput
+
+apply XmlEnums @httpRequestTests([
+ {
+ id: "XmlEnums",
+ description: "Serializes simple scalar properties",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/XmlEnums",
+ body: """
+
+ Foo
+ 0
+ 1
+
+ Foo
+ 0
+
+
+ Foo
+ 0
+
+
+
+ hi
+ Foo
+
+
+ zero
+ 0
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ fooEnum1: "Foo",
+ fooEnum2: "0",
+ fooEnum3: "1",
+ fooEnumList: ["Foo", "0"],
+ fooEnumSet: ["Foo", "0"],
+ fooEnumMap: {
+ "hi": "Foo",
+ "zero": "0"
+ }
+ }
+ }
+])
+
+apply XmlEnums @httpResponseTests([
+ {
+ id: "XmlEnums",
+ description: "Serializes simple scalar properties",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ Foo
+ 0
+ 1
+
+ Foo
+ 0
+
+
+ Foo
+ 0
+
+
+
+ hi
+ Foo
+
+
+ zero
+ 0
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ fooEnum1: "Foo",
+ fooEnum2: "0",
+ fooEnum3: "1",
+ fooEnumList: ["Foo", "0"],
+ fooEnumSet: ["Foo", "0"],
+ fooEnumMap: {
+ "hi": "Foo",
+ "zero": "0"
+ }
+ }
+ }
+])
+
+structure XmlEnumsInputOutput {
+ fooEnum1: FooEnum,
+ fooEnum2: FooEnum,
+ fooEnum3: FooEnum,
+ fooEnumList: FooEnumList,
+ fooEnumSet: FooEnumSet,
+ fooEnumMap: FooEnumMap,
+}
+
+/// Recursive shapes
+@idempotent
+@http(uri: "/RecursiveShapes", method: "PUT")
+operation RecursiveShapes(RecursiveShapesInputOutput) -> RecursiveShapesInputOutput
+
+apply RecursiveShapes @httpRequestTests([
+ {
+ id: "RecursiveShapes",
+ description: "Serializes recursive structures",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/XmlEnums",
+ body: """
+
+
+ Foo1
+
+ Bar1
+
+ Foo2
+
+ Bar2
+
+
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ foo: "Foo1",
+ nested: {
+ bar: "Bar1",
+ recursiveMember: {
+ foo: "Foo2",
+ nested: {
+ bar: "Bar2"
+ }
+ }
+ }
+ }
+ }
+ }
+])
+
+apply RecursiveShapes @httpResponseTests([
+ {
+ id: "RecursiveShapes",
+ description: "Serializes recursive structures",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+
+ Foo1
+
+ Bar1
+
+ Foo2
+
+ Bar2
+
+
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ foo: "Foo1",
+ nested: {
+ bar: "Bar1",
+ recursiveMember: {
+ foo: "Foo2",
+ nested: {
+ bar: "Bar2"
+ }
+ }
+ }
+ }
+ }
+ }
+])
+
+structure RecursiveShapesInputOutput {
+ nested: RecursiveShapesInputOutputNested1
+}
+
+structure RecursiveShapesInputOutputNested1 {
+ foo: String,
+ nested: RecursiveShapesInputOutputNested2
+}
+
+structure RecursiveShapesInputOutputNested2 {
+ bar: String,
+ recursiveMember: RecursiveShapesInputOutputNested1,
+}
+
+// XML namespace
+@http(uri: "/XmlNamespaces", method: "POST")
+operation XmlNamespaces(XmlNamespacesInputOutput) -> XmlNamespacesInputOutput
+
+apply XmlNamespaces @httpRequestTests([
+ {
+ id: "XmlNamespaces",
+ description: "Serializes XML namespaces",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/XmlNamespaces",
+ body: """
+
+
+ Foo
+
+ Bar
+ Baz
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ foo: "Foo",
+ values: [
+ "Bar",
+ "Baz"
+ ]
+ }
+ }
+ }
+])
+
+apply XmlNamespaces @httpResponseTests([
+ {
+ id: "XmlNamespaces",
+ description: "Serializes XML namespaces",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+
+ Foo
+
+ Bar
+ Baz
+
+
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ foo: "Foo",
+ values: [
+ "Bar",
+ "Baz"
+ ]
+ }
+ }
+ }
+])
+
+@xmlNamespace(uri: "http://foo.com")
+structure XmlNamespacesInputOutput {
+ nested: XmlNamespaceNested
+}
+
+// Ingored since it's not at the top-level
+@xmlNamespace(uri: "http://foo.com")
+structure XmlNamespaceNested {
+ @xmlNamespace(uri: "http://baz.com", prefix: "baz")
+ foo: String,
+
+ @xmlNamespace(uri: "http://qux.com")
+ values: XmlNamespacedList
+}
+
+list XmlNamespacedList {
+ @xmlNamespace(uri: "http://bux.com")
+ member: String,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/document-xml-attributes.smithy b/smithy-aws-protocol-tests/model/rest-xml/document-xml-attributes.smithy
new file mode 100644
index 00000000000..b73816902d6
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/document-xml-attributes.smithy
@@ -0,0 +1,125 @@
+// This file defines test cases that serialize XML attributes.
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// This example serializes an XML attributes on synthesized document.
+@idempotent
+@http(uri: "/XmlAttributes", method: "PUT")
+operation XmlAttributes(XmlAttributesInputOutput) -> XmlAttributesInputOutput
+
+apply XmlAttributes @httpRequestTests([
+ {
+ id: "XmlAttributes",
+ description: "Serializes XML attributes on the synthesized document",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/XmlAttributes",
+ body: """
+
+ hi
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ foo: "hi",
+ attr: "test"
+ }
+ }
+])
+
+apply XmlAttributes @httpResponseTests([
+ {
+ id: "XmlAttributes",
+ description: "Serializes simple scalar properties",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ hi
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ foo: "hi",
+ attr: "test"
+ }
+ }
+])
+
+structure XmlAttributesInputOutput {
+ foo: String,
+
+ @xmlAttribute
+ @xmlName("test")
+ attr: String,
+}
+
+/// This example serializes an XML attributes on a document targeted by httpPayload.
+@idempotent
+@http(uri: "/XmlAttributesOnPayload", method: "PUT")
+operation XmlAttributesOnPayload(XmlAttributesOnPayloadInputOutput) -> XmlAttributesOnPayloadInputOutput
+
+apply XmlAttributesOnPayload @httpRequestTests([
+ {
+ id: "XmlAttributesOnPayload",
+ description: "Serializes XML attributes on the synthesized document",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/XmlAttributesOnPayload",
+ body: """
+
+ hi
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ payload: {
+ foo: "hi",
+ attr: "test"
+ }
+ }
+ }
+])
+
+apply XmlAttributesOnPayload @httpResponseTests([
+ {
+ id: "XmlAttributesOnPayload",
+ description: "Serializes simple scalar properties",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ hi
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ payload: {
+ foo: "hi",
+ attr: "test"
+ }
+ }
+ }
+])
+
+structure XmlAttributesOnPayloadInputOutput {
+ @httpPayload
+ payload: XmlAttributesInputOutput
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/empty-input-output.smithy b/smithy-aws-protocol-tests/model/rest-xml/empty-input-output.smithy
new file mode 100644
index 00000000000..0d1f6bf32cf
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/empty-input-output.smithy
@@ -0,0 +1,97 @@
+// This file defines test cases that test the basics of empty input and
+// output shape serialization.
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// The example tests how requests and responses are serialized when there's
+/// no request or response payload because the operation has no input or output.
+/// While this should be rare, code generators must support this.
+@http(uri: "/NoInputAndNoOutput", method: "POST")
+operation NoInputAndNoOutput()
+
+apply NoInputAndNoOutput @httpRequestTests([
+ {
+ id: "NoInputAndNoOutput",
+ description: "No input serializes no payload",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/NoInputAndOutput",
+ body: ""
+ }
+])
+
+apply NoInputAndNoOutput @httpResponseTests([
+ {
+ id: "NoInputAndNoOutput",
+ description: "No output serializes no payload",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: ""
+ }
+])
+
+/// The example tests how requests and responses are serialized when there's
+/// no request or response payload because the operation has no input and the
+/// output is empty. While this should be rare, code generators must support
+/// this.
+@http(uri: "/NoInputAndOutputOutput", method: "POST")
+operation NoInputAndOutput() -> NoInputAndOutputOutput
+
+apply NoInputAndOutput @httpRequestTests([
+ {
+ id: "NoInputAndOutput",
+ description: "No input serializes no payload",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/NoInputAndOutput",
+ body: ""
+ }
+])
+
+apply NoInputAndOutput @httpResponseTests([
+ {
+ id: "NoInputAndOutput",
+ description: "Empty output serializes no payload",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: ""
+ }
+])
+
+structure NoInputAndOutputOutput {}
+
+/// The example tests how requests and responses are serialized when there's
+/// no request or response payload because the operation has an empty input
+/// and empty output structure that reuses the same shape. While this should
+/// be rare, code generators must support this.
+@http(uri: "/EmptyInputAndEmptyOutput", method: "POST")
+operation EmptyInputAndEmptyOutput(EmptyInputAndEmptyOutputInput) -> EmptyInputAndEmptyOutputOutput
+
+apply EmptyInputAndEmptyOutput @httpRequestTests([
+ {
+ id: "EmptyInputAndEmptyOutput",
+ description: "Empty input serializes no payload",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/EmptyInputAndEmptyOutput",
+ body: ""
+ },
+])
+
+apply EmptyInputAndEmptyOutput @httpResponseTests([
+ {
+ id: "EmptyInputAndEmptyOutput",
+ description: "Empty output serializes no payload",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: ""
+ },
+])
+
+structure EmptyInputAndEmptyOutputInput {}
+structure EmptyInputAndEmptyOutputOutput {}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/endpoint-host-trait.smithy b/smithy-aws-protocol-tests/model/rest-xml/endpoint-host-trait.smithy
new file mode 100644
index 00000000000..bce723721f6
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/endpoint-host-trait.smithy
@@ -0,0 +1,11 @@
+// This file defines test cases that change the endpoint based on the endpoint trait.
+// See: https://awslabs.github.io/smithy/spec/core.html#endpoint-trait
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+// TODO: Write endpoint tests
diff --git a/smithy-aws-protocol-tests/model/rest-xml/errors.smithy b/smithy-aws-protocol-tests/model/rest-xml/errors.smithy
new file mode 100644
index 00000000000..5b3a92926a5
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/errors.smithy
@@ -0,0 +1,127 @@
+// This file defines test cases that test error serialization.
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// This operation has three possible return values:
+///
+/// 1. A successful response in the form of GreetingWithErrorsOutput
+/// 2. An InvalidGreeting error.
+/// 3. A BadRequest error.
+///
+/// Implementations must be able to successfully take a response and
+/// properly (de)serialize successful and error responses based on the
+/// the presence of the
+@idempotent
+@http(uri: "/GreetingWithErrors", method: "PUT")
+operation GreetingWithErrors() -> GreetingWithErrorsOutput errors [InvalidGreeting, ComplexError]
+
+apply GreetingWithErrors @httpResponseTests([
+ {
+ id: "GreetingWithErrors",
+ description: "Ensures that operations with errors successfully know how to deserialize the successful response",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: "",
+ headers: {
+ "X-Greeting": "Hello"
+ },
+ params: {
+ greeting: "Hello"
+ }
+ }
+])
+
+structure GreetingWithErrorsOutput {
+ @httpHeader("X-Greeting")
+ greeting: String,
+}
+
+/// This error is thrown when an invalid greeting value is provided.
+@error("client")
+@httpError(400)
+structure InvalidGreeting {
+ Message: String,
+}
+
+apply InvalidGreeting @httpResponseTests([
+ {
+ id: "InvalidGreetingError",
+ description: "Parses simple XML errors",
+ protocol: "aws.rest-xml",
+ params: {
+ Message: "Hi"
+ },
+ code: 400,
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ body: """
+
+
+ Sender
+ InvalidGreeting
+ Hi
+ setting
+
+ foo-id
+
+ """,
+ bodyMediaType: "application/xml",
+ }
+])
+
+/// This error is thrown when a request is invalid.
+@error("client")
+@httpError(403)
+structure ComplexError {
+ // Errors support HTTP bindings!
+ @httpHeader("X-Header")
+ Header: String,
+
+ TopLevel: String,
+
+ Nested: ComplexNestedErrorData,
+}
+
+apply ComplexError @httpResponseTests([
+ {
+ id: "ComplexError",
+ protocol: "aws.rest-xml",
+ params: {
+ Header: "Header",
+ TopLevel: "Top level",
+ Nested: {
+ Foo: "bar"
+ }
+ },
+ code: 400,
+ headers: {
+ "Content-Type": "application/xml",
+ "X-Header": "Header",
+ },
+ body: """
+
+
+ Sender
+ ComplexError
+ Hi
+ Top level
+
+ bar
+
+
+ foo-id
+
+ """,
+ bodyMediaType: "application/xml",
+ }
+])
+
+structure ComplexNestedErrorData {
+ Foo: String,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/http-headers.smithy b/smithy-aws-protocol-tests/model/rest-xml/http-headers.smithy
new file mode 100644
index 00000000000..23f5e6921c6
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/http-headers.smithy
@@ -0,0 +1,385 @@
+// This file defines test cases that test HTTP header bindings.
+// See: https://awslabs.github.io/smithy/spec/http.html#httpheader-trait
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// The example tests how requests and responses are serialized when there is
+/// no input or output payload but there are HTTP header bindings.
+@http(uri: "/InputAndOutputWithHeaders", method: "POST")
+operation InputAndOutputWithHeaders(InputAndOutputWithHeadersIO) -> InputAndOutputWithHeadersIO
+
+apply InputAndOutputWithHeaders @httpRequestTests([
+ {
+ id: "InputAndOutputWithStringHeaders",
+ description: "Tests requests with string header bindings",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/InputAndOutputWithHeaders",
+ headers: {
+ "X-String": "Hello",
+ "X-StringList": "a, b, c",
+ "X-StringSet": "a, b, c"
+ },
+ body: "",
+ params: {
+ headerString: "Hello",
+ headerStringList: ["a", "b", "c"],
+ headerStringSet: ["a", "b", "c"],
+ }
+ },
+ {
+ id: "InputAndOutputWithNumericHeaders",
+ description: "Tests requests with numeric header bindings",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/InputAndOutputWithHeaders",
+ headers: {
+ "X-Byte": "1",
+ "X-Short": "123",
+ "X-Integer": "123",
+ "X-Long": "123",
+ "X-Float": "1.0",
+ "X-Double": "1.0",
+ "X-HeaderIntegerList": "1, 2, 3",
+ },
+ body: "",
+ params: {
+ headerByte: 1,
+ headerShort: 123,
+ headerInteger: 123,
+ headerLong: 123,
+ headerFloat: 1.0,
+ headerDouble: 1.0,
+ headerIntegerList: [1, 2, 3],
+ }
+ },
+ {
+ id: "InputAndOutputWithBooleanHeaders",
+ description: "Tests requests with boolean header bindings",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/InputAndOutputWithHeaders",
+ headers: {
+ "X-Boolean1": "true",
+ "X-Boolean2": "false",
+ "X-HeaderBooleanList": "true, false, true"
+ },
+ body: "",
+ params: {
+ headerTrueBool: true,
+ headerFalseBool: true,
+ headerBooleanList: [true, false, true]
+ }
+ },
+ {
+ id: "InputAndOutputWithTimestampHeaders",
+ description: "Tests requests with timestamp header bindings",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/InputAndOutputWithHeaders",
+ headers: {
+ "X-HeaderTimestampList": "Mon, 16 Dec 2019 23:48:18 GMT, Mon, 16 Dec 2019 23:48:18 GMT"
+ },
+ body: "",
+ params: {
+ headerTimestampList: [1576540098, 1576540098]
+ }
+ },
+ {
+ id: "InputAndOutputWithEnumHeaders",
+ description: "Tests requests with enum header bindings",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/InputAndOutputWithHeaders",
+ headers: {
+ "X-Enum": "Foo",
+ "X-EnumList": "Foo, Baz, Bar"
+ },
+ body: "",
+ params: {
+ headerEnum: "Foo",
+ headerEnumList: ["Foo", "Bar", "Baz"],
+ }
+ },
+])
+
+apply InputAndOutputWithHeaders @httpResponseTests([
+ {
+ id: "InputAndOutputWithStringHeaders",
+ description: "Tests responses with string header bindings",
+ protocol: "aws.rest-xml",
+ code: 200,
+ headers: {
+ "X-String": "Hello",
+ "X-StringList": "a, b, c",
+ "X-StringSet": "a, b, c"
+ },
+ body: "",
+ params: {
+ headerString: "Hello",
+ headerStringList: ["a", "b", "c"],
+ headerStringSet: ["a", "b", "c"],
+ }
+ },
+ {
+ id: "InputAndOutputWithNumericHeaders",
+ description: "Tests responses with numeric header bindings",
+ protocol: "aws.rest-xml",
+ code: 200,
+ headers: {
+ "X-Byte": "1",
+ "X-Short": "123",
+ "X-Integer": "123",
+ "X-Long": "123",
+ "X-Float": "1.0",
+ "X-Double": "1.0",
+ "X-HeaderIntegerList": "1, 2, 3",
+ },
+ body: "",
+ params: {
+ headerByte: 1,
+ headerShort: 123,
+ headerInteger: 123,
+ headerLong: 123,
+ headerFloat: 1.0,
+ headerDouble: 1.0,
+ headerIntegerList: [1, 2, 3],
+ }
+ },
+ {
+ id: "InputAndOutputWithBooleanHeaders",
+ description: "Tests responses with boolean header bindings",
+ protocol: "aws.rest-xml",
+ code: 200,
+ headers: {
+ "X-Boolean1": "true",
+ "X-Boolean2": "false",
+ "X-HeaderBooleanList": "true, false, true"
+ },
+ body: "",
+ params: {
+ headerTrueBool: true,
+ headerFalseBool: true,
+ headerBooleanList: [true, false, true]
+ }
+ },
+ {
+ id: "InputAndOutputWithTimestampHeaders",
+ description: "Tests responses with timestamp header bindings",
+ protocol: "aws.rest-xml",
+ code: 200,
+ headers: {
+ "X-HeaderTimestampList": "Mon, 16 Dec 2019 23:48:18 GMT, Mon, 16 Dec 2019 23:48:18 GMT"
+ },
+ body: "",
+ params: {
+ headerTimestampList: [1576540098, 1576540098]
+ }
+ },
+ {
+ id: "InputAndOutputWithEnumHeaders",
+ description: "Tests responses with enum header bindings",
+ protocol: "aws.rest-xml",
+ code: 200,
+ headers: {
+ "X-Enum": "Foo",
+ "X-EnumList": "Foo, Baz, Bar"
+ },
+ body: "",
+ params: {
+ headerEnum: "Foo",
+ headerEnumList: ["Foo", "Bar", "Baz"],
+ }
+ },
+])
+
+structure InputAndOutputWithHeadersIO {
+ @httpHeader("X-String")
+ headerString: String,
+
+ @httpHeader("X-Byte")
+ headerByte: Byte,
+
+ @httpHeader("X-Short")
+ headerShort: Short,
+
+ @httpHeader("X-Integer")
+ headerInteger: Integer,
+
+ @httpHeader("X-Long")
+ headerLong: Long,
+
+ @httpHeader("X-Float")
+ headerFloat: Float,
+
+ @httpHeader("X-Double")
+ headerDouble: Double,
+
+ @httpHeader("X-Boolean1")
+ headerTrueBool: Boolean,
+
+ @httpHeader("X-Boolean2")
+ headerFalseBool: Boolean,
+
+ @httpHeader("X-StringList")
+ headerStringList: StringList,
+
+ @httpHeader("X-StringSet")
+ headerStringSet: StringSet,
+
+ @httpHeader("X-IntegerList")
+ headerIntegerList: IntegerList,
+
+ @httpHeader("X-BooleanList")
+ headerBooleanList: BooleanList,
+
+ @httpHeader("X-TimestampList")
+ headerTimestampList: TimestampList,
+
+ @httpHeader("X-Enum")
+ headerEnum: FooEnum,
+
+ @httpHeader("X-EnumList")
+ headerEnumList: FooEnumList,
+}
+
+/// Null and empty headers are not sent over the wire.
+@readonly
+@http(uri: "/NullAndEmptyHeaders", method: "GET")
+operation NullAndEmptyHeaders(NullAndEmptyHeadersIO) -> NullAndEmptyHeadersIO
+
+apply NullAndEmptyHeaders @httpRequestTests([
+ {
+ id: "NullAndEmptyHeaders",
+ description: "Do not send null values, empty strings, or empty lists over the wire in headers",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/NullAndEmptyHeaders",
+ forbidHeaders: ["X-A", "X-B", "X-C"],
+ body: "",
+ params: {
+ a: null,
+ b: "",
+ c: [],
+ }
+ },
+])
+
+apply NullAndEmptyHeaders @httpResponseTests([
+ {
+ id: "NullAndEmptyHeaders",
+ description: "Do not send null or empty headers",
+ protocol: "aws.rest-xml",
+ code: 200,
+ forbidHeaders: ["X-A", "X-B", "X-C"],
+ body: "",
+ params: {
+ a: null,
+ b: "",
+ c: [],
+ }
+ },
+])
+
+structure NullAndEmptyHeadersIO {
+ @httpHeader("X-A")
+ a: String,
+
+ @httpHeader("X-B")
+ b: String,
+
+ @httpHeader("X-C")
+ c: StringList,
+}
+
+/// The example tests how timestamp request and response headers are serialized.
+@http(uri: "/TimestampFormatHeaders", method: "POST")
+operation TimestampFormatHeaders(TimestampFormatHeadersIO) -> TimestampFormatHeadersIO
+
+apply TimestampFormatHeaders @httpRequestTests([
+ {
+ id: "TimestampFormatHeaders",
+ description: "Tests how timestamp request headers are serialized",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/TimestampFormatHeaders",
+ headers: {
+ "X-memberEpochSeconds": "1576540098",
+ "X-memberHttpDate": "Mon, 16 Dec 2019 23:48:18 GMT",
+ "X-memberDateTime": "2019-12-16T23:48:18Z",
+ "X-defaultFormat": "Mon, 16 Dec 2019 23:48:18 GMT",
+ "X-targetEpochSeconds": "1576540098",
+ "X-targetHttpDate": "Mon, 16 Dec 2019 23:48:18 GMT",
+ "X-targetDateTime": "2019-12-16T23:48:18Z",
+ },
+ body: "",
+ params: {
+ memberEpochSeconds: 1576540098,
+ memberHttpDate: 1576540098,
+ memberDateTime: 1576540098,
+ defaultFormat: 1576540098,
+ targetEpochSeconds: 1576540098,
+ targetHttpDate: 1576540098,
+ targetDateTime: 1576540098,
+ }
+ },
+])
+
+apply TimestampFormatHeaders @httpResponseTests([
+ {
+ id: "TimestampFormatHeaders",
+ description: "Tests how timestamp response headers are serialized",
+ protocol: "aws.rest-xml",
+ code: 200,
+ headers: {
+ "X-memberEpochSeconds": "1576540098",
+ "X-memberHttpDate": "Mon, 16 Dec 2019 23:48:18 GMT",
+ "X-memberDateTime": "2019-12-16T23:48:18Z",
+ "X-defaultFormat": "Mon, 16 Dec 2019 23:48:18 GMT",
+ "X-targetEpochSeconds": "1576540098",
+ "X-targetHttpDate": "Mon, 16 Dec 2019 23:48:18 GMT",
+ "X-targetDateTime": "2019-12-16T23:48:18Z",
+ },
+ body: "",
+ params: {
+ memberEpochSeconds: 1576540098,
+ memberHttpDate: 1576540098,
+ memberDateTime: 1576540098,
+ defaultFormat: 1576540098,
+ targetEpochSeconds: 1576540098,
+ targetHttpDate: 1576540098,
+ targetDateTime: 1576540098,
+ }
+ },
+])
+
+structure TimestampFormatHeadersIO {
+ @httpHeader("X-memberEpochSeconds")
+ @timestampFormat("epoch-seconds")
+ memberEpochSeconds: Timestamp,
+
+ @httpHeader("X-memberHttpDate")
+ @timestampFormat("http-date")
+ memberHttpDate: Timestamp,
+
+ @httpHeader("X-memberDateTime")
+ @timestampFormat("date-time")
+ memberDateTime: Timestamp,
+
+ @httpHeader("X-defaultFormat")
+ defaultFormat: Timestamp,
+
+ @httpHeader("X-targetEpochSeconds")
+ targetEpochSeconds: EpochSeconds,
+
+ @httpHeader("X-targetHttpDate")
+ targetHttpDate: HttpDate,
+
+ @httpHeader("X-targetDateTime")
+ targetDateTime: HttpDate,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/http-labels.smithy b/smithy-aws-protocol-tests/model/rest-xml/http-labels.smithy
new file mode 100644
index 00000000000..352b0ac8358
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/http-labels.smithy
@@ -0,0 +1,169 @@
+// This file defines test cases that test HTTP URI label bindings.
+// See: https://awslabs.github.io/smithy/spec/http.html#httplabel-trait
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// The example tests how requests are serialized when there's no input
+/// payload but there are HTTP labels.
+@readonly
+@http(method: "GET", uri: "/HttpRequestWithLabels/{string}/{short}/{integer}/{long}/{float}/{double}/{boolean}/{timestamp}")
+operation HttpRequestWithLabels(HttpRequestWithLabelsInput)
+
+apply HttpRequestWithLabels @httpRequestTests([
+ {
+ id: "InputWithHeadersAndAllParams",
+ description: "Sends a GET request that uses URI label bindings",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/HttpRequestWithLabels/string/1/2/3/4.0/5.0/true/2019-12-16T23%3A48%3A18Z",
+ body: "",
+ params: {
+ string: "string",
+ short: 1,
+ integer: 2,
+ long: 3,
+ float: 4.0,
+ double: 5.0,
+ boolean: true,
+ timestamp: 1576540098
+ }
+ },
+])
+
+structure HttpRequestWithLabelsInput {
+ @httpLabel
+ @required
+ string: String,
+
+ @httpLabel
+ @required
+ short: Short,
+
+ @httpLabel
+ @required
+ integer: Integer,
+
+ @httpLabel
+ @required
+ long: Long,
+
+ @httpLabel
+ @required
+ float: Float,
+
+ @httpLabel
+ @required
+ double: Double,
+
+ /// Serialized in the path as true or false.
+ @httpLabel
+ @required
+ boolean: Boolean,
+
+ /// Note that this member has no format, so it's serialized as an RFC 3399 date-time.
+ @httpLabel
+ @required
+ timestamp: Timestamp,
+}
+
+/// The example tests how requests serialize different timestamp formats in the
+/// URI path.
+@readonly
+@http(method: "GET", uri: "/HttpRequestWithLabelsAndTimestampFormat/{memberEpochSeconds}/{memberHttpDate}/{memberDateTime}/{defaultFormat}/{targetEpochSeconds}/{targetHttpDate}/{targetDateTime}")
+operation HttpRequestWithLabelsAndTimestampFormat(HttpRequestWithLabelsAndTimestampFormatInput)
+
+apply HttpRequestWithLabelsAndTimestampFormat @httpRequestTests([
+ {
+ id: "HttpRequestWithLabelsAndTimestampFormat",
+ description: "Serializes different timestamp formats in URI labels",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: """
+ /HttpRequestWithLabelsAndTimestampFormat\
+ /1576540098\
+ /Mon%2C+16+Dec+2019+23%3A48%3A18+GMT\
+ /2019-12-16T23%3A48%3A18Z\
+ /2019-12-16T23%3A48%3A18Z\
+ /1576540098\
+ /Mon%2C+16+Dec+2019+23%3A48%3A18+GMT\
+ /2019-12-16T23%3A48%3A18Z""",
+ body: "",
+ params: {
+ memberEpochSeconds: 1576540098,
+ memberHttpDate: 1576540098,
+ memberDateTime: 1576540098,
+ defaultFormat: 1576540098,
+ targetEpochSeconds: 1576540098,
+ targetHttpDate: 1576540098,
+ targetDateTime: 1576540098,
+ }
+ },
+])
+
+structure HttpRequestWithLabelsAndTimestampFormatInput {
+ @httpLabel
+ @required
+ @timestampFormat("epoch-seconds")
+ memberEpochSeconds: Timestamp,
+
+ @httpLabel
+ @required
+ @timestampFormat("http-date")
+ memberHttpDate: Timestamp,
+
+ @httpLabel
+ @required
+ @timestampFormat("date-time")
+ memberDateTime: Timestamp,
+
+ @httpLabel
+ @required
+ defaultFormat: Timestamp,
+
+ @httpLabel
+ @required
+ targetEpochSeconds: EpochSeconds,
+
+ @httpLabel
+ @required
+ targetHttpDate: HttpDate,
+
+ @httpLabel
+ @required
+ targetDateTime: HttpDate,
+}
+
+// This example uses a greedy label and a normal label.
+@readonly
+@http(method: "GET", uri: "/HttpRequestWithGreedyLabelInPath/foo/{foo}/baz/{baz+}")
+operation HttpRequestWithGreedyLabelInPath(HttpRequestWithGreedyLabelInPathInput)
+
+apply HttpRequestWithGreedyLabelInPath @httpRequestTests([
+ {
+ id: "HttpRequestWithGreedyLabelInPath",
+ description: "Serializes greedy labels and normal labels",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/HttpRequestWithGreedyLabelInPath/foo/hello/baz/there/guy",
+ body: "",
+ params: {
+ foo: "hello",
+ baz: "there/guy",
+ }
+ },
+])
+
+structure HttpRequestWithGreedyLabelInPathInput {
+ @httpLabel
+ @required
+ foo: String,
+
+ @httpLabel
+ @required
+ baz: String,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/http-payload.smithy b/smithy-aws-protocol-tests/model/rest-xml/http-payload.smithy
new file mode 100644
index 00000000000..15e82c25942
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/http-payload.smithy
@@ -0,0 +1,381 @@
+// This file defines test cases that test HTTP payload bindings.
+// See: https://awslabs.github.io/smithy/spec/http.html#httppayload-trait
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// This examples serializes a blob shape in the payload.
+///
+/// In this example, no XML document is synthesized because the payload is
+/// not a structure or a union type.
+@http(uri: "/HttpPayloadTraits", method: "POST")
+operation HttpPayloadTraits(HttpPayloadTraitsInputOutput) -> HttpPayloadTraitsInputOutput
+
+apply HttpPayloadTraits @httpRequestTests([
+ {
+ id: "HttpPayloadTraitsWithBlob",
+ description: "Serializes a blob in the HTTP payload",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/HttpPayloadTraits",
+ body: "blobby blob blob",
+ headers: {
+ "X-Foo": "Foo"
+ },
+ params: {
+ foo: "Foo",
+ blob: "blobby blob blob"
+ }
+ },
+ {
+ id: "HttpPayloadTraitsWithNoBlobBody",
+ description: "Serializes an empty blob in the HTTP payload",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/HttpPayloadTraits",
+ body: "",
+ headers: {
+ "X-Foo": "Foo"
+ },
+ params: {
+ foo: "Foo"
+ }
+ },
+])
+
+apply HttpPayloadTraits @httpResponseTests([
+ {
+ id: "HttpPayloadTraitsWithBlob",
+ description: "Serializes a blob in the HTTP payload",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: "blobby blob blob",
+ headers: {
+ "X-Foo": "Foo"
+ },
+ params: {
+ foo: "Foo",
+ blob: "blobby blob blob"
+ }
+ },
+ {
+ id: "HttpPayloadTraitsWithNoBlobBody",
+ description: "Serializes an empty blob in the HTTP payload",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: "",
+ headers: {
+ "X-Foo": "Foo"
+ },
+ params: {
+ foo: "Foo"
+ }
+ }
+])
+
+structure HttpPayloadTraitsInputOutput {
+ @httpHeader("X-Foo")
+ foo: String,
+
+ @httpPayload
+ blob: Blob,
+}
+
+/// This examples uses a `@mediaType` trait on the payload to force a custom
+/// content-type to be serialized.
+@http(uri: "/HttpPayloadTraitsWithMediaType", method: "POST")
+operation HttpPayloadTraitsWithMediaType(HttpPayloadTraitsWithMediaTypeInputOutput) -> HttpPayloadTraitsWithMediaTypeInputOutput
+
+apply HttpPayloadTraitsWithMediaType @httpRequestTests([
+ {
+ id: "HttpPayloadTraitsWithMediaTypeWithBlob",
+ description: "Serializes a blob in the HTTP payload with a content-type",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/HttpPayloadTraitsWithMediaType",
+ body: "blobby blob blob",
+ headers: {
+ "X-Foo": "Foo",
+ "Content-Type": "text/plain"
+ },
+ params: {
+ foo: "Foo",
+ blob: "blobby blob blob"
+ }
+ }
+])
+
+apply HttpPayloadTraitsWithMediaType @httpResponseTests([
+ {
+ id: "HttpPayloadTraitsWithMediaTypeWithBlob",
+ description: "Serializes a blob in the HTTP payload with a content-type",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: "blobby blob blob",
+ headers: {
+ "X-Foo": "Foo",
+ "Content-Type": "text/plain"
+ },
+ params: {
+ foo: "Foo",
+ blob: "blobby blob blob"
+ }
+ }
+])
+
+structure HttpPayloadTraitsWithMediaTypeInputOutput {
+ @httpHeader("X-Foo")
+ foo: String,
+
+ @httpPayload
+ blob: TextPlainBlob,
+}
+
+/// This examples serializes a structure in the payload.
+///
+/// Note that serializing a structure changes the wrapper element name
+/// to match the targeted structure.
+@idempotent
+@http(uri: "/HttpPayloadWithStructure", method: "PUT")
+operation HttpPayloadWithStructure(HttpPayloadWithStructureInputOutput) -> HttpPayloadWithStructureInputOutput
+
+apply HttpPayloadWithStructure @httpRequestTests([
+ {
+ id: "HttpPayloadWithStructure",
+ description: "Serializes a structure in the payload",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/HttpPayloadWithStructure",
+ body: """
+
+ hello
+ Phreddy
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ greeting: "hello",
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+apply HttpPayloadWithStructure @httpResponseTests([
+ {
+ id: "HttpPayloadWithStructure",
+ description: "Serializes a structure in the payload",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ hello
+ Phreddy
+
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ greeting: "hello",
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+structure HttpPayloadWithStructureInputOutput {
+ @httpPayload
+ nested: NestedPayload,
+}
+
+structure NestedPayload {
+ greeting: String,
+ name: String,
+}
+
+/// The following example serializes a payload that uses an XML name,
+/// changing the wrapper name.
+@idempotent
+@http(uri: "/HttpPayloadWithXmlName", method: "PUT")
+operation HttpPayloadWithXmlName(HttpPayloadWithXmlNameInputOutput) -> HttpPayloadWithXmlNameInputOutput
+
+apply HttpPayloadWithXmlName @httpRequestTests([
+ {
+ id: "HttpPayloadWithXmlName",
+ description: "Serializes a structure in the payload using a wrapper name based on xmlName",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/HttpPayloadWithStructure",
+ body: "Phreddy",
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+apply HttpPayloadWithXmlName @httpResponseTests([
+ {
+ id: "HttpPayloadWithXmlName",
+ description: "Serializes a structure in the payload using a wrapper name based on xmlName",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: "Phreddy",
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+structure HttpPayloadWithXmlNameInputOutput {
+ @httpPayload
+ nested: PayloadWithXmlName,
+}
+
+@xmlName("Hello")
+structure PayloadWithXmlName {
+ name: String
+}
+
+/// The following example serializes a payload that uses an XML namespace.
+@idempotent
+@http(uri: "/HttpPayloadWithXmlNamespace", method: "PUT")
+operation HttpPayloadWithXmlNamespace(HttpPayloadWithXmlNamespaceInputOutput) -> HttpPayloadWithXmlNamespaceInputOutput
+
+apply HttpPayloadWithXmlNamespace @httpRequestTests([
+ {
+ id: "HttpPayloadWithXmlNamespace",
+ description: "Serializes a structure in the payload using a wrapper with an XML namespace",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/HttpPayloadWithXmlNamespace",
+ body: """
+
+ Phreddy
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+apply HttpPayloadWithXmlNamespace @httpResponseTests([
+ {
+ id: "HttpPayloadWithXmlNamespace",
+ description: "Serializes a structure in the payload using a wrapper with an XML namespace",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ Phreddy
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+structure HttpPayloadWithXmlNamespaceInputOutput {
+ @httpPayload
+ nested: PayloadWithXmlNamespace,
+}
+
+@xmlNamespace(uri: "http://foo.com")
+structure PayloadWithXmlNamespace {
+ name: String
+}
+
+/// The following example serializes a payload that uses an XML namespace.
+@idempotent
+@http(uri: "/HttpPayloadWithXmlNamespaceAndPrefix", method: "PUT")
+operation HttpPayloadWithXmlNamespaceAndPrefix(HttpPayloadWithXmlNamespaceAndPrefixInputOutput)
+ -> HttpPayloadWithXmlNamespaceAndPrefixInputOutput
+
+apply HttpPayloadWithXmlNamespaceAndPrefix @httpRequestTests([
+ {
+ id: "HttpPayloadWithXmlNamespaceAndPrefix",
+ description: "Serializes a structure in the payload using a wrapper with an XML namespace",
+ protocol: "aws.rest-xml",
+ method: "PUT",
+ uri: "/HttpPayloadWithXmlNamespaceAndPrefix",
+ body: """
+
+ Phreddy
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+apply HttpPayloadWithXmlNamespaceAndPrefix @httpResponseTests([
+ {
+ id: "HttpPayloadWithXmlNamespaceAndPrefix",
+ description: "Serializes a structure in the payload using a wrapper with an XML namespace",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: """
+
+ Phreddy
+ """,
+ bodyMediaType: "application/xml",
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ params: {
+ nested: {
+ name: "Phreddy"
+ }
+ }
+ }
+])
+
+structure HttpPayloadWithXmlNamespaceAndPrefixInputOutput {
+ @httpPayload
+ nested: PayloadWithXmlNamespaceAndPrefix,
+}
+
+@xmlNamespace(uri: "http://foo.com", prefix: "baz")
+structure PayloadWithXmlNamespaceAndPrefix {
+ name: String
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/http-prefix-headers.smithy b/smithy-aws-protocol-tests/model/rest-xml/http-prefix-headers.smithy
new file mode 100644
index 00000000000..c8a05347958
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/http-prefix-headers.smithy
@@ -0,0 +1,102 @@
+// This file defines test cases that test httpPrefix headers.
+// See: https://awslabs.github.io/smithy/spec/http.html#httpprefixheaders-trait
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// This examples adds headers to the input of a request and response by prefix.
+@readonly
+@http(uri: "/HttpPrefixHeaders", method: "GET")
+@externalDocumentation("https://awslabs.github.io/smithy/spec/http.html#httpprefixheaders-trait")
+operation HttpPrefixHeaders(HttpPrefixHeadersInputOutput) -> HttpPrefixHeadersInputOutput
+
+apply HttpPrefixHeaders @httpRequestTests([
+ {
+ id: "HttpPrefixHeadersArePresent",
+ description: "Adds headers by prefix",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/HttpPrefixHeaders",
+ body: "",
+ headers: {
+ "X-Foo": "Foo",
+ "X-Foo-Abc": "Abc value",
+ "X-Foo-Def": "Def value",
+ },
+ params: {
+ foo: "Foo",
+ fooMap: {
+ Abc: "Abc value",
+ Def: "Def value",
+ }
+ }
+ },
+ {
+ id: "HttpPrefixHeadersAreNotPresent",
+ description: "No prefix headers are serialized because the value is empty",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/HttpPrefixHeaders",
+ body: "",
+ headers: {
+ "X-Foo": "Foo"
+ },
+ params: {
+ foo: "Foo",
+ fooMap: {}
+ }
+ },
+])
+
+apply HttpPrefixHeaders @httpResponseTests([
+ {
+ id: "HttpPrefixHeadersArePresent",
+ description: "Adds headers by prefix",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: "",
+ headers: {
+ "X-Foo": "Foo",
+ "X-Foo-Abc": "Abc value",
+ "X-Foo-Def": "Def value",
+ },
+ params: {
+ foo: "Foo",
+ fooMap: {
+ Abc: "Abc value",
+ Def: "Def value",
+ }
+ }
+ },
+ {
+ id: "HttpPrefixHeadersAreNotPresent",
+ description: "No prefix headers are serialized because the value is empty",
+ protocol: "aws.rest-xml",
+ code: 200,
+ body: "",
+ headers: {
+ "X-Foo": "Foo"
+ },
+ params: {
+ foo: "Foo",
+ fooMap: {}
+ }
+ },
+])
+
+structure HttpPrefixHeadersInputOutput {
+ @httpHeader("X-Foo")
+ foo: String,
+
+ @httpPrefixHeaders("X-Foo-")
+ fooMap: FooPrefixHeaders,
+}
+
+map FooPrefixHeaders {
+ key: String,
+ value: String,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/http-query.smithy b/smithy-aws-protocol-tests/model/rest-xml/http-query.smithy
new file mode 100644
index 00000000000..7f59a145754
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/http-query.smithy
@@ -0,0 +1,318 @@
+// This file defines test cases that test HTTP query string bindings.
+// See: https://awslabs.github.io/smithy/spec/http.html#httpquery-trait
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// This example uses all query string types.
+@readonly
+@http(uri: "/AllQueryStringTypesInput", method: "GET")
+operation AllQueryStringTypes(AllQueryStringTypesInput)
+
+apply AllQueryStringTypes @httpRequestTests([
+ {
+ id: "AllQueryStringTypes",
+ description: "Serializes query string parameters with all supported types",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/AllQueryStringTypes",
+ body: "",
+ queryParams: [
+ "String=Hello%20there",
+ "StringList=a",
+ "StringList=b",
+ "StringList=c",
+ "StringSet=a",
+ "StringSet=b",
+ "StringSet=c",
+ "Byte=1",
+ "Short=2",
+ "Integer=3",
+ "IntegerList=1",
+ "IntegerList=2",
+ "IntegerList=3",
+ "IntegerSet=1",
+ "IntegerSet=2",
+ "IntegerSet=3",
+ "Long=4",
+ "Float=1",
+ "Double=1",
+ "DoubleList=1.0",
+ "DoubleList=2.0",
+ "DoubleList=3.0",
+ "Boolean=true",
+ "BooleanList=true",
+ "BooleanList=false",
+ "BooleanList=true",
+ "Timestamp=1",
+ "TimestampList=1",
+ "TimestampList=2",
+ "TimestampList=3",
+ "Enum=Foo",
+ "EnumList=Foo",
+ "EnumList=Baz",
+ "EnumList=Bar",
+ ],
+ params: {
+ queryString: "Hello there",
+ queryStringList: ["a", "b", "c"],
+ queryStringSet: ["a", "b", "c"],
+ queryByte: 1,
+ queryShort: 2,
+ queryInteger: 3,
+ queryIntegerList: [1, 2, 3],
+ queryIntegerSet: [1, 2, 3],
+ queryLong: 4,
+ queryFloat: 1,
+ queryDouble: 1,
+ queryDoubleList: [1.0, 2.0, 3.0],
+ queryBoolean: true,
+ queryBooleanList: [true, false, true],
+ queryTimestamp: 1,
+ queryTimestampList: [1, 2, 3],
+ queryEnum: "Foo",
+ queryEnumList: ["Foo", "Baz", "Bar"],
+ }
+ }
+])
+
+structure AllQueryStringTypesInput {
+ @httpQuery("String")
+ queryString: String,
+
+ @httpQuery("StringList")
+ queryStringList: StringList,
+
+ @httpQuery("StringSet")
+ queryStringSet: StringSet,
+
+ @httpQuery("Byte")
+ queryByte: Byte,
+
+ @httpQuery("Short")
+ queryShort: Short,
+
+ @httpQuery("Integer")
+ queryInteger: Integer,
+
+ @httpQuery("IntegerList")
+ queryIntegerList: IntegerList,
+
+ @httpQuery("IntegerSet")
+ queryIntegerSet: IntegerSet,
+
+ @httpQuery("Long")
+ queryLong: Long,
+
+ @httpQuery("Float")
+ queryFloat: Float,
+
+ @httpQuery("Double")
+ queryDouble: Double,
+
+ @httpQuery("DoubleList")
+ queryDoubleList: DoubleList,
+
+ @httpQuery("Boolean")
+ queryBoolean: Boolean,
+
+ @httpQuery("BooleanList")
+ queryBooleanList: BooleanList,
+
+ @httpQuery("Timestamp")
+ queryTimestamp: Timestamp,
+
+ @httpQuery("TimestampList")
+ queryTimestampList: TimestampList,
+
+ @httpQuery("Enum")
+ queryEnum: FooEnum,
+
+ @httpQuery("EnumList")
+ queryEnumList: FooEnumList,
+}
+
+/// This example uses a constant query string parameters and a label.
+/// This simply tests that labels and query string parameters are
+/// compatible. The fixed query string parameter named "hello" should
+/// in no way conflict with the label, `{hello}`.
+@readonly
+@http(uri: "/ConstantQueryString/{hello}?foo=bar&hello", method: "GET")
+@httpRequestTests([
+ {
+ id: "ConstantQueryString",
+ description: "Includes constant query string parameters",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/ConstantQueryString/hi",
+ queryParams: [
+ "foo=bar",
+ "hello",
+ ],
+ body: "",
+ params: {
+ hello: "hi"
+ }
+ },
+])
+operation ConstantQueryString(ConstantQueryStringInput)
+
+structure ConstantQueryStringInput {
+ @httpLabel
+ @required
+ hello: String,
+}
+
+/// This example uses fixed query string params and variable query string params.
+/// The fixed query string parameters and variable parameters must both be
+/// serialized (implementations may need to merge them together).
+@readonly
+@http(uri: "/ConstantAndVariableQueryString?foo=bar", method: "GET")
+operation ConstantAndVariableQueryString(ConstantAndVariableQueryStringInput)
+
+apply ConstantAndVariableQueryString @httpRequestTests([
+ {
+ id: "ConstantAndVariableQueryStringMissingOneValue",
+ description: "Mixes constant and variable query string parameters",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/ConstantAndVariableQueryString",
+ queryParams: [
+ "foo=bar",
+ "baz=bam",
+ ],
+ forbidQueryParams: ["maybeSet"],
+ body: "",
+ params: {
+ baz: "bam"
+ }
+ },
+ {
+ id: "ConstantAndVariableQueryStringAllValues",
+ description: "Mixes constant and variable query string parameters",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/ConstantAndVariableQueryString",
+ queryParams: [
+ "foo=bar",
+ "baz=bam",
+ "maybeSet=yes"
+ ],
+ body: "",
+ params: {
+ baz: "bam",
+ maybeSet: "yes"
+ }
+ },
+])
+
+structure ConstantAndVariableQueryStringInput {
+ @httpQuery("baz")
+ baz: String,
+
+ @httpQuery("maybeSet")
+ maybeSet: String,
+}
+
+/// This example ensures that query string bound request parameters are
+/// serialized in the body of responses if the structure is used in both
+/// the request and response.
+@readonly
+@http(uri: "/IgnoreQueryParamsInResponse", method: "GET")
+operation IgnoreQueryParamsInResponse() -> IgnoreQueryParamsInResponseOutput
+
+apply IgnoreQueryParamsInResponse @httpResponseTests([
+ {
+ id: "IgnoreQueryParamsInResponse",
+ description: "Query parameters must be ignored when serializing the output of an operation",
+ protocol: "aws.rest-xml",
+ code: 200,
+ headers: {
+ "Content-Type": "application/xml"
+ },
+ body: "bam",
+ bodyMediaType: "xml",
+ params: {
+ baz: "bam"
+ }
+ }
+])
+
+structure IgnoreQueryParamsInResponseOutput {
+ @httpQuery("baz")
+ baz: String
+}
+
+/// Omits null, but serializes empty string value.
+@readonly
+@http(uri: "/OmitsNullSerializesEmptyString", method: "GET")
+operation OmitsNullSerializesEmptyString(OmitsNullSerializesEmptyStringInput)
+
+apply OmitsNullSerializesEmptyString @httpRequestTests([
+ {
+ id: "OmitsNullSerializesEmptyString",
+ description: "Serializes empty query strings but omits null",
+ protocol: "aws.rest-xml",
+ method: "GET",
+ uri: "/OmitsNullSerializesEmptyString",
+ body: "",
+ queryParams: [
+ "Empty=",
+ ],
+ params: {
+ nullValue: null,
+ emptyString: "",
+ }
+ }
+])
+
+structure OmitsNullSerializesEmptyStringInput {
+ @httpQuery("Null")
+ nullValue: String,
+
+ @httpQuery("Empty")
+ emptyString: String,
+}
+
+/// Automatically adds idempotency tokens.
+@http(uri: "/QueryIdempotencyTokenAutoFill", method: "POST")
+@tags(["client-only"])
+operation QueryIdempotencyTokenAutoFill(QueryIdempotencyTokenAutoFillInput)
+
+apply QueryIdempotencyTokenAutoFill @httpRequestTests([
+ {
+ id: "QueryIdempotencyTokenAutoFill",
+ description: "Automatically adds idempotency token when not set",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/QueryIdempotencyTokenAutoFill",
+ body: "",
+ queryParams: [
+ "token=00000000-0000-4000-8000-000000000000",
+ ]
+ },
+ {
+ id: "QueryIdempotencyTokenAutoFillIsSet",
+ description: "Uses the given idempotency token as-is",
+ protocol: "aws.rest-xml",
+ method: "POST",
+ uri: "/QueryIdempotencyTokenAutoFill",
+ body: "",
+ queryParams: [
+ "token=00000000-0000-4000-8000-000000000123",
+ ],
+ params: {
+ token: "00000000-0000-4000-8000-000000000123"
+ }
+ }
+])
+
+structure QueryIdempotencyTokenAutoFillInput {
+ @httpQuery("token")
+ @idempotencyToken
+ token: String,
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/main.smithy b/smithy-aws-protocol-tests/model/rest-xml/main.smithy
new file mode 100644
index 00000000000..8c32f6fa9be
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/main.smithy
@@ -0,0 +1,68 @@
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+/// A REST XML service that sends XML requests and responses.
+@protocols([{"name": "aws.rest-xml"}])
+service RestXml {
+ version: "2019-12-16",
+ operations: [
+ // Basic input and output tests
+ NoInputAndNoOutput,
+ NoInputAndOutput,
+ EmptyInputAndEmptyOutput,
+
+ // @httpHeader tests
+ InputAndOutputWithHeaders,
+ NullAndEmptyHeaders,
+ TimestampFormatHeaders,
+
+ // @httpLabel tests
+ HttpRequestWithLabels,
+ HttpRequestWithLabelsAndTimestampFormat,
+ HttpRequestWithGreedyLabelInPath,
+
+ // @httpQuery tests
+ AllQueryStringTypes,
+ ConstantQueryString,
+ ConstantAndVariableQueryString,
+ IgnoreQueryParamsInResponse,
+ OmitsNullSerializesEmptyString,
+ QueryIdempotencyTokenAutoFill,
+
+ // @httpPrefixHeaders tests
+ HttpPrefixHeaders,
+
+ // @httpPayload tests
+ HttpPayloadTraits,
+ HttpPayloadTraitsWithMediaType,
+ HttpPayloadWithStructure,
+ HttpPayloadWithXmlName,
+ HttpPayloadWithXmlNamespace,
+ HttpPayloadWithXmlNamespaceAndPrefix,
+
+ // Errors
+ GreetingWithErrors,
+
+ // Synthesized XML document body tests
+ SimpleScalarProperties,
+ XmlTimestamps,
+ XmlEnums,
+ RecursiveShapes,
+ XmlLists,
+ XmlMaps,
+ XmlMapsXmlName,
+ FlattenedXmlMap,
+ FlattenedXmlMapWithXmlName,
+
+ // @xmlAttribute tests
+ XmlAttributes,
+ XmlAttributesOnPayload,
+
+ // @xmlNamespace trait tests
+ XmlNamespaces,
+ ]
+}
diff --git a/smithy-aws-protocol-tests/model/rest-xml/shared-types.smithy b/smithy-aws-protocol-tests/model/rest-xml/shared-types.smithy
new file mode 100644
index 00000000000..2b227276881
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rest-xml/shared-types.smithy
@@ -0,0 +1,76 @@
+// This file contains shared types that are used throughout the rest-xml
+// test cases. Anything that is generic enough that it could potentially
+// be reused should be defined in this file. However, things like input
+// or output structures or other test-case specific shapes should be
+// defined closer to the test case and in its same file.
+
+$version: "0.5.0"
+
+namespace aws.protocols.tests.restxml
+
+list StringList {
+ member: String,
+}
+
+set StringSet {
+ member: String,
+}
+
+/// A list of lists of strings.
+list NestedStringList {
+ member: StringList,
+}
+
+list IntegerList {
+ member: Integer,
+}
+
+set IntegerSet {
+ member: Integer,
+}
+
+list DoubleList {
+ member: Double,
+}
+
+list BooleanList {
+ member: PrimitiveBoolean,
+}
+
+list TimestampList {
+ member: Timestamp,
+}
+
+@enum(
+ Foo: {},
+ Baz: {},
+ Bar: {},
+ "1": {},
+ "0": {},
+)
+string FooEnum
+
+list FooEnumList {
+ member: FooEnum,
+}
+
+set FooEnumSet {
+ member: FooEnum,
+}
+
+map FooEnumMap {
+ key: String,
+ value: FooEnum,
+}
+
+@timestampFormat("date-time")
+timestamp DateTime
+
+@timestampFormat("epoch-seconds")
+timestamp EpochSeconds
+
+@timestampFormat("http-date")
+timestamp HttpDate
+
+@mediaType("text/plain")
+blob TextPlainBlob