diff --git a/model/base/src/main/java/org/eclipse/ditto/model/base/exceptions/UnsupportedMediaTypeException.java b/model/base/src/main/java/org/eclipse/ditto/model/base/exceptions/UnsupportedMediaTypeException.java index 8806f4ca46..c32f721b39 100644 --- a/model/base/src/main/java/org/eclipse/ditto/model/base/exceptions/UnsupportedMediaTypeException.java +++ b/model/base/src/main/java/org/eclipse/ditto/model/base/exceptions/UnsupportedMediaTypeException.java @@ -43,7 +43,6 @@ public final class UnsupportedMediaTypeException extends DittoRuntimeException { private static final HttpStatusCode STATUS_CODE = HttpStatusCode.UNSUPPORTED_MEDIA_TYPE; - /** * Constructs a new {@code UnsupportedMediaTypeException} object. * @@ -54,11 +53,11 @@ public final class UnsupportedMediaTypeException extends DittoRuntimeException { * @param href a link to a resource which provides further information about the exception. * @throws NullPointerException if {@code dittoHeaders} is {@code null}. */ - protected UnsupportedMediaTypeException(final DittoHeaders dittoHeaders, - @Nullable final String message, - @Nullable final String description, - @Nullable final Throwable cause, - @Nullable final URI href) { + private UnsupportedMediaTypeException(final DittoHeaders dittoHeaders, + @Nullable final String message, + @Nullable final String description, + @Nullable final Throwable cause, + @Nullable final URI href) { super(ERROR_CODE, STATUS_CODE, dittoHeaders, message, description, cause, href); } diff --git a/services/gateway/endpoints/src/main/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirective.java b/services/gateway/endpoints/src/main/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirective.java index 75a7184bec..47aeeb8e10 100644 --- a/services/gateway/endpoints/src/main/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirective.java +++ b/services/gateway/endpoints/src/main/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirective.java @@ -32,19 +32,19 @@ import akka.http.javadsl.server.Route; /** - * Used to validate the Content-Type of a request. + * Used to validate the content-type of a http request. */ public final class ContentTypeValidationDirective { private static final Logger LOGGER = LoggerFactory.getLogger(ContentTypeValidationDirective.class); private ContentTypeValidationDirective() { - // no op + throw new AssertionError(); } /** - * verifies that the content-type of the entity is one of the allowed media-types, - * otherwise the request will be completed with 415 ("Unsupported Media Type"). + * Verifies that the content-type of the entity is one of the allowed media-types, + * otherwise the request will be completed with status code 415 ("Unsupported Media Type"). * * @param supportedMediaTypes the media-type which are allowed for the wrapped route. * @param ctx the context of the request. @@ -73,15 +73,6 @@ public static Route ensureValidContentType(final Set supportedMediaTypes }); } - protected static String requestToLogString(final HttpRequest request) { - final String msgPatten = "{0} {1} {2}"; - return MessageFormat.format(msgPatten, - request.getUri().getHost().address(), - request.method().value(), - request.getUri().getPathString()); - } - - /** * Uses either the raw-header or the content-type parsed by akka-http. * The parsed content-type is never null, because akka-http sets a default. @@ -95,13 +86,21 @@ protected static String requestToLogString(final HttpRequest request) { * @return the extracted media-type. * @see Akkas Header model */ - protected static String extractMediaType(HttpRequest request) { + private static String extractMediaType(final HttpRequest request) { final Optional rawContentType = StreamSupport.stream(request.getHeaders().spliterator(), false) - .filter(header -> header.name().equals(DittoHeaderDefinition.CONTENT_TYPE.getKey())) + .filter(header -> header.name().toLowerCase().equals(DittoHeaderDefinition.CONTENT_TYPE.getKey())) .findFirst(); return rawContentType.map(HttpHeader::value) .orElse(request.entity().getContentType().mediaType().toString()); } + private static String requestToLogString(final HttpRequest request) { + final String msgPatten = "{0} {1} {2}"; + return MessageFormat.format(msgPatten, + request.getUri().getHost().address(), + request.method().value(), + request.getUri().getPathString()); + } + } \ No newline at end of file diff --git a/services/gateway/endpoints/src/test/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirectiveTest.java b/services/gateway/endpoints/src/test/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirectiveTest.java index 9ea7c90da4..99c249d9ad 100644 --- a/services/gateway/endpoints/src/test/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirectiveTest.java +++ b/services/gateway/endpoints/src/test/java/org/eclipse/ditto/services/gateway/endpoints/directives/ContentTypeValidationDirectiveTest.java @@ -46,7 +46,7 @@ public class ContentTypeValidationDirectiveTest extends JUnitRouteTest { @Test public void testValidContentType() { // Arrange - DittoHeaders dittoHeaders = DittoHeaders.empty(); + final DittoHeaders dittoHeaders = DittoHeaders.empty(); final ContentType type = ContentTypes.APPLICATION_JSON; // Act @@ -63,7 +63,7 @@ public void testValidContentType() { @Test public void testNonValidContentType() { // Arrange - DittoHeaders dittoHeaders = DittoHeaders.empty(); + final DittoHeaders dittoHeaders = DittoHeaders.empty(); final String type = MediaTypes.APPLICATION_JSON.toString(); final ContentType differentType = ContentTypes.APPLICATION_X_WWW_FORM_URLENCODED; @@ -83,7 +83,7 @@ public void testNonValidContentType() { @Test public void testWithContentTypeWithoutCharset() { // Arrange - DittoHeaders dittoHeaders = DittoHeaders.empty(); + final DittoHeaders dittoHeaders = DittoHeaders.empty(); final String type = MediaTypes.TEXT_PLAIN.toString(); final ContentType typeMissingCharset = MediaTypes.TEXT_PLAIN.toContentTypeWithMissingCharset(); @@ -100,7 +100,7 @@ public void testWithContentTypeWithoutCharset() { @Test public void testWithoutEntityNoNPEExpected() { // Arrange - DittoHeaders dittoHeaders = DittoHeaders.empty(); + final DittoHeaders dittoHeaders = DittoHeaders.empty(); final String type = ContentTypes.APPLICATION_JSON.mediaType().toString(); final RequestContext mockedCtx = mock(RequestContext.class); @@ -118,7 +118,7 @@ public void testWithoutEntityNoNPEExpected() { @Test public void testExceptionContainsDittoHeaders() { // Arrange - DittoHeaders dittoHeaders = DittoHeaders.of(Map.of("someHeaderKey", "someHeaderVal")); + final DittoHeaders dittoHeaders = DittoHeaders.of(Map.of("someHeaderKey", "someHeaderVal")); final String type = ContentTypes.APPLICATION_JSON.mediaType().toString(); final RequestContext mockedCtx = mock(RequestContext.class); @@ -140,7 +140,7 @@ public void testExceptionContainsDittoHeaders() { @Test public void testWithNonParsableContentType() { // Arrange - DittoHeaders dittoHeaders = DittoHeaders.empty(); + final DittoHeaders dittoHeaders = DittoHeaders.empty(); final String nonParsableMediaType = "application-json"; // Act @@ -155,4 +155,21 @@ public void testWithNonParsableContentType() { result.assertStatusCode(StatusCodes.OK); } + @Test + public void testWithUpperCaseContentTypeHeaderName() { + // Arrange + final DittoHeaders dittoHeaders = DittoHeaders.empty(); + final String type = MediaTypes.APPLICATION_JSON.toString(); + + // Act + final TestRouteResult result = + testRoute(extractRequestContext( + ctx -> ensureValidContentType(Set.of(type), ctx, dittoHeaders, COMPLETE_OK))) + .run(HttpRequest.PUT("someUrl") + .addHeader(HttpHeader.parse("CONTENT-TYPE", type)) + .withEntity(ContentTypes.APPLICATION_OCTET_STREAM, "something".getBytes())); + + // Assert + result.assertStatusCode(StatusCodes.OK); + } } \ No newline at end of file diff --git a/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfig.java b/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfig.java index fbe9cb0fe8..e2915509c3 100644 --- a/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfig.java +++ b/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfig.java @@ -52,8 +52,8 @@ public final class GatewayHttpConfig implements HttpConfig { private final boolean enableCors; private final Duration requestTimeout; private final String actorPropsFactoryFullQualifiedClassname; - private final Set additionalAcceptedMediaTypes; private final Set queryParamsAsHeaders; + private final Set additionalAcceptedMediaTypes; private GatewayHttpConfig(final DefaultHttpConfig basicHttpConfig, final ScopedConfig scopedConfig) { hostname = basicHttpConfig.getHostname(); @@ -203,10 +203,9 @@ public Set getAdditionalAcceptedMediaTypes() { return additionalAcceptedMediaTypes; } - @SuppressWarnings("OverlyComplexMethod") @Override - public boolean equals(@Nullable final Object o) { + public boolean equals(final Object o) { if (this == o) { return true; } diff --git a/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/HttpConfig.java b/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/HttpConfig.java index 05e065b389..e4144eaf3f 100644 --- a/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/HttpConfig.java +++ b/services/gateway/util/src/main/java/org/eclipse/ditto/services/gateway/util/config/endpoints/HttpConfig.java @@ -99,6 +99,7 @@ public interface HttpConfig extends org.eclipse.ditto.services.base.config.http. * Whitelisted Media-Types, which should also be accepted by the endpoints besides the one they accept. * * @return media-types. + * @since 1.1.0 */ Set getAdditionalAcceptedMediaTypes(); @@ -163,6 +164,8 @@ enum GatewayHttpConfigValue implements KnownConfigValue { * additional media-types can be specified, which will also be accepted. Default value * 'application/octet-stream' is for unknown or not further specified payload and request without any * content-type declaration will also be mapped to this type by akka-http. + * + * @since 1.1.0 */ ADDITIONAL_ACCEPTED_MEDIA_TYPES("additional-accepted-media-types", MediaTypes.APPLICATION_OCTET_STREAM.toString()); diff --git a/services/gateway/util/src/test/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfigTest.java b/services/gateway/util/src/test/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfigTest.java index 20e78c5f60..5f7202447d 100644 --- a/services/gateway/util/src/test/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfigTest.java +++ b/services/gateway/util/src/test/java/org/eclipse/ditto/services/gateway/util/config/endpoints/GatewayHttpConfigTest.java @@ -32,31 +32,6 @@ public final class GatewayHttpConfigTest { @Rule public final JUnitSoftAssertions softly = new JUnitSoftAssertions(); - @Test - public void testMultipleCommaSeparatedMediaTypes() { - Config gatewayTestConfig = ConfigFactory.parseString("http {\n additional-accepted-media-types = " + - "\"application/json,application/x-www-form-urlencoded,text/plain\"\n}"); - - final GatewayHttpConfig underTest = GatewayHttpConfig.of(gatewayTestConfig); - - softly.assertThat(underTest.getAdditionalAcceptedMediaTypes()) - .as(HttpConfig.GatewayHttpConfigValue.ADDITIONAL_ACCEPTED_MEDIA_TYPES.getConfigPath()) - .contains(MediaTypes.APPLICATION_JSON.toString(), - MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED.toString(), - MediaTypes.TEXT_PLAIN.toString()); - } - - @Test - public void testNonParsableMediaType() { - Config gatewayTestConfig = ConfigFactory.parseString("http {\n additional-accepted-media-types = \"application-json\"\n}"); - - final GatewayHttpConfig underTest = GatewayHttpConfig.of(gatewayTestConfig); - - softly.assertThat(underTest.getAdditionalAcceptedMediaTypes()) - .as(HttpConfig.GatewayHttpConfigValue.ADDITIONAL_ACCEPTED_MEDIA_TYPES.getConfigPath()) - .contains("application-json"); - } - @Test public void assertImmutability() { assertInstancesOf(GatewayHttpConfig.class, areImmutable(), @@ -80,4 +55,29 @@ public void underTestReturnsDefaultValuesIfBaseConfigWasEmpty() { .as(HttpConfig.GatewayHttpConfigValue.ADDITIONAL_ACCEPTED_MEDIA_TYPES.getConfigPath()) .contains(MediaTypes.APPLICATION_OCTET_STREAM.toString()); } + + @Test + public void testMultipleCommaSeparatedMediaTypes() { + final Config gatewayTestConfig = ConfigFactory.parseString("http {\n additional-accepted-media-types = " + + "\"application/json,application/x-www-form-urlencoded,text/plain\"\n}"); + + final GatewayHttpConfig underTest = GatewayHttpConfig.of(gatewayTestConfig); + + softly.assertThat(underTest.getAdditionalAcceptedMediaTypes()) + .as(HttpConfig.GatewayHttpConfigValue.ADDITIONAL_ACCEPTED_MEDIA_TYPES.getConfigPath()) + .contains(MediaTypes.APPLICATION_JSON.toString(), + MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED.toString(), + MediaTypes.TEXT_PLAIN.toString()); + } + + @Test + public void testNonParsableMediaType() { + final Config gatewayTestConfig = ConfigFactory.parseString("http {\n additional-accepted-media-types = \"application-json\"\n}"); + + final GatewayHttpConfig underTest = GatewayHttpConfig.of(gatewayTestConfig); + + softly.assertThat(underTest.getAdditionalAcceptedMediaTypes()) + .as(HttpConfig.GatewayHttpConfigValue.ADDITIONAL_ACCEPTED_MEDIA_TYPES.getConfigPath()) + .contains("application-json"); + } } \ No newline at end of file