diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java
index 2d61120288706..0d2cc5fccb043 100644
--- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java
+++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java
@@ -19,6 +19,10 @@
package org.elasticsearch.common.xcontent;
+import java.util.Collections;
+import java.util.Map;
+import java.util.regex.Pattern;
+
/**
* Abstracts a Media Type and a format parameter.
* Media types are used as values on Content-Type and Accept headers
@@ -46,7 +50,11 @@ public interface MediaType {
/**
* returns a string representation of a media type.
*/
- default String typeWithSubtype(){
+ default String typeWithSubtype() {
return type() + "/" + subtype();
}
+
+ default Map validatedParameters() {
+ return Collections.emptyMap();
+ }
}
diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeDefinition.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeDefinition.java
new file mode 100644
index 0000000000000..f28329eaeb136
--- /dev/null
+++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeDefinition.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.xcontent;
+
+import org.elasticsearch.common.Nullable;
+
+import java.util.Map;
+import java.util.Objects;
+
+public class MediaTypeDefinition {
+
+ private final String format;
+ private final String mediaTypeString;
+ private final Map mediaTypeParameters;
+ private final MediaType mediaType;
+
+ public MediaTypeDefinition(String mediaTypeString, MediaType mediaType, Map mediaTypeParameters) {
+ this(null, mediaTypeString, mediaType, mediaTypeParameters);
+ }
+
+ public MediaTypeDefinition(MediaType mediaType, Map mediaTypeParameters) {
+ this(mediaType.format(), mediaType.typeWithSubtype(), mediaType, mediaTypeParameters);
+ }
+
+ private MediaTypeDefinition(String format, String mediaTypeString, MediaType mediaType, Map mediaTypeParameters) {
+ this.format = format;
+ this.mediaTypeString = Objects.requireNonNull(mediaTypeString);
+ this.mediaType = Objects.requireNonNull(mediaType);
+ this.mediaTypeParameters = Objects.requireNonNull(mediaTypeParameters);
+ }
+
+ @Nullable
+ public String format() {
+ return format;
+ }
+
+ public String getMediaTypeString() {
+ return mediaTypeString;
+ }
+
+ public Map getMediaTypeParameters() {
+ return mediaTypeParameters;
+ }
+
+ public MediaType getMediaType() {
+ return mediaType;
+ }
+}
diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeParser.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeParser.java
index 62a3f3fd915d0..dec6223b927b1 100644
--- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeParser.java
+++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeParser.java
@@ -1,3 +1,4 @@
+
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
@@ -25,27 +26,24 @@
import java.util.regex.Pattern;
public class MediaTypeParser {
- private final Map formatToMediaType;
- private final Map typeWithSubtypeToMediaType;
- private final Map> parametersMap;
-
- public MediaTypeParser(Map formatToMediaType, Map typeWithSubtypeToMediaType,
- Map> parametersMap) {
- this.formatToMediaType = Map.copyOf(formatToMediaType);
- this.typeWithSubtypeToMediaType = Map.copyOf(typeWithSubtypeToMediaType);
- this.parametersMap = Map.copyOf(parametersMap);
+ private final MediaTypeRegistry mediaTypeRegistry;
+
+ public MediaTypeParser(MediaTypeRegistry mediaTypeRegistry) {
+ this.mediaTypeRegistry = mediaTypeRegistry;
}
+ @SuppressWarnings("unchecked")
public T fromMediaType(String mediaType) {
ParsedMediaType parsedMediaType = parseMediaType(mediaType);
- return parsedMediaType != null ? parsedMediaType.getMediaType() : null;
+ return parsedMediaType != null ? (T) parsedMediaType.getMediaType() : null;
}
+ @SuppressWarnings("unchecked")
public T fromFormat(String format) {
if (format == null) {
return null;
}
- return formatToMediaType.get(format.toLowerCase(Locale.ROOT));
+ return (T) mediaTypeRegistry.formatToMediaType(format.toLowerCase(Locale.ROOT));
}
/**
@@ -65,7 +63,7 @@ public ParsedMediaType parseMediaType(String headerValue) {
String type = typeSubtype[0];
String subtype = typeSubtype[1];
String typeWithSubtype = type + "/" + subtype;
- T xContentType = typeWithSubtypeToMediaType.get(typeWithSubtype);
+ MediaType xContentType = mediaTypeRegistry.typeWithSubtypeToMediaType(typeWithSubtype);
if (xContentType != null) {
Map parameters = new HashMap<>();
for (int i = 1; i < split.length; i++) {
@@ -90,8 +88,8 @@ public ParsedMediaType parseMediaType(String headerValue) {
}
private boolean isValidParameter(String typeWithSubtype, String parameterName, String parameterValue) {
- if (parametersMap.containsKey(typeWithSubtype)) {
- Map parameters = parametersMap.get(typeWithSubtype);
+ if (mediaTypeRegistry.parametersFor(typeWithSubtype) != null) {
+ Map parameters = mediaTypeRegistry.parametersFor(typeWithSubtype);
if (parameters.containsKey(parameterName)) {
Pattern regex = parameters.get(parameterName);
return regex.matcher(parameterValue).matches();
@@ -104,19 +102,32 @@ private boolean hasSpaces(String s) {
return s.trim().equals(s) == false;
}
+ private static final String COMPATIBLE_WITH_PARAMETER_NAME = "compatible-with";
+
+ public Byte parseVersion(String mediaType) {
+ ParsedMediaType parsedMediaType = parseMediaType(mediaType);
+ if (parsedMediaType != null) {
+ String version = parsedMediaType
+ .getParameters()
+ .get(COMPATIBLE_WITH_PARAMETER_NAME);
+ return version != null ? Byte.parseByte(version) : null;
+ }
+ return null;
+ }
+
/**
* A media type object that contains all the information provided on a Content-Type or Accept header
*/
- public class ParsedMediaType {
+ public static class ParsedMediaType {
private final Map parameters;
- private final T mediaType;
+ private final MediaType mediaType;
- public ParsedMediaType(T mediaType, Map parameters) {
+ public ParsedMediaType(MediaType mediaType, Map parameters) {
this.parameters = parameters;
this.mediaType = mediaType;
}
- public T getMediaType() {
+ public MediaType getMediaType() {
return mediaType;
}
@@ -125,36 +136,4 @@ public Map getParameters() {
}
}
- public static class Builder {
- private final Map formatToMediaType = new HashMap<>();
- private final Map typeWithSubtypeToMediaType = new HashMap<>();
- private final Map> parametersMap = new HashMap<>();
-
- public Builder withMediaTypeAndParams(String alternativeMediaType, T mediaType, Map paramNameAndValueRegex) {
- typeWithSubtypeToMediaType.put(alternativeMediaType.toLowerCase(Locale.ROOT), mediaType);
- formatToMediaType.put(mediaType.format(), mediaType);
-
- Map parametersForMediaType = new HashMap<>(paramNameAndValueRegex.size());
- for (Map.Entry params : paramNameAndValueRegex.entrySet()) {
- String parameterName = params.getKey().toLowerCase(Locale.ROOT);
- String parameterRegex = params.getValue();
- Pattern pattern = Pattern.compile(parameterRegex, Pattern.CASE_INSENSITIVE);
- parametersForMediaType.put(parameterName, pattern);
- }
- parametersMap.put(alternativeMediaType, parametersForMediaType);
-
- return this;
- }
-
- public Builder copyFromMediaTypeParser(MediaTypeParser extends T> mediaTypeParser) {
- formatToMediaType.putAll(mediaTypeParser.formatToMediaType);
- typeWithSubtypeToMediaType.putAll(mediaTypeParser.typeWithSubtypeToMediaType);
- parametersMap.putAll(mediaTypeParser.parametersMap);
- return this;
- }
-
- public MediaTypeParser build() {
- return new MediaTypeParser<>(formatToMediaType, typeWithSubtypeToMediaType, parametersMap);
- }
- }
}
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
new file mode 100644
index 0000000000000..c6ef7ca85b74d
--- /dev/null
+++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.xcontent;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+public class MediaTypeRegistry {
+
+ private final Map formatToMediaType;
+ private final Map typeWithSubtypeToMediaType;
+ private final Map> parametersMap;
+
+ public MediaTypeRegistry(List definitions) {
+ Map formatToMediaType = new HashMap<>();
+ Map typeWithSubtypeToMediaType = new HashMap<>();
+ Map> parametersMap = new HashMap<>();
+ definitions.forEach(definition -> {
+ if (definition.format() != null) {
+ formatToMediaType.put(definition.format(), definition.getMediaType());
+ }
+ typeWithSubtypeToMediaType.put(definition.getMediaTypeString(), definition.getMediaType());
+ Map uncompiledParams = definition.getMediaTypeParameters();
+ Map parametersForMediaType = new HashMap<>(uncompiledParams.size());
+ for (Entry params : uncompiledParams.entrySet()) {
+ String parameterName = params.getKey().toLowerCase(Locale.ROOT);
+ String parameterRegex = params.getValue();
+ Pattern pattern = Pattern.compile(parameterRegex, Pattern.CASE_INSENSITIVE);
+ parametersForMediaType.put(parameterName, pattern);
+ }
+ parametersMap.put(definition.getMediaTypeString(), parametersForMediaType);
+ });
+
+ this.formatToMediaType = Map.copyOf(formatToMediaType);
+ this.typeWithSubtypeToMediaType = Map.copyOf(typeWithSubtypeToMediaType);
+ this.parametersMap = Map.copyOf(parametersMap);
+ }
+
+ public MediaType formatToMediaType(String format) {
+ return formatToMediaType.get(format);
+ }
+
+ public MediaType typeWithSubtypeToMediaType(String typeWithSubtype) {
+ return typeWithSubtypeToMediaType.get(typeWithSubtype);
+ }
+
+ public Map parametersFor(String typeWithSubtype) {
+ return parametersMap.get(typeWithSubtype);
+ }
+
+}
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 076a20bad006a..54f671f4ad9cd 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
@@ -24,7 +24,7 @@
import org.elasticsearch.common.xcontent.smile.SmileXContent;
import org.elasticsearch.common.xcontent.yaml.YamlXContent;
-import java.util.Collections;
+import java.util.List;
import java.util.Map;
/**
@@ -114,26 +114,28 @@ public XContent xContent() {
}
};
- private static final String COMPATIBLE_WITH_PARAMETER_NAME = "compatible-with";
- private static final String VERSION_PATTERN = "\\d+";
- public static final MediaTypeParser mediaTypeParser = new MediaTypeParser.Builder()
- .withMediaTypeAndParams("application/smile", SMILE, Collections.emptyMap())
- .withMediaTypeAndParams("application/cbor", CBOR, Collections.emptyMap())
- .withMediaTypeAndParams("application/json", JSON, Map.of("charset", "UTF-8"))
- .withMediaTypeAndParams("application/yaml", YAML, Map.of("charset", "UTF-8"))
- .withMediaTypeAndParams("application/*", JSON, Map.of("charset", "UTF-8"))
- .withMediaTypeAndParams("application/x-ndjson", JSON, Map.of("charset", "UTF-8"))
- .withMediaTypeAndParams("application/vnd.elasticsearch+json", JSON,
+ public static final String COMPATIBLE_WITH_PARAMETER_NAME = "compatible-with";
+ public static final String VERSION_PATTERN = "\\d+";
+
+ public static final List MEDIA_TYPE_DEFINITIONS = List.of(
+ new MediaTypeDefinition(SMILE, Map.of()),
+ new MediaTypeDefinition(CBOR, Map.of()),
+ new MediaTypeDefinition(JSON, Map.of("charset", "UTF-8")),
+ new MediaTypeDefinition(YAML, Map.of("charset", "UTF-8")),
+ new MediaTypeDefinition("application/x-ndjson", JSON, Map.of("charset", "UTF-8")),
+ new MediaTypeDefinition("application/vnd.elasticsearch+json", JSON,
+ Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8")),
+ new MediaTypeDefinition("application/vnd.elasticsearch+smile", SMILE,
+ Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8")),
+ new MediaTypeDefinition("application/vnd.elasticsearch+yaml", YAML,
+ Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8")),
+ new MediaTypeDefinition("application/vnd.elasticsearch+cbor", CBOR,
+ Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8")),
+ new MediaTypeDefinition("application/vnd.elasticsearch+x-ndjson", JSON,
Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8"))
- .withMediaTypeAndParams("application/vnd.elasticsearch+smile", SMILE,
- Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8"))
- .withMediaTypeAndParams("application/vnd.elasticsearch+yaml", YAML,
- Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8"))
- .withMediaTypeAndParams("application/vnd.elasticsearch+cbor", CBOR,
- Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8"))
- .withMediaTypeAndParams("application/vnd.elasticsearch+x-ndjson", JSON,
- Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN, "charset", "UTF-8"))
- .build();
+ );
+
+ private static final MediaTypeParser PARSER = new MediaTypeParser<>(new MediaTypeRegistry(MEDIA_TYPE_DEFINITIONS));
/**
* Accepts a format string, which is most of the time is equivalent to {@link XContentType#subtype()}
@@ -142,7 +144,7 @@ public XContent xContent() {
* This method will return {@code null} if no match is found
*/
public static XContentType fromFormat(String mediaType) {
- return mediaTypeParser.fromFormat(mediaType);
+ return PARSER.fromFormat(mediaType);
}
/**
@@ -152,26 +154,15 @@ public static XContentType fromFormat(String mediaType) {
* This method will return {@code null} if no match is found
*/
public static XContentType fromMediaType(String mediaTypeHeaderValue) {
- return mediaTypeParser.fromMediaType(mediaTypeHeaderValue);
+ return PARSER.fromMediaType(mediaTypeHeaderValue);
}
- private int index;
+ private final int index;
XContentType(int index) {
this.index = index;
}
- public static Byte parseVersion(String mediaType) {
- MediaTypeParser.ParsedMediaType parsedMediaType = mediaTypeParser.parseMediaType(mediaType);
- if (parsedMediaType != null) {
- String version = parsedMediaType
- .getParameters()
- .get(COMPATIBLE_WITH_PARAMETER_NAME);
- return version != null ? Byte.parseByte(version) : null;
- }
- return null;
- }
-
public int index() {
return index;
}
diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MediaTypeParserTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MediaTypeParserTests.java
index 08ca08a3d2240..9f38dfc593f7b 100644
--- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MediaTypeParserTests.java
+++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/MediaTypeParserTests.java
@@ -29,12 +29,8 @@
import static org.hamcrest.Matchers.nullValue;
public class MediaTypeParserTests extends ESTestCase {
-
- MediaTypeParser mediaTypeParser = new MediaTypeParser.Builder()
- .withMediaTypeAndParams("application/vnd.elasticsearch+json",
- XContentType.JSON, Map.of("compatible-with", "\\d+",
- "charset", "UTF-8"))
- .build();
+ MediaTypeRegistry mediaTypeRegistry = new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS);
+ MediaTypeParser mediaTypeParser = new MediaTypeParser<>(mediaTypeRegistry);
public void testJsonWithParameters() throws Exception {
String mediaType = "application/vnd.elasticsearch+json";
@@ -74,4 +70,26 @@ public void testInvalidParameters() {
assertThat(mediaTypeParser.parseMediaType(mediaType + "; key=") ,
is(nullValue()));
}
+
+ public void testVersionParsing() {
+ byte version = (byte) Math.abs(randomByte());
+ assertThat(mediaTypeParser.parseVersion("application/vnd.elasticsearch+json;compatible-with=" + version),
+ equalTo(version));
+ assertThat(mediaTypeParser.parseVersion("application/json"),
+ nullValue());
+
+
+ assertThat(mediaTypeParser.parseVersion("APPLICATION/VND.ELASTICSEARCH+JSON;COMPATIBLE-WITH=" + version),
+ equalTo(version));
+ assertThat(mediaTypeParser.parseVersion("APPLICATION/JSON"),
+ nullValue());
+
+ assertThat(mediaTypeParser.parseVersion("application/json;compatible-with=" + version + ".0"),
+ is(nullValue()));
+ }
+
+ public void testUnrecognizedParameter() {
+ assertThat(mediaTypeParser.parseVersion("application/json; sth=123"),
+ is(nullValue())); }
+
}
diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java
index b0010a31375c8..2fc177d5361c4 100644
--- a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java
+++ b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java
@@ -51,6 +51,7 @@
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.concurrent.EsExecutors;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.core.internal.io.IOUtils;
import org.elasticsearch.core.internal.net.NetUtils;
@@ -147,8 +148,8 @@ public class Netty4HttpServerTransport extends AbstractHttpServerTransport {
public Netty4HttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, ThreadPool threadPool,
NamedXContentRegistry xContentRegistry, Dispatcher dispatcher, ClusterSettings clusterSettings,
- SharedGroupFactory sharedGroupFactory) {
- super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings);
+ SharedGroupFactory sharedGroupFactory, MediaTypeParser> mediaTypeParser) {
+ super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings, mediaTypeParser);
Netty4Utils.setAvailableProcessors(EsExecutors.NODE_PROCESSORS_SETTING.get(settings));
NettyAllocator.logAllocatorDescriptionIfNeeded();
this.sharedGroupFactory = sharedGroupFactory;
diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/Netty4Plugin.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/Netty4Plugin.java
index 1428eb7b17113..8e537582efdb1 100644
--- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/Netty4Plugin.java
+++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/Netty4Plugin.java
@@ -29,8 +29,10 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.http.HttpServerTransport;
+import org.elasticsearch.http.HttpServerTransport.Dispatcher;
import org.elasticsearch.http.netty4.Netty4HttpServerTransport;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.plugins.NetworkPlugin;
@@ -84,16 +86,20 @@ public Map> getTransports(Settings settings, ThreadP
}
@Override
- public Map> getHttpTransports(Settings settings, ThreadPool threadPool, BigArrays bigArrays,
+ public Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
+ BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher dispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher dispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser
+ ) {
return Collections.singletonMap(NETTY_HTTP_TRANSPORT_NAME,
() -> new Netty4HttpServerTransport(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher,
- clusterSettings, getSharedGroupFactory(settings)));
+ clusterSettings, getSharedGroupFactory(settings), mediaTypeParser));
}
private SharedGroupFactory getSharedGroupFactory(Settings settings) {
diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java
index abd918f706bc7..2f0515c7fcc22 100644
--- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java
+++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java
@@ -29,6 +29,9 @@
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.http.HttpTransportSettings;
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
@@ -91,7 +94,7 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext,
Settings settings = Settings.builder().put(HttpTransportSettings.SETTING_HTTP_PORT.getKey(), getPortRange()).build();
try (HttpServerTransport httpServerTransport = new Netty4HttpServerTransport(settings, networkService, bigArrays, threadPool,
xContentRegistry(), dispatcher, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
- new SharedGroupFactory(Settings.EMPTY))) {
+ new SharedGroupFactory(Settings.EMPTY), new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)))) {
httpServerTransport.start();
final TransportAddress transportAddress = randomFrom(httpServerTransport.boundAddress().boundAddresses());
diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java
index a873293ab5b9f..0e9d49e64fcc4 100644
--- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java
+++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java
@@ -35,6 +35,9 @@
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.http.HttpPipelinedRequest;
import org.elasticsearch.http.HttpResponse;
import org.elasticsearch.http.HttpServerTransport;
@@ -120,7 +123,8 @@ class CustomNettyHttpServerTransport extends Netty4HttpServerTransport {
Netty4HttpServerPipeliningTests.this.bigArrays,
Netty4HttpServerPipeliningTests.this.threadPool,
xContentRegistry(), new NullDispatcher(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
- new SharedGroupFactory(settings));
+ new SharedGroupFactory(settings),
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
}
@Override
diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java
index e2ce3a6878849..b6c53a948ade0 100644
--- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java
+++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java
@@ -57,6 +57,9 @@
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.http.BindHttpException;
import org.elasticsearch.http.CorsHandler;
import org.elasticsearch.http.HttpServerTransport;
@@ -70,8 +73,8 @@
import org.elasticsearch.test.rest.FakeRestRequest;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
-import org.elasticsearch.transport.SharedGroupFactory;
import org.elasticsearch.transport.NettyAllocator;
+import org.elasticsearch.transport.SharedGroupFactory;
import org.junit.After;
import org.junit.Before;
@@ -101,6 +104,7 @@ public class Netty4HttpServerTransportTests extends ESTestCase {
private ThreadPool threadPool;
private MockBigArrays bigArrays;
private ClusterSettings clusterSettings;
+ private MediaTypeParser> mediaTypeParser;
@Before
public void setup() throws Exception {
@@ -108,6 +112,7 @@ public void setup() throws Exception {
threadPool = new TestThreadPool("test");
bigArrays = new MockBigArrays(new MockPageCacheRecycler(Settings.EMPTY), new NoneCircuitBreakerService());
clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
+ mediaTypeParser = new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS));
}
@After
@@ -174,7 +179,7 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext,
}
};
try (Netty4HttpServerTransport transport = new Netty4HttpServerTransport(settings, networkService, bigArrays, threadPool,
- xContentRegistry(), dispatcher, clusterSettings, new SharedGroupFactory(settings))) {
+ xContentRegistry(), dispatcher, clusterSettings, new SharedGroupFactory(settings), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
try (Netty4HttpClient client = new Netty4HttpClient()) {
@@ -208,7 +213,7 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext,
public void testBindUnavailableAddress() {
Settings initialSettings = createSettings();
try (Netty4HttpServerTransport transport = new Netty4HttpServerTransport(initialSettings, networkService, bigArrays, threadPool,
- xContentRegistry(), new NullDispatcher(), clusterSettings, new SharedGroupFactory(Settings.EMPTY))) {
+ xContentRegistry(), new NullDispatcher(), clusterSettings, new SharedGroupFactory(Settings.EMPTY), mediaTypeParser)) {
transport.start();
TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
Settings settings = Settings.builder()
@@ -216,7 +221,7 @@ public void testBindUnavailableAddress() {
.put("network.host", remoteAddress.getAddress())
.build();
try (Netty4HttpServerTransport otherTransport = new Netty4HttpServerTransport(settings, networkService, bigArrays, threadPool,
- xContentRegistry(), new NullDispatcher(), clusterSettings, new SharedGroupFactory(settings))) {
+ xContentRegistry(), new NullDispatcher(), clusterSettings, new SharedGroupFactory(settings), mediaTypeParser)) {
BindHttpException bindHttpException = expectThrows(BindHttpException.class, otherTransport::start);
assertEquals(
"Failed to bind to " + NetworkAddress.format(remoteAddress.address()),
@@ -262,7 +267,7 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th
try (Netty4HttpServerTransport transport = new Netty4HttpServerTransport(
settings, networkService, bigArrays, threadPool, xContentRegistry(), dispatcher, clusterSettings,
- new SharedGroupFactory(settings))) {
+ new SharedGroupFactory(settings), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
@@ -312,7 +317,7 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th
try (Netty4HttpServerTransport transport = new Netty4HttpServerTransport(
Settings.EMPTY, networkService, bigArrays, threadPool, xContentRegistry(), dispatcher, clusterSettings,
- new SharedGroupFactory(Settings.EMPTY))) {
+ new SharedGroupFactory(Settings.EMPTY), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
@@ -372,7 +377,7 @@ public void dispatchBadRequest(final RestChannel channel,
try (Netty4HttpServerTransport transport = new Netty4HttpServerTransport(settings, networkService, bigArrays, threadPool,
xContentRegistry(), dispatcher, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
- new SharedGroupFactory(settings))) {
+ new SharedGroupFactory(settings), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
@@ -435,7 +440,7 @@ public void dispatchBadRequest(final RestChannel channel,
NioEventLoopGroup group = new NioEventLoopGroup();
try (Netty4HttpServerTransport transport = new Netty4HttpServerTransport(settings, networkService, bigArrays, threadPool,
xContentRegistry(), dispatcher, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
- new SharedGroupFactory(settings))) {
+ new SharedGroupFactory(settings), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
diff --git a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpServerTransport.java b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpServerTransport.java
index f10d9538d9e34..816d50ea3da1d 100644
--- a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpServerTransport.java
+++ b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpServerTransport.java
@@ -30,6 +30,7 @@
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.http.AbstractHttpServerTransport;
import org.elasticsearch.http.HttpChannel;
@@ -86,8 +87,9 @@ public class NioHttpServerTransport extends AbstractHttpServerTransport {
public NioHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler, ThreadPool threadPool, NamedXContentRegistry xContentRegistry,
- Dispatcher dispatcher, NioGroupFactory nioGroupFactory, ClusterSettings clusterSettings) {
- super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings);
+ Dispatcher dispatcher, NioGroupFactory nioGroupFactory, ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser) {
+ super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings, mediaTypeParser);
this.pageAllocator = new PageAllocator(pageCacheRecycler);
this.nioGroupFactory = nioGroupFactory;
diff --git a/plugins/transport-nio/src/main/java/org/elasticsearch/transport/nio/NioTransportPlugin.java b/plugins/transport-nio/src/main/java/org/elasticsearch/transport/nio/NioTransportPlugin.java
index 1da90e35ba7f4..bcf2811385b99 100644
--- a/plugins/transport-nio/src/main/java/org/elasticsearch/transport/nio/NioTransportPlugin.java
+++ b/plugins/transport-nio/src/main/java/org/elasticsearch/transport/nio/NioTransportPlugin.java
@@ -31,8 +31,10 @@
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
import org.elasticsearch.common.util.concurrent.EsExecutors;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.http.HttpServerTransport;
+import org.elasticsearch.http.HttpServerTransport.Dispatcher;
import org.elasticsearch.http.nio.NioHttpServerTransport;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.plugins.NetworkPlugin;
@@ -82,16 +84,20 @@ public Map> getTransports(Settings settings, ThreadP
}
@Override
- public Map> getHttpTransports(Settings settings, ThreadPool threadPool, BigArrays bigArrays,
+ public Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
+ BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher dispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher dispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser
+ ) {
return Collections.singletonMap(NIO_HTTP_TRANSPORT_NAME,
() -> new NioHttpServerTransport(settings, networkService, bigArrays, pageCacheRecycler, threadPool, xContentRegistry,
- dispatcher, getNioGroupFactory(settings), clusterSettings));
+ dispatcher, getNioGroupFactory(settings), clusterSettings, mediaTypeParser));
}
private synchronized NioGroupFactory getNioGroupFactory(Settings settings) {
diff --git a/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java b/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java
index d76864bfb5be2..f7e61abc61157 100644
--- a/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java
+++ b/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java
@@ -45,6 +45,9 @@
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.http.BindHttpException;
import org.elasticsearch.http.CorsHandler;
import org.elasticsearch.http.HttpServerTransport;
@@ -89,6 +92,7 @@ public class NioHttpServerTransportTests extends ESTestCase {
private ThreadPool threadPool;
private MockBigArrays bigArrays;
private MockPageCacheRecycler pageRecycler;
+ private MediaTypeParser> mediaTypeParser;
@Before
public void setup() throws Exception {
@@ -96,6 +100,7 @@ public void setup() throws Exception {
threadPool = new TestThreadPool("test");
pageRecycler = new MockPageCacheRecycler(Settings.EMPTY);
bigArrays = new MockBigArrays(pageRecycler, new NoneCircuitBreakerService());
+ mediaTypeParser = new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS));
}
@After
@@ -162,7 +167,7 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext,
};
try (NioHttpServerTransport transport = new NioHttpServerTransport(settings, networkService, bigArrays, pageRecycler, threadPool,
xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))) {
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
try (NioHttpClient client = new NioHttpClient()) {
@@ -197,7 +202,7 @@ public void testBindUnavailableAddress() {
final Settings initialSettings = createSettings();
try (NioHttpServerTransport transport = new NioHttpServerTransport(initialSettings, networkService, bigArrays, pageRecycler,
threadPool, xContentRegistry(), new NullDispatcher(), new NioGroupFactory(Settings.EMPTY, logger),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))) {
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser)) {
transport.start();
TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
Settings settings = Settings.builder()
@@ -206,7 +211,7 @@ threadPool, xContentRegistry(), new NullDispatcher(), new NioGroupFactory(Settin
.build();
try (NioHttpServerTransport otherTransport = new NioHttpServerTransport(settings, networkService, bigArrays, pageRecycler,
threadPool, xContentRegistry(), new NullDispatcher(), new NioGroupFactory(Settings.EMPTY, logger),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))) {
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser)) {
BindHttpException bindHttpException = expectThrows(BindHttpException.class, () -> otherTransport.start());
assertEquals(
"Failed to bind to " + NetworkAddress.format(remoteAddress.address()),
@@ -243,7 +248,7 @@ public void dispatchBadRequest(final RestChannel channel,
try (NioHttpServerTransport transport = new NioHttpServerTransport(settings, networkService, bigArrays, pageRecycler,
threadPool, xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))) {
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
@@ -305,7 +310,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th
try (NioHttpServerTransport transport = new NioHttpServerTransport(
Settings.EMPTY, networkService, bigArrays, pageRecycler, threadPool, xContentRegistry(), dispatcher,
- new NioGroupFactory(Settings.EMPTY, logger), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))) {
+ new NioGroupFactory(Settings.EMPTY, logger), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
+ mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
@@ -361,7 +367,7 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th
try (NioHttpServerTransport transport = new NioHttpServerTransport(settings, networkService, bigArrays, pageRecycler,
threadPool, xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))) {
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
@@ -411,7 +417,7 @@ public void dispatchBadRequest(final RestChannel channel,
try (NioHttpServerTransport transport = new NioHttpServerTransport(settings, networkService, bigArrays, pageRecycler,
threadPool, xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))) {
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser)) {
transport.start();
final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses());
diff --git a/server/src/main/java/org/elasticsearch/action/ActionModule.java b/server/src/main/java/org/elasticsearch/action/ActionModule.java
index 3956b29d13b78..eb06509293c94 100644
--- a/server/src/main/java/org/elasticsearch/action/ActionModule.java
+++ b/server/src/main/java/org/elasticsearch/action/ActionModule.java
@@ -246,6 +246,7 @@
import org.elasticsearch.common.settings.IndexScopedSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsFilter;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.gateway.TransportNodesListGatewayMetaState;
import org.elasticsearch.gateway.TransportNodesListGatewayStartedShards;
import org.elasticsearch.index.seqno.GlobalCheckpointSyncAction;
@@ -461,7 +462,8 @@ public ActionModule(Settings settings, IndexNameExpressionResolver indexNameExpr
indicesAliasesRequestRequestValidators = new RequestValidators<>(
actionPlugins.stream().flatMap(p -> p.indicesAliasesRequestValidators().stream()).collect(Collectors.toList()));
- restController = new RestController(headers, restWrapper, nodeClient, circuitBreakerService, usageService, compatibleVersion);
+ restController =
+ new RestController(headers, restWrapper, nodeClient, circuitBreakerService, usageService, compatibleVersion);
}
public Map> getActions() {
diff --git a/server/src/main/java/org/elasticsearch/common/network/NetworkModule.java b/server/src/main/java/org/elasticsearch/common/network/NetworkModule.java
index 870d63ed5e57c..2fe581996291f 100644
--- a/server/src/main/java/org/elasticsearch/common/network/NetworkModule.java
+++ b/server/src/main/java/org/elasticsearch/common/network/NetworkModule.java
@@ -36,6 +36,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.http.HttpServerTransport;
@@ -114,11 +115,12 @@ public NetworkModule(Settings settings, List plugins, ThreadPool
NamedWriteableRegistry namedWriteableRegistry,
NamedXContentRegistry xContentRegistry,
NetworkService networkService, HttpServerTransport.Dispatcher dispatcher,
- ClusterSettings clusterSettings) {
+ ClusterSettings clusterSettings, MediaTypeParser> mediaTypeParser) {
this.settings = settings;
for (NetworkPlugin plugin : plugins) {
Map> httpTransportFactory = plugin.getHttpTransports(settings, threadPool, bigArrays,
- pageCacheRecycler, circuitBreakerService, xContentRegistry, networkService, dispatcher, clusterSettings);
+ pageCacheRecycler, circuitBreakerService, xContentRegistry, networkService, dispatcher, clusterSettings, mediaTypeParser
+ );
for (Map.Entry> entry : httpTransportFactory.entrySet()) {
registerHttpTransport(entry.getKey(), entry.getValue());
}
diff --git a/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java b/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java
index af8095d6dece1..0dc952b18dbc1 100644
--- a/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java
+++ b/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java
@@ -40,6 +40,7 @@
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestRequest;
@@ -77,6 +78,7 @@ public abstract class AbstractHttpServerTransport extends AbstractLifecycleCompo
protected final Dispatcher dispatcher;
protected final CorsHandler corsHandler;
private final NamedXContentRegistry xContentRegistry;
+ private final MediaTypeParser mediaTypeParser;
protected final PortsRange port;
protected final ByteSizeValue maxContentLength;
@@ -91,12 +93,14 @@ public abstract class AbstractHttpServerTransport extends AbstractLifecycleCompo
private final HttpTracer tracer;
protected AbstractHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, ThreadPool threadPool,
- NamedXContentRegistry xContentRegistry, Dispatcher dispatcher, ClusterSettings clusterSettings) {
+ NamedXContentRegistry xContentRegistry, Dispatcher dispatcher, ClusterSettings clusterSettings,
+ MediaTypeParser mediaTypeParser) {
this.settings = settings;
this.networkService = networkService;
this.bigArrays = bigArrays;
this.threadPool = threadPool;
this.xContentRegistry = xContentRegistry;
+ this.mediaTypeParser = mediaTypeParser;
this.dispatcher = dispatcher;
this.handlingSettings = HttpHandlingSettings.fromSettings(settings);
this.corsHandler = CorsHandler.fromSettings(settings);
@@ -344,13 +348,13 @@ private void handleIncomingRequest(final HttpRequest httpRequest, final HttpChan
{
RestRequest innerRestRequest;
try {
- innerRestRequest = RestRequest.request(xContentRegistry, httpRequest, httpChannel);
+ innerRestRequest = RestRequest.request(xContentRegistry, httpRequest, httpChannel, mediaTypeParser);
} catch (final RestRequest.ContentTypeHeaderException e) {
badRequestCause = ExceptionsHelper.useOrSuppress(badRequestCause, e);
innerRestRequest = requestWithoutContentTypeHeader(httpRequest, httpChannel, badRequestCause);
} catch (final RestRequest.BadParameterException e) {
badRequestCause = ExceptionsHelper.useOrSuppress(badRequestCause, e);
- innerRestRequest = RestRequest.requestWithoutParameters(xContentRegistry, httpRequest, httpChannel);
+ innerRestRequest = RestRequest.requestWithoutParameters(xContentRegistry, httpRequest, httpChannel, mediaTypeParser);
}
restRequest = innerRestRequest;
}
@@ -373,7 +377,8 @@ private void handleIncomingRequest(final HttpRequest httpRequest, final HttpChan
trace);
} catch (final IllegalArgumentException e) {
badRequestCause = ExceptionsHelper.useOrSuppress(badRequestCause, e);
- final RestRequest innerRequest = RestRequest.requestWithoutParameters(xContentRegistry, httpRequest, httpChannel);
+ final RestRequest innerRequest =
+ RestRequest.requestWithoutParameters(xContentRegistry, httpRequest, httpChannel, mediaTypeParser);
innerChannel =
new DefaultRestChannel(httpChannel, httpRequest, innerRequest, bigArrays, handlingSettings, threadContext, corsHandler,
trace);
@@ -387,10 +392,10 @@ private void handleIncomingRequest(final HttpRequest httpRequest, final HttpChan
private RestRequest requestWithoutContentTypeHeader(HttpRequest httpRequest, HttpChannel httpChannel, Exception badRequestCause) {
HttpRequest httpRequestWithoutContentType = httpRequest.removeHeader("Content-Type");
try {
- return RestRequest.request(xContentRegistry, httpRequestWithoutContentType, httpChannel);
+ return RestRequest.request(xContentRegistry, httpRequestWithoutContentType, httpChannel, mediaTypeParser);
} catch (final RestRequest.BadParameterException e) {
badRequestCause.addSuppressed(e);
- return RestRequest.requestWithoutParameters(xContentRegistry, httpRequestWithoutContentType, httpChannel);
+ return RestRequest.requestWithoutParameters(xContentRegistry, httpRequestWithoutContentType, httpChannel, mediaTypeParser);
}
}
diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java
index 15ca8e4f66bbd..cfa7637609a32 100644
--- a/server/src/main/java/org/elasticsearch/node/Node.java
+++ b/server/src/main/java/org/elasticsearch/node/Node.java
@@ -87,7 +87,11 @@
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeDefinition;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.internal.io.IOUtils;
import org.elasticsearch.discovery.Discovery;
import org.elasticsearch.discovery.DiscoveryModule;
@@ -330,6 +334,7 @@ protected Node(final Environment initialEnvironment,
.collect(Collectors.toSet());
DiscoveryNode.setAdditionalRoles(additionalRoles);
+
/*
* Create the environment based on the finalized view of the settings. This is to ensure that components get the same setting
* values, no matter they ask for them from.
@@ -529,16 +534,26 @@ protected Node(final Environment initialEnvironment,
repositoriesServiceReference::get).stream())
.collect(Collectors.toList());
+ List mediaTypeDefinitions = pluginsService.filterPlugins(ActionPlugin.class)
+ .stream()
+ .flatMap(plugin -> plugin.getAdditionalMediaTypes().stream())
+ .collect(toList());
+ mediaTypeDefinitions.addAll(XContentType.MEDIA_TYPE_DEFINITIONS);
+
+ MediaTypeRegistry globalMediaTypeRegistry = new MediaTypeRegistry(mediaTypeDefinitions);
+
+ CompatibleVersion restCompatibleFunction = getRestCompatibleFunction();
+
ActionModule actionModule = new ActionModule(settings, clusterModule.getIndexNameExpressionResolver(),
settingsModule.getIndexScopedSettings(), settingsModule.getClusterSettings(), settingsModule.getSettingsFilter(),
threadPool, pluginsService.filterPlugins(ActionPlugin.class), client, circuitBreakerService, usageService,
- systemIndices, getRestCompatibleFunction());
+ systemIndices, restCompatibleFunction);
modules.add(actionModule);
final RestController restController = actionModule.getRestController();
final NetworkModule networkModule = new NetworkModule(settings, pluginsService.filterPlugins(NetworkPlugin.class),
threadPool, bigArrays, pageCacheRecycler, circuitBreakerService, namedWriteableRegistry, xContentRegistry,
- networkService, restController, clusterService.getClusterSettings());
+ networkService, restController, clusterService.getClusterSettings(), new MediaTypeParser<>(globalMediaTypeRegistry));
Collection>> indexTemplateMetadataUpgraders =
pluginsService.filterPlugins(Plugin.class).stream()
.map(Plugin::getIndexTemplateMetadataUpgrader)
diff --git a/server/src/main/java/org/elasticsearch/plugins/ActionPlugin.java b/server/src/main/java/org/elasticsearch/plugins/ActionPlugin.java
index ef9861f266222..c7e37ba8c4c6d 100644
--- a/server/src/main/java/org/elasticsearch/plugins/ActionPlugin.java
+++ b/server/src/main/java/org/elasticsearch/plugins/ActionPlugin.java
@@ -19,9 +19,9 @@
package org.elasticsearch.plugins;
-import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
+import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
@@ -34,6 +34,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsFilter;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeDefinition;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.rest.RestHeaderDefinition;
@@ -181,4 +182,7 @@ default Collection> in
return Collections.emptyList();
}
+ default List getAdditionalMediaTypes() {
+ return List.of();
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/plugins/NetworkPlugin.java b/server/src/main/java/org/elasticsearch/plugins/NetworkPlugin.java
index a7c9e7bd842b7..d5e907e1a0288 100644
--- a/server/src/main/java/org/elasticsearch/plugins/NetworkPlugin.java
+++ b/server/src/main/java/org/elasticsearch/plugins/NetworkPlugin.java
@@ -18,11 +18,6 @@
*/
package org.elasticsearch.plugins;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
-
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.network.NetworkService;
import org.elasticsearch.common.settings.ClusterSettings;
@@ -30,13 +25,20 @@
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.http.HttpServerTransport;
+import org.elasticsearch.http.HttpServerTransport.Dispatcher;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportInterceptor;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
/**
* Plugin for extending network and transport related classes
*/
@@ -69,13 +71,16 @@ default Map> getTransports(Settings settings, Thread
* Returns a map of {@link HttpServerTransport} suppliers.
* See {@link org.elasticsearch.common.network.NetworkModule#HTTP_TYPE_SETTING} to configure a specific implementation.
*/
- default Map> getHttpTransports(Settings settings, ThreadPool threadPool, BigArrays bigArrays,
+ default Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
+ BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher dispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher dispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser) {
return Collections.emptyMap();
}
}
diff --git a/server/src/main/java/org/elasticsearch/plugins/RestCompatibilityPlugin.java b/server/src/main/java/org/elasticsearch/plugins/RestCompatibilityPlugin.java
index 9fd73a24ee87b..c5e4fcbe60059 100644
--- a/server/src/main/java/org/elasticsearch/plugins/RestCompatibilityPlugin.java
+++ b/server/src/main/java/org/elasticsearch/plugins/RestCompatibilityPlugin.java
@@ -21,6 +21,7 @@
import org.elasticsearch.Version;
import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
/**
@@ -35,5 +36,6 @@ public interface RestCompatibilityPlugin {
* @param hasContent - a flag indicating if a request has content
* @return a requested Compatible API Version
*/
- Version getCompatibleVersion(@Nullable String acceptHeader, @Nullable String contentTypeHeader, boolean hasContent);
+ Version getCompatibleVersion(@Nullable ParsedMediaType acceptHeader, @Nullable ParsedMediaType contentTypeHeader, boolean hasContent);
+
}
diff --git a/server/src/main/java/org/elasticsearch/rest/AbstractRestChannel.java b/server/src/main/java/org/elasticsearch/rest/AbstractRestChannel.java
index 6f5aa618ae4d0..8cd1ea1be29ef 100644
--- a/server/src/main/java/org/elasticsearch/rest/AbstractRestChannel.java
+++ b/server/src/main/java/org/elasticsearch/rest/AbstractRestChannel.java
@@ -101,11 +101,12 @@ public XContentBuilder newBuilder(@Nullable XContentType requestContentType, boo
public XContentBuilder newBuilder(@Nullable XContentType requestContentType, @Nullable XContentType responseContentType,
boolean useFiltering) throws IOException {
if (responseContentType == null) {
- if (Strings.hasText(format)) {
- responseContentType = XContentType.fromFormat(format);
+ if (request.getFormatMediaType() != null && request.getFormatMediaType() instanceof XContentType) {
+ responseContentType = (XContentType) request.getFormatMediaType();
}
- if (responseContentType == null) {
- responseContentType = XContentType.fromMediaType(acceptHeader);
+ if (responseContentType == null && request.getAcceptMediaType() != null &&
+ request.getAcceptMediaType().getMediaType() instanceof XContentType) {
+ responseContentType = (XContentType) request.getAcceptMediaType().getMediaType();
}
}
// try to determine the response content type from the media type or the format query string parameter, with the format parameter
diff --git a/server/src/main/java/org/elasticsearch/rest/CompatibleVersion.java b/server/src/main/java/org/elasticsearch/rest/CompatibleVersion.java
index 48ef5a8f8a87e..1f7461ea65f5b 100644
--- a/server/src/main/java/org/elasticsearch/rest/CompatibleVersion.java
+++ b/server/src/main/java/org/elasticsearch/rest/CompatibleVersion.java
@@ -21,6 +21,7 @@
import org.elasticsearch.Version;
import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
/**
* An interface used to specify a function that returns a compatible API version
@@ -28,7 +29,7 @@
*/
@FunctionalInterface
public interface CompatibleVersion {
- Version get(@Nullable String acceptHeader, @Nullable String contentTypeHeader, boolean hasContent);
+ Version get(@Nullable ParsedMediaType acceptMediaType, @Nullable ParsedMediaType contentTypeHeader, boolean hasContent);
CompatibleVersion CURRENT_VERSION = (acceptHeader, contentTypeHeader, hasContent) -> Version.CURRENT;
}
diff --git a/server/src/main/java/org/elasticsearch/rest/RestController.java b/server/src/main/java/org/elasticsearch/rest/RestController.java
index 5f404100939eb..0484c9089633d 100644
--- a/server/src/main/java/org/elasticsearch/rest/RestController.java
+++ b/server/src/main/java/org/elasticsearch/rest/RestController.java
@@ -34,6 +34,7 @@
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.path.PathTrie;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.internal.io.Streams;
@@ -91,7 +92,7 @@ public class RestController implements HttpServerTransport.Dispatcher {
/** Rest headers that are copied to internal requests made during a rest request. */
private final Set headersToCopy;
private final UsageService usageService;
- private CompatibleVersion compatibleVersion;
+ private final CompatibleVersion compatibleVersion;
public RestController(Set headersToCopy, UnaryOperator handlerWrapper,
@@ -229,14 +230,16 @@ private void dispatchRequest(RestRequest request, RestChannel channel, RestHandl
throws Exception {
final int contentLength = request.contentLength();
if (contentLength > 0) {
- final XContentType xContentType = request.getXContentType();
- if (xContentType == null) {
+ final ParsedMediaType parsedMediaType = request.getContentType();
+ if (parsedMediaType == null) {
sendContentTypeErrorMessage(request.getAllHeaderValues("Content-Type"), channel);
return;
}
- if (handler.supportsContentStream() && xContentType != XContentType.JSON && xContentType != XContentType.SMILE) {
+ if (handler.supportsContentStream() && parsedMediaType.getMediaType() != XContentType.JSON &&
+ parsedMediaType.getMediaType() != XContentType.SMILE) {
channel.sendResponse(BytesRestResponse.createSimpleErrorResponse(channel, RestStatus.NOT_ACCEPTABLE,
- "Content-Type [" + xContentType + "] does not support stream parsing. Use JSON or SMILE instead"));
+ "Content-Type [" + parsedMediaType.getMediaType().typeWithSubtype() + "] does not support stream parsing. " +
+ "Use JSON or SMILE instead"));
return;
}
}
@@ -324,8 +327,8 @@ private void tryAllHandlers(final RestRequest request, final RestChannel channel
final String rawPath = request.rawPath();
final String uri = request.uri();
final RestRequest.Method requestMethod;
- //TODO: USAGE_1 now that we have a version we can implement a REST handler that accepts path, method AND version
- Version version = compatibleVersion.get(request.header("Accept"), request.header("Content-Type"), request.hasContent());
+
+ Version version = compatibleVersion.get(request.getContentType(), request.getContentType(), request.hasContent());
try {
// Resolves the HTTP method and fails if the method is invalid
diff --git a/server/src/main/java/org/elasticsearch/rest/RestRequest.java b/server/src/main/java/org/elasticsearch/rest/RestRequest.java
index 83f1a89798531..1197e5d5f4e44 100644
--- a/server/src/main/java/org/elasticsearch/rest/RestRequest.java
+++ b/server/src/main/java/org/elasticsearch/rest/RestRequest.java
@@ -19,7 +19,6 @@
package org.elasticsearch.rest;
-import org.apache.lucene.util.SetOnce;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.CheckedConsumer;
@@ -31,6 +30,9 @@
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
+import org.elasticsearch.common.xcontent.MediaType;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentParser;
@@ -48,7 +50,6 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.elasticsearch.common.unit.ByteSizeValue.parseBytesSizeValue;
@@ -56,9 +57,6 @@
public class RestRequest implements ToXContent.Params {
- // tchar pattern as defined by RFC7230 section 3.2.6
- private static final Pattern TCHAR_PATTERN = Pattern.compile("[a-zA-z0-9!#$%&'*+\\-.\\^_`|~]+");
-
private static final AtomicLong requestIdGenerator = new AtomicLong();
private final NamedXContentRegistry xContentRegistry;
@@ -66,8 +64,14 @@ public class RestRequest implements ToXContent.Params {
private final Map> headers;
private final String rawPath;
private final Set consumedParams = new HashSet<>();
- private final SetOnce xContentType = new SetOnce<>();
private final HttpChannel httpChannel;
+ private final MediaTypeParser> mediaTypeParser;
+ @Nullable
+ private final ParsedMediaType contentType;
+ @Nullable
+ private final ParsedMediaType acceptMediaType;
+ @Nullable
+ private final MediaType formatMediaType;
private HttpRequest httpRequest;
@@ -81,27 +85,21 @@ public boolean isContentConsumed() {
// for testing
protected RestRequest(NamedXContentRegistry xContentRegistry, Map params, String path,
- Map> headers, HttpRequest httpRequest, HttpChannel httpChannel) {
- this(xContentRegistry, params, path, headers, httpRequest, httpChannel, requestIdGenerator.incrementAndGet());
+ Map> headers, HttpRequest httpRequest, HttpChannel httpChannel,
+ MediaTypeParser> mediaTypeParser) {
+ this(xContentRegistry, params, path, headers, httpRequest, httpChannel, mediaTypeParser, requestIdGenerator.incrementAndGet());
}
protected RestRequest(RestRequest restRequest) {
this(restRequest.getXContentRegistry(), restRequest.params(), restRequest.path(), restRequest.getHeaders(),
- restRequest.getHttpRequest(), restRequest.getHttpChannel(), restRequest.getRequestId());
+ restRequest.getHttpRequest(), restRequest.getHttpChannel(), restRequest.mediaTypeParser, restRequest.getRequestId());
}
private RestRequest(NamedXContentRegistry xContentRegistry, Map params, String path,
- Map> headers, HttpRequest httpRequest, HttpChannel httpChannel, long requestId) {
- final XContentType xContentType;
- try {
- xContentType = parseContentType(headers.get("Content-Type"));
- } catch (final IllegalArgumentException e) {
- throw new ContentTypeHeaderException(e);
- }
- if (xContentType != null) {
- this.xContentType.set(xContentType);
- }
+ Map> headers, HttpRequest httpRequest, HttpChannel httpChannel,
+ MediaTypeParser> mediaTypeParser, long requestId) {
+ this.mediaTypeParser = mediaTypeParser;
this.xContentRegistry = xContentRegistry;
this.httpRequest = httpRequest;
this.httpChannel = httpChannel;
@@ -109,6 +107,9 @@ private RestRequest(NamedXContentRegistry xContentRegistry, Map
this.rawPath = path;
this.headers = Collections.unmodifiableMap(headers);
this.requestId = requestId;
+ this.acceptMediaType = mediaTypeParser.parseMediaType(header("Accept"));
+ this.contentType = mediaTypeParser.parseMediaType(header("Content-Type"));
+ this.formatMediaType = mediaTypeParser.fromFormat(param("format"));
}
@@ -132,10 +133,11 @@ void ensureSafeBuffers() {
* @throws BadParameterException if the parameters can not be decoded
* @throws ContentTypeHeaderException if the Content-Type header can not be parsed
*/
- public static RestRequest request(NamedXContentRegistry xContentRegistry, HttpRequest httpRequest, HttpChannel httpChannel) {
+ public static RestRequest request(NamedXContentRegistry xContentRegistry, HttpRequest httpRequest, HttpChannel httpChannel,
+ MediaTypeParser> mediaTypeParser) {
Map params = params(httpRequest.uri());
String path = path(httpRequest.uri());
- return new RestRequest(xContentRegistry, params, path, httpRequest.getHeaders(), httpRequest, httpChannel,
+ return new RestRequest(xContentRegistry, params, path, httpRequest.getHeaders(), httpRequest, httpChannel, mediaTypeParser,
requestIdGenerator.incrementAndGet());
}
@@ -171,10 +173,10 @@ private static String path(final String uri) {
* @throws ContentTypeHeaderException if the Content-Type header can not be parsed
*/
public static RestRequest requestWithoutParameters(NamedXContentRegistry xContentRegistry, HttpRequest httpRequest,
- HttpChannel httpChannel) {
+ HttpChannel httpChannel, MediaTypeParser> mediaTypeParser) {
Map params = Collections.emptyMap();
return new RestRequest(xContentRegistry, params, httpRequest.uri(), httpRequest.getHeaders(), httpRequest, httpChannel,
- requestIdGenerator.incrementAndGet());
+ mediaTypeParser, requestIdGenerator.incrementAndGet());
}
public enum Method {
@@ -231,7 +233,7 @@ public BytesReference content() {
public final BytesReference requiredContent() {
if (hasContent() == false) {
throw new ElasticsearchParseException("request body is required");
- } else if (xContentType.get() == null) {
+ } else if (contentType == null) {
throw new IllegalStateException("unknown content type");
}
return content();
@@ -277,7 +279,29 @@ public final long getRequestId() {
*/
@Nullable
public final XContentType getXContentType() {
- return xContentType.get();
+ if (contentType != null) {
+ if (contentType.getMediaType() instanceof XContentType) {
+ return (XContentType) contentType.getMediaType();
+ } else {
+ throw new IllegalStateException("content type [" + contentType.getMediaType().typeWithSubtype() + "] is not xcontent");
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public final ParsedMediaType getContentType() {
+ return contentType;
+ }
+
+ @Nullable
+ public final ParsedMediaType getAcceptMediaType() {
+ return acceptMediaType;
+ }
+
+ @Nullable
+ public final MediaType getFormatMediaType() {
+ return formatMediaType;
}
public HttpChannel getHttpChannel() {
@@ -426,7 +450,12 @@ public NamedXContentRegistry getXContentRegistry() {
*/
public final XContentParser contentParser() throws IOException {
BytesReference content = requiredContent(); // will throw exception if body or content type missing
- return xContentType.get().xContent().createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, content.streamInput());
+ if (contentType.getMediaType() instanceof XContentType) {
+ return ((XContentType) contentType.getMediaType()).xContent()
+ .createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, content.streamInput());
+ } else {
+ throw new IllegalStateException("content type [" + contentType.getMediaType().typeWithSubtype() + "] is not xcontent");
+ }
}
/**
@@ -486,7 +515,11 @@ public final Tuple contentOrSourceParam() {
if (hasContentOrSourceParam() == false) {
throw new ElasticsearchParseException("request body or source parameter is required");
} else if (hasContent()) {
- return new Tuple<>(xContentType.get(), requiredContent());
+ if (contentType.getMediaType() instanceof XContentType) {
+ return new Tuple<>((XContentType) contentType.getMediaType(), requiredContent());
+ } else {
+ throw new IllegalStateException("content type [" + contentType.getMediaType().typeWithSubtype() + "] is not xcontent");
+ }
}
String source = param("source");
String typeParam = param("source_content_type");
@@ -505,7 +538,7 @@ public final Tuple contentOrSourceParam() {
* Parses the given content type string for the media type. This method currently ignores parameters.
*/
// TODO stop ignoring parameters such as charset...
- public static XContentType parseContentType(List header) {
+ public XContentType parseContentType(List header) {
if (header == null || header.isEmpty()) {
return null;
} else if (header.size() > 1) {
@@ -513,17 +546,16 @@ public static XContentType parseContentType(List header) {
}
String rawContentType = header.get(0);
- final String[] elements = rawContentType.split("[ \t]*;");
- if (elements.length > 0) {
- final String[] splitMediaType = elements[0].split("/");
- if (splitMediaType.length == 2 && TCHAR_PATTERN.matcher(splitMediaType[0]).matches()
- && TCHAR_PATTERN.matcher(splitMediaType[1].trim()).matches()) {
- return XContentType.fromMediaType(elements[0]);
+ ParsedMediaType parsedMediaType = mediaTypeParser.parseMediaType(rawContentType);
+ if (parsedMediaType != null) {
+ if (parsedMediaType.getMediaType() instanceof XContentType) {
+ return (XContentType) parsedMediaType.getMediaType();
} else {
- throw new IllegalArgumentException("invalid Content-Type header [" + rawContentType + "]");
+ throw new IllegalArgumentException("content type [" + contentType.getMediaType().typeWithSubtype() + "] is not xcontent");
}
+ } else {
+ throw new IllegalArgumentException("unsupported Content-Type header [" + rawContentType + "]");
}
- throw new IllegalArgumentException("empty Content-Type header");
}
public static class ContentTypeHeaderException extends RuntimeException {
diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java
index bbb746baafe92..b287bd791b4f8 100644
--- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java
+++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java
@@ -59,10 +59,11 @@ public static RestResponse buildResponse(Table table, RestChannel channel) throw
}
private static XContentType getXContentType(RestRequest request) {
- if (request.hasParam("format")) {
- return XContentType.fromFormat(request.param("format"));
+ if (request.getFormatMediaType() != null && request.getFormatMediaType() instanceof XContentType) {
+ return (XContentType) request.getFormatMediaType();
}
- return XContentType.fromMediaType(request.header("Accept"));
+ return request.getAcceptMediaType() != null && request.getAcceptMediaType().getMediaType() instanceof XContentType ?
+ (XContentType) request.getAcceptMediaType().getMediaType() : null;
}
public static RestResponse buildXContentBuilder(Table table, RestChannel channel) throws Exception {
diff --git a/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java b/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java
index 43fd669f71da3..abb4582f2deab 100644
--- a/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java
+++ b/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java
@@ -27,9 +27,13 @@
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.http.HttpInfo;
import org.elasticsearch.http.HttpServerTransport;
+import org.elasticsearch.http.HttpServerTransport.Dispatcher;
import org.elasticsearch.http.HttpStats;
import org.elasticsearch.http.NullDispatcher;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
@@ -112,14 +116,17 @@ public void testRegisterHttpTransport() {
NetworkModule module = newNetworkModule(settings, new NetworkPlugin() {
@Override
- public Map> getHttpTransports(Settings settings, ThreadPool threadPool,
+ public Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher requestDispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher requestDispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser
+ ) {
return Collections.singletonMap("custom", custom);
}
});
@@ -150,14 +157,17 @@ public Map> getTransports(Settings settings, ThreadP
}
@Override
- public Map> getHttpTransports(Settings settings, ThreadPool threadPool,
+ public Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher requestDispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher requestDispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser
+ ) {
Map> supplierMap = new HashMap<>();
supplierMap.put("custom", custom);
supplierMap.put("default_custom", def);
@@ -186,14 +196,17 @@ public Map> getTransports(Settings settings, ThreadP
}
@Override
- public Map> getHttpTransports(Settings settings, ThreadPool threadPool,
+ public Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher requestDispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher requestDispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser
+ ) {
Map> supplierMap = new HashMap<>();
supplierMap.put("custom", custom);
supplierMap.put("default_custom", def);
@@ -259,6 +272,7 @@ public List getTransportInterceptors(NamedWriteableRegistr
private NetworkModule newNetworkModule(Settings settings, NetworkPlugin... plugins) {
return new NetworkModule(settings, Arrays.asList(plugins), threadPool, null, null, null, null,
xContentRegistry(), null, new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
}
}
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 fa937f84d615c..6bc8ed29f15e7 100644
--- a/server/src/test/java/org/elasticsearch/common/xcontent/XContentTypeTests.java
+++ b/server/src/test/java/org/elasticsearch/common/xcontent/XContentTypeTests.java
@@ -23,7 +23,6 @@
import java.util.Locale;
import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
public class XContentTypeTests extends ESTestCase {
@@ -118,33 +117,6 @@ public void testVersionedMediaType() {
equalTo(XContentType.JSON));
}
- public void testVersionParsing() {
- byte version = (byte) Math.abs(randomByte());
- assertThat(XContentType.parseVersion("application/vnd.elasticsearch+json;compatible-with=" + version),
- equalTo(version));
- assertThat(XContentType.parseVersion("application/vnd.elasticsearch+cbor;compatible-with=" + version),
- equalTo(version));
- assertThat(XContentType.parseVersion("application/vnd.elasticsearch+smile;compatible-with=" + version),
- equalTo(version));
- assertThat(XContentType.parseVersion("application/vnd.elasticsearch+x-ndjson;compatible-with=" + version),
- equalTo(version));
- assertThat(XContentType.parseVersion("application/json"),
- nullValue());
-
-
- assertThat(XContentType.parseVersion("APPLICATION/VND.ELASTICSEARCH+JSON;COMPATIBLE-WITH=" + version),
- equalTo(version));
- assertThat(XContentType.parseVersion("APPLICATION/JSON"),
- nullValue());
-
- assertThat(XContentType.parseVersion("application/json;compatible-with=" + version + ".0"),
- is(nullValue()));
- }
-
- public void testUnrecognizedParameter() {
- assertThat(XContentType.parseVersion("application/json; sth=123"),
- is(nullValue())); }
-
public void testMediaTypeWithoutESSubtype() {
String version = String.valueOf(Math.abs(randomByte()));
assertThat(XContentType.fromMediaType("application/json;compatible-with=" + version), nullValue());
diff --git a/server/src/test/java/org/elasticsearch/http/AbstractHttpServerTransportTests.java b/server/src/test/java/org/elasticsearch/http/AbstractHttpServerTransportTests.java
index 348e8a83af731..2ec7a39d514f5 100644
--- a/server/src/test/java/org/elasticsearch/http/AbstractHttpServerTransportTests.java
+++ b/server/src/test/java/org/elasticsearch/http/AbstractHttpServerTransportTests.java
@@ -33,7 +33,10 @@
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestRequest;
@@ -140,7 +143,8 @@ public void dispatchBadRequest(final RestChannel channel,
try (AbstractHttpServerTransport transport =
new AbstractHttpServerTransport(Settings.EMPTY, networkService, bigArrays, threadPool, xContentRegistry(), dispatcher,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)) {
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS))) {
@Override
protected HttpServerChannel bind(InetSocketAddress hostAddress) {
@@ -199,7 +203,7 @@ public void dispatchRequest(RestRequest request, RestChannel channel, ThreadCont
public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext, Throwable cause) {
channel.sendResponse(emptyResponse(RestStatus.BAD_REQUEST));
}
- }, clusterSettings) {
+ }, clusterSettings, new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS))) {
@Override
protected HttpServerChannel bind(InetSocketAddress hostAddress) {
return null;
diff --git a/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java b/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java
index d633c2211b6e2..f9a43c97187bb 100644
--- a/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java
+++ b/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java
@@ -31,7 +31,10 @@
import org.elasticsearch.common.util.ByteArray;
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
import org.elasticsearch.rest.BytesRestResponse;
@@ -148,7 +151,8 @@ public void testHeadersSet() {
Settings settings = Settings.builder().build();
final TestHttpRequest httpRequest = new TestHttpRequest(HttpRequest.HttpVersion.HTTP_1_1, RestRequest.Method.GET, "/");
httpRequest.getHeaders().put(Task.X_OPAQUE_ID, Collections.singletonList("abc"));
- final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel);
+ final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel,
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
HttpHandlingSettings handlingSettings = HttpHandlingSettings.fromSettings(settings);
// send a response
@@ -176,7 +180,8 @@ public void testCookiesSet() {
Settings settings = Settings.builder().put(HttpTransportSettings.SETTING_HTTP_RESET_COOKIES.getKey(), true).build();
final TestHttpRequest httpRequest = new TestHttpRequest(HttpRequest.HttpVersion.HTTP_1_1, RestRequest.Method.GET, "/");
httpRequest.getHeaders().put(Task.X_OPAQUE_ID, Collections.singletonList("abc"));
- final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel);
+ final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel,
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
HttpHandlingSettings handlingSettings = HttpHandlingSettings.fromSettings(settings);
// send a response
@@ -197,7 +202,8 @@ public void testCookiesSet() {
public void testReleaseInListener() throws IOException {
final Settings settings = Settings.builder().build();
final TestHttpRequest httpRequest = new TestHttpRequest(HttpRequest.HttpVersion.HTTP_1_1, RestRequest.Method.GET, "/");
- final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel);
+ final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel,
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
HttpHandlingSettings handlingSettings = HttpHandlingSettings.fromSettings(settings);
DefaultRestChannel channel = new DefaultRestChannel(httpChannel, httpRequest, request, bigArrays, handlingSettings,
@@ -251,7 +257,8 @@ public void testConnectionClose() throws Exception {
httpRequest.getHeaders().put(DefaultRestChannel.CONNECTION, Collections.singletonList(DefaultRestChannel.KEEP_ALIVE));
}
}
- final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel);
+ final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel,
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
HttpHandlingSettings handlingSettings = HttpHandlingSettings.fromSettings(settings);
@@ -283,7 +290,7 @@ public void testUnsupportedHttpMethod() {
public RestRequest.Method method() {
throw new IllegalArgumentException("test");
}
- }, httpChannel);
+ }, httpChannel, new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
request.getHttpRequest().getHeaders().put(DefaultRestChannel.CONNECTION, Collections.singletonList(httpConnectionHeaderValue));
DefaultRestChannel channel = new DefaultRestChannel(httpChannel, request.getHttpRequest(), request, bigArrays,
@@ -321,7 +328,7 @@ public void testCloseOnException() {
public HttpResponse createResponse(RestStatus status, BytesReference content) {
throw new IllegalArgumentException("test");
}
- }, httpChannel);
+ }, httpChannel, new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
request.getHttpRequest().getHeaders().put(DefaultRestChannel.CONNECTION, Collections.singletonList(httpConnectionHeaderValue));
DefaultRestChannel channel = new DefaultRestChannel(httpChannel, request.getHttpRequest(), request, bigArrays,
@@ -352,7 +359,8 @@ private TestHttpResponse executeRequest(final Settings settings, final String or
httpRequest.getHeaders().put(CorsHandler.ORIGIN, Collections.singletonList(originValue));
}
httpRequest.getHeaders().put(CorsHandler.HOST, Collections.singletonList(host));
- final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel);
+ final RestRequest request = RestRequest.request(xContentRegistry(), httpRequest, httpChannel,
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
HttpHandlingSettings httpHandlingSettings = HttpHandlingSettings.fromSettings(settings);
RestChannel channel = new DefaultRestChannel(httpChannel, httpRequest, request, bigArrays, httpHandlingSettings,
diff --git a/server/src/test/java/org/elasticsearch/node/NodeTests.java b/server/src/test/java/org/elasticsearch/node/NodeTests.java
index 3741b172653a1..a8537b39e4267 100644
--- a/server/src/test/java/org/elasticsearch/node/NodeTests.java
+++ b/server/src/test/java/org/elasticsearch/node/NodeTests.java
@@ -28,6 +28,7 @@
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.BoundTransportAddress;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.engine.Engine.Searcher;
@@ -345,14 +346,14 @@ public void setCircuitBreaker(CircuitBreaker circuitBreaker) {
public static class TestRestCompatibility1 extends Plugin implements RestCompatibilityPlugin {
@Override
- public Version getCompatibleVersion(String acceptHeader, String contentTypeHeader, boolean hasContent) {
+ public Version getCompatibleVersion(ParsedMediaType acceptMediaType, ParsedMediaType contentType, boolean hasContent) {
return Version.CURRENT.previousMajor();
}
}
public static class TestRestCompatibility2 extends Plugin implements RestCompatibilityPlugin {
@Override
- public Version getCompatibleVersion(String acceptHeader, String contentTypeHeader, boolean hasContent) {
+ public Version getCompatibleVersion(ParsedMediaType acceptMediaType, ParsedMediaType contentType, boolean hasContent) {
return null;
}
}
@@ -377,7 +378,7 @@ public void testCorrectUsageOfRestCompatibilityPlugin() throws IOException {
try (Node node = new MockNode(settings.build(), plugins)) {
CompatibleVersion restCompatibleFunction = node.getRestCompatibleFunction();
- assertThat(restCompatibleFunction.get("", "", false), equalTo(Version.CURRENT.previousMajor()));
+ assertThat(restCompatibleFunction.get(null, null, false), equalTo(Version.CURRENT.previousMajor()));
}
}
@@ -390,7 +391,7 @@ public void testDefaultingRestCompatibilityPlugin() throws IOException {
try (Node node = new MockNode(settings.build(), plugins)) {
CompatibleVersion restCompatibleFunction = node.getRestCompatibleFunction();
- assertThat(restCompatibleFunction.get("", "", false), equalTo(Version.CURRENT));
+ assertThat(restCompatibleFunction.get(null, null, false), equalTo(Version.CURRENT));
}
}
}
diff --git a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java
index 2dd6d00b43e88..10d1026590e48 100644
--- a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java
+++ b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java
@@ -30,6 +30,8 @@
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
@@ -98,7 +100,7 @@ public void setup() {
HttpServerTransport httpServerTransport = new TestHttpServerTransport();
client = new NoOpNodeClient(this.getTestName());
restController = new RestController(Collections.emptySet(), null, client, circuitBreakerService, usageService,
- , CompatibleVersion.CURRENT_VERSION);
+ CompatibleVersion.CURRENT_VERSION);
restController.registerHandler(RestRequest.Method.GET, "/",
(request, channel, client) -> channel.sendResponse(
new BytesRestResponse(RestStatus.OK, BytesRestResponse.TEXT_CONTENT_TYPE, BytesArray.EMPTY)));
@@ -394,7 +396,7 @@ public void testDispatchWithContentStream() {
String content = randomAlphaOfLength((int) Math.round(BREAKER_LIMIT.getBytes() / inFlightRequestsBreaker.getOverhead()));
final List contentTypeHeader = Collections.singletonList(mimeType);
FakeRestRequest fakeRestRequest = new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY)
- .withContent(new BytesArray(content), RestRequest.parseContentType(contentTypeHeader)).withPath("/foo")
+ .withContent(new BytesArray(content), mimeType.contains("json") ? XContentType.JSON : XContentType.SMILE).withPath("/foo")
.withHeaders(Collections.singletonMap("Content-Type", contentTypeHeader)).build();
AssertingChannel channel = new AssertingChannel(fakeRestRequest, true, RestStatus.OK);
restController.registerHandler(RestRequest.Method.GET, "/foo", new RestHandler() {
@@ -616,7 +618,7 @@ public HttpRequest releaseAndCopy() {
public Exception getInboundException() {
return null;
}
- }, null);
+ }, null, new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
final AssertingChannel channel = new AssertingChannel(request, true, RestStatus.METHOD_NOT_ALLOWED);
assertFalse(channel.getSendResponseCalled());
diff --git a/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java b/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java
index 487bbed5a5999..e435e07b036a6 100644
--- a/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java
@@ -23,6 +23,8 @@
import org.elasticsearch.common.CheckedConsumer;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
@@ -94,7 +96,8 @@ private void runConsumesContentTest(
when (httpRequest.getHeaders()).thenReturn(
Collections.singletonMap("Content-Type", Collections.singletonList(randomFrom("application/json", "application/x-ndjson"))));
final RestRequest request =
- RestRequest.request(mock(NamedXContentRegistry.class), httpRequest, mock(HttpChannel.class));
+ RestRequest.request(mock(NamedXContentRegistry.class), httpRequest, mock(HttpChannel.class),
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
assertFalse(request.isContentConsumed());
try {
consumer.accept(request);
@@ -265,7 +268,8 @@ private static final class ContentRestRequest extends RestRequest {
private ContentRestRequest(RestRequest restRequest) {
super(restRequest.getXContentRegistry(), restRequest.params(), restRequest.path(), restRequest.getHeaders(),
- restRequest.getHttpRequest(), restRequest.getHttpChannel());
+ restRequest.getHttpRequest(), restRequest.getHttpChannel(),
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
this.restRequest = restRequest;
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/FakeRestRequest.java b/test/framework/src/main/java/org/elasticsearch/test/rest/FakeRestRequest.java
index e36d4ae13b668..1d82e024b2d30 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/rest/FakeRestRequest.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/rest/FakeRestRequest.java
@@ -22,6 +22,8 @@
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.http.HttpChannel;
@@ -45,7 +47,8 @@ public FakeRestRequest() {
private FakeRestRequest(NamedXContentRegistry xContentRegistry, HttpRequest httpRequest, Map params,
HttpChannel httpChannel) {
- super(xContentRegistry, params, httpRequest.uri(), httpRequest.getHeaders(), httpRequest, httpChannel);
+ super(xContentRegistry, params, httpRequest.uri(), httpRequest.getHeaders(), httpRequest, httpChannel,
+ new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS)));
}
private static class FakeHttpRequest implements HttpRequest {
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java
index 54555c2639968..1395f2c27879c 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java
@@ -34,11 +34,13 @@
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.core.internal.io.IOUtils;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.http.HttpServerTransport;
+import org.elasticsearch.http.HttpServerTransport.Dispatcher;
import org.elasticsearch.index.IndexModule;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.analysis.TokenizerFactory;
@@ -309,16 +311,21 @@ public Map> getTransports(Settings settings, ThreadP
}
@Override
- public Map> getHttpTransports(Settings settings, ThreadPool threadPool, BigArrays bigArrays,
+ public Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
+ BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher dispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher dispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser
+ ) {
Map> transports = new HashMap<>();
filterPlugins(NetworkPlugin.class).stream().forEach(p -> transports.putAll(p.getHttpTransports(settings, threadPool, bigArrays,
- pageCacheRecycler, circuitBreakerService, xContentRegistry, networkService, dispatcher, clusterSettings)));
+ pageCacheRecycler, circuitBreakerService, xContentRegistry, networkService, dispatcher, clusterSettings, mediaTypeParser
+ )));
return transports;
}
diff --git a/x-pack/plugin/rest-compatibility/src/main/java/org/elasticsearch/compat/CompatibleVersionPlugin.java b/x-pack/plugin/rest-compatibility/src/main/java/org/elasticsearch/compat/CompatibleVersionPlugin.java
index 2d4be753839eb..107ee3af12917 100644
--- a/x-pack/plugin/rest-compatibility/src/main/java/org/elasticsearch/compat/CompatibleVersionPlugin.java
+++ b/x-pack/plugin/rest-compatibility/src/main/java/org/elasticsearch/compat/CompatibleVersionPlugin.java
@@ -8,68 +8,97 @@
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.Version;
import org.elasticsearch.common.Nullable;
-import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.RestCompatibilityPlugin;
import org.elasticsearch.rest.RestStatus;
+import static org.elasticsearch.common.xcontent.XContentType.COMPATIBLE_WITH_PARAMETER_NAME;
+
public class CompatibleVersionPlugin extends Plugin implements RestCompatibilityPlugin {
@Override
- public Version getCompatibleVersion(@Nullable String acceptHeader, @Nullable String contentTypeHeader, boolean hasContent) {
- Byte aVersion = XContentType.parseVersion(acceptHeader);
- byte acceptVersion = aVersion == null ? Version.CURRENT.major : Integer.valueOf(aVersion).byteValue();
- Byte cVersion = XContentType.parseVersion(contentTypeHeader);
- byte contentTypeVersion = cVersion == null ? Version.CURRENT.major : Integer.valueOf(cVersion).byteValue();
+ public Version getCompatibleVersion(@Nullable ParsedMediaType acceptMediaType, @Nullable ParsedMediaType contentTypeMediaType,
+ boolean hasContent) {
+ final VersionInfo acceptVersionInfo = majorVersionFromMediaType(acceptMediaType);
+ final VersionInfo contentTypeVersionInfo = majorVersionFromMediaType(contentTypeMediaType);
// accept version must be current or prior
- if (acceptVersion > Version.CURRENT.major || acceptVersion < Version.CURRENT.major - 1) {
+ if (acceptVersionInfo.version() > Version.CURRENT.major || acceptVersionInfo.version() < Version.CURRENT.major - 1) {
throw new ElasticsearchStatusException(
"Compatible version must be equal or less then the current version. Accept={}} Content-Type={}}",
RestStatus.BAD_REQUEST,
- acceptHeader,
- contentTypeHeader
+ acceptMediaType, // TODO implement toString!!!
+ contentTypeMediaType
);
}
if (hasContent) {
// content-type version must be current or prior
- if (contentTypeVersion > Version.CURRENT.major || contentTypeVersion < Version.CURRENT.major - 1) {
+ if (contentTypeVersionInfo.version() > Version.CURRENT.major || contentTypeVersionInfo.version() < Version.CURRENT.major - 1) {
throw new ElasticsearchStatusException(
"Compatible version must be equal or less then the current version. Accept={} Content-Type={}",
RestStatus.BAD_REQUEST,
- acceptHeader,
- contentTypeHeader,
+ acceptMediaType,
+ contentTypeMediaType,
RestStatus.BAD_REQUEST
);
}
// if both accept and content-type are sent, the version must match
- if (contentTypeVersion != acceptVersion) {
+ if (contentTypeVersionInfo.version() != acceptVersionInfo.version()) {
throw new ElasticsearchStatusException(
"Content-Type and Accept version requests have to match. Accept={} Content-Type={}",
RestStatus.BAD_REQUEST,
- acceptHeader,
- contentTypeHeader
+ acceptMediaType,
+ contentTypeMediaType
);
}
// both headers should be versioned or none
- if ((cVersion == null && aVersion != null) || (aVersion == null && cVersion != null)) {
+ if (contentTypeVersionInfo.isVersioned() != acceptVersionInfo.isVersioned()) {
throw new ElasticsearchStatusException(
"Versioning is required on both Content-Type and Accept headers. Accept={} Content-Type={}",
RestStatus.BAD_REQUEST,
- acceptHeader,
- contentTypeHeader
+ acceptMediaType,
+ contentTypeMediaType
);
}
- if (contentTypeVersion < Version.CURRENT.major) {
+ if (contentTypeVersionInfo.version() < Version.CURRENT.major) {
return Version.CURRENT.previousMajor();
}
}
- if (acceptVersion < Version.CURRENT.major) {
+ if (acceptVersionInfo.version() < Version.CURRENT.major) {
return Version.CURRENT.previousMajor();
}
return Version.CURRENT;
}
+
+ private static VersionInfo majorVersionFromMediaType(@Nullable ParsedMediaType parsedMediaType) {
+ if (parsedMediaType != null) {
+ String versionString = parsedMediaType.getParameters().get(COMPATIBLE_WITH_PARAMETER_NAME);
+ if (versionString != null && versionString.isBlank() == false) {
+ return new VersionInfo(true, Byte.parseByte(versionString));
+ }
+ }
+ return new VersionInfo(false, Version.CURRENT.major);
+ }
+
+ private static class VersionInfo {
+ private final boolean isVersioned;
+ private final byte version;
+
+ VersionInfo(boolean isVersioned, byte version) {
+ this.isVersioned = isVersioned;
+ this.version = version;
+ }
+
+ boolean isVersioned() {
+ return isVersioned;
+ }
+
+ byte version() {
+ return version;
+ }
+ }
}
diff --git a/x-pack/plugin/rest-compatibility/src/test/java/org/elasticsearch/compat/CompatibleVersionPluginTests.java b/x-pack/plugin/rest-compatibility/src/test/java/org/elasticsearch/compat/CompatibleVersionPluginTests.java
index 32f792ffc2a92..6d9d9cc1648e3 100644
--- a/x-pack/plugin/rest-compatibility/src/test/java/org/elasticsearch/compat/CompatibleVersionPluginTests.java
+++ b/x-pack/plugin/rest-compatibility/src/test/java/org/elasticsearch/compat/CompatibleVersionPluginTests.java
@@ -8,11 +8,19 @@
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.Version;
+import org.elasticsearch.common.xcontent.MediaType;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.hamcrest.ElasticsearchMatchers;
import org.hamcrest.Matcher;
+import java.util.HashMap;
+import java.util.Map;
+
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
public class CompatibleVersionPluginTests extends ESTestCase {
@@ -182,30 +190,86 @@ private String bodyPresent() {
return "some body";
}
- private String contentTypeHeader(int version) {
+ private ParsedMediaType contentTypeHeader(int version) {
return mediaType(String.valueOf(version));
}
- private String acceptHeader(int version) {
+ private ParsedMediaType acceptHeader(int version) {
return mediaType(String.valueOf(version));
}
- private String acceptHeader(String value) {
- return value;
+ private ParsedMediaType acceptHeader(String value) {
+ return fromString(value);
+ }
+
+ private ParsedMediaType contentTypeHeader(String value) {
+ return fromString(value);
}
- private String contentTypeHeader(String value) {
- return value;
+ private ParsedMediaType fromString(String value) {
+ if (value == null) {
+ return null;
+ }
+ String[] splitParams = value.split(";");
+ assertThat(splitParams.length, greaterThanOrEqualTo(1));
+ String mediaType = splitParams[0];
+ String[] splitMediaType = mediaType.split("/");
+ assertThat(splitMediaType.length, is(2));
+ String type = splitMediaType[0];
+ String subtype = splitMediaType[1];
+
+ Map params;
+ if (splitParams.length > 1) {
+ params = new HashMap<>();
+ for (int i = 1; i < splitParams.length; i++) {
+ String[] paramAndValue = splitParams[i].split("=");
+ assertThat(paramAndValue.length, is(2));
+ params.put(paramAndValue[0], paramAndValue[1]);
+ }
+ } else {
+ params = Map.of();
+ }
+ return new ParsedMediaType(new MediaType() {
+ @Override
+ public String type() {
+ return type;
+ }
+
+ @Override
+ public String subtype() {
+ return subtype;
+ }
+
+ @Override
+ public String format() {
+ return null;
+ }
+ }, params);
}
- private String mediaType(String version) {
+ private ParsedMediaType mediaType(String version) {
if (version != null) {
- return "application/vnd.elasticsearch+json;compatible-with=" + version;
+ return new ParsedMediaType(new MediaType() {
+ @Override
+ public String type() {
+ return "application";
+ }
+
+ @Override
+ public String subtype() {
+ return "vnd.elasticsearch+json";
+ }
+
+ @Override
+ public String format() {
+ return null;
+ }
+ }, Map.of(XContentType.COMPATIBLE_WITH_PARAMETER_NAME, version));
}
return null;
}
- private Version requestWith(String accept, String contentType, String body) {
+ private Version requestWith(ParsedMediaType accept, ParsedMediaType contentType, String body) {
return compatibleVersionPlugin.getCompatibleVersion(accept, contentType, body.isEmpty() == false);
}
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
index 4474e401ecbaf..97e62685463d2 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
@@ -37,10 +37,12 @@
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.set.Sets;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.http.HttpServerTransport;
+import org.elasticsearch.http.HttpServerTransport.Dispatcher;
import org.elasticsearch.index.IndexModule;
import org.elasticsearch.indices.SystemIndexDescriptor;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
@@ -988,13 +990,17 @@ public Map> getTransports(Settings settings, ThreadP
}
@Override
- public Map> getHttpTransports(Settings settings, ThreadPool threadPool, BigArrays bigArrays,
+ public Map> getHttpTransports(Settings settings,
+ ThreadPool threadPool,
+ BigArrays bigArrays,
PageCacheRecycler pageCacheRecycler,
CircuitBreakerService circuitBreakerService,
NamedXContentRegistry xContentRegistry,
NetworkService networkService,
- HttpServerTransport.Dispatcher dispatcher,
- ClusterSettings clusterSettings) {
+ Dispatcher dispatcher,
+ ClusterSettings clusterSettings,
+ MediaTypeParser> mediaTypeParser
+ ) {
if (enabled == false) { // don't register anything if we are not enabled
return Collections.emptyMap();
}
@@ -1002,10 +1008,10 @@ public Map> getHttpTransports(Settings set
Map> httpTransports = new HashMap<>();
httpTransports.put(SecurityField.NAME4, () -> new SecurityNetty4HttpServerTransport(settings, networkService, bigArrays,
ipFilter.get(), getSslService(), threadPool, xContentRegistry, dispatcher, clusterSettings,
- getNettySharedGroupFactory(settings)));
+ getNettySharedGroupFactory(settings), mediaTypeParser));
httpTransports.put(SecurityField.NIO, () -> new SecurityNioHttpServerTransport(settings, networkService, bigArrays,
pageCacheRecycler, threadPool, xContentRegistry, dispatcher, ipFilter.get(), getSslService(), getNioGroupFactory(settings),
- clusterSettings));
+ clusterSettings, mediaTypeParser));
return httpTransports;
}
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java
index e3207605658f0..03e9a3f9fd0d0 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java
@@ -14,6 +14,7 @@
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.http.HttpChannel;
import org.elasticsearch.http.netty4.Netty4HttpServerTransport;
@@ -39,8 +40,9 @@ public class SecurityNetty4HttpServerTransport extends Netty4HttpServerTransport
public SecurityNetty4HttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, IPFilter ipFilter,
SSLService sslService, ThreadPool threadPool, NamedXContentRegistry xContentRegistry,
Dispatcher dispatcher, ClusterSettings clusterSettings,
- SharedGroupFactory sharedGroupFactory) {
- super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings, sharedGroupFactory);
+ SharedGroupFactory sharedGroupFactory, MediaTypeParser> mediaTypeParser) {
+ super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings, sharedGroupFactory,
+ mediaTypeParser);
this.securityExceptionHandler = new SecurityHttpExceptionHandler(logger, lifecycle, (c, e) -> super.onException(c, e));
this.ipFilter = ipFilter;
final boolean ssl = HTTP_SSL_ENABLED.get(settings);
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransport.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransport.java
index ff2c91da208ea..02e78054c0f39 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransport.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransport.java
@@ -12,6 +12,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.http.nio.HttpReadWriteHandler;
import org.elasticsearch.http.nio.NioHttpChannel;
@@ -55,9 +56,9 @@ public SecurityNioHttpServerTransport(Settings settings, NetworkService networkS
PageCacheRecycler pageCacheRecycler, ThreadPool threadPool,
NamedXContentRegistry xContentRegistry, Dispatcher dispatcher, IPFilter ipFilter,
SSLService sslService, NioGroupFactory nioGroupFactory,
- ClusterSettings clusterSettings) {
+ ClusterSettings clusterSettings, MediaTypeParser> mediaTypeParser) {
super(settings, networkService, bigArrays, pageCacheRecycler, threadPool, xContentRegistry, dispatcher, nioGroupFactory,
- clusterSettings);
+ clusterSettings, mediaTypeParser);
this.securityExceptionHandler = new SecurityHttpExceptionHandler(logger, lifecycle, (c, e) -> super.onException(c, e));
this.ipFilter = ipFilter;
this.sslEnabled = HTTP_SSL_ENABLED.get(settings);
diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java
index 951c2d5fb4238..0e4d144e6cebd 100644
--- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java
+++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java
@@ -13,6 +13,9 @@
import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.http.NullDispatcher;
@@ -42,6 +45,8 @@ public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
private Environment env;
private Path testnodeCert;
private Path testnodeKey;
+ private MediaTypeParser> mediaTypeParser;
+
@Before
public void createSSLService() {
testnodeCert = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt");
@@ -58,6 +63,7 @@ public void createSSLService() {
.build();
env = TestEnvironment.newEnvironment(settings);
sslService = new SSLService(env);
+ mediaTypeParser = new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS));
}
public void testDefaultClientAuth() throws Exception {
@@ -68,7 +74,8 @@ public void testDefaultClientAuth() throws Exception {
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(IPFilter.class), sslService,
mock(ThreadPool.class), xContentRegistry(), new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings),
+ mediaTypeParser);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(false));
@@ -85,7 +92,8 @@ public void testOptionalClientAuth() throws Exception {
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(IPFilter.class), sslService,
mock(ThreadPool.class), xContentRegistry(), new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings),
+ mediaTypeParser);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(false));
@@ -102,7 +110,8 @@ public void testRequiredClientAuth() throws Exception {
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(IPFilter.class), sslService,
mock(ThreadPool.class), xContentRegistry(), new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings),
+ mediaTypeParser);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(true));
@@ -119,7 +128,8 @@ public void testNoClientAuth() throws Exception {
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(IPFilter.class), sslService,
mock(ThreadPool.class), xContentRegistry(), new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings),
+ mediaTypeParser);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(false));
@@ -134,7 +144,8 @@ public void testCustomSSLConfiguration() throws Exception {
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(IPFilter.class), sslService,
mock(ThreadPool.class), xContentRegistry(), new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings),
+ mediaTypeParser);
ChannelHandler handler = transport.configureServerChannelHandler();
EmbeddedChannel ch = new EmbeddedChannel(handler);
SSLEngine defaultEngine = ch.pipeline().get(SslHandler.class).engine();
@@ -147,7 +158,8 @@ public void testCustomSSLConfiguration() throws Exception {
sslService = new SSLService(TestEnvironment.newEnvironment(settings));
transport = new SecurityNetty4HttpServerTransport(settings, new NetworkService(Collections.emptyList()),
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class), xContentRegistry(), new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings),
+ mediaTypeParser);
handler = transport.configureServerChannelHandler();
ch = new EmbeddedChannel(handler);
SSLEngine customEngine = ch.pipeline().get(SslHandler.class).engine();
@@ -170,7 +182,8 @@ public void testNoExceptionWhenConfiguredWithoutSslKeySSLDisabled() throws Excep
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(IPFilter.class), sslService,
mock(ThreadPool.class), xContentRegistry(), new NullDispatcher(),
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), new SharedGroupFactory(settings),
+ mediaTypeParser);
assertNotNull(transport.configureServerChannelHandler());
}
}
diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransportTests.java
index 39d19578cec6f..9b028e1155e05 100644
--- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransportTests.java
+++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioHttpServerTransportTests.java
@@ -11,6 +11,9 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
+import org.elasticsearch.common.xcontent.MediaTypeParser;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.http.NullDispatcher;
@@ -49,6 +52,7 @@ public class SecurityNioHttpServerTransportTests extends ESTestCase {
private Environment env;
private InetSocketAddress address = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
private NioGroupFactory nioGroupFactory;
+ private MediaTypeParser> mediaTypeParser;
@Before
public void createSSLService() {
@@ -65,6 +69,7 @@ public void createSSLService() {
.build();
env = TestEnvironment.newEnvironment(settings);
sslService = new SSLService(env);
+ mediaTypeParser = new MediaTypeParser<>(new MediaTypeRegistry(XContentType.MEDIA_TYPE_DEFINITIONS));
}
public void testDefaultClientAuth() throws IOException {
@@ -76,7 +81,7 @@ public void testDefaultClientAuth() throws IOException {
SecurityNioHttpServerTransport transport = new SecurityNioHttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(PageCacheRecycler.class), mock(ThreadPool.class),
xContentRegistry(), new NullDispatcher(), mock(IPFilter.class), sslService, nioGroupFactory,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser);
SecurityNioHttpServerTransport.SecurityHttpChannelFactory factory = transport.channelFactory();
SocketChannel socketChannel = mock(SocketChannel.class);
when(socketChannel.getRemoteAddress()).thenReturn(address);
@@ -98,7 +103,7 @@ public void testOptionalClientAuth() throws IOException {
SecurityNioHttpServerTransport transport = new SecurityNioHttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(PageCacheRecycler.class), mock(ThreadPool.class),
xContentRegistry(), new NullDispatcher(), mock(IPFilter.class), sslService, nioGroupFactory,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser);
SecurityNioHttpServerTransport.SecurityHttpChannelFactory factory = transport.channelFactory();
SocketChannel socketChannel = mock(SocketChannel.class);
@@ -120,7 +125,7 @@ public void testRequiredClientAuth() throws IOException {
SecurityNioHttpServerTransport transport = new SecurityNioHttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(PageCacheRecycler.class), mock(ThreadPool.class),
xContentRegistry(), new NullDispatcher(), mock(IPFilter.class), sslService, nioGroupFactory,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser);
SecurityNioHttpServerTransport.SecurityHttpChannelFactory factory = transport.channelFactory();
SocketChannel socketChannel = mock(SocketChannel.class);
@@ -142,7 +147,7 @@ public void testNoClientAuth() throws IOException {
SecurityNioHttpServerTransport transport = new SecurityNioHttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(PageCacheRecycler.class), mock(ThreadPool.class),
xContentRegistry(), new NullDispatcher(), mock(IPFilter.class), sslService, nioGroupFactory,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser);
SecurityNioHttpServerTransport.SecurityHttpChannelFactory factory = transport.channelFactory();
SocketChannel socketChannel = mock(SocketChannel.class);
@@ -162,7 +167,7 @@ public void testCustomSSLConfiguration() throws IOException {
SecurityNioHttpServerTransport transport = new SecurityNioHttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(PageCacheRecycler.class), mock(ThreadPool.class),
xContentRegistry(), new NullDispatcher(), mock(IPFilter.class), sslService, nioGroupFactory,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser);
SecurityNioHttpServerTransport.SecurityHttpChannelFactory factory = transport.channelFactory();
SocketChannel socketChannel = mock(SocketChannel.class);
when(socketChannel.getRemoteAddress()).thenReturn(address);
@@ -179,7 +184,7 @@ public void testCustomSSLConfiguration() throws IOException {
transport = new SecurityNioHttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(PageCacheRecycler.class), mock(ThreadPool.class),
xContentRegistry(), new NullDispatcher(), mock(IPFilter.class), sslService, nioGroupFactory,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser);
factory = transport.channelFactory();
channel = factory.createChannel(mock(NioSelector.class), socketChannel, mock(Config.Socket.class));
SSLEngine customEngine = SSLEngineUtils.getSSLEngine(channel);
@@ -203,6 +208,6 @@ public void testNoExceptionWhenConfiguredWithoutSslKeySSLDisabled() {
SecurityNioHttpServerTransport transport = new SecurityNioHttpServerTransport(settings,
new NetworkService(Collections.emptyList()), mock(BigArrays.class), mock(PageCacheRecycler.class), mock(ThreadPool.class),
xContentRegistry(), new NullDispatcher(), mock(IPFilter.class), sslService, nioGroupFactory,
- new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
+ new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), mediaTypeParser);
}
}
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java
index 763e460170cf0..5cb3df91663be 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java
@@ -8,6 +8,7 @@
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.xcontent.MediaType;
+import org.elasticsearch.common.xcontent.MediaTypeParser.ParsedMediaType;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
@@ -20,6 +21,7 @@
import org.elasticsearch.xpack.sql.action.SqlQueryAction;
import org.elasticsearch.xpack.sql.action.SqlQueryRequest;
import org.elasticsearch.xpack.sql.action.SqlQueryResponse;
+import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import java.io.IOException;
@@ -31,10 +33,10 @@
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.xpack.sql.proto.Protocol.URL_PARAM_DELIMITER;
+import static org.elasticsearch.xpack.sql.proto.Protocol.URL_PARAM_FORMAT;
public class RestSqlQueryAction extends BaseRestHandler {
- private final SqlMediaTypeParser sqlMediaTypeParser = new SqlMediaTypeParser();
MediaType responseMediaType;
@Override
@@ -52,7 +54,9 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
sqlRequest = SqlQueryRequest.fromXContent(parser);
}
- responseMediaType = sqlMediaTypeParser.getMediaType(request, sqlRequest);
+ final MediaType localMediaType = getMediaType(request, sqlRequest);
+ // this is a hack and we shouldn't rely on the class instance value asynchronously
+ this.responseMediaType = localMediaType;
long startNanos = System.nanoTime();
return channel -> client.execute(SqlQueryAction.INSTANCE, sqlRequest, new RestResponseListener(channel) {
@@ -61,13 +65,13 @@ public RestResponse buildResponse(SqlQueryResponse response) throws Exception {
RestResponse restResponse;
// XContent branch
- if (responseMediaType != null && responseMediaType instanceof XContentType) {
- XContentType type = (XContentType) responseMediaType;
+ if (localMediaType instanceof XContentType) {
+ XContentType type = (XContentType) localMediaType;
XContentBuilder builder = channel.newBuilder(request.getXContentType(), type, true);
response.toXContent(builder, request);
restResponse = new BytesRestResponse(RestStatus.OK, builder);
} else { // TextFormat
- TextFormat type = (TextFormat)responseMediaType;
+ TextFormat type = (TextFormat) localMediaType;
final String data = type.format(request, response);
restResponse = new BytesRestResponse(RestStatus.OK, type.contentType(request),
@@ -84,14 +88,48 @@ public RestResponse buildResponse(SqlQueryResponse response) throws Exception {
});
}
-
-
-
@Override
protected Set responseParams() {
return responseMediaType == TextFormat.CSV ? Collections.singleton(URL_PARAM_DELIMITER) : Collections.emptySet();
}
+ /*
+ * Since we support {@link TextFormat} and
+ * {@link XContent} outputs we can't use {@link RestToXContentListener}
+ * like everything else. We want to stick as closely as possible to
+ * Elasticsearch's defaults though, while still layering in ways to
+ * control the output more easily.
+ *
+ * First we find the string that the user used to specify the response
+ * format. If there is a {@code format} parameter we use that. If there
+ * isn't but there is a {@code Accept} header then we use that. If there
+ * isn't then we use the {@code Content-Type} header which is required.
+ */
+ public MediaType getMediaType(RestRequest request, SqlQueryRequest sqlRequest) {
+ if (Mode.isDedicatedClient(sqlRequest.requestInfo().mode())
+ && (sqlRequest.binaryCommunication() == null || sqlRequest.binaryCommunication())) {
+ // enforce CBOR response for drivers and CLI (unless instructed differently through the config param)
+ return XContentType.CBOR;
+ } else if (request.getFormatMediaType() != null) {
+ return validateColumnarRequest(sqlRequest.columnar(), request.getFormatMediaType());
+ }
+ if (request.getAcceptMediaType() != null) {
+ return validateColumnarRequest(sqlRequest.columnar(), request.getAcceptMediaType().getMediaType());
+ }
+
+ ParsedMediaType contentType = request.getContentType();
+ assert contentType != null : "The Content-Type header is required";
+ return validateColumnarRequest(sqlRequest.columnar(), contentType.getMediaType());
+ }
+
+ private static MediaType validateColumnarRequest(boolean requestIsColumnar, MediaType fromMediaType) {
+ if (requestIsColumnar && fromMediaType instanceof TextFormat){
+ throw new IllegalArgumentException("Invalid use of [columnar] argument: cannot be used in combination with "
+ + "txt, csv or tsv formats");
+ }
+ return fromMediaType;
+ }
+
@Override
public String getName() {
return "sql_query";
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlMediaTypeParser.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlMediaTypeParser.java
deleted file mode 100644
index 189dc137b654c..0000000000000
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlMediaTypeParser.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.xpack.sql.plugin;
-
-import org.elasticsearch.common.xcontent.MediaType;
-import org.elasticsearch.common.xcontent.MediaTypeParser;
-import org.elasticsearch.common.xcontent.XContentType;
-import org.elasticsearch.rest.RestRequest;
-import org.elasticsearch.xpack.sql.action.SqlQueryRequest;
-import org.elasticsearch.xpack.sql.proto.Mode;
-
-import java.util.Map;
-
-import static org.elasticsearch.xpack.sql.proto.Protocol.URL_PARAM_FORMAT;
-
-public class SqlMediaTypeParser {
- private static final MediaTypeParser extends MediaType> parser = new MediaTypeParser.Builder<>()
- .copyFromMediaTypeParser(XContentType.mediaTypeParser)
- .withMediaTypeAndParams(TextFormat.PLAIN_TEXT.typeWithSubtype(), TextFormat.PLAIN_TEXT,
- Map.of("header", "present|absent", "charset", "utf-8"))
- .withMediaTypeAndParams(TextFormat.CSV.typeWithSubtype(), TextFormat.CSV,
- Map.of("header", "present|absent", "charset", "utf-8",
- "delimiter", ".+"))// more detailed parsing is in TextFormat.CSV#delimiter
- .withMediaTypeAndParams(TextFormat.TSV.typeWithSubtype(), TextFormat.TSV,
- Map.of("header", "present|absent", "charset", "utf-8"))
- .build();
-
- /*
- * Since we support {@link TextFormat} and
- * {@link XContent} outputs we can't use {@link RestToXContentListener}
- * like everything else. We want to stick as closely as possible to
- * Elasticsearch's defaults though, while still layering in ways to
- * control the output more easily.
- *
- * First we find the string that the user used to specify the response
- * format. If there is a {@code format} parameter we use that. If there
- * isn't but there is a {@code Accept} header then we use that. If there
- * isn't then we use the {@code Content-Type} header which is required.
- */
- public MediaType getMediaType(RestRequest request, SqlQueryRequest sqlRequest) {
-
- if (Mode.isDedicatedClient(sqlRequest.requestInfo().mode())
- && (sqlRequest.binaryCommunication() == null || sqlRequest.binaryCommunication())) {
- // enforce CBOR response for drivers and CLI (unless instructed differently through the config param)
- return XContentType.CBOR;
- } else if (request.hasParam(URL_PARAM_FORMAT)) {
- return validateColumnarRequest(sqlRequest.columnar(), parser.fromFormat(request.param(URL_PARAM_FORMAT)));
- }
- if (request.getHeaders().containsKey("Accept")) {
- String accept = request.header("Accept");
- // */* means "I don't care" which we should treat like not specifying the header
- if ("*/*".equals(accept) == false) {
- return validateColumnarRequest(sqlRequest.columnar(), parser.fromMediaType(accept));
- }
- }
-
- String contentType = request.header("Content-Type");
- assert contentType != null : "The Content-Type header is required";
- return validateColumnarRequest(sqlRequest.columnar(), parser.fromMediaType(contentType));
- }
-
- private static MediaType validateColumnarRequest(boolean requestIsColumnar, MediaType fromMediaType) {
- if(requestIsColumnar && fromMediaType instanceof TextFormat){
- throw new IllegalArgumentException("Invalid use of [columnar] argument: cannot be used in combination with "
- + "txt, csv or tsv formats");
- }
- return fromMediaType;
- }
-
-}
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java
index 6f490f892b712..ec9a6396f5c4a 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java
@@ -16,7 +16,10 @@
import org.elasticsearch.common.settings.IndexScopedSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsFilter;
+import org.elasticsearch.common.xcontent.MediaTypeDefinition;
+import org.elasticsearch.common.xcontent.MediaTypeRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.license.LicenseUtils;
@@ -45,8 +48,12 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.function.Supplier;
+import static org.elasticsearch.common.xcontent.XContentType.COMPATIBLE_WITH_PARAMETER_NAME;
+import static org.elasticsearch.common.xcontent.XContentType.VERSION_PATTERN;
+
public class SqlPlugin extends Plugin implements ActionPlugin {
private final SqlLicenseChecker sqlLicenseChecker = new SqlLicenseChecker(
@@ -124,4 +131,20 @@ public List getRestHandlers(Settings settings, RestController restC
usageAction,
infoAction);
}
+
+ @Override
+ public List getAdditionalMediaTypes() {
+ return List.of(
+ new MediaTypeDefinition(TextFormat.PLAIN_TEXT, Map.of("header", "present|absent", "charset", "utf-8")),
+ new MediaTypeDefinition(TextFormat.CSV, Map.of("header", "present|absent", "charset", "utf-8", "delimiter", ".+")),
+ new MediaTypeDefinition(TextFormat.TSV, Map.of("header", "present|absent", "charset", "utf-8")),
+ new MediaTypeDefinition("text/vnd.elasticsearch+plain", TextFormat.PLAIN_TEXT,
+ Map.of("header", "present|absent", "charset", "utf-8", COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN)),
+ new MediaTypeDefinition("text/vnd.elasticsearch+csv", TextFormat.CSV,
+ Map.of("header", "present|absent", "charset", "utf-8", "delimiter", ".+", COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN)),
+ new MediaTypeDefinition("text/vnd.elasticsearch+tsv", TextFormat.TSV,
+ Map.of("header", "present|absent", "charset", "utf-8", COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))
+ );
+ }
+
}
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java
index d397d2316959c..71fd4babc5701 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java
@@ -24,8 +24,10 @@
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
+import java.util.regex.Pattern;
import static org.elasticsearch.xpack.sql.action.BasicFormatter.FormatOption.TEXT;
import static org.elasticsearch.xpack.sql.proto.Protocol.URL_PARAM_DELIMITER;
@@ -107,7 +109,11 @@ public String subtype() {
return "plain";
}
-
+ @Override
+ public Map validatedParameters() {
+ return Map.of("header", Pattern.compile("present|absent", Pattern.CASE_INSENSITIVE),
+ "charset", Pattern.compile("utf-8", Pattern.CASE_INSENSITIVE));
+ }
},
/**
@@ -227,6 +233,13 @@ boolean hasHeader(RestRequest request) {
public String subtype() {
return "csv";
}
+
+ @Override
+ public Map validatedParameters() {
+ return Map.of("header", Pattern.compile("present|absent", Pattern.CASE_INSENSITIVE),
+ "charset", Pattern.compile("utf-8", Pattern.CASE_INSENSITIVE),
+ "delimiter", Pattern.compile(".+", Pattern.CASE_INSENSITIVE));
+ }
},
TSV() {
@@ -281,6 +294,12 @@ String maybeEscape(String value, Character __) {
public String subtype() {
return "tab-separated-values";
}
+
+ @Override
+ public Map validatedParameters() {
+ return Map.of("header", Pattern.compile("present|absent", Pattern.CASE_INSENSITIVE),
+ "charset", Pattern.compile("utf-8", Pattern.CASE_INSENSITIVE));
+ }
};
private static final String FORMAT_TEXT = "txt";
diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlMediaTypeParserTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlMediaTypeParserTests.java
deleted file mode 100644
index 0459b777b15f9..0000000000000
--- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlMediaTypeParserTests.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.xpack.sql.plugin;
-
-import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.common.xcontent.MediaType;
-import org.elasticsearch.common.xcontent.NamedXContentRegistry;
-import org.elasticsearch.rest.RestRequest;
-import org.elasticsearch.test.ESTestCase;
-import org.elasticsearch.test.rest.FakeRestRequest;
-import org.elasticsearch.xpack.sql.action.SqlQueryRequest;
-import org.elasticsearch.xpack.sql.proto.Mode;
-import org.elasticsearch.xpack.sql.proto.RequestInfo;
-
-import java.util.Collections;
-import java.util.Map;
-
-import static org.elasticsearch.xpack.sql.plugin.TextFormat.CSV;
-import static org.elasticsearch.xpack.sql.plugin.TextFormat.PLAIN_TEXT;
-import static org.elasticsearch.xpack.sql.plugin.TextFormat.TSV;
-import static org.elasticsearch.xpack.sql.proto.RequestInfo.CLIENT_IDS;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-
-public class SqlMediaTypeParserTests extends ESTestCase {
- SqlMediaTypeParser parser = new SqlMediaTypeParser();
-
- public void testPlainTextDetection() {
- MediaType text = parser.getMediaType(reqWithAccept("text/plain"), createTestInstance(false, Mode.PLAIN, false));
- assertThat(text, is(PLAIN_TEXT));
- }
-
- public void testCsvDetection() {
- MediaType text = parser.getMediaType(reqWithAccept("text/csv"), createTestInstance(false, Mode.PLAIN, false));
- assertThat(text, is(CSV));
-
- text = parser.getMediaType(reqWithAccept("text/csv; delimiter=x"), createTestInstance(false, Mode.PLAIN, false));
- assertThat(text, is(CSV));
- }
-
- public void testTsvDetection() {
- MediaType text = parser.getMediaType(reqWithAccept("text/tab-separated-values"), createTestInstance(false, Mode.PLAIN, false));
- assertThat(text, is(TSV));
- }
-
- public void testMediaTypeDetectionWithParameters() {
- assertThat(parser.getMediaType(reqWithAccept("text/plain; charset=utf-8"),
- createTestInstance(false, Mode.PLAIN, false)), is(PLAIN_TEXT));
- assertThat(parser.getMediaType(reqWithAccept("text/plain; header=present"),
- createTestInstance(false, Mode.PLAIN, false)), is(PLAIN_TEXT));
- assertThat(parser.getMediaType(reqWithAccept("text/plain; charset=utf-8; header=present"),
- createTestInstance(false, Mode.PLAIN, false)), is(PLAIN_TEXT));
-
- assertThat(parser.getMediaType(reqWithAccept("text/csv; charset=utf-8"),
- createTestInstance(false, Mode.PLAIN, false)), is(CSV));
- assertThat(parser.getMediaType(reqWithAccept("text/csv; header=present"),
- createTestInstance(false, Mode.PLAIN, false)), is(CSV));
- assertThat(parser.getMediaType(reqWithAccept("text/csv; charset=utf-8; header=present"),
- createTestInstance(false, Mode.PLAIN, false)), is(CSV));
-
- assertThat(parser.getMediaType(reqWithAccept("text/tab-separated-values; charset=utf-8"),
- createTestInstance(false, Mode.PLAIN, false)), is(TSV));
- assertThat(parser.getMediaType(reqWithAccept("text/tab-separated-values; header=present"),
- createTestInstance(false, Mode.PLAIN, false)), is(TSV));
- assertThat(parser.getMediaType(reqWithAccept("text/tab-separated-values; charset=utf-8; header=present"),
- createTestInstance(false, Mode.PLAIN, false)), is(TSV));
- }
-
- public void testInvalidFormat() {
- MediaType mediaType = parser.getMediaType(reqWithAccept("text/garbage"), createTestInstance(false, Mode.PLAIN, false));
- assertThat(mediaType, is(nullValue()));
- }
-
- private static RestRequest reqWithAccept(String acceptHeader) {
-
- return new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY)
- .withHeaders(Map.of("Content-Type", Collections.singletonList("application/json"),
- "Accept", Collections.singletonList(acceptHeader)))
- .build();
- }
-
- protected SqlQueryRequest createTestInstance(boolean binaryCommunication, Mode mode, boolean columnar) {
- return new SqlQueryRequest(randomAlphaOfLength(10), Collections.emptyList(), null,
- randomZone(), between(1, Integer.MAX_VALUE), TimeValue.parseTimeValue(randomTimeValue(), null, "test"),
- TimeValue.parseTimeValue(randomTimeValue(), null, "test"), columnar, randomAlphaOfLength(10),
- new RequestInfo(mode, randomFrom(randomFrom(CLIENT_IDS), randomAlphaOfLengthBetween(10, 20))),
- randomBoolean(), randomBoolean()).binaryCommunication(binaryCommunication);
- }
-}