diff --git a/core/src/main/java/org/elasticsearch/action/ActionModule.java b/core/src/main/java/org/elasticsearch/action/ActionModule.java index 6cfd89d2d26bd..c6e0cef8e3bc4 100644 --- a/core/src/main/java/org/elasticsearch/action/ActionModule.java +++ b/core/src/main/java/org/elasticsearch/action/ActionModule.java @@ -203,8 +203,12 @@ import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.settings.Setting.Property; +import org.elasticsearch.common.unit.ByteSizeUnit; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.ActionPlugin.ActionHandler; @@ -318,6 +322,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.function.UnaryOperator; @@ -333,6 +338,10 @@ public class ActionModule extends AbstractModule { private static final Logger logger = ESLoggerFactory.getLogger(ActionModule.class); + public static final Setting SETTING_SEARCH_MAX_CONTENT_LENGTH = + Setting.byteSizeSetting("http.search.max_content_length", new ByteSizeValue(1, ByteSizeUnit.MB), + Property.NodeScope, Property.Dynamic); + private final boolean transportClient; private final Settings settings; private final IndexNameExpressionResolver indexNameExpressionResolver; @@ -345,6 +354,7 @@ public class ActionModule extends AbstractModule { private final AutoCreateIndex autoCreateIndex; private final DestructiveOperations destructiveOperations; private final RestController restController; + private final AtomicReference maxSearchContentLength; public ActionModule(boolean transportClient, Settings settings, IndexNameExpressionResolver indexNameExpressionResolver, IndexScopedSettings indexScopedSettings, ClusterSettings clusterSettings, SettingsFilter settingsFilter, @@ -378,9 +388,10 @@ public ActionModule(boolean transportClient, Settings settings, IndexNameExpress } else { restController = new RestController(settings, headers, restWrapper, nodeClient, circuitBreakerService, usageService); } + maxSearchContentLength = new AtomicReference<>(clusterSettings.get(SETTING_SEARCH_MAX_CONTENT_LENGTH)); + clusterSettings.addSettingsUpdateConsumer(SETTING_SEARCH_MAX_CONTENT_LENGTH, maxSearchContentLength::set); } - public Map> getActions() { return actions; } @@ -587,10 +598,10 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestBulkAction(settings, restController)); registerHandler.accept(new RestUpdateAction(settings, restController)); - registerHandler.accept(new RestSearchAction(settings, restController)); + registerHandler.accept(new RestSearchAction(settings, restController, maxSearchContentLength)); registerHandler.accept(new RestSearchScrollAction(settings, restController)); registerHandler.accept(new RestClearScrollAction(settings, restController)); - registerHandler.accept(new RestMultiSearchAction(settings, restController)); + registerHandler.accept(new RestMultiSearchAction(settings, restController, maxSearchContentLength)); registerHandler.accept(new RestValidateQueryAction(settings, restController)); diff --git a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index b56b56c788e9d..f5b9ac20ae8dd 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.common.settings; +import org.elasticsearch.action.ActionModule; import org.elasticsearch.action.admin.indices.close.TransportCloseIndexAction; import org.elasticsearch.action.search.TransportSearchAction; import org.elasticsearch.action.support.AutoCreateIndex; @@ -410,6 +411,7 @@ public void apply(Settings value, Settings current, Settings previous) { ThreadPool.ESTIMATED_TIME_INTERVAL_SETTING, FastVectorHighlighter.SETTING_TV_HIGHLIGHT_MULTI_VALUE, Node.BREAKER_TYPE_KEY, - IndexGraveyard.SETTING_MAX_TOMBSTONES + IndexGraveyard.SETTING_MAX_TOMBSTONES, + ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH ))); } diff --git a/core/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java b/core/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java index 7c2cc4b33d4df..a27bd6cad530c 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java @@ -20,6 +20,7 @@ package org.elasticsearch.rest.action.search; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.action.ActionModule; import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; @@ -28,6 +29,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; @@ -42,6 +44,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeBooleanValue; @@ -55,9 +58,11 @@ public class RestMultiSearchAction extends BaseRestHandler { private static final Set RESPONSE_PARAMS = Collections.singleton(RestSearchAction.TYPED_KEYS_PARAM); private final boolean allowExplicitIndex; + private final AtomicReference maxSearchContentLength; - public RestMultiSearchAction(Settings settings, RestController controller) { + public RestMultiSearchAction(Settings settings, RestController controller, AtomicReference maxSearchContentLength) { super(settings); + this.maxSearchContentLength = maxSearchContentLength; controller.registerHandler(GET, "/_msearch", this); controller.registerHandler(POST, "/_msearch", this); @@ -76,14 +81,15 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { - MultiSearchRequest multiSearchRequest = parseRequest(request, allowExplicitIndex); + MultiSearchRequest multiSearchRequest = parseRequest(request, allowExplicitIndex, maxSearchContentLength.get()); return channel -> client.multiSearch(multiSearchRequest, new RestToXContentListener<>(channel)); } /** * Parses a {@link RestRequest} body and returns a {@link MultiSearchRequest} */ - public static MultiSearchRequest parseRequest(RestRequest restRequest, boolean allowExplicitIndex) throws IOException { + public static MultiSearchRequest parseRequest(RestRequest restRequest, boolean allowExplicitIndex, + ByteSizeValue maxSearchContentLength) throws IOException { MultiSearchRequest multiRequest = new MultiSearchRequest(); if (restRequest.hasParam("max_concurrent_searches")) { multiRequest.maxConcurrentSearchRequests(restRequest.paramAsInt("max_concurrent_searches", 0)); @@ -92,14 +98,15 @@ public static MultiSearchRequest parseRequest(RestRequest restRequest, boolean a int preFilterShardSize = restRequest.paramAsInt("pre_filter_shard_size", SearchRequest.DEFAULT_PRE_FILTER_SHARD_SIZE); - parseMultiLineRequest(restRequest, multiRequest.indicesOptions(), allowExplicitIndex, (searchRequest, parser) -> { - try { - searchRequest.source(SearchSourceBuilder.fromXContent(parser)); - multiRequest.add(searchRequest); - } catch (IOException e) { - throw new ElasticsearchParseException("Exception when parsing search request", e); - } - }); + parseMultiLineRequest(restRequest, multiRequest.indicesOptions(), allowExplicitIndex, maxSearchContentLength, + (searchRequest, parser) -> { + try { + searchRequest.source(SearchSourceBuilder.fromXContent(parser)); + multiRequest.add(searchRequest); + } catch (IOException e) { + throw new ElasticsearchParseException("Exception when parsing search request", e); + } + }); List requests = multiRequest.requests(); preFilterShardSize = Math.max(1, preFilterShardSize / (requests.size()+1)); for (SearchRequest request : requests) { @@ -113,7 +120,7 @@ public static MultiSearchRequest parseRequest(RestRequest restRequest, boolean a * Parses a multi-line {@link RestRequest} body, instantiating a {@link SearchRequest} for each line and applying the given consumer. */ public static void parseMultiLineRequest(RestRequest request, IndicesOptions indicesOptions, boolean allowExplicitIndex, - BiConsumer consumer) throws IOException { + ByteSizeValue maxSearchContentLength, BiConsumer consumer) throws IOException { String[] indices = Strings.splitStringByCommaToArray(request.param("index")); String[] types = Strings.splitStringByCommaToArray(request.param("type")); @@ -193,7 +200,14 @@ public static void parseMultiLineRequest(RestRequest request, IndicesOptions ind if (nextMarker == -1) { break; } - BytesReference bytes = data.slice(from, nextMarker - from); + final int reqLength = nextMarker - from; + if (reqLength > maxSearchContentLength.getBytes()) { + throw new IllegalArgumentException("Search request body has a size of [" + new ByteSizeValue(reqLength) + + "] which is larger than the configured limit of [" + maxSearchContentLength + + "]. If you really need to send such large requests, you can update the [" + + ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH.getKey() +"] cluster setting to a higher value."); + } + BytesReference bytes = data.slice(from, reqLength); try (XContentParser parser = xContent.createParser(request.getXContentRegistry(), bytes)) { consumer.accept(searchRequest, parser); } diff --git a/core/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/core/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index 2e97560cf789f..0fa24de9c54bb 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -19,11 +19,13 @@ package org.elasticsearch.rest.action.search; +import org.elasticsearch.action.ActionModule; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.rest.BaseRestHandler; @@ -44,6 +46,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.common.unit.TimeValue.parseTimeValue; import static org.elasticsearch.rest.RestRequest.Method.GET; @@ -55,8 +58,11 @@ public class RestSearchAction extends BaseRestHandler { public static final String TYPED_KEYS_PARAM = "typed_keys"; private static final Set RESPONSE_PARAMS = Collections.singleton(TYPED_KEYS_PARAM); - public RestSearchAction(Settings settings, RestController controller) { + private final AtomicReference maxSearchContentLength; + + public RestSearchAction(Settings settings, RestController controller, AtomicReference maxSearchContentLength) { super(settings); + this.maxSearchContentLength = maxSearchContentLength; controller.registerHandler(GET, "/_search", this); controller.registerHandler(POST, "/_search", this); controller.registerHandler(GET, "/{index}/_search", this); @@ -72,6 +78,17 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { + if (request.hasContent()) { + final int length = request.requiredContent().length(); + final ByteSizeValue maxSearchContentLength = this.maxSearchContentLength.get(); + if (length > maxSearchContentLength.getBytes()) { + throw new IllegalArgumentException("Search request body has a size of [" + new ByteSizeValue(length) + + "] which is larger than the configured limit of [" + maxSearchContentLength + + "]. If you really need to send such large requests, you can update the [" + + ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH.getKey() +"] cluster setting to a higher value."); + } + } + SearchRequest searchRequest = new SearchRequest(); request.withContentOrSourceParamParserOrNull(parser -> parseSearchRequest(searchRequest, request, parser)); diff --git a/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java b/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java index 3a162f302bc3b..060a3a5c97405 100644 --- a/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.MatchAllQueryBuilder; @@ -85,7 +86,7 @@ public void testSimpleAddWithCarriageReturn() throws Exception { "{\"query\" : {\"match_all\" :{}}}\r\n"; FakeRestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()) .withContent(new BytesArray(requestContent), XContentType.JSON).build(); - MultiSearchRequest request = RestMultiSearchAction.parseRequest(restRequest, true); + MultiSearchRequest request = RestMultiSearchAction.parseRequest(restRequest, true, new ByteSizeValue(16_384)); assertThat(request.requests().size(), equalTo(1)); assertThat(request.requests().get(0).indices()[0], equalTo("test")); assertThat(request.requests().get(0).indicesOptions(), @@ -177,13 +178,13 @@ public void testMsearchTerminatedByNewline() throws Exception { RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()) .withContent(new BytesArray(mserchAction.getBytes(StandardCharsets.UTF_8)), XContentType.JSON).build(); IllegalArgumentException expectThrows = expectThrows(IllegalArgumentException.class, - () -> RestMultiSearchAction.parseRequest(restRequest, true)); + () -> RestMultiSearchAction.parseRequest(restRequest, true, new ByteSizeValue(16_384))); assertEquals("The msearch request must be terminated by a newline [\n]", expectThrows.getMessage()); String mserchActionWithNewLine = mserchAction + "\n"; RestRequest restRequestWithNewLine = new FakeRestRequest.Builder(xContentRegistry()) .withContent(new BytesArray(mserchActionWithNewLine.getBytes(StandardCharsets.UTF_8)), XContentType.JSON).build(); - MultiSearchRequest msearchRequest = RestMultiSearchAction.parseRequest(restRequestWithNewLine, true); + MultiSearchRequest msearchRequest = RestMultiSearchAction.parseRequest(restRequestWithNewLine, true, new ByteSizeValue(16_384)); assertEquals(3, msearchRequest.requests().size()); } @@ -191,7 +192,7 @@ private MultiSearchRequest parseMultiSearchRequest(String sample) throws IOExcep byte[] data = StreamsUtils.copyToBytesFromClasspath(sample); RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()) .withContent(new BytesArray(data), XContentType.JSON).build(); - return RestMultiSearchAction.parseRequest(restRequest, true); + return RestMultiSearchAction.parseRequest(restRequest, true, new ByteSizeValue(16_384)); } @Override diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java index f129a5b15ec7c..69131232226cb 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -75,8 +76,10 @@ public static MultiSearchTemplateRequest parseRequest(RestRequest restRequest, b multiRequest.maxConcurrentSearchRequests(restRequest.paramAsInt("max_concurrent_searches", 0)); } + // ignore the limit for now, we will evaluate it after the script is executed + ByteSizeValue limit = new ByteSizeValue(Long.MAX_VALUE); RestMultiSearchAction.parseMultiLineRequest(restRequest, multiRequest.indicesOptions(), allowExplicitIndex, - (searchRequest, bytes) -> { + limit, (searchRequest, bytes) -> { try { SearchTemplateRequest searchTemplateRequest = RestSearchTemplateAction.parse(bytes); if (searchTemplateRequest.getScript() != null) { diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java index 167190d8f5d04..e43d25bdfab00 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java @@ -20,6 +20,7 @@ package org.elasticsearch.script.mustache; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ActionModule; import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.MultiSearchResponse; import org.elasticsearch.action.search.SearchRequest; @@ -28,7 +29,9 @@ import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.script.ScriptService; import org.elasticsearch.threadpool.ThreadPool; @@ -44,17 +47,20 @@ public class TransportMultiSearchTemplateAction extends HandledTransportAction maxSearchContentLength = value); } @Override @@ -72,7 +78,8 @@ protected void doExecute(MultiSearchTemplateRequest request, ActionListener maxSearchContentLength = value); } @Override protected void doExecute(SearchTemplateRequest request, ActionListener listener) { final SearchTemplateResponse response = new SearchTemplateResponse(); try { - SearchRequest searchRequest = convert(request, response, scriptService, xContentRegistry); + SearchRequest searchRequest = convert(request, response, scriptService, xContentRegistry, maxSearchContentLength); if (searchRequest != null) { searchAction.execute(searchRequest, new ActionListener() { @Override @@ -95,13 +103,19 @@ public void onFailure(Exception t) { } static SearchRequest convert(SearchTemplateRequest searchTemplateRequest, SearchTemplateResponse response, ScriptService scriptService, - NamedXContentRegistry xContentRegistry) throws IOException { + NamedXContentRegistry xContentRegistry, ByteSizeValue maxSearchContentLength) throws IOException { Script script = new Script(searchTemplateRequest.getScriptType(), searchTemplateRequest.getScriptType() == ScriptType.STORED ? null : TEMPLATE_LANG, searchTemplateRequest.getScript(), searchTemplateRequest.getScriptParams() == null ? Collections.emptyMap() : searchTemplateRequest.getScriptParams()); TemplateScript compiledScript = scriptService.compile(script, TemplateScript.CONTEXT).newInstance(script.getParams()); - String source = compiledScript.execute(); - response.setSource(new BytesArray(source)); + BytesReference source = new BytesArray(compiledScript.execute()); + if (source.length() > maxSearchContentLength.getBytes()) { + throw new IllegalArgumentException("Generated search request body has a size of [" + new ByteSizeValue(source.length()) + + "] which is larger than the configured limit of [" + maxSearchContentLength + + "]. If you really need to send such large requests, you can update the [" + + ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH.getKey() +"] cluster setting to a higher value."); + } + response.setSource(source); SearchRequest searchRequest = searchTemplateRequest.getRequest(); if (searchTemplateRequest.isSimulate()) { diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java index 91fc4db43dddd..e187141c1a667 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java @@ -19,12 +19,17 @@ package org.elasticsearch.script.mustache; +import org.elasticsearch.action.ActionFuture; +import org.elasticsearch.action.ActionModule; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.ScriptType; import org.elasticsearch.test.ESIntegTestCase; +import org.hamcrest.Matchers; import java.util.Arrays; import java.util.Collection; @@ -175,4 +180,53 @@ public void testBasic() throws Exception { assertThat(searchTemplateResponse5.getSource().utf8ToString(), equalTo("{\"query\":{\"terms\":{\"group\":[1,2,3,]}}}")); } + + public void testMaxContentLength() throws Exception { + indexRandom(true, client().prepareIndex("test", "doc").setSource()); + try { + client().admin().cluster().prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH.getKey(), "30B") + .build()).get(); + + MultiSearchTemplateRequest multiRequest = new MultiSearchTemplateRequest(); + + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("test"); + String query = + "{" + " \"source\" : \"{ \\\"size\\\": \\\"{{size}}\\\", \\\"query\\\":{\\\"match_all\\\":{}}}\"," + + " \"params\":{" + + " \"size\": 1" + + " }" + + "}"; + SearchTemplateRequest request = RestSearchTemplateAction.parse(createParser(JsonXContent.jsonXContent, query)); + request.setRequest(searchRequest); + multiRequest.add(request); + + searchRequest = new SearchRequest(); + searchRequest.indices("test"); + query = + "{" + " \"source\" : \"{ \\\"size\\\": \\\"{{size}}\\\"}\"," + + " \"params\":{" + + " \"size\": 1" + + " }" + + "}"; + request = RestSearchTemplateAction.parse(createParser(JsonXContent.jsonXContent, query)); + request.setRequest(searchRequest); + multiRequest.add(request); + + MultiSearchTemplateResponse response = client().execute(MultiSearchTemplateAction.INSTANCE, multiRequest).get(); + assertThat(response.getResponses(), arrayWithSize(2)); + + assertTrue(response.getResponses()[0].isFailure()); + assertFalse(response.getResponses()[1].isFailure()); + + assertThat(response.getResponses()[0].getFailureMessage(), Matchers.containsString("Generated search request body has a size" + + " of [40b] which is larger than the configured limit of [30b].")); + } finally { + // reset cluster setting + client().admin().cluster().prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH.getKey(), (String) null) + .build()).get(); + } + } } diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index 39657bc177736..9d8568826e3b2 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -19,16 +19,20 @@ package org.elasticsearch.script.mustache; import org.elasticsearch.ResourceNotFoundException; +import org.elasticsearch.action.ActionFuture; +import org.elasticsearch.action.ActionModule; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.ScriptType; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.hamcrest.Matchers; import org.junit.Before; import java.io.IOException; @@ -340,4 +344,30 @@ public void testIndexedTemplateWithArray() throws Exception { assertHitCount(searchResponse.getResponse(), 5); } + public void testMaxContentLength() throws Exception { + try { + client().admin().cluster().prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH.getKey(), "10B") + .build()).get(); + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("test"); + String query = + "{" + " \"source\" : \"{ \\\"size\\\": \\\"{{size}}\\\", \\\"query\\\":{\\\"match_all\\\":{}}}\"," + + " \"params\":{" + + " \"size\": 1" + + " }" + + "}"; + SearchTemplateRequest request = RestSearchTemplateAction.parse(createParser(JsonXContent.jsonXContent, query)); + request.setRequest(searchRequest); + ActionFuture future = client().execute(SearchTemplateAction.INSTANCE, request); + Exception e = expectThrows(Exception.class, future::get); + assertThat(e.getMessage(), Matchers.containsString("Generated search request body has a size of [40b] which is larger than " + + "the configured limit of [10b].")); + } finally { + // reset cluster setting + client().admin().cluster().prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(ActionModule.SETTING_SEARCH_MAX_CONTENT_LENGTH.getKey(), (String) null) + .build()).get(); + } + } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/30_max_content_length.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/30_max_content_length.yml new file mode 100644 index 0000000000000..a4793e18e760a --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/30_max_content_length.yml @@ -0,0 +1,36 @@ +--- +setup: + - do: + index: + index: test + type: doc + id: 1 + body: {} + + - do: + indices.refresh: {} + + - do: + cluster.put_settings: + body: + transient: + http.search.max_content_length: "30b" + +--- +teardown: + + - do: + cluster.put_settings: + body: + transient: + http.search.max_content_length: null + +--- +"Max content length limit": + - do: + catch: /Search request body .* is larger than the configured limit/ + msearch: + body: + - index: test + - query: + term: { some_feld: "some_value" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/160_max_content_length.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/160_max_content_length.yml new file mode 100644 index 0000000000000..ed1ad6ea80a24 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/160_max_content_length.yml @@ -0,0 +1,36 @@ +--- +setup: + - do: + index: + index: test + type: doc + id: 1 + body: {} + + - do: + indices.refresh: {} + + - do: + cluster.put_settings: + body: + transient: + http.search.max_content_length: "10b" + +--- +teardown: + + - do: + cluster.put_settings: + body: + transient: + http.search.max_content_length: null + +--- +"Max content length limit": + - do: + catch: /Search request body .* is larger than the configured limit/ + search: + index: test + body: + query: + match_all: {}