diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index 9dccb5286e34..6daa15d8eed0 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -239,6 +239,7 @@ + diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/SerializerEncoding.java b/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/SerializerEncoding.java index 4fdc7326e962..287a3a5c8bfe 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/SerializerEncoding.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/util/serializer/SerializerEncoding.java @@ -3,8 +3,11 @@ package com.azure.core.util.serializer; - import com.azure.core.http.HttpHeaders; +import com.azure.core.util.logging.ClientLogger; + +import java.util.Map; +import java.util.TreeMap; /** * Supported serialization encoding formats. @@ -20,21 +23,68 @@ public enum SerializerEncoding { */ XML; + private static final ClientLogger LOGGER = new ClientLogger(SerializerEncoding.class); + private static final String CONTENT_TYPE = "Content-Type"; + private static final Map SUPPORTED_MIME_TYPES; + private static final TreeMap SUPPORTED_SUFFIXES; + private static final SerializerEncoding DEFAULT_ENCODING = JSON; + + + static { + // Encodings and suffixes from: https://tools.ietf.org/html/rfc6838 + SUPPORTED_MIME_TYPES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + SUPPORTED_MIME_TYPES.put("text/xml", XML); + SUPPORTED_MIME_TYPES.put("application/xml", XML); + SUPPORTED_MIME_TYPES.put("application/json", JSON); + + SUPPORTED_SUFFIXES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + SUPPORTED_SUFFIXES.put("xml", XML); + SUPPORTED_SUFFIXES.put("json", JSON); + } + /** * Determines the serializer encoding to use based on the Content-Type header. * - * @param headers the headers to check the encoding for - * @return the serializer encoding to use for the body + * @param headers the headers to check the encoding for. + * @return the serializer encoding to use for the body. {@link #JSON} if there is no Content-Type header or an + * unrecognized Content-Type encoding is returned. */ public static SerializerEncoding fromHeaders(HttpHeaders headers) { - String mimeContentType = headers.getValue("Content-Type"); - if (mimeContentType != null) { - String[] parts = mimeContentType.split(";"); - if (parts[0].equalsIgnoreCase("application/xml") || parts[0].equalsIgnoreCase("text/xml")) { - return XML; - } + final String mimeContentType = headers.getValue(CONTENT_TYPE); + if (mimeContentType == null || mimeContentType.isEmpty()) { + LOGGER.warning("'{}' not found. Returning default encoding: {}", CONTENT_TYPE, DEFAULT_ENCODING); + return DEFAULT_ENCODING; + } + + final SerializerEncoding encoding = SUPPORTED_MIME_TYPES.get(mimeContentType); + if (encoding != null) { + return encoding; + } + + final String[] parts = mimeContentType.split(";"); + final String[] mimeTypeParts = parts[0].split("/"); + if (mimeTypeParts.length != 2) { + LOGGER.warning("Content-Type '{}' does not match mime-type formatting 'type'/'subtype'. " + + "Returning default: {}", parts[0], DEFAULT_ENCODING); + return DEFAULT_ENCODING; + } + + // Check the suffix if it does not match the full types. + final String subtype = mimeTypeParts[1]; + final int lastIndex = subtype.lastIndexOf("+"); + if (lastIndex == -1) { + return DEFAULT_ENCODING; + } + + final String mimeTypeSuffix = subtype.substring(lastIndex + 1); + final SerializerEncoding serializerEncoding = SUPPORTED_SUFFIXES.get(mimeTypeSuffix); + if (serializerEncoding != null) { + return serializerEncoding; } - return JSON; + LOGGER.warning("Content-Type '{}' does not match any supported one. Returning default: {}", + mimeContentType, DEFAULT_ENCODING); + + return DEFAULT_ENCODING; } } diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/util/serializer/SerializerEncodingTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/util/serializer/SerializerEncodingTests.java new file mode 100644 index 000000000000..06f425da099d --- /dev/null +++ b/sdk/core/azure-core/src/test/java/com/azure/core/util/serializer/SerializerEncodingTests.java @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.core.util.serializer; + +import com.azure.core.http.HttpHeaders; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Collections; + +/** + * Tests for {@link SerializerEncoding}. + */ +class SerializerEncodingTests { + private static final String CONTENT_TYPE = "Content-Type"; + + @ParameterizedTest + @ValueSource(strings = {"application/xml", "application/atom+xml", "text/xml", "application/foo+XML", "TEXT/XML"}) + void recognizeXml(String mimeType) { + // Arrange + HttpHeaders headers = new HttpHeaders(Collections.singletonMap(CONTENT_TYPE, mimeType)); + + // Act & Assert + Assertions.assertEquals(SerializerEncoding.XML, SerializerEncoding.fromHeaders(headers)); + } + + @ParameterizedTest + @ValueSource(strings = {"application/json", "application/kv+json", "APPLICATION/JSON", "application/FOO+JSON"}) + void recognizeJson(String mimeType) { + // Arrange + HttpHeaders headers = new HttpHeaders(Collections.singletonMap(CONTENT_TYPE, mimeType)); + + // Act & Assert + Assertions.assertEquals(SerializerEncoding.JSON, SerializerEncoding.fromHeaders(headers)); + } + + @Test + void defaultNoContentType() { + // Arrange + HttpHeaders headers = new HttpHeaders(Collections.singletonMap("Http-Method", "GET")); + + // Act & Assert + Assertions.assertEquals(SerializerEncoding.JSON, SerializerEncoding.fromHeaders(headers)); + } + + @ParameterizedTest + @ValueSource(strings = {"application/binary", "invalid-mime-type"}) + void defaultUnsupportedType(String mimeType) { + // Arrange + HttpHeaders headers = new HttpHeaders(Collections.singletonMap(CONTENT_TYPE, mimeType)); + + // Act & Assert + Assertions.assertEquals(SerializerEncoding.JSON, SerializerEncoding.fromHeaders(headers)); + } +}