Skip to content

Commit ebf075d

Browse files
author
Jaeho Yoo
committed
Add support for request and response compression
1 parent 4056fe5 commit ebf075d

File tree

3 files changed

+92
-9
lines changed

3 files changed

+92
-9
lines changed

gateway-ha/src/main/java/io/trino/gateway/proxyserver/ProxyRequestHandler.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,7 @@ private void performRequest(
166166
for (String name : list(servletRequest.getHeaderNames())) {
167167
for (String value : list(servletRequest.getHeaders(name))) {
168168
// TODO: decide what should and shouldn't be forwarded
169-
if (!name.equalsIgnoreCase("Accept-Encoding")
170-
&& !name.equalsIgnoreCase("Host")
169+
if (!name.equalsIgnoreCase("Host")
171170
&& (addXForwardedHeaders || !name.startsWith("X-Forwarded"))) {
172171
requestBuilder.addHeader(name, value);
173172
}
@@ -269,26 +268,27 @@ private static WebApplicationException badRequest(String message)
269268
private ProxyResponse recordBackendForQueryId(Request request, ProxyResponse response, Optional<String> username,
270269
RoutingDestination routingDestination)
271270
{
272-
log.debug("For Request [%s] got Response [%s]", request.getUri(), response.body());
271+
String body = response.decompressedBody();
272+
log.debug("For Request [%s] got Response [%s]", request.getUri(), body);
273273

274274
QueryHistoryManager.QueryDetail queryDetail = getQueryDetailsFromRequest(request, username);
275275

276276
log.debug("Extracting proxy destination : [%s] for request : [%s]", queryDetail.getBackendUrl(), request.getUri());
277277

278278
if (response.statusCode() == OK.getStatusCode()) {
279279
try {
280-
HashMap<String, String> results = OBJECT_MAPPER.readValue(response.body(), HashMap.class);
280+
HashMap<String, String> results = OBJECT_MAPPER.readValue(body, HashMap.class);
281281
queryDetail.setQueryId(results.get("id"));
282282
routingManager.setBackendForQueryId(queryDetail.getQueryId(), queryDetail.getBackendUrl());
283283
routingManager.setRoutingGroupForQueryId(queryDetail.getQueryId(), routingDestination.routingGroup());
284284
log.debug("QueryId [%s] mapped with proxy [%s]", queryDetail.getQueryId(), queryDetail.getBackendUrl());
285285
}
286286
catch (IOException e) {
287-
log.error("Failed to get QueryId from response [%s] , Status code [%s]", response.body(), response.statusCode());
287+
log.error("Failed to get QueryId from response [%s] , Status code [%s]", body, response.statusCode());
288288
}
289289
}
290290
else {
291-
log.error("Non OK HTTP Status code with response [%s] , Status code [%s], user: [%s]", response.body(), response.statusCode(), username.orElse(null));
291+
log.error("Non OK HTTP Status code with response [%s] , Status code [%s], user: [%s]", body, response.statusCode(), username.orElse(null));
292292
}
293293
queryDetail.setRoutingGroup(routingDestination.routingGroup());
294294
queryDetail.setExternalUrl(routingDestination.externalUrl());

gateway-ha/src/main/java/io/trino/gateway/proxyserver/ProxyResponseHandler.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
2222
import io.trino.gateway.ha.config.ProxyResponseConfiguration;
2323
import io.trino.gateway.proxyserver.ProxyResponseHandler.ProxyResponse;
2424

25+
import java.io.ByteArrayInputStream;
2526
import java.io.IOException;
26-
import java.nio.charset.StandardCharsets;
27+
import java.io.InputStream;
28+
import java.util.zip.GZIPInputStream;
2729

30+
import static java.nio.charset.StandardCharsets.UTF_8;
2831
import static java.util.Objects.requireNonNull;
2932

3033
public class ProxyResponseHandler
@@ -47,7 +50,9 @@ public ProxyResponse handleException(Request request, Exception exception)
4750
public ProxyResponse handle(Request request, Response response)
4851
{
4952
try {
50-
return new ProxyResponse(response.getStatusCode(), response.getHeaders(), new String(response.getInputStream().readNBytes((int) responseSize.toBytes()), StandardCharsets.UTF_8));
53+
// Store raw bytes to preserve compression
54+
byte[] responseBodyBytes = response.getInputStream().readNBytes((int) responseSize.toBytes());
55+
return new ProxyResponse(response.getStatusCode(), response.getHeaders(), responseBodyBytes);
5156
}
5257
catch (IOException e) {
5358
throw new ProxyException("Failed reading response from remote Trino server", e);
@@ -57,11 +62,36 @@ public ProxyResponse handle(Request request, Response response)
5762
public record ProxyResponse(
5863
int statusCode,
5964
ListMultimap<HeaderName, String> headers,
60-
String body)
65+
byte[] body)
6166
{
6267
public ProxyResponse
6368
{
6469
requireNonNull(headers, "headers is null");
70+
requireNonNull(body, "body is null");
71+
}
72+
73+
/**
74+
* Get the response body as a decompressed string for JSON parsing and logging.
75+
* Only call this when you need to parse the content, not when passing through
76+
* to clients.
77+
*/
78+
public String decompressedBody()
79+
{
80+
// Check if the response is gzip-compressed
81+
String contentEncoding = headers.get(HeaderName.of("Content-Encoding")).stream().findFirst().orElse(null);
82+
83+
if ("gzip".equalsIgnoreCase(contentEncoding)) {
84+
try (InputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(body))) {
85+
return new String(inputStream.readAllBytes(), UTF_8);
86+
}
87+
catch (IOException e) {
88+
// If decompression fails, return the body as UTF-8 string
89+
return new String(body, UTF_8);
90+
}
91+
}
92+
93+
// Not compressed, convert bytes to string
94+
return new String(body, UTF_8);
6595
}
6696
}
6797
}

gateway-ha/src/test/java/io/trino/gateway/proxyserver/TestProxyRequestHandler.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ public MockResponse dispatch(RecordedRequest request)
8181
.setBody("{\"starting\": false}");
8282
}
8383

84+
if (request.getPath().equals(healthCheckEndpoint + "?test-compression")) {
85+
// Return the Accept-Encoding header value for compression testing
86+
String acceptEncoding = request.getHeader("Accept-Encoding");
87+
return new MockResponse().setResponseCode(200)
88+
.setHeader(CONTENT_TYPE, JSON_UTF_8)
89+
.setBody(acceptEncoding != null ? acceptEncoding : "null");
90+
}
91+
8492
if (request.getMethod().equals("PUT") && request.getPath().equals(customPutEndpoint)) {
8593
return new MockResponse().setResponseCode(200)
8694
.setHeader(CONTENT_TYPE, JSON_UTF_8)
@@ -160,4 +168,49 @@ void testGetQueryDetailsFromRequest()
160168
assertThat(queryDetail.getSource()).isEqualTo("trino-cli");
161169
assertThat(queryDetail.getBackendUrl()).isEqualTo("http://localhost:" + routerPort);
162170
}
171+
172+
@Test
173+
void testAcceptEncodingHeaderForwarding()
174+
throws Exception
175+
{
176+
// Test that Accept-Encoding header is properly forwarded to backends
177+
String url = "http://localhost:" + routerPort + healthCheckEndpoint + "?test-compression";
178+
String expectedAcceptEncoding = "gzip, deflate, br";
179+
180+
Request request = new Request.Builder()
181+
.url(url)
182+
.get()
183+
.addHeader("Accept-Encoding", expectedAcceptEncoding)
184+
.build();
185+
186+
try (Response response = httpClient.newCall(request).execute()) {
187+
assertThat(response.code()).isEqualTo(200);
188+
assertThat(response.body()).isNotNull();
189+
190+
// The mock backend returns the Accept-Encoding header value in the response body
191+
assertThat(response.body().string()).isEqualTo(expectedAcceptEncoding);
192+
}
193+
}
194+
195+
@Test
196+
void testDefaultAcceptEncodingHeaderForwarding()
197+
throws Exception
198+
{
199+
// Test that requests without explicit Accept-Encoding header work correctly
200+
// Note: OkHttp automatically adds "Accept-Encoding: gzip" when none is specified
201+
String url = "http://localhost:" + routerPort + healthCheckEndpoint + "?test-compression";
202+
203+
Request request = new Request.Builder()
204+
.url(url)
205+
.get()
206+
.build(); // No explicit Accept-Encoding header
207+
208+
try (Response response = httpClient.newCall(request).execute()) {
209+
assertThat(response.code()).isEqualTo(200);
210+
assertThat(response.body()).isNotNull();
211+
212+
// OkHttp automatically adds "Accept-Encoding: gzip" when none is specified
213+
assertThat(response.body().string()).isEqualTo("gzip");
214+
}
215+
}
163216
}

0 commit comments

Comments
 (0)