Skip to content

Commit 816fec7

Browse files
andreidanHakky54
andauthored
Enable support for decompression of compressed response within RestHighLevelClient (#53533) (#54811)
Added decompression of gzip when gzip value is return as an header from Elasticsearch (cherry picked from commit 4a195b5) Signed-off-by: Andrei Dan <[email protected]> Co-authored-by: Hakky54 <[email protected]>
1 parent 4cba1e6 commit 816fec7

File tree

3 files changed

+122
-2
lines changed

3 files changed

+122
-2
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
package org.elasticsearch.client;
2121

22+
import org.apache.http.Header;
2223
import org.apache.http.HttpEntity;
24+
import org.apache.http.client.entity.GzipDecompressingEntity;
2325
import org.elasticsearch.ElasticsearchException;
2426
import org.elasticsearch.ElasticsearchStatusException;
2527
import org.elasticsearch.action.ActionListener;
@@ -1872,11 +1874,18 @@ protected final ElasticsearchStatusException parseResponseException(ResponseExce
18721874
return elasticsearchException;
18731875
}
18741876

1875-
protected final <Resp> Resp parseEntity(final HttpEntity entity,
1877+
protected final <Resp> Resp parseEntity(final HttpEntity httpEntity,
18761878
final CheckedFunction<XContentParser, Resp, IOException> entityParser) throws IOException {
1877-
if (entity == null) {
1879+
if (httpEntity == null) {
18781880
throw new IllegalStateException("Response body expected but not returned");
18791881
}
1882+
1883+
final HttpEntity entity = Optional.ofNullable(httpEntity.getContentEncoding())
1884+
.map(Header::getValue)
1885+
.filter("gzip"::equalsIgnoreCase)
1886+
.map(gzipHeaderValue -> (HttpEntity) new GzipDecompressingEntity(httpEntity))
1887+
.orElse(httpEntity);
1888+
18801889
if (entity.getContentType() == null) {
18811890
throw new IllegalStateException("Elasticsearch didn't return the [Content-Type] header, unable to parse response body");
18821891
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client;
20+
21+
import org.apache.http.HttpHeaders;
22+
import org.apache.http.client.methods.HttpPost;
23+
import org.apache.http.client.methods.HttpPut;
24+
import org.elasticsearch.action.search.SearchRequest;
25+
import org.elasticsearch.action.search.SearchResponse;
26+
27+
import java.io.IOException;
28+
29+
import static org.hamcrest.Matchers.equalTo;
30+
31+
public class HighLevelRestClientCompressionIT extends ESRestHighLevelClientTestCase {
32+
33+
private static final String GZIP_ENCODING = "gzip";
34+
private static final String SAMPLE_DOCUMENT = "{\"name\":{\"first name\":\"Steve\",\"last name\":\"Jobs\"}}";
35+
36+
public void testCompressesResponseIfRequested() throws IOException {
37+
Request doc = new Request(HttpPut.METHOD_NAME, "/company/_doc/1");
38+
doc.setJsonEntity(SAMPLE_DOCUMENT);
39+
client().performRequest(doc);
40+
client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh"));
41+
42+
RequestOptions requestOptions = RequestOptions.DEFAULT.toBuilder()
43+
.addHeader(HttpHeaders.ACCEPT_ENCODING, GZIP_ENCODING)
44+
.build();
45+
46+
SearchRequest searchRequest = new SearchRequest("company");
47+
SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync, requestOptions);
48+
49+
assertThat(searchResponse.status().getStatus(), equalTo(200));
50+
assertEquals(1L, searchResponse.getHits().getTotalHits().value);
51+
assertEquals(SAMPLE_DOCUMENT, searchResponse.getHits().getHits()[0].getSourceAsString());
52+
}
53+
54+
}

client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.http.message.BasicStatusLine;
3434
import org.apache.http.nio.entity.NByteArrayEntity;
3535
import org.apache.http.nio.entity.NStringEntity;
36+
import org.apache.lucene.util.BytesRef;
3637
import org.elasticsearch.ElasticsearchException;
3738
import org.elasticsearch.action.ActionListener;
3839
import org.elasticsearch.action.ActionRequest;
@@ -117,10 +118,12 @@
117118
import org.hamcrest.Matchers;
118119
import org.junit.Before;
119120

121+
import java.io.ByteArrayOutputStream;
120122
import java.io.IOException;
121123
import java.lang.reflect.Method;
122124
import java.lang.reflect.Modifier;
123125
import java.net.SocketTimeoutException;
126+
import java.nio.charset.StandardCharsets;
124127
import java.util.ArrayList;
125128
import java.util.Arrays;
126129
import java.util.Collections;
@@ -134,6 +137,7 @@
134137
import java.util.concurrent.atomic.AtomicReference;
135138
import java.util.stream.Collectors;
136139
import java.util.stream.Stream;
140+
import java.util.zip.GZIPOutputStream;
137141

138142
import static org.elasticsearch.client.ml.dataframe.evaluation.MlEvaluationNamedXContentProvider.registeredMetricName;
139143
import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
@@ -322,6 +326,59 @@ public void testParseEntity() throws IOException {
322326
}
323327
}
324328

329+
public void testParseCompressedEntity() throws IOException {
330+
CheckedFunction<XContentParser, String, IOException> entityParser = parser -> {
331+
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
332+
assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
333+
assertTrue(parser.nextToken().isValue());
334+
String value = parser.text();
335+
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
336+
return value;
337+
};
338+
339+
HttpEntity jsonEntity = createGzipEncodedEntity("{\"field\":\"value\"}", ContentType.APPLICATION_JSON);
340+
assertEquals("value", restHighLevelClient.parseEntity(jsonEntity, entityParser));
341+
HttpEntity yamlEntity = createGzipEncodedEntity("---\nfield: value\n", ContentType.create("application/yaml"));
342+
assertEquals("value", restHighLevelClient.parseEntity(yamlEntity, entityParser));
343+
HttpEntity smileEntity = createGzipEncodedEntity(SmileXContent.contentBuilder(), ContentType.create("application/smile"));
344+
assertEquals("value", restHighLevelClient.parseEntity(smileEntity, entityParser));
345+
HttpEntity cborEntity = createGzipEncodedEntity(CborXContent.contentBuilder(), ContentType.create("application/cbor"));
346+
assertEquals("value", restHighLevelClient.parseEntity(cborEntity, entityParser));
347+
}
348+
349+
private HttpEntity createGzipEncodedEntity(String content, ContentType contentType) throws IOException {
350+
byte[] gzipEncodedContent = compressContentWithGzip(content.getBytes(StandardCharsets.UTF_8));
351+
NByteArrayEntity httpEntity = new NByteArrayEntity(gzipEncodedContent, contentType);
352+
httpEntity.setContentEncoding("gzip");
353+
354+
return httpEntity;
355+
}
356+
357+
private HttpEntity createGzipEncodedEntity(XContentBuilder xContentBuilder, ContentType contentType) throws IOException {
358+
try (XContentBuilder builder = xContentBuilder) {
359+
builder.startObject();
360+
builder.field("field", "value");
361+
builder.endObject();
362+
363+
BytesRef bytesRef = BytesReference.bytes(xContentBuilder).toBytesRef();
364+
byte[] gzipEncodedContent = compressContentWithGzip(bytesRef.bytes);
365+
NByteArrayEntity httpEntity = new NByteArrayEntity(gzipEncodedContent, contentType);
366+
httpEntity.setContentEncoding("gzip");
367+
368+
return httpEntity;
369+
}
370+
}
371+
372+
private static byte[] compressContentWithGzip(byte[] content) throws IOException {
373+
ByteArrayOutputStream bos = new ByteArrayOutputStream(content.length);
374+
GZIPOutputStream gzip = new GZIPOutputStream(bos);
375+
gzip.write(content);
376+
gzip.close();
377+
bos.close();
378+
379+
return bos.toByteArray();
380+
}
381+
325382
private static HttpEntity createBinaryEntity(XContentBuilder xContentBuilder, ContentType contentType) throws IOException {
326383
try (XContentBuilder builder = xContentBuilder) {
327384
builder.startObject();

0 commit comments

Comments
 (0)