diff --git a/core/src/main/java/org/elasticsearch/common/Strings.java b/core/src/main/java/org/elasticsearch/common/Strings.java index 63afe9a0323..955b836ca1c 100644 --- a/core/src/main/java/org/elasticsearch/common/Strings.java +++ b/core/src/main/java/org/elasticsearch/common/Strings.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; @@ -509,7 +510,19 @@ public class Strings { else return s.split(","); } + /** + * A convenience method for splitting a delimited string into + * a set and trimming leading and trailing whitespace from all + * split strings. + * + * @param s the string to split + * @param c the delimiter to split on + * @return the set of split strings + */ public static Set splitStringToSet(final String s, final char c) { + if (s == null || s.isEmpty()) { + return Collections.emptySet(); + } final char[] chars = s.toCharArray(); int count = 1; for (final char x : chars) { @@ -521,16 +534,25 @@ public class Strings { final int len = chars.length; int start = 0; // starting index in chars of the current substring. int pos = 0; // current index in chars. + int end = 0; // the position of the end of the current token for (; pos < len; pos++) { if (chars[pos] == c) { - int size = pos - start; + int size = end - start; if (size > 0) { // only add non empty strings result.add(new String(chars, start, size)); } start = pos + 1; + end = start; + } else if (Character.isWhitespace(chars[pos])) { + if (start == pos) { + // skip over preceding whitespace + start++; + } + } else { + end = pos + 1; } } - int size = pos - start; + int size = end - start; if (size > 0) { result.add(new String(chars, start, size)); } diff --git a/core/src/main/java/org/elasticsearch/http/HttpTransportSettings.java b/core/src/main/java/org/elasticsearch/http/HttpTransportSettings.java index 72f8f380df8..60bc3449d0b 100644 --- a/core/src/main/java/org/elasticsearch/http/HttpTransportSettings.java +++ b/core/src/main/java/org/elasticsearch/http/HttpTransportSettings.java @@ -40,9 +40,9 @@ public final class HttpTransportSettings { public static final Setting SETTING_CORS_MAX_AGE = Setting.intSetting("http.cors.max-age", 1728000, Property.NodeScope); public static final Setting SETTING_CORS_ALLOW_METHODS = - new Setting<>("http.cors.allow-methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE", (value) -> value, Property.NodeScope); + new Setting<>("http.cors.allow-methods", "OPTIONS,HEAD,GET,POST,PUT,DELETE", (value) -> value, Property.NodeScope); public static final Setting SETTING_CORS_ALLOW_HEADERS = - new Setting<>("http.cors.allow-headers", "X-Requested-With, Content-Type, Content-Length", (value) -> value, Property.NodeScope); + new Setting<>("http.cors.allow-headers", "X-Requested-With,Content-Type,Content-Length", (value) -> value, Property.NodeScope); public static final Setting SETTING_CORS_ALLOW_CREDENTIALS = Setting.boolSetting("http.cors.allow-credentials", false, Property.NodeScope); public static final Setting SETTING_PIPELINING = diff --git a/core/src/test/java/org/elasticsearch/common/StringsTests.java b/core/src/test/java/org/elasticsearch/common/StringsTests.java index 406b4160519..1b987d47796 100644 --- a/core/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/core/src/test/java/org/elasticsearch/common/StringsTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.common; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.test.ESTestCase; @@ -73,4 +74,32 @@ public class StringsTests extends ESTestCase { assertThat(toString, containsString("\"ok\":\"here\"")); assertThat(toString, containsString("\"catastrophe\":\"\"")); } + + public void testSplitStringToSet() { + assertEquals(Strings.splitStringByCommaToSet(null), Sets.newHashSet()); + assertEquals(Strings.splitStringByCommaToSet(""), Sets.newHashSet()); + assertEquals(Strings.splitStringByCommaToSet("a,b,c"), Sets.newHashSet("a","b","c")); + assertEquals(Strings.splitStringByCommaToSet("a, b, c"), Sets.newHashSet("a","b","c")); + assertEquals(Strings.splitStringByCommaToSet(" a , b, c "), Sets.newHashSet("a","b","c")); + assertEquals(Strings.splitStringByCommaToSet("aa, bb, cc"), Sets.newHashSet("aa","bb","cc")); + assertEquals(Strings.splitStringByCommaToSet(" a "), Sets.newHashSet("a")); + assertEquals(Strings.splitStringByCommaToSet(" a "), Sets.newHashSet("a")); + assertEquals(Strings.splitStringByCommaToSet(" aa "), Sets.newHashSet("aa")); + assertEquals(Strings.splitStringByCommaToSet(" "), Sets.newHashSet()); + + assertEquals(Strings.splitStringToSet(null, ' '), Sets.newHashSet()); + assertEquals(Strings.splitStringToSet("", ' '), Sets.newHashSet()); + assertEquals(Strings.splitStringToSet("a b c", ' '), Sets.newHashSet("a","b","c")); + assertEquals(Strings.splitStringToSet("a, b, c", ' '), Sets.newHashSet("a,","b,","c")); + assertEquals(Strings.splitStringToSet(" a b c ", ' '), Sets.newHashSet("a","b","c")); + assertEquals(Strings.splitStringToSet(" a b c ", ' '), Sets.newHashSet("a","b","c")); + assertEquals(Strings.splitStringToSet("aa bb cc", ' '), Sets.newHashSet("aa","bb","cc")); + assertEquals(Strings.splitStringToSet(" a ", ' '), Sets.newHashSet("a")); + assertEquals(Strings.splitStringToSet(" a ", ' '), Sets.newHashSet("a")); + assertEquals(Strings.splitStringToSet(" a ", ' '), Sets.newHashSet("a")); + assertEquals(Strings.splitStringToSet("a ", ' '), Sets.newHashSet("a")); + assertEquals(Strings.splitStringToSet(" aa ", ' '), Sets.newHashSet("aa")); + assertEquals(Strings.splitStringToSet("aa ", ' '), Sets.newHashSet("aa")); + assertEquals(Strings.splitStringToSet(" ", ' '), Sets.newHashSet()); + } } diff --git a/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/Netty3HttpServerTransport.java b/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/Netty3HttpServerTransport.java index c480155dceb..edbcc74e646 100644 --- a/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/Netty3HttpServerTransport.java +++ b/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/Netty3HttpServerTransport.java @@ -81,9 +81,11 @@ import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static org.elasticsearch.common.settings.Setting.boolSetting; import static org.elasticsearch.common.settings.Setting.byteSizeSetting; @@ -390,14 +392,10 @@ public class Netty3HttpServerTransport extends AbstractLifecycleComponent implem if (SETTING_CORS_ALLOW_CREDENTIALS.get(settings)) { builder.allowCredentials(); } - String[] strMethods = settings.getAsArray(SETTING_CORS_ALLOW_METHODS.getKey()); - HttpMethod[] methods = Arrays.asList(strMethods) - .stream() - .map(HttpMethod::valueOf) - .toArray(size -> new HttpMethod[size]); - return builder.allowedRequestMethods(methods) + Set strMethods = Strings.splitStringByCommaToSet(SETTING_CORS_ALLOW_METHODS.get(settings)); + return builder.allowedRequestMethods(strMethods.stream().map(HttpMethod::valueOf).collect(Collectors.toSet())) .maxAge(SETTING_CORS_MAX_AGE.get(settings)) - .allowedRequestHeaders(settings.getAsArray(SETTING_CORS_ALLOW_HEADERS.getKey())) + .allowedRequestHeaders(Strings.splitStringByCommaToSet(SETTING_CORS_ALLOW_HEADERS.get(settings))) .shortCircuit() .build(); } diff --git a/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/cors/Netty3CorsConfigBuilder.java b/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/cors/Netty3CorsConfigBuilder.java index 947ec86b161..e7b94898b14 100644 --- a/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/cors/Netty3CorsConfigBuilder.java +++ b/modules/transport-netty3/src/main/java/org/elasticsearch/http/netty3/cors/Netty3CorsConfigBuilder.java @@ -193,8 +193,8 @@ public final class Netty3CorsConfigBuilder { * @param methods the {@link HttpMethod}s that should be allowed. * @return {@link Netty3CorsConfigBuilder} to support method chaining. */ - public Netty3CorsConfigBuilder allowedRequestMethods(final HttpMethod... methods) { - requestMethods.addAll(Arrays.asList(methods)); + public Netty3CorsConfigBuilder allowedRequestMethods(final Set methods) { + requestMethods.addAll(methods); return this; } @@ -214,8 +214,8 @@ public final class Netty3CorsConfigBuilder { * @param headers the headers to be added to the preflight 'Access-Control-Allow-Headers' response header. * @return {@link Netty3CorsConfigBuilder} to support method chaining. */ - public Netty3CorsConfigBuilder allowedRequestHeaders(final String... headers) { - requestHeaders.addAll(Arrays.asList(headers)); + public Netty3CorsConfigBuilder allowedRequestHeaders(final Set headers) { + requestHeaders.addAll(headers); return this; } diff --git a/modules/transport-netty3/src/test/java/org/elasticsearch/http/netty3/Netty3HttpServerTransportTests.java b/modules/transport-netty3/src/test/java/org/elasticsearch/http/netty3/Netty3HttpServerTransportTests.java index da7e320a557..901d517bf95 100644 --- a/modules/transport-netty3/src/test/java/org/elasticsearch/http/netty3/Netty3HttpServerTransportTests.java +++ b/modules/transport-netty3/src/test/java/org/elasticsearch/http/netty3/Netty3HttpServerTransportTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.http.netty3.cors.Netty3CorsConfig; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.test.ESTestCase; @@ -86,4 +87,19 @@ public class Netty3HttpServerTransportTests extends ESTestCase { assertThat(corsConfig.allowedRequestMethods().stream().map(HttpMethod::getName).collect(Collectors.toSet()), equalTo(methods)); transport.close(); } + + public void testCorsConfigDefaults() { + final Set headers = Sets.newHashSet("X-Requested-With", "Content-Type", "Content-Length"); + final Set methods = Sets.newHashSet("OPTIONS", "HEAD", "GET", "POST", "PUT", "DELETE"); + final Settings settings = Settings.builder() + .put(SETTING_CORS_ENABLED.getKey(), true) + .put(SETTING_CORS_ALLOW_ORIGIN.getKey(), "*") + .put(SETTING_CORS_ALLOW_CREDENTIALS.getKey(), true) + .build(); + final Netty3HttpServerTransport transport = new Netty3HttpServerTransport(settings, networkService, bigArrays, threadPool); + final Netty3CorsConfig corsConfig = transport.getCorsConfig(); + assertThat(corsConfig.allowedRequestHeaders(), equalTo(headers)); + assertThat(corsConfig.allowedRequestMethods().stream().map(HttpMethod::getName).collect(Collectors.toSet()), equalTo(methods)); + transport.close(); + } } diff --git a/modules/transport-netty3/src/test/java/org/elasticsearch/http/netty3/Netty3RestIT.java b/modules/transport-netty3/src/test/java/org/elasticsearch/http/netty3/Netty3RestIT.java new file mode 100644 index 00000000000..a276922354a --- /dev/null +++ b/modules/transport-netty3/src/test/java/org/elasticsearch/http/netty3/Netty3RestIT.java @@ -0,0 +1,40 @@ +/* + * 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.netty3; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.elasticsearch.test.rest.ESRestTestCase; +import org.elasticsearch.test.rest.RestTestCandidate; +import org.elasticsearch.test.rest.parser.RestTestParseException; + +import java.io.IOException; + +public class Netty3RestIT extends ESRestTestCase { + + public Netty3RestIT(@Name("yaml") RestTestCandidate testCandidate) { + super(testCandidate); + } + + @ParametersFactory + public static Iterable parameters() throws IOException, RestTestParseException { + return ESRestTestCase.createParameters(0, 1); + } +} diff --git a/modules/transport-netty3/src/test/resources/rest-api-spec/test/10_basic.yaml b/modules/transport-netty3/src/test/resources/rest-api-spec/test/10_basic.yaml index eaf51de4484..e25074fb90c 100644 --- a/modules/transport-netty3/src/test/resources/rest-api-spec/test/10_basic.yaml +++ b/modules/transport-netty3/src/test/resources/rest-api-spec/test/10_basic.yaml @@ -10,4 +10,4 @@ - do: nodes.info: {} - - match: { nodes.$master.modules.0.name: transport-netty3 } \ No newline at end of file + - match: { nodes.$master.modules.0.name: transport-netty3 }