Forbid direct usage of ContentType.create() methods (#26457)
It's easy to create a wrong Content-Type header when converting a XContentType to a Apache HTTP ContentType instance. This commit the direct usages of ContentType.create() methods in favor of a Request.createContentType(XContentType) method that does the right thing. Closes #26438
This commit is contained in:
parent
6bdf591193
commit
ad355f3e0b
|
@ -59,4 +59,5 @@ forbiddenApisMain {
|
||||||
// core does not depend on the httpclient for compile so we add the signatures here. We don't add them for test as they are already
|
// core does not depend on the httpclient for compile so we add the signatures here. We don't add them for test as they are already
|
||||||
// specified
|
// specified
|
||||||
signaturesURLs += [PrecommitTasks.getResource('/forbidden/http-signatures.txt')]
|
signaturesURLs += [PrecommitTasks.getResource('/forbidden/http-signatures.txt')]
|
||||||
|
signaturesURLs += [file('src/main/resources/forbidden/rest-high-level-signatures.txt').toURI().toURL()]
|
||||||
}
|
}
|
|
@ -42,6 +42,7 @@ import org.elasticsearch.action.support.WriteRequest;
|
||||||
import org.elasticsearch.action.update.UpdateRequest;
|
import org.elasticsearch.action.update.UpdateRequest;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.SuppressForbidden;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.lucene.uid.Versions;
|
import org.elasticsearch.common.lucene.uid.Versions;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
@ -57,6 +58,7 @@ import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -139,8 +141,8 @@ final class Request {
|
||||||
bulkContentType = XContentType.JSON;
|
bulkContentType = XContentType.JSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte separator = bulkContentType.xContent().streamSeparator();
|
final byte separator = bulkContentType.xContent().streamSeparator();
|
||||||
ContentType requestContentType = ContentType.create(bulkContentType.mediaType());
|
final ContentType requestContentType = createContentType(bulkContentType);
|
||||||
|
|
||||||
ByteArrayOutputStream content = new ByteArrayOutputStream();
|
ByteArrayOutputStream content = new ByteArrayOutputStream();
|
||||||
for (DocWriteRequest<?> request : bulkRequest.requests()) {
|
for (DocWriteRequest<?> request : bulkRequest.requests()) {
|
||||||
|
@ -268,7 +270,7 @@ final class Request {
|
||||||
parameters.withWaitForActiveShards(indexRequest.waitForActiveShards());
|
parameters.withWaitForActiveShards(indexRequest.waitForActiveShards());
|
||||||
|
|
||||||
BytesRef source = indexRequest.source().toBytesRef();
|
BytesRef source = indexRequest.source().toBytesRef();
|
||||||
ContentType contentType = ContentType.create(indexRequest.getContentType().mediaType());
|
ContentType contentType = createContentType(indexRequest.getContentType());
|
||||||
HttpEntity entity = new ByteArrayEntity(source.bytes, source.offset, source.length, contentType);
|
HttpEntity entity = new ByteArrayEntity(source.bytes, source.offset, source.length, contentType);
|
||||||
|
|
||||||
return new Request(method, endpoint, parameters.getParams(), entity);
|
return new Request(method, endpoint, parameters.getParams(), entity);
|
||||||
|
@ -352,7 +354,7 @@ final class Request {
|
||||||
|
|
||||||
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
|
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
|
||||||
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
|
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
|
||||||
return new ByteArrayEntity(source.bytes, source.offset, source.length, ContentType.create(xContentType.mediaType()));
|
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String endpoint(String[] indices, String[] types, String endpoint) {
|
static String endpoint(String[] indices, String[] types, String endpoint) {
|
||||||
|
@ -372,6 +374,17 @@ final class Request {
|
||||||
return joiner.toString();
|
return joiner.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link ContentType} from a given {@link XContentType}.
|
||||||
|
*
|
||||||
|
* @param xContentType the {@link XContentType}
|
||||||
|
* @return the {@link ContentType}
|
||||||
|
*/
|
||||||
|
@SuppressForbidden(reason = "Only allowed place to convert a XContentType to a ContentType")
|
||||||
|
static ContentType createContentType(final XContentType xContentType) {
|
||||||
|
return ContentType.create(xContentType.mediaTypeWithoutParameters(), (Charset) null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to build request's parameters map and centralize all parameter names.
|
* Utility class to build request's parameters map and centralize all parameter names.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
@defaultMessage Use Request#createContentType(XContentType) to be sure to pass the right MIME type
|
||||||
|
org.apache.http.entity.ContentType#create(java.lang.String)
|
||||||
|
org.apache.http.entity.ContentType#create(java.lang.String,java.lang.String)
|
||||||
|
org.apache.http.entity.ContentType#create(java.lang.String,java.nio.charset.Charset)
|
||||||
|
org.apache.http.entity.ContentType#create(java.lang.String,org.apache.http.NameValuePair[])
|
|
@ -278,7 +278,7 @@ public class RequestTests extends ESTestCase {
|
||||||
|
|
||||||
HttpEntity entity = request.entity;
|
HttpEntity entity = request.entity;
|
||||||
assertTrue(entity instanceof ByteArrayEntity);
|
assertTrue(entity instanceof ByteArrayEntity);
|
||||||
assertEquals(indexRequest.getContentType().mediaType(), entity.getContentType().getValue());
|
assertEquals(indexRequest.getContentType().mediaTypeWithoutParameters(), entity.getContentType().getValue());
|
||||||
try (XContentParser parser = createParser(xContentType.xContent(), entity.getContent())) {
|
try (XContentParser parser = createParser(xContentType.xContent(), entity.getContent())) {
|
||||||
assertEquals(nbFields, parser.map().size());
|
assertEquals(nbFields, parser.map().size());
|
||||||
}
|
}
|
||||||
|
@ -488,7 +488,7 @@ public class RequestTests extends ESTestCase {
|
||||||
assertEquals("/_bulk", request.endpoint);
|
assertEquals("/_bulk", request.endpoint);
|
||||||
assertEquals(expectedParams, request.params);
|
assertEquals(expectedParams, request.params);
|
||||||
assertEquals("POST", request.method);
|
assertEquals("POST", request.method);
|
||||||
assertEquals(xContentType.mediaType(), request.entity.getContentType().getValue());
|
assertEquals(xContentType.mediaTypeWithoutParameters(), request.entity.getContentType().getValue());
|
||||||
byte[] content = new byte[(int) request.entity.getContentLength()];
|
byte[] content = new byte[(int) request.entity.getContentLength()];
|
||||||
try (InputStream inputStream = request.entity.getContent()) {
|
try (InputStream inputStream = request.entity.getContent()) {
|
||||||
Streams.readFully(inputStream, content);
|
Streams.readFully(inputStream, content);
|
||||||
|
@ -541,7 +541,7 @@ public class RequestTests extends ESTestCase {
|
||||||
bulkRequest.add(new DeleteRequest("index", "type", "2"));
|
bulkRequest.add(new DeleteRequest("index", "type", "2"));
|
||||||
|
|
||||||
Request request = Request.bulk(bulkRequest);
|
Request request = Request.bulk(bulkRequest);
|
||||||
assertEquals(XContentType.JSON.mediaType(), request.entity.getContentType().getValue());
|
assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), request.entity.getContentType().getValue());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE);
|
XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE);
|
||||||
|
@ -551,7 +551,7 @@ public class RequestTests extends ESTestCase {
|
||||||
bulkRequest.add(new DeleteRequest("index", "type", "2"));
|
bulkRequest.add(new DeleteRequest("index", "type", "2"));
|
||||||
|
|
||||||
Request request = Request.bulk(bulkRequest);
|
Request request = Request.bulk(bulkRequest);
|
||||||
assertEquals(xContentType.mediaType(), request.entity.getContentType().getValue());
|
assertEquals(xContentType.mediaTypeWithoutParameters(), request.entity.getContentType().getValue());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE);
|
XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE);
|
||||||
|
@ -563,7 +563,7 @@ public class RequestTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Request request = Request.bulk(new BulkRequest().add(updateRequest));
|
Request request = Request.bulk(new BulkRequest().add(updateRequest));
|
||||||
assertEquals(xContentType.mediaType(), request.entity.getContentType().getValue());
|
assertEquals(xContentType.mediaTypeWithoutParameters(), request.entity.getContentType().getValue());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
BulkRequest bulkRequest = new BulkRequest();
|
BulkRequest bulkRequest = new BulkRequest();
|
||||||
|
@ -732,7 +732,7 @@ public class RequestTests extends ESTestCase {
|
||||||
assertEquals("/_search/scroll", request.endpoint);
|
assertEquals("/_search/scroll", request.endpoint);
|
||||||
assertEquals(0, request.params.size());
|
assertEquals(0, request.params.size());
|
||||||
assertToXContentBody(searchScrollRequest, request.entity);
|
assertToXContentBody(searchScrollRequest, request.entity);
|
||||||
assertEquals(Request.REQUEST_BODY_CONTENT_TYPE.mediaType(), request.entity.getContentType().getValue());
|
assertEquals(Request.REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.entity.getContentType().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClearScroll() throws IOException {
|
public void testClearScroll() throws IOException {
|
||||||
|
@ -746,12 +746,12 @@ public class RequestTests extends ESTestCase {
|
||||||
assertEquals("/_search/scroll", request.endpoint);
|
assertEquals("/_search/scroll", request.endpoint);
|
||||||
assertEquals(0, request.params.size());
|
assertEquals(0, request.params.size());
|
||||||
assertToXContentBody(clearScrollRequest, request.entity);
|
assertToXContentBody(clearScrollRequest, request.entity);
|
||||||
assertEquals(Request.REQUEST_BODY_CONTENT_TYPE.mediaType(), request.entity.getContentType().getValue());
|
assertEquals(Request.REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.entity.getContentType().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException {
|
private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException {
|
||||||
BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, Request.REQUEST_BODY_CONTENT_TYPE, false);
|
BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, Request.REQUEST_BODY_CONTENT_TYPE, false);
|
||||||
assertEquals(XContentType.JSON.mediaType(), actualEntity.getContentType().getValue());
|
assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());
|
||||||
assertEquals(expectedBytes, new BytesArray(EntityUtils.toByteArray(actualEntity)));
|
assertEquals(expectedBytes, new BytesArray(EntityUtils.toByteArray(actualEntity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -793,6 +793,11 @@ public class RequestTests extends ESTestCase {
|
||||||
assertEquals("/a/_create", Request.endpoint("a", null, null, "_create"));
|
assertEquals("/a/_create", Request.endpoint("a", null, null, "_create"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCreateContentType() {
|
||||||
|
final XContentType xContentType = randomFrom(XContentType.values());
|
||||||
|
assertEquals(xContentType.mediaTypeWithoutParameters(), Request.createContentType(xContentType).getMimeType());
|
||||||
|
}
|
||||||
|
|
||||||
public void testEnforceSameContentType() {
|
public void testEnforceSameContentType() {
|
||||||
XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE);
|
XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE);
|
||||||
IndexRequest indexRequest = new IndexRequest().source(singletonMap("field", "value"), xContentType);
|
IndexRequest indexRequest = new IndexRequest().source(singletonMap("field", "value"), xContentType);
|
||||||
|
|
Loading…
Reference in New Issue