Fixed NPEs caused by requests without content. (#23497)

REST handlers that require a body will throw an an ElasticsearchParseException "request body required".
REST handlers that require a body OR source param will throw an ElasticsearchParseException "request body or source param required".
Replaced asserts in BulkRequest parsing code with a more descriptive IllegalArgumentException if the line contains an empty object.
Updated bulk REST test to verify an empty action line is rejected properly.
Updated BulkRequestTests with randomized testing for an empty action line.
Used try-with-resouces for XContentParser in AbstractBulkByQueryRestHandler.
This commit is contained in:
Alex Benusovich 2017-06-05 08:08:14 -07:00 committed by Jay Modi
parent 73307a2144
commit 5463294ec4
29 changed files with 306 additions and 93 deletions

View File

@ -78,8 +78,8 @@ public class RestNoopBulkAction extends BaseRestHandler {
}
bulkRequest.timeout(request.paramAsTime("timeout", BulkShardRequest.DEFAULT_TIMEOUT));
bulkRequest.setRefreshPolicy(request.param("refresh"));
bulkRequest.add(request.content(), defaultIndex, defaultType, defaultRouting, defaultFields, null, defaultPipeline, null, true,
request.getXContentType());
bulkRequest.add(request.requiredContent(), defaultIndex, defaultType, defaultRouting, defaultFields,
null, defaultPipeline, null, true, request.getXContentType());
// short circuit the call to the transport layer
return channel -> {

View File

@ -41,7 +41,6 @@ import org.elasticsearch.common.lucene.uid.Versions;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.VersionType;
@ -300,10 +299,16 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
if (token == null) {
continue;
}
assert token == XContentParser.Token.START_OBJECT;
if (token != XContentParser.Token.START_OBJECT) {
throw new IllegalArgumentException("Malformed action/metadata line [" + line + "], expected "
+ XContentParser.Token.START_OBJECT + " but found [" + token + "]");
}
// Move to FIELD_NAME, that's the action
token = parser.nextToken();
assert token == XContentParser.Token.FIELD_NAME;
if (token != XContentParser.Token.FIELD_NAME) {
throw new IllegalArgumentException("Malformed action/metadata line [" + line + "], expected "
+ XContentParser.Token.FIELD_NAME + " but found [" + token + "]");
}
String action = parser.currentName();
String index = defaultIndex;

View File

@ -136,6 +136,18 @@ public abstract class RestRequest implements ToXContent.Params {
public abstract BytesReference content();
/**
* @return content of the request body or throw an exception if the body or content type is missing
*/
public final BytesReference requiredContent() {
if (hasContent() == false) {
throw new ElasticsearchParseException("request body is required");
} else if (xContentType.get() == null) {
throw new IllegalStateException("unknown content type");
}
return content();
}
/**
* Get the value of the header or {@code null} if not found. This method only retrieves the first header value if multiple values are
* sent. Use of {@link #getAllHeaderValues(String)} should be preferred
@ -329,12 +341,7 @@ public abstract class RestRequest implements ToXContent.Params {
* {@link #contentOrSourceParamParser()} for requests that support specifying the request body in the {@code source} param.
*/
public final XContentParser contentParser() throws IOException {
BytesReference content = content();
if (content.length() == 0) {
throw new ElasticsearchParseException("Body required");
} else if (xContentType.get() == null) {
throw new IllegalStateException("unknown content type");
}
BytesReference content = requiredContent(); // will throw exception if body or content type missing
return xContentType.get().xContent().createParser(xContentRegistry, content);
}
@ -364,11 +371,7 @@ public abstract class RestRequest implements ToXContent.Params {
*/
public final XContentParser contentOrSourceParamParser() throws IOException {
Tuple<XContentType, BytesReference> tuple = contentOrSourceParam();
BytesReference content = tuple.v2();
if (content.length() == 0) {
throw new ElasticsearchParseException("Body required");
}
return tuple.v1().xContent().createParser(xContentRegistry, content);
return tuple.v1().xContent().createParser(xContentRegistry, tuple.v2());
}
/**
@ -377,10 +380,10 @@ public abstract class RestRequest implements ToXContent.Params {
* back to the user when there isn't request content.
*/
public final void withContentOrSourceParamParserOrNull(CheckedConsumer<XContentParser, IOException> withParser) throws IOException {
Tuple<XContentType, BytesReference> tuple = contentOrSourceParam();
BytesReference content = tuple.v2();
XContentType xContentType = tuple.v1();
if (content.length() > 0) {
if (hasContentOrSourceParam()) {
Tuple<XContentType, BytesReference> tuple = contentOrSourceParam();
BytesReference content = tuple.v2();
XContentType xContentType = tuple.v1();
try (XContentParser parser = xContentType.xContent().createParser(xContentRegistry, content)) {
withParser.accept(parser);
}
@ -390,28 +393,26 @@ public abstract class RestRequest implements ToXContent.Params {
}
/**
* Get the content of the request or the contents of the {@code source} param. Prefer {@link #contentOrSourceParamParser()} or
* {@link #withContentOrSourceParamParserOrNull(CheckedConsumer)} if you need a parser.
* Get the content of the request or the contents of the {@code source} param or throw an exception if both are missing.
* Prefer {@link #contentOrSourceParamParser()} or {@link #withContentOrSourceParamParserOrNull(CheckedConsumer)} if you need a parser.
*/
public final Tuple<XContentType, BytesReference> contentOrSourceParam() {
if (hasContent()) {
if (xContentType.get() == null) {
throw new IllegalStateException("unknown content type");
}
return new Tuple<>(xContentType.get(), content());
if (hasContentOrSourceParam() == false) {
throw new ElasticsearchParseException("request body or source parameter is required");
} else if (hasContent()) {
return new Tuple<>(xContentType.get(), requiredContent());
}
String source = param("source");
String typeParam = param("source_content_type");
if (source != null && typeParam != null) {
BytesArray bytes = new BytesArray(source);
final XContentType xContentType = parseContentType(Collections.singletonList(typeParam));
if (xContentType == null) {
throw new IllegalStateException("Unknown value for source_content_type [" + typeParam + "]");
}
return new Tuple<>(xContentType, bytes);
if (source == null || typeParam == null) {
throw new IllegalStateException("source and source_content_type parameters are required");
}
return new Tuple<>(XContentType.JSON, BytesArray.EMPTY);
BytesArray bytes = new BytesArray(source);
final XContentType xContentType = parseContentType(Collections.singletonList(typeParam));
if (xContentType == null) {
throw new IllegalStateException("Unknown value for source_content_type [" + typeParam + "]");
}
return new Tuple<>(xContentType, bytes);
}
/**

View File

@ -64,7 +64,7 @@ public class RestPutStoredScriptAction extends BaseRestHandler {
lang = null;
}
BytesReference content = request.content();
BytesReference content = request.requiredContent();
if (lang != null) {
deprecationLogger.deprecated(

View File

@ -62,7 +62,7 @@ public class RestPutIndexTemplateAction extends BaseRestHandler {
putRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putRequest.masterNodeTimeout()));
putRequest.create(request.paramAsBoolean("create", false));
putRequest.cause(request.param("cause", ""));
putRequest.source(request.content(), request.getXContentType());
putRequest.source(request.requiredContent(), request.getXContentType());
return channel -> client.admin().indices().putTemplate(putRequest, new AcknowledgedRestListener<>(channel));
}

View File

@ -69,7 +69,7 @@ public class RestPutMappingAction extends BaseRestHandler {
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
PutMappingRequest putMappingRequest = putMappingRequest(Strings.splitStringByCommaToArray(request.param("index")));
putMappingRequest.type(request.param("type"));
putMappingRequest.source(request.content(), request.getXContentType());
putMappingRequest.source(request.requiredContent(), request.getXContentType());
putMappingRequest.updateAllTypes(request.paramAsBoolean("update_all_types", false));
putMappingRequest.timeout(request.paramAsTime("timeout", putMappingRequest.timeout()));
putMappingRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putMappingRequest.masterNodeTimeout()));

View File

@ -59,18 +59,16 @@ public class RestUpdateSettingsAction extends BaseRestHandler {
updateSettingsRequest.indicesOptions(IndicesOptions.fromRequest(request, updateSettingsRequest.indicesOptions()));
Map<String, Object> settings = new HashMap<>();
if (request.hasContent()) {
try (XContentParser parser = request.contentParser()) {
Map<String, Object> bodySettings = parser.map();
Object innerBodySettings = bodySettings.get("settings");
// clean up in case the body is wrapped with "settings" : { ... }
if (innerBodySettings instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> innerBodySettingsMap = (Map<String, Object>) innerBodySettings;
settings.putAll(innerBodySettingsMap);
} else {
settings.putAll(bodySettings);
}
try (XContentParser parser = request.contentParser()) {
Map<String, Object> bodySettings = parser.map();
Object innerBodySettings = bodySettings.get("settings");
// clean up in case the body is wrapped with "settings" : { ... }
if (innerBodySettings instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> innerBodySettingsMap = (Map<String, Object>) innerBodySettings;
settings.putAll(innerBodySettingsMap);
} else {
settings.putAll(bodySettings);
}
}
updateSettingsRequest.settings(settings);

View File

@ -91,7 +91,7 @@ public class RestBulkAction extends BaseRestHandler {
}
bulkRequest.timeout(request.paramAsTime("timeout", BulkShardRequest.DEFAULT_TIMEOUT));
bulkRequest.setRefreshPolicy(request.param("refresh"));
bulkRequest.add(request.content(), defaultIndex, defaultType, defaultRouting, defaultFields,
bulkRequest.add(request.requiredContent(), defaultIndex, defaultType, defaultRouting, defaultFields,
defaultFetchSourceContext, defaultPipeline, null, allowExplicitIndex, request.getXContentType());
return channel -> client.bulk(bulkRequest, new RestStatusToXContentListener<>(channel));

View File

@ -74,7 +74,7 @@ public class RestIndexAction extends BaseRestHandler {
indexRequest.routing(request.param("routing"));
indexRequest.parent(request.param("parent"));
indexRequest.setPipeline(request.param("pipeline"));
indexRequest.source(request.content(), request.getXContentType());
indexRequest.source(request.requiredContent(), request.getXContentType());
indexRequest.timeout(request.paramAsTime("timeout", IndexRequest.DEFAULT_TIMEOUT));
indexRequest.setRefreshPolicy(request.param("refresh"));
indexRequest.version(RestActions.parseVersion(request));

View File

@ -177,6 +177,31 @@ public class BulkRequestTests extends ESTestCase {
assertThat(bulkRequest.numberOfActions(), equalTo(9));
}
public void testBulkEmptyObject() throws Exception {
String bulkIndexAction = "{ \"index\":{\"_index\":\"test\",\"_type\":\"type1\",\"_id\":\"1\"} }\r\n";
String bulkIndexSource = "{ \"field1\" : \"value1\" }\r\n";
String emptyObject = "{}\r\n";
StringBuilder bulk = new StringBuilder();
int emptyLine;
if (randomBoolean()) {
bulk.append(emptyObject);
emptyLine = 1;
} else {
int actions = randomIntBetween(1, 10);
int emptyAction = randomIntBetween(1, actions);
emptyLine = emptyAction * 2 - 1;
for (int i = 1; i <= actions; i++) {
bulk.append(i == emptyAction ? emptyObject : bulkIndexAction);
bulk.append(randomBoolean() ? emptyObject : bulkIndexSource);
}
}
String bulkAction = bulk.toString();
BulkRequest bulkRequest = new BulkRequest();
IllegalArgumentException exc = expectThrows(IllegalArgumentException.class,
() -> bulkRequest.add(bulkAction.getBytes(StandardCharsets.UTF_8), 0, bulkAction.length(), null, null, XContentType.JSON));
assertThat(exc.getMessage(), containsString("Malformed action/metadata line [" + emptyLine + "], expected FIELD_NAME but found [END_OBJECT]"));
}
// issue 7361
public void testBulkRequestWithRefresh() throws Exception {
BulkRequest bulkRequest = new BulkRequest();

View File

@ -43,11 +43,14 @@ public class RestRequestTests extends ESTestCase {
public void testContentParser() throws IOException {
Exception e = expectThrows(ElasticsearchParseException.class, () ->
new ContentRestRequest("", emptyMap()).contentParser());
assertEquals("Body required", e.getMessage());
assertEquals("request body is required", e.getMessage());
e = expectThrows(ElasticsearchParseException.class, () ->
new ContentRestRequest("", singletonMap("source", "{}")).contentParser());
assertEquals("Body required", e.getMessage());
assertEquals("request body is required", e.getMessage());
assertEquals(emptyMap(), new ContentRestRequest("{}", emptyMap()).contentParser().map());
e = expectThrows(ElasticsearchParseException.class, () ->
new ContentRestRequest("", emptyMap(), emptyMap()).contentParser());
assertEquals("request body is required", e.getMessage());
}
public void testApplyContentParser() throws IOException {
@ -59,7 +62,9 @@ public class RestRequestTests extends ESTestCase {
}
public void testContentOrSourceParam() throws IOException {
assertEquals(BytesArray.EMPTY, new ContentRestRequest("", emptyMap()).contentOrSourceParam().v2());
Exception e = expectThrows(ElasticsearchParseException.class, () ->
new ContentRestRequest("", emptyMap()).contentOrSourceParam());
assertEquals("request body or source parameter is required", e.getMessage());
assertEquals(new BytesArray("stuff"), new ContentRestRequest("stuff", emptyMap()).contentOrSourceParam().v2());
assertEquals(new BytesArray("stuff"),
new ContentRestRequest("stuff", MapBuilder.<String, String>newMapBuilder()
@ -68,6 +73,10 @@ public class RestRequestTests extends ESTestCase {
new ContentRestRequest("", MapBuilder.<String, String>newMapBuilder()
.put("source", "{\"foo\": \"stuff\"}").put("source_content_type", "application/json").immutableMap())
.contentOrSourceParam().v2());
e = expectThrows(IllegalStateException.class, () ->
new ContentRestRequest("", MapBuilder.<String, String>newMapBuilder()
.put("source", "stuff2").immutableMap()).contentOrSourceParam());
assertEquals("source and source_content_type parameters are required", e.getMessage());
}
public void testHasContentOrSourceParam() throws IOException {
@ -80,7 +89,7 @@ public class RestRequestTests extends ESTestCase {
public void testContentOrSourceParamParser() throws IOException {
Exception e = expectThrows(ElasticsearchParseException.class, () ->
new ContentRestRequest("", emptyMap()).contentOrSourceParamParser());
assertEquals("Body required", e.getMessage());
assertEquals("request body or source parameter is required", e.getMessage());
assertEquals(emptyMap(), new ContentRestRequest("{}", emptyMap()).contentOrSourceParamParser().map());
assertEquals(emptyMap(), new ContentRestRequest("{}", singletonMap("source", "stuff2")).contentOrSourceParamParser().map());
assertEquals(emptyMap(), new ContentRestRequest("", MapBuilder.<String, String>newMapBuilder()
@ -138,6 +147,24 @@ public class RestRequestTests extends ESTestCase {
assertEquals("only one Content-Type header should be provided", e.getMessage());
}
public void testRequiredContent() {
Exception e = expectThrows(ElasticsearchParseException.class, () ->
new ContentRestRequest("", emptyMap()).requiredContent());
assertEquals("request body is required", e.getMessage());
assertEquals(new BytesArray("stuff"), new ContentRestRequest("stuff", emptyMap()).requiredContent());
assertEquals(new BytesArray("stuff"),
new ContentRestRequest("stuff", MapBuilder.<String, String>newMapBuilder()
.put("source", "stuff2").put("source_content_type", "application/json").immutableMap()).requiredContent());
e = expectThrows(ElasticsearchParseException.class, () ->
new ContentRestRequest("", MapBuilder.<String, String>newMapBuilder()
.put("source", "{\"foo\": \"stuff\"}").put("source_content_type", "application/json").immutableMap())
.requiredContent());
assertEquals("request body is required", e.getMessage());
e = expectThrows(IllegalStateException.class, () ->
new ContentRestRequest("test", null, Collections.emptyMap()).requiredContent());
assertEquals("unknown content type", e.getMessage());
}
private static final class ContentRestRequest extends RestRequest {
private final BytesArray content;

View File

@ -203,3 +203,16 @@ teardown:
catch: missing
ingest.get_pipeline:
id: "my_pipeline"
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body or source parameter is required/
raw:
method: PUT
path: _ingest/pipeline/my_pipeline

View File

@ -605,3 +605,16 @@ teardown:
- length: { docs.0.processor_results.1: 2 }
- match: { docs.0.processor_results.1.tag: "rename-1" }
- match: { docs.0.processor_results.1.doc._source.new_status: 200 }
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body or source parameter is required/
raw:
method: POST
path: _ingest/pipeline/my_pipeline/_simulate

View File

@ -62,10 +62,6 @@ public class RestMultiSearchTemplateAction extends BaseRestHandler {
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
if (request.hasContentOrSourceParam() == false) {
throw new ElasticsearchException("request body is required");
}
MultiSearchTemplateRequest multiRequest = parseRequest(request, allowExplicitIndex);
return channel -> client.execute(MultiSearchTemplateAction.INSTANCE, multiRequest, new RestToXContentListener<>(channel));
}

View File

@ -51,7 +51,7 @@ public class RestPutSearchTemplateAction extends BaseRestHandler {
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
String id = request.param("id");
BytesReference content = request.content();
BytesReference content = request.requiredContent();
PutStoredScriptRequest put = new PutStoredScriptRequest(id, Script.DEFAULT_TEMPLATE_LANG, TemplateScript.CONTEXT.name,
content, request.getXContentType());

View File

@ -92,10 +92,6 @@ public class RestSearchTemplateAction extends BaseRestHandler {
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
if (request.hasContentOrSourceParam() == false) {
throw new ElasticsearchException("request body is required");
}
// Creates the search request with all required params
SearchRequest searchRequest = new SearchRequest();
RestSearchAction.parseSearchRequest(searchRequest, request, null);

View File

@ -58,3 +58,16 @@
put_template:
id: "1"
body: { "template": { "query": { "match{{}}_all": {}}, "size": "{{my_size}}" } }
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: POST
path: _search/template/1

View File

@ -122,3 +122,16 @@
- match: { hits.total: 1 }
- length: { hits.hits: 1 }
- length: { profile: 1 }
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body or source parameter is required/
raw:
method: POST
path: _search/template

View File

@ -157,3 +157,15 @@ setup:
- match: { responses.1.hits.total: 1 }
- match: { responses.2.hits.total: 1 }
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body or source parameter is required/
raw:
method: POST
path: _msearch/template

View File

@ -19,7 +19,6 @@
package org.elasticsearch.index.reindex;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.action.GenericAction;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.settings.Settings;
@ -55,18 +54,9 @@ public abstract class AbstractBulkByQueryRestHandler<
int scrollSize = searchRequest.source().size();
searchRequest.source().size(SIZE_ALL_MATCHES);
restRequest.withContentOrSourceParamParserOrNull(parser -> {
XContentParser searchRequestParser = extractRequestSpecificFieldsAndReturnSearchCompatibleParser(parser, bodyConsumers);
/* searchRequestParser might be parser or it might be a new parser built from parser's contents. If it is parser then
* withContentOrSourceParamParserOrNull will close it for us but if it isn't then we should close it. Technically close on
* the generated parser probably is a noop but we should do the accounting just in case. It doesn't hurt to close twice but it
* really hurts not to close if by some miracle we have to. */
try {
RestSearchAction.parseSearchRequest(searchRequest, restRequest, searchRequestParser);
} finally {
IOUtils.close(searchRequestParser);
}
});
try (XContentParser parser = extractRequestSpecificFields(restRequest, bodyConsumers)) {
RestSearchAction.parseSearchRequest(searchRequest, restRequest, parser);
}
internal.setSize(searchRequest.source().size());
searchRequest.source().size(restRequest.paramAsInt("scroll_size", scrollSize));
@ -89,12 +79,13 @@ public abstract class AbstractBulkByQueryRestHandler<
* should get better when SearchRequest has full ObjectParser support
* then we can delegate and stuff.
*/
private XContentParser extractRequestSpecificFieldsAndReturnSearchCompatibleParser(XContentParser parser,
Map<String, Consumer<Object>> bodyConsumers) throws IOException {
if (parser == null) {
return parser;
private XContentParser extractRequestSpecificFields(RestRequest restRequest,
Map<String, Consumer<Object>> bodyConsumers) throws IOException {
if (restRequest.hasContentOrSourceParam() == false) {
return null; // body is optional
}
try {
try (XContentParser parser = restRequest.contentOrSourceParamParser();
XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType())) {
Map<String, Object> body = parser.map();
for (Map.Entry<String, Consumer<Object>> consumer : bodyConsumers.entrySet()) {
@ -103,12 +94,7 @@ public abstract class AbstractBulkByQueryRestHandler<
consumer.getValue().accept(value);
}
}
try (XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType())) {
return parser.contentType().xContent().createParser(parser.getXContentRegistry(), builder.map(body).bytes());
}
} finally {
parser.close();
return parser.contentType().xContent().createParser(parser.getXContentRegistry(), builder.map(body).bytes());
}
}
}

View File

@ -52,9 +52,6 @@ public class RestDeleteByQueryAction extends AbstractBulkByQueryRestHandler<Dele
@Override
protected DeleteByQueryRequest buildRequest(RestRequest request) throws IOException {
if (false == request.hasContent()) {
throw new ElasticsearchException("_delete_by_query requires a request body");
}
/*
* Passing the search request through DeleteByQueryRequest first allows
* it to set its own defaults which differ from SearchRequest's

View File

@ -299,3 +299,16 @@
index: source
metric: search
- match: {indices.source.total.search.open_contexts: 0}
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: POST
path: _reindex

View File

@ -58,3 +58,33 @@
index: test
- match: { count: 2 }
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: POST
path: _bulk
---
"empty action":
- skip:
version: " - 5.99.99"
reason: confusing exception messaged caused by empty object fixed in 6.0.0
- do:
catch: /Malformed action\/metadata line \[3\], expected FIELD_NAME but found \[END_OBJECT\]/
headers:
Content-Type: application/json
bulk:
body: |
{"index": {"_index": "test_index", "_type": "test_type", "_id": "test_id"}}
{"f1": "v1", "f2": 42}
{}

View File

@ -0,0 +1,12 @@
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: POST
path: _scripts/lang

View File

@ -75,3 +75,15 @@
- match: {defaults.node.attr.testattr: "test"}
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: PUT
path: _settings

View File

@ -32,3 +32,16 @@
type: type
id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
body: { foo: bar }
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: POST
path: idx/type/123

View File

@ -67,3 +67,16 @@
properties:
"":
type: keyword
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: POST
path: test_index/test_type/_mapping

View File

@ -210,3 +210,16 @@
catch: missing
indices.get_template:
name: "my_template"
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body is required/
raw:
method: PUT
path: _template/my_template

View File

@ -62,3 +62,15 @@ setup:
- match: { responses.3.error.root_cause.0.index: index_3 }
- match: { responses.4.hits.total: 4 }
---
"missing body":
- skip:
version: " - 5.99.99"
reason: NPE caused by missing body fixed in 6.0.0
- do:
catch: /request body or source parameter is required/
raw:
method: POST
path: _msearch