diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml
index 609a7cf2ea66f..c3956ca9d40a7 100644
--- a/buildSrc/src/main/resources/checkstyle_suppressions.xml
+++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml
@@ -505,8 +505,6 @@
-
-
diff --git a/buildSrc/version.properties b/buildSrc/version.properties
index 6009021da14ed..7aedd395b93b5 100644
--- a/buildSrc/version.properties
+++ b/buildSrc/version.properties
@@ -1,5 +1,5 @@
elasticsearch = 7.0.0-alpha1
-lucene = 7.4.0-snapshot-1cbadda4d3
+lucene = 7.4.0-snapshot-0a7c3f462f
# optional dependencies
spatial4j = 0.7
diff --git a/docs/reference/index.asciidoc b/docs/reference/index.asciidoc
index 8567ed63b3418..b38a554d6815b 100644
--- a/docs/reference/index.asciidoc
+++ b/docs/reference/index.asciidoc
@@ -63,7 +63,7 @@ include::{xes-repo-dir}/monitoring/index.asciidoc[]
include::{xes-repo-dir}/rollup/index.asciidoc[]
-include::{xes-repo-dir}/rest-api/index.asciidoc[]
+include::rest-api/index.asciidoc[]
include::{xes-repo-dir}/commands/index.asciidoc[]
diff --git a/docs/reference/query-dsl/query-string-syntax.asciidoc b/docs/reference/query-dsl/query-string-syntax.asciidoc
index 937fcdae5fed6..765b54b588359 100644
--- a/docs/reference/query-dsl/query-string-syntax.asciidoc
+++ b/docs/reference/query-dsl/query-string-syntax.asciidoc
@@ -254,7 +254,6 @@ would look like this:
}
}
-****
===== Grouping
diff --git a/docs/reference/rest-api/index.asciidoc b/docs/reference/rest-api/index.asciidoc
new file mode 100644
index 0000000000000..8c58246a0a658
--- /dev/null
+++ b/docs/reference/rest-api/index.asciidoc
@@ -0,0 +1,29 @@
+[role="xpack"]
+[[xpack-api]]
+= {xpack} APIs
+
+[partintro]
+--
+{xpack} exposes REST APIs that are used by the UI components and can be called
+directly to configure and access {xpack} features.
+
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+--
+
+
+include::{xes-repo-dir}/rest-api/info.asciidoc[]
+include::{xes-repo-dir}/rest-api/graph/explore.asciidoc[]
+include::{xes-repo-dir}/rest-api/licensing.asciidoc[]
+include::{xes-repo-dir}/rest-api/migration.asciidoc[]
+include::{xes-repo-dir}/rest-api/ml-api.asciidoc[]
+include::{xes-repo-dir}/rest-api/rollup-api.asciidoc[]
+include::{xes-repo-dir}/rest-api/security.asciidoc[]
+include::{xes-repo-dir}/rest-api/watcher.asciidoc[]
+include::{xes-repo-dir}/rest-api/defs.asciidoc[]
diff --git a/modules/lang-expression/licenses/lucene-expressions-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/modules/lang-expression/licenses/lucene-expressions-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..25e2291d36e8b
--- /dev/null
+++ b/modules/lang-expression/licenses/lucene-expressions-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+bf2cfa0551ebdf08a2cf3079f3c74643bd9dbb76
\ No newline at end of file
diff --git a/modules/lang-expression/licenses/lucene-expressions-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/modules/lang-expression/licenses/lucene-expressions-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index 3bbaa2ba0a715..0000000000000
--- a/modules/lang-expression/licenses/lucene-expressions-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-98c920972b2f5e8563540e805d87e6a3bc888972
\ No newline at end of file
diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java
index 084f29b8c9a87..1be1acb1317dc 100644
--- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java
+++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java
@@ -131,6 +131,7 @@ public void testXContentRoundtrip() throws IOException {
}
}
+ @AwaitsFix(bugUrl="https://github.com/elastic/elasticsearch/issues/31104")
public void testXContentParsingIsNotLenient() throws IOException {
RatedRequest testItem = createTestItem(randomBoolean());
XContentType xContentType = randomFrom(XContentType.values());
diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/cors/Netty4CorsConfig.java b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/cors/Netty4CorsConfig.java
index 9c81c07e66314..939d5540ecfdf 100644
--- a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/cors/Netty4CorsConfig.java
+++ b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/cors/Netty4CorsConfig.java
@@ -76,7 +76,8 @@ public boolean isCorsSupportEnabled() {
}
/**
- * Determines whether a wildcard origin, '*', is supported.
+ * Determines whether a wildcard origin, '*', is supported. This also means that null origins are
+ * supported.
*
* @return {@code boolean} true if any origin is allowed.
*/
@@ -121,21 +122,21 @@ public boolean isNullOriginAllowed() {
}
/**
- * Determines if cookies are supported for CORS requests.
+ * Determines if credentials are supported for CORS requests.
*
- * By default cookies are not included in CORS requests but if isCredentialsAllowed returns
- * true cookies will be added to CORS requests. Setting this value to true will set the
+ * By default credentials are not included in CORS requests but if isCredentialsAllowed returns
+ * true credentials will be added to CORS requests. Setting this value to true will set the
* CORS 'Access-Control-Allow-Credentials' response header to true.
*
- * Please note that cookie support needs to be enabled on the client side as well.
- * The client needs to opt-in to send cookies by calling:
+ * Please note that credentials support needs to be enabled on the client side as well.
+ * The client needs to opt-in to send credentials by calling:
*
* xhr.withCredentials = true;
*
- * The default value for 'withCredentials' is false in which case no cookies are sent.
- * Setting this to true will included cookies in cross origin requests.
+ * The default value for 'withCredentials' is false in which case no credentials are sent.
+ * Setting this to true will included credentials in cross origin requests.
*
- * @return {@code true} if cookies are supported.
+ * @return {@code true} if credentials are supported.
*/
public boolean isCredentialsAllowed() {
return allowCredentials;
diff --git a/plugins/analysis-icu/licenses/lucene-analyzers-icu-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/plugins/analysis-icu/licenses/lucene-analyzers-icu-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..3fdd3366122cb
--- /dev/null
+++ b/plugins/analysis-icu/licenses/lucene-analyzers-icu-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+82d83fcac1d9c8948aa0247fc9c87f177ddbd59b
\ No newline at end of file
diff --git a/plugins/analysis-icu/licenses/lucene-analyzers-icu-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/plugins/analysis-icu/licenses/lucene-analyzers-icu-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index 7f3d3b5ccf63c..0000000000000
--- a/plugins/analysis-icu/licenses/lucene-analyzers-icu-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-844e2b76f4bc6e646e1c3257d668ac598e03f36a
\ No newline at end of file
diff --git a/plugins/analysis-kuromoji/licenses/lucene-analyzers-kuromoji-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/plugins/analysis-kuromoji/licenses/lucene-analyzers-kuromoji-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..855d6ebe4aeb0
--- /dev/null
+++ b/plugins/analysis-kuromoji/licenses/lucene-analyzers-kuromoji-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+73fd4364f2931e7c8303b5927e140a7d21116c36
\ No newline at end of file
diff --git a/plugins/analysis-kuromoji/licenses/lucene-analyzers-kuromoji-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/plugins/analysis-kuromoji/licenses/lucene-analyzers-kuromoji-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index 65423fff2a441..0000000000000
--- a/plugins/analysis-kuromoji/licenses/lucene-analyzers-kuromoji-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2f2bd2d67c7952e4ae14ab3f742824a45d0d1719
\ No newline at end of file
diff --git a/plugins/analysis-nori/licenses/lucene-analyzers-nori-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/plugins/analysis-nori/licenses/lucene-analyzers-nori-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..091097f1a8477
--- /dev/null
+++ b/plugins/analysis-nori/licenses/lucene-analyzers-nori-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+0a2c4417fa9a8be078864f590a5a66b98d551cf5
\ No newline at end of file
diff --git a/plugins/analysis-nori/licenses/lucene-analyzers-nori-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/plugins/analysis-nori/licenses/lucene-analyzers-nori-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index 04fa62ce64a1d..0000000000000
--- a/plugins/analysis-nori/licenses/lucene-analyzers-nori-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-46ad7ebcfcdbdb60dd54aae4d720356a7a51c7c0
\ No newline at end of file
diff --git a/plugins/analysis-phonetic/licenses/lucene-analyzers-phonetic-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/plugins/analysis-phonetic/licenses/lucene-analyzers-phonetic-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..b18addf0b5819
--- /dev/null
+++ b/plugins/analysis-phonetic/licenses/lucene-analyzers-phonetic-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+6fa179924f139a30fc0e5399256e1a44562ed32b
\ No newline at end of file
diff --git a/plugins/analysis-phonetic/licenses/lucene-analyzers-phonetic-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/plugins/analysis-phonetic/licenses/lucene-analyzers-phonetic-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index 55bc8869196e0..0000000000000
--- a/plugins/analysis-phonetic/licenses/lucene-analyzers-phonetic-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-548e9f2b4d4a985dc174b2eee4007c0bd5642e68
\ No newline at end of file
diff --git a/plugins/analysis-smartcn/licenses/lucene-analyzers-smartcn-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/plugins/analysis-smartcn/licenses/lucene-analyzers-smartcn-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..7b7141b6f407c
--- /dev/null
+++ b/plugins/analysis-smartcn/licenses/lucene-analyzers-smartcn-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+5ed135d34d7868b71a725257a46dc8d8735a15d4
\ No newline at end of file
diff --git a/plugins/analysis-smartcn/licenses/lucene-analyzers-smartcn-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/plugins/analysis-smartcn/licenses/lucene-analyzers-smartcn-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index be66854321699..0000000000000
--- a/plugins/analysis-smartcn/licenses/lucene-analyzers-smartcn-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b90e66f4104f0234cfef335762f65a6fed695231
\ No newline at end of file
diff --git a/plugins/analysis-stempel/licenses/lucene-analyzers-stempel-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/plugins/analysis-stempel/licenses/lucene-analyzers-stempel-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..73be96c477eab
--- /dev/null
+++ b/plugins/analysis-stempel/licenses/lucene-analyzers-stempel-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+875911b36b99c2103719f94559878a0ecb862fb6
\ No newline at end of file
diff --git a/plugins/analysis-stempel/licenses/lucene-analyzers-stempel-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/plugins/analysis-stempel/licenses/lucene-analyzers-stempel-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index b77acdc34f31c..0000000000000
--- a/plugins/analysis-stempel/licenses/lucene-analyzers-stempel-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-929a4eb52b11f6d3f0df9c8eba014f5ee2464c67
\ No newline at end of file
diff --git a/plugins/analysis-ukrainian/licenses/lucene-analyzers-morfologik-7.4.0-snapshot-0a7c3f462f.jar.sha1 b/plugins/analysis-ukrainian/licenses/lucene-analyzers-morfologik-7.4.0-snapshot-0a7c3f462f.jar.sha1
new file mode 100644
index 0000000000000..0c85d3f6c8522
--- /dev/null
+++ b/plugins/analysis-ukrainian/licenses/lucene-analyzers-morfologik-7.4.0-snapshot-0a7c3f462f.jar.sha1
@@ -0,0 +1 @@
+e7191628df8cb72382a20da79224aef677117849
\ No newline at end of file
diff --git a/plugins/analysis-ukrainian/licenses/lucene-analyzers-morfologik-7.4.0-snapshot-1cbadda4d3.jar.sha1 b/plugins/analysis-ukrainian/licenses/lucene-analyzers-morfologik-7.4.0-snapshot-1cbadda4d3.jar.sha1
deleted file mode 100644
index cce4b6ff18df5..0000000000000
--- a/plugins/analysis-ukrainian/licenses/lucene-analyzers-morfologik-7.4.0-snapshot-1cbadda4d3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0e6575a411b65cd95e0e54f04d3da278b68be521
\ No newline at end of file
diff --git a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/HttpReadWriteHandler.java b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/HttpReadWriteHandler.java
index 681736a311db5..49e560363089b 100644
--- a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/HttpReadWriteHandler.java
+++ b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/HttpReadWriteHandler.java
@@ -36,6 +36,8 @@
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.http.HttpHandlingSettings;
import org.elasticsearch.http.HttpPipelinedRequest;
+import org.elasticsearch.http.nio.cors.NioCorsConfig;
+import org.elasticsearch.http.nio.cors.NioCorsHandler;
import org.elasticsearch.nio.FlushOperation;
import org.elasticsearch.nio.InboundChannelBuffer;
import org.elasticsearch.nio.NioSocketChannel;
@@ -50,6 +52,8 @@
import java.util.List;
import java.util.function.BiConsumer;
+import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ENABLED;
+
public class HttpReadWriteHandler implements ReadWriteHandler {
private final NettyAdaptor adaptor;
@@ -57,14 +61,16 @@ public class HttpReadWriteHandler implements ReadWriteHandler {
private final NioHttpServerTransport transport;
private final HttpHandlingSettings settings;
private final NamedXContentRegistry xContentRegistry;
+ private final NioCorsConfig corsConfig;
private final ThreadContext threadContext;
HttpReadWriteHandler(NioSocketChannel nioChannel, NioHttpServerTransport transport, HttpHandlingSettings settings,
- NamedXContentRegistry xContentRegistry, ThreadContext threadContext) {
+ NamedXContentRegistry xContentRegistry, NioCorsConfig corsConfig, ThreadContext threadContext) {
this.nioChannel = nioChannel;
this.transport = transport;
this.settings = settings;
this.xContentRegistry = xContentRegistry;
+ this.corsConfig = corsConfig;
this.threadContext = threadContext;
List handlers = new ArrayList<>(5);
@@ -78,6 +84,9 @@ public class HttpReadWriteHandler implements ReadWriteHandler {
if (settings.isCompression()) {
handlers.add(new HttpContentCompressor(settings.getCompressionLevel()));
}
+ if (settings.isCorsEnabled()) {
+ handlers.add(new NioCorsHandler(corsConfig));
+ }
handlers.add(new NioHttpPipeliningHandler(transport.getLogger(), settings.getPipeliningMaxEvents()));
adaptor = new NettyAdaptor(handlers.toArray(new ChannelHandler[0]));
@@ -178,7 +187,7 @@ private void handleRequest(Object msg) {
int sequence = pipelinedRequest.getSequence();
BigArrays bigArrays = transport.getBigArrays();
try {
- innerChannel = new NioHttpChannel(nioChannel, bigArrays, httpRequest, sequence, settings, threadContext);
+ innerChannel = new NioHttpChannel(nioChannel, bigArrays, httpRequest, sequence, settings, corsConfig, threadContext);
} catch (final IllegalArgumentException e) {
if (badRequestCause == null) {
badRequestCause = e;
@@ -191,7 +200,7 @@ private void handleRequest(Object msg) {
Collections.emptyMap(), // we are going to dispatch the request as a bad request, drop all parameters
copiedRequest.uri(),
copiedRequest);
- innerChannel = new NioHttpChannel(nioChannel, bigArrays, innerRequest, sequence, settings, threadContext);
+ innerChannel = new NioHttpChannel(nioChannel, bigArrays, innerRequest, sequence, settings, corsConfig, threadContext);
}
channel = innerChannel;
}
diff --git a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpChannel.java b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpChannel.java
index 61cafed86a51c..634421b34ea48 100644
--- a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpChannel.java
+++ b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/NioHttpChannel.java
@@ -41,6 +41,8 @@
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.http.HttpHandlingSettings;
+import org.elasticsearch.http.nio.cors.NioCorsConfig;
+import org.elasticsearch.http.nio.cors.NioCorsHandler;
import org.elasticsearch.nio.NioSocketChannel;
import org.elasticsearch.rest.AbstractRestChannel;
import org.elasticsearch.rest.RestResponse;
@@ -58,17 +60,19 @@ public class NioHttpChannel extends AbstractRestChannel {
private final BigArrays bigArrays;
private final int sequence;
+ private final NioCorsConfig corsConfig;
private final ThreadContext threadContext;
private final FullHttpRequest nettyRequest;
private final NioSocketChannel nioChannel;
private final boolean resetCookies;
NioHttpChannel(NioSocketChannel nioChannel, BigArrays bigArrays, NioHttpRequest request, int sequence,
- HttpHandlingSettings settings, ThreadContext threadContext) {
+ HttpHandlingSettings settings, NioCorsConfig corsConfig, ThreadContext threadContext) {
super(request, settings.getDetailedErrorsEnabled());
this.nioChannel = nioChannel;
this.bigArrays = bigArrays;
this.sequence = sequence;
+ this.corsConfig = corsConfig;
this.threadContext = threadContext;
this.nettyRequest = request.getRequest();
this.resetCookies = settings.isResetCookies();
@@ -87,6 +91,8 @@ public void sendResponse(RestResponse response) {
}
resp.setStatus(getStatus(response.status()));
+ NioCorsHandler.setCorsResponseHeaders(nettyRequest, resp, corsConfig);
+
String opaque = nettyRequest.headers().get("X-Opaque-Id");
if (opaque != null) {
setHeaderField(resp, "X-Opaque-Id", opaque);
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 de5c166de3f5b..ce0ed83aad420 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
@@ -19,6 +19,7 @@
package org.elasticsearch.http.nio;
+import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.timeout.ReadTimeoutException;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
@@ -28,6 +29,7 @@
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.PlainActionFuture;
+import org.elasticsearch.common.Strings;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.network.NetworkService;
import org.elasticsearch.common.settings.Setting;
@@ -38,11 +40,13 @@
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.http.AbstractHttpServerTransport;
import org.elasticsearch.http.BindHttpException;
import org.elasticsearch.http.HttpHandlingSettings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.http.HttpStats;
-import org.elasticsearch.http.AbstractHttpServerTransport;
+import org.elasticsearch.http.nio.cors.NioCorsConfig;
+import org.elasticsearch.http.nio.cors.NioCorsConfigBuilder;
import org.elasticsearch.nio.AcceptingSelector;
import org.elasticsearch.nio.AcceptorEventHandler;
import org.elasticsearch.nio.BytesChannelContext;
@@ -56,6 +60,7 @@
import org.elasticsearch.nio.SocketChannelContext;
import org.elasticsearch.nio.SocketEventHandler;
import org.elasticsearch.nio.SocketSelector;
+import org.elasticsearch.rest.RestUtils;
import org.elasticsearch.threadpool.ThreadPool;
import java.io.IOException;
@@ -64,15 +69,23 @@
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.regex.Pattern;
import static org.elasticsearch.common.settings.Setting.intSetting;
import static org.elasticsearch.common.util.concurrent.EsExecutors.daemonThreadFactory;
+import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_CREDENTIALS;
+import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_HEADERS;
+import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_METHODS;
+import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_ORIGIN;
+import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_ENABLED;
+import static org.elasticsearch.http.HttpTransportSettings.SETTING_CORS_MAX_AGE;
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_COMPRESSION;
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_COMPRESSION_LEVEL;
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_DETAILED_ERRORS_ENABLED;
@@ -86,6 +99,7 @@
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_TCP_REUSE_ADDRESS;
import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_TCP_SEND_BUFFER_SIZE;
import static org.elasticsearch.http.HttpTransportSettings.SETTING_PIPELINING_MAX_EVENTS;
+import static org.elasticsearch.http.nio.cors.NioCorsHandler.ANY_ORIGIN;
public class NioHttpServerTransport extends AbstractHttpServerTransport {
@@ -115,6 +129,7 @@ public class NioHttpServerTransport extends AbstractHttpServerTransport {
private final Set socketChannels = Collections.newSetFromMap(new ConcurrentHashMap<>());
private NioGroup nioGroup;
private HttpChannelFactory channelFactory;
+ private final NioCorsConfig corsConfig;
public NioHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, ThreadPool threadPool,
NamedXContentRegistry xContentRegistry, HttpServerTransport.Dispatcher dispatcher) {
@@ -136,6 +151,7 @@ public NioHttpServerTransport(Settings settings, NetworkService networkService,
SETTING_HTTP_COMPRESSION_LEVEL.get(settings),
SETTING_HTTP_DETAILED_ERRORS_ENABLED.get(settings),
pipeliningMaxEvents);
+ this.corsConfig = buildCorsConfig(settings);
this.tcpNoDelay = SETTING_HTTP_TCP_NO_DELAY.get(settings);
this.tcpKeepAlive = SETTING_HTTP_TCP_KEEP_ALIVE.get(settings);
@@ -279,6 +295,38 @@ protected void nonChannelExceptionCaught(Exception ex) {
logger.warn(new ParameterizedMessage("exception caught on transport layer [thread={}]", Thread.currentThread().getName()), ex);
}
+ static NioCorsConfig buildCorsConfig(Settings settings) {
+ if (SETTING_CORS_ENABLED.get(settings) == false) {
+ return NioCorsConfigBuilder.forOrigins().disable().build();
+ }
+ String origin = SETTING_CORS_ALLOW_ORIGIN.get(settings);
+ final NioCorsConfigBuilder builder;
+ if (Strings.isNullOrEmpty(origin)) {
+ builder = NioCorsConfigBuilder.forOrigins();
+ } else if (origin.equals(ANY_ORIGIN)) {
+ builder = NioCorsConfigBuilder.forAnyOrigin();
+ } else {
+ Pattern p = RestUtils.checkCorsSettingForRegex(origin);
+ if (p == null) {
+ builder = NioCorsConfigBuilder.forOrigins(RestUtils.corsSettingAsArray(origin));
+ } else {
+ builder = NioCorsConfigBuilder.forPattern(p);
+ }
+ }
+ if (SETTING_CORS_ALLOW_CREDENTIALS.get(settings)) {
+ builder.allowCredentials();
+ }
+ String[] strMethods = Strings.tokenizeToStringArray(SETTING_CORS_ALLOW_METHODS.get(settings), ",");
+ HttpMethod[] methods = Arrays.stream(strMethods)
+ .map(HttpMethod::valueOf)
+ .toArray(HttpMethod[]::new);
+ return builder.allowedRequestMethods(methods)
+ .maxAge(SETTING_CORS_MAX_AGE.get(settings))
+ .allowedRequestHeaders(Strings.tokenizeToStringArray(SETTING_CORS_ALLOW_HEADERS.get(settings), ","))
+ .shortCircuit()
+ .build();
+ }
+
private void closeChannels(List channels) {
List> futures = new ArrayList<>(channels.size());
@@ -315,7 +363,7 @@ private HttpChannelFactory() {
public NioSocketChannel createChannel(SocketSelector selector, SocketChannel channel) throws IOException {
NioSocketChannel nioChannel = new NioSocketChannel(channel);
HttpReadWriteHandler httpReadWritePipeline = new HttpReadWriteHandler(nioChannel,NioHttpServerTransport.this,
- httpHandlingSettings, xContentRegistry, threadPool.getThreadContext());
+ httpHandlingSettings, xContentRegistry, corsConfig, threadPool.getThreadContext());
Consumer exceptionHandler = (e) -> exceptionCaught(nioChannel, e);
SocketChannelContext context = new BytesChannelContext(nioChannel, selector, exceptionHandler, httpReadWritePipeline,
InboundChannelBuffer.allocatingInstance());
diff --git a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/cors/NioCorsConfig.java b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/cors/NioCorsConfig.java
new file mode 100644
index 0000000000000..9848c26022e37
--- /dev/null
+++ b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/cors/NioCorsConfig.java
@@ -0,0 +1,236 @@
+/*
+ * 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.http.nio.cors;
+
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.EmptyHttpHeaders;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.regex.Pattern;
+
+/**
+ * Configuration for Cross-Origin Resource Sharing (CORS).
+ *
+ * This class was lifted from the Netty project:
+ * https://github.com/netty/netty
+ */
+public final class NioCorsConfig {
+
+ private final Optional> origins;
+ private final Optional pattern;
+ private final boolean anyOrigin;
+ private final boolean enabled;
+ private final boolean allowCredentials;
+ private final long maxAge;
+ private final Set allowedRequestMethods;
+ private final Set allowedRequestHeaders;
+ private final boolean allowNullOrigin;
+ private final Map> preflightHeaders;
+ private final boolean shortCircuit;
+
+ NioCorsConfig(final NioCorsConfigBuilder builder) {
+ origins = builder.origins.map(s -> new LinkedHashSet<>(s));
+ pattern = builder.pattern;
+ anyOrigin = builder.anyOrigin;
+ enabled = builder.enabled;
+ allowCredentials = builder.allowCredentials;
+ maxAge = builder.maxAge;
+ allowedRequestMethods = builder.requestMethods;
+ allowedRequestHeaders = builder.requestHeaders;
+ allowNullOrigin = builder.allowNullOrigin;
+ preflightHeaders = builder.preflightHeaders;
+ shortCircuit = builder.shortCircuit;
+ }
+
+ /**
+ * Determines if support for CORS is enabled.
+ *
+ * @return {@code true} if support for CORS is enabled, false otherwise.
+ */
+ public boolean isCorsSupportEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Determines whether a wildcard origin, '*', is supported. This also means that null origins are
+ * supported.
+ *
+ * @return {@code boolean} true if any origin is allowed.
+ */
+ public boolean isAnyOriginSupported() {
+ return anyOrigin;
+ }
+
+ /**
+ * Returns the set of allowed origins.
+ *
+ * @return {@code Set} the allowed origins.
+ */
+ public Optional> origins() {
+ return origins;
+ }
+
+ /**
+ * Returns whether the input origin is allowed by this configuration.
+ *
+ * @return {@code true} if the origin is allowed, otherwise {@code false}
+ */
+ public boolean isOriginAllowed(final String origin) {
+ if (origins.isPresent()) {
+ return origins.get().contains(origin);
+ } else if (pattern.isPresent()) {
+ return pattern.get().matcher(origin).matches();
+ }
+ return false;
+ }
+
+ /**
+ * Web browsers may set the 'Origin' request header to 'null' if a resource is loaded
+ * from the local file system.
+ *
+ * If isNullOriginAllowed is true then the server will response with the wildcard for the
+ * the CORS response header 'Access-Control-Allow-Origin'.
+ *
+ * @return {@code true} if a 'null' origin should be supported.
+ */
+ public boolean isNullOriginAllowed() {
+ return allowNullOrigin;
+ }
+
+ /**
+ * Determines if credentials are supported for CORS requests.
+ *
+ * By default credentials are not included in CORS requests but if isCredentialsAllowed returns
+ * true credentials will be added to CORS requests. Setting this value to true will set the
+ * CORS 'Access-Control-Allow-Credentials' response header to true.
+ *
+ * Please note that credentials support needs to be enabled on the client side as well.
+ * The client needs to opt-in to send credentials by calling:
+ *
+ * xhr.withCredentials = true;
+ *
+ * The default value for 'withCredentials' is false in which case no credentials are sent.
+ * Setting this to true will included cookies in cross origin requests.
+ *
+ * @return {@code true} if credentials are supported.
+ */
+ public boolean isCredentialsAllowed() {
+ return allowCredentials;
+ }
+
+ /**
+ * Gets the maxAge setting.
+ *
+ * When making a preflight request the client has to perform two request with can be inefficient.
+ * This setting will set the CORS 'Access-Control-Max-Age' response header and enables the
+ * caching of the preflight response for the specified time. During this time no preflight
+ * request will be made.
+ *
+ * @return {@code long} the time in seconds that a preflight request may be cached.
+ */
+ public long maxAge() {
+ return maxAge;
+ }
+
+ /**
+ * Returns the allowed set of Request Methods. The Http methods that should be returned in the
+ * CORS 'Access-Control-Request-Method' response header.
+ *
+ * @return {@code Set} of {@link HttpMethod}s that represent the allowed Request Methods.
+ */
+ public Set allowedRequestMethods() {
+ return Collections.unmodifiableSet(allowedRequestMethods);
+ }
+
+ /**
+ * Returns the allowed set of Request Headers.
+ *
+ * The header names returned from this method will be used to set the CORS
+ * 'Access-Control-Allow-Headers' response header.
+ *
+ * @return {@code Set} of strings that represent the allowed Request Headers.
+ */
+ public Set allowedRequestHeaders() {
+ return Collections.unmodifiableSet(allowedRequestHeaders);
+ }
+
+ /**
+ * Returns HTTP response headers that should be added to a CORS preflight response.
+ *
+ * @return {@link HttpHeaders} the HTTP response headers to be added.
+ */
+ public HttpHeaders preflightResponseHeaders() {
+ if (preflightHeaders.isEmpty()) {
+ return EmptyHttpHeaders.INSTANCE;
+ }
+ final HttpHeaders preflightHeaders = new DefaultHttpHeaders();
+ for (Map.Entry> entry : this.preflightHeaders.entrySet()) {
+ final Object value = getValue(entry.getValue());
+ if (value instanceof Iterable) {
+ preflightHeaders.add(entry.getKey().toString(), (Iterable>) value);
+ } else {
+ preflightHeaders.add(entry.getKey().toString(), value);
+ }
+ }
+ return preflightHeaders;
+ }
+
+ /**
+ * Determines whether a CORS request should be rejected if it's invalid before being
+ * further processing.
+ *
+ * CORS headers are set after a request is processed. This may not always be desired
+ * and this setting will check that the Origin is valid and if it is not valid no
+ * further processing will take place, and a error will be returned to the calling client.
+ *
+ * @return {@code true} if a CORS request should short-circuit upon receiving an invalid Origin header.
+ */
+ public boolean isShortCircuit() {
+ return shortCircuit;
+ }
+
+ private static T getValue(final Callable callable) {
+ try {
+ return callable.call();
+ } catch (final Exception e) {
+ throw new IllegalStateException("Could not generate value for callable [" + callable + ']', e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "CorsConfig[enabled=" + enabled +
+ ", origins=" + origins +
+ ", anyOrigin=" + anyOrigin +
+ ", isCredentialsAllowed=" + allowCredentials +
+ ", maxAge=" + maxAge +
+ ", allowedRequestMethods=" + allowedRequestMethods +
+ ", allowedRequestHeaders=" + allowedRequestHeaders +
+ ", preflightHeaders=" + preflightHeaders + ']';
+ }
+
+}
diff --git a/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/cors/NioCorsConfigBuilder.java b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/cors/NioCorsConfigBuilder.java
new file mode 100644
index 0000000000000..333e4931aa1f1
--- /dev/null
+++ b/plugins/transport-nio/src/main/java/org/elasticsearch/http/nio/cors/NioCorsConfigBuilder.java
@@ -0,0 +1,357 @@
+/*
+ * 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.http.nio.cors;
+
+import io.netty.handler.codec.http.HttpMethod;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.regex.Pattern;
+
+/**
+ * Builder used to configure and build a {@link NioCorsConfig} instance.
+ *
+ * This class was lifted from the Netty project:
+ * https://github.com/netty/netty
+ */
+public final class NioCorsConfigBuilder {
+
+ /**
+ * Creates a Builder instance with it's origin set to '*'.
+ *
+ * @return Builder to support method chaining.
+ */
+ public static NioCorsConfigBuilder forAnyOrigin() {
+ return new NioCorsConfigBuilder();
+ }
+
+ /**
+ * Creates a {@link NioCorsConfigBuilder} instance with the specified origin.
+ *
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public static NioCorsConfigBuilder forOrigin(final String origin) {
+ if ("*".equals(origin)) {
+ return new NioCorsConfigBuilder();
+ }
+ return new NioCorsConfigBuilder(origin);
+ }
+
+
+ /**
+ * Create a {@link NioCorsConfigBuilder} instance with the specified pattern origin.
+ *
+ * @param pattern the regular expression pattern to match incoming origins on.
+ * @return {@link NioCorsConfigBuilder} with the configured origin pattern.
+ */
+ public static NioCorsConfigBuilder forPattern(final Pattern pattern) {
+ if (pattern == null) {
+ throw new IllegalArgumentException("CORS pattern cannot be null");
+ }
+ return new NioCorsConfigBuilder(pattern);
+ }
+
+ /**
+ * Creates a {@link NioCorsConfigBuilder} instance with the specified origins.
+ *
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public static NioCorsConfigBuilder forOrigins(final String... origins) {
+ return new NioCorsConfigBuilder(origins);
+ }
+
+ Optional> origins;
+ Optional pattern;
+ final boolean anyOrigin;
+ boolean allowNullOrigin;
+ boolean enabled = true;
+ boolean allowCredentials;
+ long maxAge;
+ final Set requestMethods = new HashSet<>();
+ final Set requestHeaders = new HashSet<>();
+ final Map> preflightHeaders = new HashMap<>();
+ private boolean noPreflightHeaders;
+ boolean shortCircuit;
+
+ /**
+ * Creates a new Builder instance with the origin passed in.
+ *
+ * @param origins the origin to be used for this builder.
+ */
+ NioCorsConfigBuilder(final String... origins) {
+ this.origins = Optional.of(new LinkedHashSet<>(Arrays.asList(origins)));
+ pattern = Optional.empty();
+ anyOrigin = false;
+ }
+
+ /**
+ * Creates a new Builder instance allowing any origin, "*" which is the
+ * wildcard origin.
+ *
+ */
+ NioCorsConfigBuilder() {
+ anyOrigin = true;
+ origins = Optional.empty();
+ pattern = Optional.empty();
+ }
+
+ /**
+ * Creates a new Builder instance allowing any origin that matches the pattern.
+ *
+ * @param pattern the pattern to match against for incoming origins.
+ */
+ NioCorsConfigBuilder(final Pattern pattern) {
+ this.pattern = Optional.of(pattern);
+ origins = Optional.empty();
+ anyOrigin = false;
+ }
+
+ /**
+ * Web browsers may set the 'Origin' request header to 'null' if a resource is loaded
+ * from the local file system. Calling this method will enable a successful CORS response
+ * with a wildcard for the CORS response header 'Access-Control-Allow-Origin'.
+ *
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ NioCorsConfigBuilder allowNullOrigin() {
+ allowNullOrigin = true;
+ return this;
+ }
+
+ /**
+ * Disables CORS support.
+ *
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder disable() {
+ enabled = false;
+ return this;
+ }
+
+ /**
+ * By default cookies are not included in CORS requests, but this method will enable cookies to
+ * be added to CORS requests. Calling this method will set the CORS 'Access-Control-Allow-Credentials'
+ * response header to true.
+ *
+ * Please note, that cookie support needs to be enabled on the client side as well.
+ * The client needs to opt-in to send cookies by calling:
+ *
+ * xhr.withCredentials = true;
+ *
+ * The default value for 'withCredentials' is false in which case no cookies are sent.
+ * Setting this to true will included cookies in cross origin requests.
+ *
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder allowCredentials() {
+ allowCredentials = true;
+ return this;
+ }
+
+ /**
+ * When making a preflight request the client has to perform two request with can be inefficient.
+ * This setting will set the CORS 'Access-Control-Max-Age' response header and enables the
+ * caching of the preflight response for the specified time. During this time no preflight
+ * request will be made.
+ *
+ * @param max the maximum time, in seconds, that the preflight response may be cached.
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder maxAge(final long max) {
+ maxAge = max;
+ return this;
+ }
+
+ /**
+ * Specifies the allowed set of HTTP Request Methods that should be returned in the
+ * CORS 'Access-Control-Request-Method' response header.
+ *
+ * @param methods the {@link HttpMethod}s that should be allowed.
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder allowedRequestMethods(final HttpMethod... methods) {
+ requestMethods.addAll(Arrays.asList(methods));
+ return this;
+ }
+
+ /**
+ * Specifies the if headers that should be returned in the CORS 'Access-Control-Allow-Headers'
+ * response header.
+ *
+ * If a client specifies headers on the request, for example by calling:
+ *
+ * the server will receive the above header name in the 'Access-Control-Request-Headers' of the
+ * preflight request. The server will then decide if it allows this header to be sent for the
+ * real request (remember that a preflight is not the real request but a request asking the server
+ * if it allow a request).
+ *
+ * @param headers the headers to be added to the preflight 'Access-Control-Allow-Headers' response header.
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder allowedRequestHeaders(final String... headers) {
+ requestHeaders.addAll(Arrays.asList(headers));
+ return this;
+ }
+
+ /**
+ * Returns HTTP response headers that should be added to a CORS preflight response.
+ *
+ * An intermediary like a load balancer might require that a CORS preflight request
+ * have certain headers set. This enables such headers to be added.
+ *
+ * @param name the name of the HTTP header.
+ * @param values the values for the HTTP header.
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder preflightResponseHeader(final CharSequence name, final Object... values) {
+ if (values.length == 1) {
+ preflightHeaders.put(name, new ConstantValueGenerator(values[0]));
+ } else {
+ preflightResponseHeader(name, Arrays.asList(values));
+ }
+ return this;
+ }
+
+ /**
+ * Returns HTTP response headers that should be added to a CORS preflight response.
+ *
+ * An intermediary like a load balancer might require that a CORS preflight request
+ * have certain headers set. This enables such headers to be added.
+ *
+ * @param name the name of the HTTP header.
+ * @param value the values for the HTTP header.
+ * @param the type of values that the Iterable contains.
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder preflightResponseHeader(final CharSequence name, final Iterable value) {
+ preflightHeaders.put(name, new ConstantValueGenerator(value));
+ return this;
+ }
+
+ /**
+ * Returns HTTP response headers that should be added to a CORS preflight response.
+ *
+ * An intermediary like a load balancer might require that a CORS preflight request
+ * have certain headers set. This enables such headers to be added.
+ *
+ * Some values must be dynamically created when the HTTP response is created, for
+ * example the 'Date' response header. This can be accomplished by using a Callable
+ * which will have its 'call' method invoked when the HTTP response is created.
+ *
+ * @param name the name of the HTTP header.
+ * @param valueGenerator a Callable which will be invoked at HTTP response creation.
+ * @param the type of the value that the Callable can return.
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder preflightResponseHeader(final CharSequence name, final Callable valueGenerator) {
+ preflightHeaders.put(name, valueGenerator);
+ return this;
+ }
+
+ /**
+ * Specifies that no preflight response headers should be added to a preflight response.
+ *
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder noPreflightResponseHeaders() {
+ noPreflightHeaders = true;
+ return this;
+ }
+
+ /**
+ * Specifies that a CORS request should be rejected if it's invalid before being
+ * further processing.
+ *
+ * CORS headers are set after a request is processed. This may not always be desired
+ * and this setting will check that the Origin is valid and if it is not valid no
+ * further processing will take place, and a error will be returned to the calling client.
+ *
+ * @return {@link NioCorsConfigBuilder} to support method chaining.
+ */
+ public NioCorsConfigBuilder shortCircuit() {
+ shortCircuit = true;
+ return this;
+ }
+
+ /**
+ * Builds a {@link NioCorsConfig} with settings specified by previous method calls.
+ *
+ * @return {@link NioCorsConfig} the configured CorsConfig instance.
+ */
+ public NioCorsConfig build() {
+ if (preflightHeaders.isEmpty() && !noPreflightHeaders) {
+ preflightHeaders.put("date", DateValueGenerator.INSTANCE);
+ preflightHeaders.put("content-length", new ConstantValueGenerator("0"));
+ }
+ return new NioCorsConfig(this);
+ }
+
+ /**
+ * This class is used for preflight HTTP response values that do not need to be
+ * generated, but instead the value is "static" in that the same value will be returned
+ * for each call.
+ */
+ private static final class ConstantValueGenerator implements Callable