diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index d5ddbc2c17592..620be506444db 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -1177,14 +1177,14 @@ Params withWaitForEvents(Priority waitForEvents) { */ static XContentType enforceSameContentType(IndexRequest indexRequest, @Nullable XContentType xContentType) { XContentType requestContentType = indexRequest.getContentType(); - if (requestContentType != XContentType.JSON && requestContentType != XContentType.SMILE) { + if (requestContentType.canonical() != XContentType.JSON && requestContentType.canonical() != XContentType.SMILE) { throw new IllegalArgumentException("Unsupported content-type found for request with content-type [" + requestContentType + "], only JSON and SMILE are supported"); } if (xContentType == null) { return requestContentType; } - if (requestContentType != xContentType) { + if (requestContentType.canonical() != xContentType.canonical()) { throw new IllegalArgumentException("Mismatching content-type found for request with content-type [" + requestContentType + "], previous requests have content-type [" + xContentType + "]"); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index c0b6b93c4544f..247ddecff8cb8 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -1821,9 +1821,11 @@ public void testCreateContentType() { } public void testEnforceSameContentType() { - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE); + XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE, XContentType.VND_JSON, XContentType.VND_SMILE); IndexRequest indexRequest = new IndexRequest().source(singletonMap("field", "value"), xContentType); - assertEquals(xContentType, enforceSameContentType(indexRequest, null)); + // indexRequest content type is made canonical because IndexRequest's content-type is + // from XContentBuilder.getXContentType (hardcoded in JsonXContentGEnerator) + assertEquals(xContentType.canonical(), enforceSameContentType(indexRequest, null)); assertEquals(xContentType, enforceSameContentType(indexRequest, xContentType)); XContentType bulkContentType = randomBoolean() ? xContentType : null; @@ -1840,7 +1842,7 @@ public void testEnforceSameContentType() { assertEquals("Unsupported content-type found for request with content-type [YAML], only JSON and SMILE are supported", exception.getMessage()); - XContentType requestContentType = xContentType == XContentType.JSON ? XContentType.SMILE : XContentType.JSON; + XContentType requestContentType = xContentType.canonical() == XContentType.JSON ? XContentType.SMILE : XContentType.JSON; exception = expectThrows(IllegalArgumentException.class, () -> enforceSameContentType(new IndexRequest().source(singletonMap("field", "value"), requestContentType), xContentType)); diff --git a/docs/reference/search/search-template.asciidoc b/docs/reference/search/search-template.asciidoc index cf0c78bf5496d..fbbdf10e6d0a0 100644 --- a/docs/reference/search/search-template.asciidoc +++ b/docs/reference/search/search-template.asciidoc @@ -158,7 +158,7 @@ The API returns the following result: "lang" : "mustache", "source" : """{"query":{"match":{"title":"{{query_string}}"}}}""", "options": { - "content_type" : "application/json; charset=UTF-8" + "content_type" : "application/json;charset=utf-8" } }, "_id": "", diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java index 8324adfa25384..674770944516d 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java @@ -33,11 +33,11 @@ * I.e. txt used in path _sql?format=txt will return TextFormat.PLAIN_TEXT * * Multiple header representations may map to a single {@link MediaType} for example, "application/json" - * and "application/vnd.elasticsearch+json" both represent a JSON MediaType. + * and "application/x-ndjson" both represent a JSON MediaType. * A MediaType can have only one query parameter representation. * For example "json" (case insensitive) maps back to a JSON media type. * - * Additionally, a http header may optionally have parameters. For example "application/json; charset=utf-8". + * Additionally, a http header may optionally have parameters. For example "application/vnd.elasticsearch+json; compatible-with=7". * This class also allows to define a regular expression for valid values of charset. */ public class MediaTypeRegistry { diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java index 504decef7a7ac..a7db4608bc867 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java @@ -24,6 +24,7 @@ import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * A raw result of parsing media types from Accept or Content-Type headers. @@ -44,7 +45,7 @@ private ParsedMediaType(String originalHeaderValue, String type, String subType, this.originalHeaderValue = originalHeaderValue; this.type = type; this.subType = subType; - this.parameters = Collections.unmodifiableMap(parameters); + this.parameters = parameters; } /** @@ -55,7 +56,7 @@ public String mediaTypeWithoutParameters() { } public Map getParameters() { - return parameters; + return Collections.unmodifiableMap(parameters); } /** @@ -81,7 +82,7 @@ public static ParsedMediaType parseMediaType(String headerValue) { throw new IllegalArgumentException("invalid media-type [" + headerValue + "]"); } if (elements.length == 1) { - return new ParsedMediaType(headerValue, splitMediaType[0].trim(), splitMediaType[1].trim(), Collections.emptyMap()); + return new ParsedMediaType(headerValue, splitMediaType[0].trim(), splitMediaType[1].trim(), new HashMap<>()); } else { Map parameters = new HashMap<>(); for (int i = 1; i < elements.length; i++) { @@ -105,6 +106,12 @@ public static ParsedMediaType parseMediaType(String headerValue) { return null; } + public static ParsedMediaType parseMediaType(XContentType requestContentType, Map parameters) { + ParsedMediaType parsedMediaType = parseMediaType(requestContentType.mediaTypeWithoutParameters()); + parsedMediaType.parameters.putAll(parameters); + return parsedMediaType; + } + // simplistic check for media ranges. do not validate if this is a correct header private static boolean isMediaRange(String headerValue) { return headerValue.contains(","); @@ -151,4 +158,16 @@ private boolean isValidParameter(String paramName, String value, Map parameters) { + return this.mediaTypeWithoutParameters() + formatParameters(parameters); + } + + private String formatParameters(Map parameters) { + String joined = parameters.entrySet().stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining(";")); + return joined.isEmpty() ? "" : ";" + joined; + } + } diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java index e5aebb26fff4d..b19235d3e8060 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java @@ -48,8 +48,6 @@ */ public final class XContentBuilder implements Closeable, Flushable { - private byte compatibleMajorVersion; - /** * Create a new {@link XContentBuilder} using the given {@link XContent} content. *

@@ -65,20 +63,21 @@ public static XContentBuilder builder(XContent xContent) throws IOException { } /** - * Create a new {@link XContentBuilder} using the given {@link XContent} content and some inclusive and/or exclusive filters. + * Create a new {@link XContentBuilder} using the given {@link XContentType} xContentType and some inclusive and/or exclusive filters. *

* The builder uses an internal {@link ByteArrayOutputStream} output stream to build the content. When both exclusive and * inclusive filters are provided, the underlying builder will first use exclusion filters to remove fields and then will check the * remaining fields against the inclusive filters. *

* - * @param xContent the {@link XContent} + * @param xContentType the {@link XContentType} * @param includes the inclusive filters: only fields and objects that match the inclusive filters will be written to the output. * @param excludes the exclusive filters: only fields and objects that don't match the exclusive filters will be written to the output. * @throws IOException if an {@link IOException} occurs while building the content */ - public static XContentBuilder builder(XContent xContent, Set includes, Set excludes) throws IOException { - return new XContentBuilder(xContent, new ByteArrayOutputStream(), includes, excludes); + public static XContentBuilder builder(XContentType xContentType, Set includes, Set excludes) throws IOException { + return new XContentBuilder(xContentType.xContent(), new ByteArrayOutputStream(), includes, excludes, + ParsedMediaType.parseMediaType(xContentType.mediaType())); } private static final Map, Writer> WRITERS; @@ -167,12 +166,16 @@ public interface HumanReadableTransformer { */ private boolean humanReadable = false; + private byte compatibleMajorVersion; + + private ParsedMediaType responseContentType; + /** * Constructs a new builder using the provided XContent and an OutputStream. Make sure * to call {@link #close()} when the builder is done with. */ public XContentBuilder(XContent xContent, OutputStream bos) throws IOException { - this(xContent, bos, Collections.emptySet(), Collections.emptySet()); + this(xContent, bos, Collections.emptySet(), Collections.emptySet(), ParsedMediaType.parseMediaType(xContent.type().mediaType())); } /** @@ -181,8 +184,8 @@ public XContentBuilder(XContent xContent, OutputStream bos) throws IOException { * filter will be written to the output stream. Make sure to call * {@link #close()} when the builder is done with. */ - public XContentBuilder(XContent xContent, OutputStream bos, Set includes) throws IOException { - this(xContent, bos, includes, Collections.emptySet()); + public XContentBuilder(XContentType xContentType, OutputStream bos, Set includes) throws IOException { + this(xContentType.xContent(), bos, includes, Collections.emptySet(), ParsedMediaType.parseMediaType(xContentType.mediaType())); } /** @@ -191,16 +194,25 @@ public XContentBuilder(XContent xContent, OutputStream bos, Set includes * remaining fields against the inclusive filters. *

* Make sure to call {@link #close()} when the builder is done with. - * * @param os the output stream * @param includes the inclusive filters: only fields and objects that match the inclusive filters will be written to the output. * @param excludes the exclusive filters: only fields and objects that don't match the exclusive filters will be written to the output. + * @param responseContentType a content-type header value to be send back on a response */ - public XContentBuilder(XContent xContent, OutputStream os, Set includes, Set excludes) throws IOException { + public XContentBuilder(XContent xContent, OutputStream os, Set includes, Set excludes, + ParsedMediaType responseContentType) throws IOException { this.bos = os; + assert responseContentType != null : "generated response cannot be null"; + this.responseContentType = responseContentType; this.generator = xContent.createGenerator(bos, includes, excludes); } + public String getResponseContentTypeString() { + Map parameters = responseContentType != null ? + responseContentType.getParameters() : Collections.emptyMap(); + return responseContentType.responseContentTypeHeader(parameters); + } + public XContentType contentType() { return generator.contentType(); } diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentFactory.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentFactory.java index 38bc251be41dd..d0a611dc02836 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentFactory.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentFactory.java @@ -97,13 +97,14 @@ public static XContentBuilder cborBuilder(OutputStream os) throws IOException { * Constructs a xcontent builder that will output the result into the provided output stream. */ public static XContentBuilder contentBuilder(XContentType type, OutputStream outputStream) throws IOException { - if (type == XContentType.JSON) { + XContentType canonical = type.canonical(); + if (canonical == XContentType.JSON) { return jsonBuilder(outputStream); - } else if (type == XContentType.SMILE) { + } else if (canonical == XContentType.SMILE) { return smileBuilder(outputStream); - } else if (type == XContentType.YAML) { + } else if (canonical == XContentType.YAML) { return yamlBuilder(outputStream); - } else if (type == XContentType.CBOR) { + } else if (canonical == XContentType.CBOR) { return cborBuilder(outputStream); } throw new IllegalArgumentException("No matching content type for " + type); @@ -113,13 +114,15 @@ public static XContentBuilder contentBuilder(XContentType type, OutputStream out * Returns a binary content builder for the provided content type. */ public static XContentBuilder contentBuilder(XContentType type) throws IOException { - if (type == XContentType.JSON) { + XContentType canonical = type.canonical(); + + if (canonical == XContentType.JSON) { return JsonXContent.contentBuilder(); - } else if (type == XContentType.SMILE) { + } else if (canonical == XContentType.SMILE) { return SmileXContent.contentBuilder(); - } else if (type == XContentType.YAML) { + } else if (canonical == XContentType.YAML) { return YamlXContent.contentBuilder(); - } else if (type == XContentType.CBOR) { + } else if (canonical == XContentType.CBOR) { return CborXContent.contentBuilder(); } throw new IllegalArgumentException("No matching content type for " + type); diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java index c1cd11aef5f19..f8e0dc23f0233 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java @@ -43,7 +43,7 @@ public String mediaTypeWithoutParameters() { @Override public String mediaType() { - return "application/json; charset=UTF-8"; + return "application/json;charset=utf-8"; } @Override @@ -61,11 +61,7 @@ public Set headerValues() { return Set.of( new HeaderValue("application/json"), new HeaderValue("application/x-ndjson"), - new HeaderValue("application/*"), - new HeaderValue(VENDOR_APPLICATION_PREFIX + "json", - Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN)), - new HeaderValue(VENDOR_APPLICATION_PREFIX + "x-ndjson", - Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); + new HeaderValue("application/*")); } }, /** @@ -90,9 +86,7 @@ public XContent xContent() { @Override public Set headerValues() { return Set.of( - new HeaderValue("application/smile"), - new HeaderValue(VENDOR_APPLICATION_PREFIX + "smile", - Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); + new HeaderValue("application/smile")); } }, /** @@ -117,9 +111,7 @@ public XContent xContent() { @Override public Set headerValues() { return Set.of( - new HeaderValue("application/yaml"), - new HeaderValue(VENDOR_APPLICATION_PREFIX + "yaml", - Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); + new HeaderValue("application/yaml")); } }, /** @@ -144,10 +136,134 @@ public XContent xContent() { @Override public Set headerValues() { return Set.of( - new HeaderValue("application/cbor"), + new HeaderValue("application/cbor")); + } + }, + /** + * A versioned JSON based content type. + */ + VND_JSON(4) { + @Override + public String mediaTypeWithoutParameters() { + return VENDOR_APPLICATION_PREFIX + "json"; + } + + @Override + public String queryParameter() { + return "vnd_json"; + } + + @Override + public XContent xContent() { + return JsonXContent.jsonXContent; + } + + @Override + public Set headerValues() { + return Set.of( + new HeaderValue(VENDOR_APPLICATION_PREFIX + "json", + Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN)), + new HeaderValue(VENDOR_APPLICATION_PREFIX + "x-ndjson", + Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); + } + + @Override + public XContentType canonical() { + return JSON; + } + }, + /** + * Versioned jackson based smile binary format. Fast and compact binary format. + */ + VND_SMILE(5) { + @Override + public String mediaTypeWithoutParameters() { + return VENDOR_APPLICATION_PREFIX + "smile"; + } + + @Override + public String queryParameter() { + return "vnd_smile"; + } + + @Override + public XContent xContent() { + return SmileXContent.smileXContent; + } + + @Override + public Set headerValues() { + return Set.of( + new HeaderValue(VENDOR_APPLICATION_PREFIX + "smile", + Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); + } + + @Override + public XContentType canonical() { + return SMILE; + } + }, + /** + * A Versioned YAML based content type. + */ + VND_YAML(6) { + @Override + public String mediaTypeWithoutParameters() { + return VENDOR_APPLICATION_PREFIX + "yaml"; + } + + @Override + public String queryParameter() { + return "vnd_yaml"; + } + + @Override + public XContent xContent() { + return YamlXContent.yamlXContent; + } + + @Override + public Set headerValues() { + return Set.of( + new HeaderValue(VENDOR_APPLICATION_PREFIX + "yaml", + Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); + } + + @Override + public XContentType canonical() { + return YAML; + } + }, + /** + * A Versioned CBOR based content type. + */ + VND_CBOR(7) { + @Override + public String mediaTypeWithoutParameters() { + return VENDOR_APPLICATION_PREFIX + "cbor"; + } + + @Override + public String queryParameter() { + return "vnd_cbor"; + } + + @Override + public XContent xContent() { + return CborXContent.cborXContent; + } + + @Override + public Set headerValues() { + return Set.of( new HeaderValue(VENDOR_APPLICATION_PREFIX + "cbor", Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); } + + @Override + public XContentType canonical() { + return CBOR; + } }; public static final MediaTypeRegistry MEDIA_TYPE_REGISTRY = new MediaTypeRegistry() @@ -207,4 +323,16 @@ public String mediaType() { public abstract XContent xContent(); public abstract String mediaTypeWithoutParameters(); + + /** + * Returns a canonical XContentType for this XContentType. + * A canonical XContentType is used to serialize or deserialize the data from/to for HTTP. + * More specialized XContentType types such as vnd* variants still use the general data structure, + * but may have semantic differences. + * Example: XContentType.VND_JSON has a canonical XContentType.JSON + * XContentType.JSON has a canonical XContentType.JSON + */ + public XContentType canonical(){ + return this; + } } diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MapXContentParserTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MapXContentParserTests.java index b3fb9eee4662d..4aaacf743015b 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MapXContentParserTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MapXContentParserTests.java @@ -91,7 +91,7 @@ public void compareTokens(CheckedConsumer consumer try (XContentParser parser = createParser(xContentType.xContent(), BytesReference.bytes(builder))) { try (XContentParser mapParser = new MapXContentParser( xContentRegistry(), LoggingDeprecationHandler.INSTANCE, map, xContentType)) { - assertEquals(parser.contentType(), mapParser.contentType()); + assertEquals(parser.contentType(), mapParser.contentType().canonical()); XContentParser.Token token; assertEquals(parser.currentToken(), mapParser.currentToken()); assertEquals(parser.currentName(), mapParser.currentName()); @@ -101,7 +101,8 @@ public void compareTokens(CheckedConsumer consumer assertEquals(token, mapToken); assertEquals(parser.currentName(), mapParser.currentName()); if (token != null && (token.isValue() || token == XContentParser.Token.VALUE_NULL)) { - if (xContentType != XContentType.YAML || token != XContentParser.Token.VALUE_EMBEDDED_OBJECT) { + if ((xContentType.canonical() != XContentType.YAML) || + token != XContentParser.Token.VALUE_EMBEDDED_OBJECT) { // YAML struggles with converting byte arrays into text, because it // does weird base64 decoding to the values. We don't do this // weirdness in the MapXContentParser, so don't try to stringify it. diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ParsedMediaTypeTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ParsedMediaTypeTests.java index 73bc8e2e5734f..d548c3694ac6e 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ParsedMediaTypeTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ParsedMediaTypeTests.java @@ -33,6 +33,26 @@ public class ParsedMediaTypeTests extends ESTestCase { MediaTypeRegistry mediaTypeRegistry = new MediaTypeRegistry() .register(XContentType.values()); + public void testCanonicalParsing() { + assertThat(ParsedMediaType.parseMediaType("application/json") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.JSON)); + assertThat(ParsedMediaType.parseMediaType("application/yaml") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.YAML)); + assertThat(ParsedMediaType.parseMediaType("application/smile") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.SMILE)); + assertThat(ParsedMediaType.parseMediaType("application/cbor") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.CBOR)); + + assertThat(ParsedMediaType.parseMediaType("application/vnd.elasticsearch+json;compatible-with=7") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.VND_JSON)); + assertThat(ParsedMediaType.parseMediaType("application/vnd.elasticsearch+yaml;compatible-with=7") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.VND_YAML)); + assertThat(ParsedMediaType.parseMediaType("application/vnd.elasticsearch+smile;compatible-with=7") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.VND_SMILE)); + assertThat(ParsedMediaType.parseMediaType("application/vnd.elasticsearch+cbor;compatible-with=7") + .toMediaType(mediaTypeRegistry), equalTo(XContentType.VND_CBOR)); + } + public void testJsonWithParameters() throws Exception { String mediaType = "application/vnd.elasticsearch+json"; assertThat(ParsedMediaType.parseMediaType(mediaType).getParameters(), @@ -48,7 +68,7 @@ public void testJsonWithParameters() throws Exception { public void testWhiteSpaceInTypeSubtype() { String mediaType = " application/vnd.elasticsearch+json "; assertThat(ParsedMediaType.parseMediaType(mediaType).toMediaType(mediaTypeRegistry), - equalTo(XContentType.JSON)); + equalTo(XContentType.VND_JSON)); assertThat(ParsedMediaType.parseMediaType(mediaType + "; compatible-with=123; charset=UTF-8").getParameters(), equalTo(Map.of("charset", "utf-8", "compatible-with", "123"))); diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java index 262b25b97b26e..31b4e381c7ae5 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java @@ -79,10 +79,14 @@ public void testFloat() throws IOException { assertEquals(value, number.floatValue(), 0.0f); switch (xContentType) { + case VND_CBOR: + case VND_SMILE: case CBOR: case SMILE: assertThat(number, instanceOf(Float.class)); break; + case VND_JSON: + case VND_YAML: case JSON: case YAML: assertThat(number, instanceOf(Double.class)); diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java index 9750f3918f0d6..f47ab68a940d9 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java @@ -52,7 +52,7 @@ public class CustomMustacheFactory extends DefaultMustacheFactory { - static final String JSON_MIME_TYPE_WITH_CHARSET = "application/json; charset=UTF-8"; + static final String JSON_MIME_TYPE_WITH_CHARSET = "application/json;charset=utf-8"; static final String JSON_MIME_TYPE = "application/json"; static final String PLAIN_TEXT_MIME_TYPE = "text/plain"; static final String X_WWW_FORM_URLENCODED_MIME_TYPE = "application/x-www-form-urlencoded"; diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteRequestTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteRequestTests.java index 79d37cc0e5bc7..698c52a55dc33 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteRequestTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/action/PainlessExecuteRequestTests.java @@ -48,7 +48,7 @@ public class PainlessExecuteRequestTests extends AbstractWireSerializingTestCase // Testing XContent serialization manually here, because the xContentType field in ContextSetup determines // how the request needs to parse and the xcontent serialization framework randomizes that. The XContentType - // is not known and accessable when the test request instance is created in the xcontent serialization framework. + // is not known and accessible when the test request instance is created in the xcontent serialization framework. // Changing that is a big change. Writing a custom xcontent test here is the best option for now, because as far // as I know this request class is the only case where this is a problem. public final void testFromXContent() throws Exception { @@ -107,7 +107,7 @@ private static ContextSetup randomContextSetup() { String index = randomBoolean() ? randomAlphaOfLength(4) : null; QueryBuilder query = randomBoolean() ? new MatchAllQueryBuilder() : null; BytesReference doc = null; - XContentType xContentType = randomFrom(XContentType.values()); + XContentType xContentType = randomFrom(XContentType.values()).canonical(); if (randomBoolean()) { try { XContentBuilder xContentBuilder = XContentBuilder.builder(xContentType.xContent()); diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java index e9e73278c5e8a..907480e5f3b9c 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java @@ -272,7 +272,7 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeBytesReference(document); } if (documents.isEmpty() == false) { - out.writeEnum(documentXContentType); + XContentHelper.writeTo(out, documentXContentType); } } diff --git a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/DetailedErrorsDisabledIT.java b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/DetailedErrorsDisabledIT.java index 17a1f3b5d256c..3b67e7440cec3 100644 --- a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/DetailedErrorsDisabledIT.java +++ b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/DetailedErrorsDisabledIT.java @@ -54,7 +54,7 @@ public void testThatErrorTraceParamReturns400() throws IOException { getRestClient().performRequest(request)); Response response = e.getResponse(); - assertThat(response.getHeader("Content-Type"), is("application/json; charset=UTF-8")); + assertThat(response.getHeader("Content-Type"), is("application/json")); assertThat(EntityUtils.toString(e.getResponse().getEntity()), containsString("\"error\":\"error traces in responses are disabled.\"")); assertThat(response.getStatusLine().getStatusCode(), is(400)); diff --git a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/NoHandlerIT.java b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/NoHandlerIT.java index d3707031f0e35..62b4747ae2512 100644 --- a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/NoHandlerIT.java +++ b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/NoHandlerIT.java @@ -36,7 +36,7 @@ public class NoHandlerIT extends HttpSmokeTestCase { public void testNoHandlerRespectsAcceptHeader() throws IOException { runTestNoHandlerRespectsAcceptHeader( "application/json", - "application/json; charset=UTF-8", + "application/json", "\"error\":\"no handler found for uri [/foo/bar/baz/qux/quux] and method [GET]\""); runTestNoHandlerRespectsAcceptHeader( "application/yaml", diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java index 85ec9b14f097e..6407b8dea5c8b 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java @@ -127,7 +127,7 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeOptionalString(id); out.writeBytesReference(content); - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); out.writeOptionalString(context); source.writeTo(out); } diff --git a/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java b/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java index 01c9ca49a9c43..565b1e17fc99d 100644 --- a/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java +++ b/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java @@ -657,7 +657,7 @@ private void writeBody(StreamOutput out) throws IOException { out.writeLong(autoGeneratedTimestamp); if (contentType != null) { out.writeBoolean(true); - out.writeEnum(contentType); + XContentHelper.writeTo(out, contentType); } else { out.writeBoolean(false); } diff --git a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java index 212921f0e5a95..1007c1835f2b6 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; @@ -78,7 +79,7 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeString(id); out.writeBytesReference(source); - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); } @Override diff --git a/server/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java b/server/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java index 414d8835f4faa..a5818bdb0f4ec 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; import org.elasticsearch.ingest.ConfigurationUtils; @@ -101,7 +102,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(id); out.writeBoolean(verbose); out.writeBytesReference(source); - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); } @Override diff --git a/server/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java b/server/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java index b35b3f7bca472..9ab5623870737 100644 --- a/server/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java +++ b/server/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java @@ -493,7 +493,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(doc != null); if (doc != null) { out.writeBytesReference(doc); - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); } out.writeOptionalString(routing); out.writeOptionalString(preference); diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java index 9f6648de52f49..a07b66b0b7435 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java @@ -41,6 +41,7 @@ import org.elasticsearch.common.text.Text; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.joda.time.DateTimeZone; import org.joda.time.ReadableInstant; @@ -1273,6 +1274,7 @@ public void writeNamedWriteableList(List list) throws * Writes an enum with type E based on its ordinal value */ public > void writeEnum(E enumValue) throws IOException { + assert enumValue instanceof XContentType == false : "XContentHelper#writeTo should be used for XContentType serialisation"; writeVInt(enumValue.ordinal()); } diff --git a/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java b/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java index 5b828e06f1009..4f4dc6d3cb51f 100644 --- a/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java +++ b/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java @@ -20,12 +20,14 @@ package org.elasticsearch.common.xcontent; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.compress.Compressor; import org.elasticsearch.common.compress.CompressorFactory; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContent.Params; import java.io.BufferedInputStream; @@ -453,4 +455,19 @@ public static BytesReference childBytes(XContentParser parser) throws IOExceptio builder.copyCurrentStructure(parser); return BytesReference.bytes(builder); } + + /** + * Serialises new XContentType VND_ values in a bwc manner + * TODO remove in ES v9 + * @param out stream output of the destination node + * @param xContentType an instance to serialize + */ + public static void writeTo(StreamOutput out, XContentType xContentType) throws IOException { + if (out.getVersion().before(Version.V_8_0_0)) { + // when sending an enumeration to parameters = request.getParsedAccept() != null ? + request.getParsedAccept().getParameters() : Collections.emptyMap(); + ParsedMediaType responseMediaType = ParsedMediaType.parseMediaType(responseContentType, parameters); + XContentBuilder builder = - new XContentBuilder(XContentFactory.xContent(responseContentType), unclosableOutputStream, includes, excludes); + new XContentBuilder(XContentFactory.xContent(responseContentType), unclosableOutputStream, + includes, excludes, responseMediaType); if (pretty) { builder.prettyPrint().lfAtEnd(); } diff --git a/server/src/main/java/org/elasticsearch/rest/BytesRestResponse.java b/server/src/main/java/org/elasticsearch/rest/BytesRestResponse.java index 028b3a5f8e693..99fa5805099dd 100644 --- a/server/src/main/java/org/elasticsearch/rest/BytesRestResponse.java +++ b/server/src/main/java/org/elasticsearch/rest/BytesRestResponse.java @@ -56,7 +56,7 @@ public class BytesRestResponse extends RestResponse { * Creates a new response based on {@link XContentBuilder}. */ public BytesRestResponse(RestStatus status, XContentBuilder builder) { - this(status, builder.contentType().mediaType(), BytesReference.bytes(builder)); + this(status, builder.getResponseContentTypeString(), BytesReference.bytes(builder)); } /** diff --git a/server/src/main/java/org/elasticsearch/rest/RestController.java b/server/src/main/java/org/elasticsearch/rest/RestController.java index 7a232127f650e..e0deb4d96ed2a 100644 --- a/server/src/main/java/org/elasticsearch/rest/RestController.java +++ b/server/src/main/java/org/elasticsearch/rest/RestController.java @@ -237,7 +237,9 @@ private void dispatchRequest(RestRequest request, RestChannel channel, RestHandl sendContentTypeErrorMessage(request.getAllHeaderValues("Content-Type"), channel); return; } - if (handler.supportsContentStream() && xContentType != XContentType.JSON && xContentType != XContentType.SMILE) { + //TODO consider refactoring to handler.supportsContentStream(xContentType). It is only used with JSON and SMILE + if (handler.supportsContentStream() && xContentType.canonical() != XContentType.JSON + && xContentType.canonical() != XContentType.SMILE) { channel.sendResponse(BytesRestResponse.createSimpleErrorResponse(channel, RestStatus.NOT_ACCEPTABLE, "Content-Type [" + xContentType + "] does not support stream parsing. Use JSON or SMILE instead")); return; diff --git a/server/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java b/server/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java index ad34f5b9a0987..af52963b50d9a 100644 --- a/server/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java +++ b/server/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java @@ -91,7 +91,7 @@ public void testStoredFieldsValueBoolean() throws IOException { public void testStoredFieldsValueBinary() throws IOException { final byte[] value = randomUnicodeOfLength(scaledRandomIntBetween(10, 1000)).getBytes("UTF-8"); assertParseFieldsSimpleValue(value, (xcontentType, result) -> { - if (xcontentType == XContentType.JSON) { + if (xcontentType.canonical() == XContentType.JSON) { // binary values will be parsed back and returned as base64 strings when reading from json assertArrayEquals(value, Base64.getDecoder().decode((String) result)); } else { diff --git a/server/src/test/java/org/elasticsearch/common/xcontent/XContentTypeTests.java b/server/src/test/java/org/elasticsearch/common/xcontent/XContentTypeTests.java index 72bdf549b8d40..8345e57da04a5 100644 --- a/server/src/test/java/org/elasticsearch/common/xcontent/XContentTypeTests.java +++ b/server/src/test/java/org/elasticsearch/common/xcontent/XContentTypeTests.java @@ -99,21 +99,21 @@ public void testFromRubbish() throws Exception { public void testVersionedMediaType() { String version = String.valueOf(randomNonNegativeByte()); assertThat(XContentType.fromMediaType("application/vnd.elasticsearch+json;compatible-with=" + version), - equalTo(XContentType.JSON)); + equalTo(XContentType.VND_JSON)); assertThat(XContentType.fromMediaType("application/vnd.elasticsearch+cbor;compatible-with=" + version), - equalTo(XContentType.CBOR)); + equalTo(XContentType.VND_CBOR)); assertThat(XContentType.fromMediaType("application/vnd.elasticsearch+smile;compatible-with=" + version), - equalTo(XContentType.SMILE)); + equalTo(XContentType.VND_SMILE)); assertThat(XContentType.fromMediaType("application/vnd.elasticsearch+yaml;compatible-with=" + version), - equalTo(XContentType.YAML)); + equalTo(XContentType.VND_YAML)); assertThat(XContentType.fromMediaType("application/json"), equalTo(XContentType.JSON)); assertThat(XContentType.fromMediaType("application/vnd.elasticsearch+x-ndjson;compatible-with=" + version), - equalTo(XContentType.JSON)); + equalTo(XContentType.VND_JSON)); assertThat(XContentType.fromMediaType("APPLICATION/VND.ELASTICSEARCH+JSON;COMPATIBLE-WITH=" + version), - equalTo(XContentType.JSON)); + equalTo(XContentType.VND_JSON)); assertThat(XContentType.fromMediaType("APPLICATION/JSON"), equalTo(XContentType.JSON)); } diff --git a/server/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractXContentFilteringTestCase.java b/server/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractXContentFilteringTestCase.java index 4aa19b78a5ca0..b7a6fec26d318 100644 --- a/server/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractXContentFilteringTestCase.java +++ b/server/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractXContentFilteringTestCase.java @@ -54,7 +54,7 @@ private XContentBuilder createBuilder() throws IOException { } private XContentBuilder createBuilder(Set includes, Set excludes) throws IOException { - return XContentBuilder.builder(getXContentType().xContent(), includes, excludes); + return XContentBuilder.builder(getXContentType(), includes, excludes); } public void testSingleFieldObject() throws IOException { diff --git a/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java b/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java index 56f88769fbe12..b0dd16187089e 100644 --- a/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java +++ b/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java @@ -1020,7 +1020,7 @@ public void testBulkRequestExecution() throws Exception { for (DocWriteRequest docWriteRequest : bulkRequest.requests()) { IndexRequest indexRequest = TransportBulkAction.getIndexWriteRequest(docWriteRequest); assertThat(indexRequest, notNullValue()); - assertThat(indexRequest.getContentType(), equalTo(xContentType)); + assertThat(indexRequest.getContentType(), equalTo(xContentType.canonical())); } } diff --git a/server/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java b/server/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java index eb1171f66a597..fdc0bf4b5b652 100644 --- a/server/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java +++ b/server/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java @@ -65,7 +65,7 @@ public void testParser() throws IOException { XContentParser xContentParser = xContentType.xContent() .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, bytes.streamInput()); PipelineConfiguration parsed = parser.parse(xContentParser, null); - assertEquals(xContentType, parsed.getXContentType()); + assertEquals(xContentType.canonical(), parsed.getXContentType()); assertEquals("{}", XContentHelper.convertToJson(parsed.getConfig(), false, parsed.getXContentType())); assertEquals("1", parsed.getId()); } diff --git a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java index 91f229e7b60aa..5683352c84d14 100644 --- a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java +++ b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java @@ -31,7 +31,9 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.common.xcontent.MediaType; import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ParsedMediaType; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.yaml.YamlXContent; @@ -62,7 +64,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -730,11 +731,9 @@ public Version compatibleWithVersion() { } private String randomCompatibleMimeType(byte version) { - String subtype = randomFrom(Stream.of(XContentType.values()) - .map(XContentType::mediaTypeWithoutParameters) - .toArray(String[]::new)) - .split("/")[1]; - return randomFrom("application/vnd.elasticsearch+" + subtype + ";compatible-with=" + version); + XContentType type = randomFrom(XContentType.VND_JSON, XContentType.VND_SMILE, XContentType.VND_CBOR, XContentType.VND_YAML); + return ParsedMediaType.parseMediaType(type.mediaType()) + .responseContentTypeHeader(Map.of(MediaType.COMPATIBLE_WITH_PARAMETER_NAME, String.valueOf(version))); } private static final class TestHttpServerTransport extends AbstractLifecycleComponent implements diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesActionTests.java index 48c43035488a9..cf84c323ca972 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesActionTests.java @@ -54,7 +54,7 @@ public void testBareRequest() throws Exception { final RestResponse restResponse = RestGetAliasesAction.buildRestResponse(false, new String[0], openMapBuilder.build(), xContentBuilder); assertThat(restResponse.status(), equalTo(OK)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json;charset=utf-8")); assertThat(restResponse.content().utf8ToString(), equalTo("{\"index\":{\"aliases\":{\"foo\":{},\"foobar\":{}}}}")); } @@ -64,7 +64,7 @@ public void testSimpleAliasWildcardMatchingNothing() throws Exception { final RestResponse restResponse = RestGetAliasesAction.buildRestResponse(true, new String[] { "baz*" }, openMapBuilder.build(), xContentBuilder); assertThat(restResponse.status(), equalTo(OK)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json;charset=utf-8")); assertThat(restResponse.content().utf8ToString(), equalTo("{}")); } @@ -76,7 +76,7 @@ public void testMultipleAliasWildcardsSomeMatching() throws Exception { final RestResponse restResponse = RestGetAliasesAction.buildRestResponse(true, new String[] { "baz*", "foobar*" }, openMapBuilder.build(), xContentBuilder); assertThat(restResponse.status(), equalTo(OK)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json;charset=utf-8")); assertThat(restResponse.content().utf8ToString(), equalTo("{\"index\":{\"aliases\":{\"foobar\":{}}}}")); } @@ -86,7 +86,7 @@ public void testAliasWildcardsIncludeAndExcludeAll() throws Exception { final RestResponse restResponse = RestGetAliasesAction.buildRestResponse(true, new String[] { "foob*", "-foo*" }, openMapBuilder.build(), xContentBuilder); assertThat(restResponse.status(), equalTo(OK)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json;charset=utf-8")); assertThat(restResponse.content().utf8ToString(), equalTo("{}")); } @@ -98,7 +98,7 @@ public void testAliasWildcardsIncludeAndExcludeSome() throws Exception { final RestResponse restResponse = RestGetAliasesAction.buildRestResponse(true, new String[] { "foo*", "-foob*" }, openMapBuilder.build(), xContentBuilder); assertThat(restResponse.status(), equalTo(OK)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json;charset=utf-8")); assertThat(restResponse.content().utf8ToString(), equalTo("{\"index\":{\"aliases\":{\"foo\":{}}}}")); } @@ -117,7 +117,7 @@ public void testAliasWildcardsIncludeAndExcludeSomeAndExplicitMissing() throws E final RestResponse restResponse = RestGetAliasesAction.buildRestResponse(true, aliasPattern, openMapBuilder.build(), xContentBuilder); assertThat(restResponse.status(), equalTo(NOT_FOUND)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json;charset=utf-8")); assertThat(restResponse.content().utf8ToString(), equalTo("{\"error\":\"alias [missing] missing\",\"status\":404,\"index\":{\"aliases\":{\"foo\":{}}}}")); } @@ -128,7 +128,7 @@ public void testAliasWildcardsExcludeExplicitMissing() throws Exception { final RestResponse restResponse = RestGetAliasesAction.buildRestResponse(true, new String[] { "foo", "foofoo", "-foo*" }, openMapBuilder.build(), xContentBuilder); assertThat(restResponse.status(), equalTo(OK)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json;charset=utf-8")); assertThat(restResponse.content().utf8ToString(), equalTo("{}")); } } diff --git a/server/src/test/java/org/elasticsearch/rest/action/cat/RestTableTests.java b/server/src/test/java/org/elasticsearch/rest/action/cat/RestTableTests.java index 4f7bc1bedd9d6..78aeb6269f468 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/cat/RestTableTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/cat/RestTableTests.java @@ -37,6 +37,7 @@ import static org.elasticsearch.rest.action.cat.RestTable.buildResponse; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.not; @@ -269,8 +270,9 @@ private RestResponse assertResponseContentType(Map> headers public void sendResponse(RestResponse response) { } }); - - assertThat(response.contentType(), equalTo(mediaType)); + String actualWithoutWhitespaces = mediaType.replaceAll("\\s+",""); + String expectedWithoutWhitespaces = response.contentType().replaceAll("\\s+",""); + assertThat(expectedWithoutWhitespaces, equalToIgnoringCase(actualWithoutWhitespaces)); return response; } diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java index 38a3fdf1af617..c69103531d48d 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java @@ -63,7 +63,7 @@ public void testRestGetSourceAction() throws Exception { final RestResponse restResponse = listener.buildResponse(response); assertThat(restResponse.status(), equalTo(OK)); - assertThat(restResponse.contentType(), equalTo("application/json; charset=UTF-8")); + assertThat(restResponse.contentType(), equalTo("application/json"));//dropping charset as it was not on a request assertThat(restResponse.content(), equalTo(new BytesArray("{\"foo\": \"bar\"}"))); } diff --git a/server/src/test/java/org/elasticsearch/script/StoredScriptTests.java b/server/src/test/java/org/elasticsearch/script/StoredScriptTests.java index 3f8fcb829b0de..8b3602a1a84ef 100644 --- a/server/src/test/java/org/elasticsearch/script/StoredScriptTests.java +++ b/server/src/test/java/org/elasticsearch/script/StoredScriptTests.java @@ -80,7 +80,7 @@ public void testSourceParsing() throws Exception { StoredScriptSource parsed = StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON); StoredScriptSource source = new StoredScriptSource("mustache", code, - Collections.singletonMap("content_type", "application/json; charset=UTF-8")); + Collections.singletonMap("content_type", "application/json;charset=utf-8")); assertThat(parsed, equalTo(source)); } diff --git a/server/src/test/java/org/elasticsearch/search/SearchHitTests.java b/server/src/test/java/org/elasticsearch/search/SearchHitTests.java index 0a6bef86af27a..a75c615c64144 100644 --- a/server/src/test/java/org/elasticsearch/search/SearchHitTests.java +++ b/server/src/test/java/org/elasticsearch/search/SearchHitTests.java @@ -62,8 +62,9 @@ import static org.hamcrest.Matchers.nullValue; public class SearchHitTests extends AbstractWireSerializingTestCase { + public static SearchHit createTestItem(boolean withOptionalInnerHits, boolean withShardTarget) { - return createTestItem(randomFrom(XContentType.values()), withOptionalInnerHits, withShardTarget); + return createTestItem(randomFrom(XContentType.values()).canonical(), withOptionalInnerHits, withShardTarget); } public static SearchHit createTestItem(XContentType xContentType, boolean withOptionalInnerHits, boolean transportSerialization) { @@ -151,11 +152,11 @@ protected Writeable.Reader instanceReader() { @Override protected SearchHit createTestInstance() { - return createTestItem(randomFrom(XContentType.values()), randomBoolean(), randomBoolean()); + return createTestItem(randomFrom(XContentType.values()).canonical(), randomBoolean(), randomBoolean()); } public void testFromXContent() throws IOException { - XContentType xContentType = randomFrom(XContentType.values()); + XContentType xContentType = randomFrom(XContentType.values()).canonical(); SearchHit searchHit = createTestItem(xContentType, true, false); boolean humanReadable = randomBoolean(); BytesReference originalBytes = toShuffledXContent(searchHit, xContentType, ToXContent.EMPTY_PARAMS, humanReadable); diff --git a/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java b/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java index 86147270d218b..2fc488145ce4f 100644 --- a/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java +++ b/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java @@ -44,7 +44,7 @@ public class SearchHitsTests extends AbstractSerializingTestCase { public static SearchHits createTestItem(boolean withOptionalInnerHits, boolean withShardTarget) { - return createTestItem(randomFrom(XContentType.values()), withOptionalInnerHits, withShardTarget); + return createTestItem(randomFrom(XContentType.values()).canonical(), withOptionalInnerHits, withShardTarget); } private static SearchHit[] createSearchHitArray(int size, XContentType xContentType, boolean withOptionalInnerHits, @@ -105,7 +105,7 @@ protected SearchHits mutateInstance(SearchHits instance) { switch (randomIntBetween(0, 5)) { case 0: return new SearchHits(createSearchHitArray(instance.getHits().length + 1, - randomFrom(XContentType.values()), false, randomBoolean()), + randomFrom(XContentType.values()).canonical(), false, randomBoolean()), instance.getTotalHits(), instance.getMaxScore()); case 1: final TotalHits totalHits; @@ -176,7 +176,7 @@ protected SearchHits createTestInstance() { // This instance is used to test the transport serialization so it's fine // to produce shard targets (withShardTarget is true) since they are serialized // in this layer. - return createTestItem(randomFrom(XContentType.values()), true, true); + return createTestItem(randomFrom(XContentType.values()).canonical(), true, true); } @Override @@ -240,7 +240,7 @@ public void testFromXContentWithShards() throws IOException { long totalHits = 1000; float maxScore = 1.5f; SearchHits searchHits = new SearchHits(hits, new TotalHits(totalHits, TotalHits.Relation.EQUAL_TO), maxScore); - XContentType xContentType = randomFrom(XContentType.values()); + XContentType xContentType = randomFrom(XContentType.values()).canonical(); BytesReference bytes = toShuffledXContent(searchHits, xContentType, ToXContent.EMPTY_PARAMS, false); try (XContentParser parser = xContentType.xContent() .createParser(xContentRegistry(), LoggingDeprecationHandler.INSTANCE, bytes.streamInput())) { diff --git a/server/src/test/java/org/elasticsearch/search/searchafter/SearchAfterBuilderTests.java b/server/src/test/java/org/elasticsearch/search/searchafter/SearchAfterBuilderTests.java index 45da49e5f4c48..e6038a13adac0 100644 --- a/server/src/test/java/org/elasticsearch/search/searchafter/SearchAfterBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/search/searchafter/SearchAfterBuilderTests.java @@ -200,7 +200,7 @@ public void testFromXContentIllegalType() throws Exception { for (XContentType type : XContentType.values()) { // BIG_DECIMAL // ignore json and yaml, they parse floating point numbers as floats/doubles - if (type == XContentType.JSON || type == XContentType.YAML) { + if (type.canonical() == XContentType.JSON || type.canonical() == XContentType.YAML) { continue; } XContentBuilder xContent = XContentFactory.contentBuilder(type); diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractWireTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractWireTestCase.java index 3997db194e9b5..92318b7c90fd7 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractWireTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractWireTestCase.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.Writeable; +import org.hamcrest.Matchers; import java.io.IOException; import java.util.Collections; @@ -94,7 +95,7 @@ protected final void assertSerialization(T testInstance, Version version) throws */ protected void assertEqualInstances(T expectedInstance, T newInstance) { assertNotSame(newInstance, expectedInstance); - assertEquals(expectedInstance, newInstance); + assertThat(expectedInstance, Matchers.equalTo(newInstance)); assertEquals(expectedInstance.hashCode(), newInstance.hashCode()); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java index 136f51bed3c8c..82a47aca78383 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java @@ -127,7 +127,7 @@ private XContentTester( public void test() throws IOException { for (int runs = 0; runs < numberOfTestRuns; runs++) { - XContentType xContentType = randomFrom(XContentType.values()); + XContentType xContentType = randomFrom(XContentType.values()).canonical(); T testInstance = instanceSupplier.apply(xContentType); BytesReference originalXContent = toXContent.apply(testInstance, xContentType); BytesReference shuffledContent = insertRandomFieldsAndShuffle(originalXContent, xContentType, supportsUnknownFields, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/watcher/PutWatchRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/watcher/PutWatchRequest.java index fbdea0cf04067..897265f63565f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/watcher/PutWatchRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/watcher/PutWatchRequest.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.uid.Versions; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.seqno.SequenceNumbers; @@ -46,7 +47,7 @@ public PutWatchRequest(StreamInput in) throws IOException { id = in.readString(); source = in.readBytesReference(); active = in.readBoolean(); - xContentType = in.readEnum(XContentType.class); + xContentType = in.readEnum(XContentType.class);; version = in.readZLong(); ifSeqNo = in.readZLong(); ifPrimaryTerm = in.readVLong(); @@ -64,7 +65,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(id); out.writeBytesReference(source); out.writeBoolean(active); - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); out.writeZLong(version); out.writeZLong(ifSeqNo); out.writeVLong(ifPrimaryTerm); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/enrich/EnrichPolicy.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/enrich/EnrichPolicy.java index a6a8e90f8fe97..396caed7a5611 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/enrich/EnrichPolicy.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/enrich/EnrichPolicy.java @@ -258,7 +258,7 @@ public XContentType getContentType() { @Override public void writeTo(StreamOutput out) throws IOException { out.writeBytesReference(query); - out.writeEnum(contentType); + XContentHelper.writeTo(out, contentType); } @Override diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/PostDataAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/PostDataAction.java index b0d69a87dc075..1ce8bf4a2319c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/PostDataAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/PostDataAction.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.StatusToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xpack.core.ml.job.config.DataDescription; @@ -125,7 +126,7 @@ public void writeTo(StreamOutput out) throws IOException { boolean hasXContentType = xContentType != null; out.writeBoolean(hasXContentType); if (hasXContentType) { - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/monitoring/action/MonitoringBulkDoc.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/monitoring/action/MonitoringBulkDoc.java index c95dbb03a1d46..4a1218269b9a1 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/monitoring/action/MonitoringBulkDoc.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/monitoring/action/MonitoringBulkDoc.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.xpack.core.monitoring.MonitoredSystem; @@ -65,7 +66,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(id); out.writeBytesReference(source); if (source != BytesArray.EMPTY) { - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); } out.writeVLong(intervalMillis); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java index 55362390216a3..d6ec2074485ac 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.xcontent.ObjectPath; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentUtils; @@ -125,7 +126,7 @@ public static XContentSource readFrom(StreamInput in) throws IOException { public static void writeTo(XContentSource source, StreamOutput out) throws IOException { out.writeBytesReference(source.bytes); - out.writeEnum(source.contentType); + XContentHelper.writeTo(out, source.contentType); } private Object data() { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/execute/ExecuteWatchRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/execute/ExecuteWatchRequest.java index d45a1a5c8b2b4..72a0f146d4cb2 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/execute/ExecuteWatchRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/execute/ExecuteWatchRequest.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.xpack.core.watcher.client.WatchSourceBuilder; import org.elasticsearch.xpack.core.watcher.execution.ActionExecutionMode; @@ -96,7 +97,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(watchSource != null); if (watchSource != null) { out.writeBytesReference(watchSource); - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); } out.writeBoolean(debug); } diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java index 25c73ad5b581a..3786886027f08 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java @@ -41,6 +41,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ParsedMediaType; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; @@ -296,7 +297,8 @@ private static BytesReference filterSource(FetchSourceContext fetchSourceContext XContentType.SMILE.xContent(), new BytesStreamOutput(source.length()), includes, - excludes + excludes, + ParsedMediaType.parseMediaType(XContentType.SMILE, emptyMap()) ); XContentParser sourceParser = XContentHelper.createParser( NamedXContentRegistry.EMPTY, diff --git a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/action/PutPipelineRequest.java b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/action/PutPipelineRequest.java index 525be7f2a1cff..0502e9b5b3530 100644 --- a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/action/PutPipelineRequest.java +++ b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/action/PutPipelineRequest.java @@ -10,6 +10,7 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; @@ -51,7 +52,7 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeString(id); out.writeString(source); - out.writeEnum(xContentType); + XContentHelper.writeTo(out, xContentType); } @Override diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/FilteredMonitoringDoc.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/FilteredMonitoringDoc.java index 152e57fb531b6..9d44d23007fff 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/FilteredMonitoringDoc.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/FilteredMonitoringDoc.java @@ -58,7 +58,7 @@ Set getFilters() { public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { final XContent xContent = builder.contentType().xContent(); try (BytesStreamOutput out = new BytesStreamOutput()) { - try (XContentBuilder filteredBuilder = new XContentBuilder(xContent, out, filters)) { + try (XContentBuilder filteredBuilder = new XContentBuilder(builder.contentType(), out, filters)) { super.toXContent(filteredBuilder, params); } try (InputStream stream = out.bytes().streamInput(); diff --git a/x-pack/plugin/rest-compatibility/build.gradle b/x-pack/plugin/rest-compatibility/build.gradle index 7674be749f5fa..e6c16ee1ffd4f 100644 --- a/x-pack/plugin/rest-compatibility/build.gradle +++ b/x-pack/plugin/rest-compatibility/build.gradle @@ -18,6 +18,7 @@ */ apply plugin: 'elasticsearch.esplugin' +apply plugin: 'elasticsearch.java-rest-test' esplugin { name 'rest-compatibility' @@ -30,3 +31,11 @@ dependencies { compileOnly project(path: xpackModule('core'), configuration: 'default') testImplementation project(path: xpackModule('core'), configuration: 'testArtifacts') } + + +testClusters.all { + testDistribution = 'DEFAULT' + setting 'xpack.security.enabled', 'false' + setting 'xpack.ml.enabled', 'false' + setting 'xpack.license.self_generated.type', 'trial' +} diff --git a/x-pack/plugin/rest-compatibility/src/javaRestTest/java/org/elasticsearch/compat/ResponseContentTypeHeaderIT.java b/x-pack/plugin/rest-compatibility/src/javaRestTest/java/org/elasticsearch/compat/ResponseContentTypeHeaderIT.java new file mode 100644 index 0000000000000..9800fc44843db --- /dev/null +++ b/x-pack/plugin/rest-compatibility/src/javaRestTest/java/org/elasticsearch/compat/ResponseContentTypeHeaderIT.java @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.compat; + +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.elasticsearch.Version; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.rest.ESRestTestCase; + +import java.io.IOException; + +import static org.hamcrest.core.IsEqual.equalTo; + +public class ResponseContentTypeHeaderIT extends ESRestTestCase { + + public void testResponseContentType() throws IOException { + Request request = new Request("GET", "/"); + RequestOptions.Builder options = request.getOptions().toBuilder(); + options.addHeader("Accept", "application/vnd.elasticsearch+json;compatible-with=" + Version.CURRENT.major); + request.setOptions(options); + Response response = client().performRequest(request); + + assertThat( + response.getHeader("Content-Type"), + equalTo("application/vnd.elasticsearch+json;compatible-with=" + Version.CURRENT.major) + ); + } + + public void testRequestContentType() throws IOException { + Request request = new Request("PUT", "/sample_index_name"); + Settings settings = Settings.builder().put("number_of_shards", 1).put("number_of_replicas", 0).build(); + String entity = "{\"settings\": " + Strings.toString(settings) + "}"; + StringEntity stringEntity = new StringEntity( + entity, + ContentType.parse("application/vnd.elasticsearch+json;compatible-with=" + Version.CURRENT.major) + ); + request.setEntity(stringEntity); + + RequestOptions.Builder options = request.getOptions().toBuilder(); + options.addHeader("Accept", "application/vnd.elasticsearch+json;compatible-with=" + Version.CURRENT.major); + request.setOptions(options); + + Response response = client().performRequest(request); + + assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus())); + } + + public void testInvalidRequestContentType() throws IOException { + Request request = new Request("PUT", "/sample_index_name"); + Settings settings = Settings.builder().put("number_of_shards", 1).put("number_of_replicas", 0).build(); + String entity = "{\"settings\": " + Strings.toString(settings) + "}"; + StringEntity stringEntity = new StringEntity( + entity, + ContentType.parse("application/vnd.elasticsearch+xxx;compatible-with=" + Version.CURRENT.major) + ); + request.setEntity(stringEntity); + + RequestOptions.Builder options = request.getOptions().toBuilder(); + options.addHeader("Accept", "application/vnd.elasticsearch+json;compatible-with=" + Version.CURRENT.major); + request.setOptions(options); + + ResponseException exc = expectThrows(ResponseException.class, () -> client().performRequest(request)); + + assertThat(exc.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.NOT_ACCEPTABLE.getStatus())); + assertTrue( + exc.getMessage().contains("Content-Type header [application/vnd.elasticsearch+xxx; compatible-with=8] is not supported") + ); + } +} diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java index c17c0c8086f67..a7e6e637c3543 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java @@ -288,7 +288,8 @@ private void assertQuery(String sql, String columnName, String columnType, Objec private Map runSql(Mode mode, String sql, boolean columnar) throws IOException { Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT); String requestContent = query(sql).mode(mode).toString(); - String format = randomFrom(XContentType.values()).name().toLowerCase(Locale.ROOT); + String format = randomFrom(XContentType.JSON, XContentType.SMILE, XContentType.CBOR, XContentType.YAML).name() + .toLowerCase(Locale.ROOT); // add a client_id to the request if (randomBoolean()) { diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/activate/ActivateWatchTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/activate/ActivateWatchTests.java index 8abc5ccd49ac6..4ec3d11e660a5 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/activate/ActivateWatchTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/activate/ActivateWatchTests.java @@ -137,7 +137,7 @@ public void testLoadWatchWithoutAState() throws Exception { "status.last_met_condition", "status.actions.**"); - XContentBuilder builder = new XContentBuilder(XContentType.JSON.xContent(), new BytesStreamOutput(), filters); + XContentBuilder builder = new XContentBuilder(XContentType.JSON, new BytesStreamOutput(), filters); source.toXContent(builder, ToXContent.EMPTY_PARAMS); // now that we filtered out the watch status state, lets put it back in diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpRequest.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpRequest.java index 3cda915b7f336..93f5a44c7bdd3 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpRequest.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpRequest.java @@ -10,9 +10,9 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ParsedMediaType; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; -import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; @@ -499,17 +499,18 @@ public interface Field { * Write a request via toXContent, but filter certain parts of it - this is needed to not expose secrets * * @param request The HttpRequest object to serialize - * @param xContent The xContent from the parent outputstream builder + * @param xContentType The XContentType from the parent outputstream builder * @param params The ToXContentParams from the parent write * @param excludeField The field to exclude * @return A bytearrayinputstream that contains the serialized request * @throws IOException if an IOException is triggered in the underlying toXContent method */ - public static InputStream filterToXContent(HttpRequest request, XContent xContent, ToXContent.Params params, + public static InputStream filterToXContent(HttpRequest request, XContentType xContentType, Params params, String excludeField) throws IOException { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); - XContentBuilder filteredBuilder = new XContentBuilder(xContent, bos, - Collections.emptySet(), Collections.singleton(excludeField))) { + XContentBuilder filteredBuilder = new XContentBuilder(xContentType.xContent(), bos, + Collections.emptySet(), Collections.singleton(excludeField), + ParsedMediaType.parseMediaType(xContentType.mediaType()))) { request.toXContent(filteredBuilder, params); filteredBuilder.flush(); return new ByteArrayInputStream(bos.toByteArray()); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/text/TextTemplate.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/text/TextTemplate.java index b082ab57f4410..45bdf149ebae4 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/text/TextTemplate.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/text/TextTemplate.java @@ -43,7 +43,7 @@ public TextTemplate(String template, @Nullable XContentType contentType, ScriptT if (type == ScriptType.INLINE) { options = new HashMap<>(); if (contentType != null) { - options.put(Script.CONTENT_TYPE_OPTION, contentType.mediaType()); + options.put(Script.CONTENT_TYPE_OPTION, contentType.canonical().mediaType()); } } if (params == null) { diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/SentEvent.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/SentEvent.java index 9bc040511a4a3..917804cebc537 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/SentEvent.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/SentEvent.java @@ -87,8 +87,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws // as this makes debugging pagerduty services much harder, this should be changed to only filter for // body.service_key - however the body is currently just a string, making filtering much harder if (WatcherParams.hideSecrets(params)) { - try (InputStream is = HttpRequest.filterToXContent(request, builder.contentType().xContent(), - params, "body")) { + try (InputStream is = HttpRequest.filterToXContent(request, builder.contentType(), + params, "body")) { builder.rawField(XField.REQUEST.getPreferredName(), is, builder.contentType()); } } else { diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SentMessages.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SentMessages.java index a9670c5ffd0ce..3ec00f33a826f 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SentMessages.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SentMessages.java @@ -118,8 +118,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (WatcherParams.hideSecrets(params)) { // this writes out the request to the byte array output stream with the correct excludes // for slack - try (InputStream is = HttpRequest.filterToXContent(request, builder.contentType().xContent(), - params, "path")) { + try (InputStream is = HttpRequest.filterToXContent(request, builder.contentType(), + params, "path")) { builder.rawField(REQUEST.getPreferredName(), is, builder.contentType()); } } else { diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java index 42788b2ea7ad1..001d2dc1ef7cf 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java @@ -267,7 +267,7 @@ public void testThatExpectedContentTypeOverridesReturnedContentType() throws Exc ExecutableHttpInput input = new ExecutableHttpInput(httpInput, httpClient, templateEngine); Map headers = new HashMap<>(1); - String contentType = randomFrom("application/json", "application/json; charset=UTF-8", "text/html", "application/yaml", + String contentType = randomFrom("application/json", "application/json;charset=utf-8", "text/html", "application/yaml", "application/smile", "application/cbor"); headers.put("Content-Type", new String[] { contentType }); String body = "{\"foo\":\"bar\"}";