Skip to content

Commit

Permalink
convert header name to lower case because DittoHeaderDefinition key e…
Browse files Browse the repository at this point in the history
…ntries are all lower case;

add test case;

Signed-off-by: Stefan Maute <stefan.maute@bosch.io>
  • Loading branch information
Stefan Maute committed Apr 27, 2020
1 parent db1b0ef commit d600821
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 55 deletions.
Expand Up @@ -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.
*
Expand All @@ -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);
}

Expand Down
Expand Up @@ -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.
Expand Down Expand Up @@ -73,15 +73,6 @@ public static Route ensureValidContentType(final Set<String> 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.
Expand All @@ -95,13 +86,21 @@ protected static String requestToLogString(final HttpRequest request) {
* @return the extracted media-type.
* @see <a href="https://doc.akka.io/docs/akka-http/current/common/http-model.html#http-headers">Akkas Header model</a>
*/
protected static String extractMediaType(HttpRequest request) {
private static String extractMediaType(final HttpRequest request) {
final Optional<HttpHeader> 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());
}

}
Expand Up @@ -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
Expand All @@ -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;

Expand All @@ -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();

Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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
Expand All @@ -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);
}
}
Expand Up @@ -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<String> additionalAcceptedMediaTypes;
private final Set<HeaderDefinition> queryParamsAsHeaders;
private final Set<String> additionalAcceptedMediaTypes;

private GatewayHttpConfig(final DefaultHttpConfig basicHttpConfig, final ScopedConfig scopedConfig) {
hostname = basicHttpConfig.getHostname();
Expand Down Expand Up @@ -203,10 +203,9 @@ public Set<String> getAdditionalAcceptedMediaTypes() {
return additionalAcceptedMediaTypes;
}


@SuppressWarnings("OverlyComplexMethod")
@Override
public boolean equals(@Nullable final Object o) {
public boolean equals(final Object o) {
if (this == o) {
return true;
}
Expand Down
Expand Up @@ -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<String> getAdditionalAcceptedMediaTypes();

Expand Down Expand Up @@ -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());

Expand Down
Expand Up @@ -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(),
Expand All @@ -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");
}
}

0 comments on commit d600821

Please sign in to comment.