From ec280a89a2c75876afc3028331f27a948d973ec5 Mon Sep 17 00:00:00 2001 From: JordonPhillips Date: Fri, 5 Feb 2021 12:18:55 +0100 Subject: [PATCH] Add host validation to http request tests This adds the ability for the http request tests to model assertions on the host. This can be useful for asserting that the endpoint trait is properly supported, as well as allowing for testing things like AWS clients' ability to resolve hosts based on the service id and region. --- .../spec/http-protocol-compliance-tests.rst | 11 +++++++ .../traits/HttpRequestTestCase.java | 33 +++++++++++++++++++ .../META-INF/smithy/smithy.test.smithy | 11 +++++++ .../errorfiles/all-request-features.smithy | 2 ++ 4 files changed, 57 insertions(+) diff --git a/docs/source/1.0/spec/http-protocol-compliance-tests.rst b/docs/source/1.0/spec/http-protocol-compliance-tests.rst index a5ef5c1241c..014f336d38b 100644 --- a/docs/source/1.0/spec/http-protocol-compliance-tests.rst +++ b/docs/source/1.0/spec/http-protocol-compliance-tests.rst @@ -103,6 +103,17 @@ that support the following members: - ``string`` - **Required**. The request-target of the HTTP request, not including the query string (for example, "/foo/bar"). + * - host + - ``string`` + - The host / endpoint provided to the client, not including the path or + scheme (for example, "example.com"). + * - resolvedHost + - ``string`` + - The host / endpoint that the client should send to, not including the + path or scheme (for example, "prefix.example.com"). + + This can differ from the host provided to the client if, for instance, + the operation has a member with the :ref:`endpoint-trait`. * - authScheme - shape ID - A shape ID that specifies the optional authentication scheme to diff --git a/smithy-protocol-test-traits/src/main/java/software/amazon/smithy/protocoltests/traits/HttpRequestTestCase.java b/smithy-protocol-test-traits/src/main/java/software/amazon/smithy/protocoltests/traits/HttpRequestTestCase.java index 1f7150a5bd9..f59f65488f8 100644 --- a/smithy-protocol-test-traits/src/main/java/software/amazon/smithy/protocoltests/traits/HttpRequestTestCase.java +++ b/smithy-protocol-test-traits/src/main/java/software/amazon/smithy/protocoltests/traits/HttpRequestTestCase.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import software.amazon.smithy.model.node.ArrayNode; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; @@ -32,12 +33,16 @@ public final class HttpRequestTestCase extends HttpMessageTestCase implements To private static final String METHOD = "method"; private static final String URI = "uri"; + private static final String HOST = "host"; + private static final String RESOLVED_HOST = "resolvedHost"; private static final String QUERY_PARAMS = "queryParams"; private static final String FORBID_QUERY_PARAMS = "forbidQueryParams"; private static final String REQUIRE_QUERY_PARAMS = "requireQueryParams"; private final String method; private final String uri; + private final String host; + private final String resolvedHost; private final List queryParams; private final List forbidQueryParams; private final List requireQueryParams; @@ -46,6 +51,8 @@ private HttpRequestTestCase(Builder builder) { super(builder); method = SmithyBuilder.requiredState(METHOD, builder.method); uri = SmithyBuilder.requiredState(URI, builder.uri); + host = builder.host; + resolvedHost = builder.resolvedHost; queryParams = ListUtils.copyOf(builder.queryParams); forbidQueryParams = ListUtils.copyOf(builder.forbidQueryParams); requireQueryParams = ListUtils.copyOf(builder.requireQueryParams); @@ -59,6 +66,14 @@ public String getUri() { return uri; } + public Optional getHost() { + return Optional.ofNullable(host); + } + + public Optional getResolvedHost() { + return Optional.ofNullable(resolvedHost); + } + public List getQueryParams() { return queryParams; } @@ -77,6 +92,8 @@ public static HttpRequestTestCase fromNode(Node node) { ObjectNode o = node.expectObjectNode(); builder.method(o.expectStringMember(METHOD).getValue()); builder.uri(o.expectStringMember(URI).getValue()); + o.getStringMember(HOST).ifPresent(stringNode -> builder.host(stringNode.getValue())); + o.getStringMember(RESOLVED_HOST).ifPresent(stringNode -> builder.resolvedHost(stringNode.getValue())); o.getArrayMember(QUERY_PARAMS).ifPresent(queryParams -> { builder.queryParams(queryParams.getElementsAs(StringNode::getValue)); }); @@ -94,6 +111,8 @@ public Node toNode() { ObjectNode.Builder node = super.toNode().expectObjectNode().toBuilder(); node.withMember(METHOD, getMethod()); node.withMember(URI, getUri()); + node.withOptionalMember(HOST, getHost().map(StringNode::from)); + node.withOptionalMember(RESOLVED_HOST, getResolvedHost().map(StringNode::from)); if (!queryParams.isEmpty()) { node.withMember(QUERY_PARAMS, ArrayNode.fromStrings(getQueryParams())); } @@ -114,6 +133,8 @@ public Builder toBuilder() { .queryParams(getQueryParams()) .forbidQueryParams(getForbidQueryParams()) .requireQueryParams(getRequireQueryParams()); + getHost().ifPresent(builder::host); + getResolvedHost().ifPresent(builder::resolvedHost); updateBuilder(builder); return builder; } @@ -129,6 +150,8 @@ public static final class Builder extends HttpMessageTestCase.Builder queryParams = new ArrayList<>(); private final List forbidQueryParams = new ArrayList<>(); private final List requireQueryParams = new ArrayList<>(); @@ -145,6 +168,16 @@ public Builder uri(String uri) { return this; } + public Builder host(String host) { + this.host = host; + return this; + } + + public Builder resolvedHost(String resolvedHost) { + this.resolvedHost = resolvedHost; + return this; + } + public Builder queryParams(List queryParams) { this.queryParams.clear(); this.queryParams.addAll(queryParams); diff --git a/smithy-protocol-test-traits/src/main/resources/META-INF/smithy/smithy.test.smithy b/smithy-protocol-test-traits/src/main/resources/META-INF/smithy/smithy.test.smithy index dc8a8a7e008..f8db846d3ed 100644 --- a/smithy-protocol-test-traits/src/main/resources/META-INF/smithy/smithy.test.smithy +++ b/smithy-protocol-test-traits/src/main/resources/META-INF/smithy/smithy.test.smithy @@ -37,6 +37,17 @@ structure HttpRequestTestCase { @length(min: 1) uri: String, + /// The host / endpoint provided to the client, not including the path + /// or scheme (for example, "example.com"). + host: String, + + /// The host / endpoint that the client should send to, not including + /// the path or scheme (for example, "prefix.example.com"). + /// + /// This can differ from the host provided to the client if the `hostPrefix` + /// member of the `endpoint` trait is set, for instance. + resolvedHost: String, + /// The optional authentication scheme shape ID to assume. It's /// possible that specific authentication schemes might influence /// the serialization logic of an HTTP request. diff --git a/smithy-protocol-test-traits/src/test/resources/software/amazon/smithy/protocoltests/traits/errorfiles/all-request-features.smithy b/smithy-protocol-test-traits/src/test/resources/software/amazon/smithy/protocoltests/traits/errorfiles/all-request-features.smithy index f57e678a6cf..35a6800803d 100644 --- a/smithy-protocol-test-traits/src/test/resources/software/amazon/smithy/protocoltests/traits/errorfiles/all-request-features.smithy +++ b/smithy-protocol-test-traits/src/test/resources/software/amazon/smithy/protocoltests/traits/errorfiles/all-request-features.smithy @@ -19,6 +19,8 @@ structure testScheme {} authScheme: testScheme, method: "POST", uri: "/", + host: "example.com", + resolvedHost: "prefix.example.com", queryParams: ["foo=baz"], forbidQueryParams: ["Nope"], requireQueryParams: ["Yap"],