Skip to content

Commit

Permalink
Add HTTP compression support (#3831)
Browse files Browse the repository at this point in the history
Partial #3827
  • Loading branch information
devinrsmith committed May 19, 2023
1 parent c92f5ce commit 556de7c
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.arrow.flight.auth2.Auth2Constants;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http2.HTTP2Connection;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.parser.RateControl;
Expand All @@ -34,13 +35,16 @@
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HandlerContainer;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.FilterHolder;
Expand Down Expand Up @@ -207,7 +211,23 @@ public <T> T getEndpointInstance(Class<T> endpointClass) {
JsPlugins.maybeAdd(handlers::addHandler);
// Set up /*
handlers.addHandler(context);
jetty.setHandler(handlers);

final Handler handler;
if (config.httpCompressionOrDefault()) {
final GzipHandler gzipHandler = new GzipHandler();
// The default of 32 bytes seems a bit small.
gzipHandler.setMinGzipSize(1024);
// The GzipHandler documentation says GET is the default, but the constructor shows both GET and POST.
// This should ensure our gRPC messages don't get compressed for now, but we may need to be more explicit in
// the future as gRPC can technically operate over GET.
gzipHandler.setIncludedMethods(HttpMethod.GET.asString());
// Otherwise, the other defaults seem reasonable.
gzipHandler.setHandler(handlers);
handler = gzipHandler;
} else {
handler = handlers;
}
jetty.setHandler(handler);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public abstract class JettyConfig implements ServerConfig {
public static final String HTTP_WEBSOCKETS = "http.websockets";
public static final String HTTP_HTTP1 = "http.http1";
public static final String HTTP_STREAM_TIMEOUT = "http2.stream.idleTimeoutMs";
public static final String HTTP_COMPRESSION = "http.compression";

/**
* Values to indicate what kind of websocket support should be offered.
Expand All @@ -40,7 +41,8 @@ public enum WebsocketsSupport {
NONE,
/**
* Establish one websocket per grpc stream (including unary calls). Compatible with the websocket client
* provided by https://github.com/improbable-eng/grpc-web/, but not recommended.
* provided by <a href="https://github.com/improbable-eng/grpc-web/">improbable-eng/grpc-web</a>, but not
* recommended.
*/
GRPC_WEBSOCKET,
/**
Expand Down Expand Up @@ -75,8 +77,10 @@ public static JettyConfig defaultConfig() {
* {@link ServerConfig#buildFromConfig(ServerConfig.Builder, Configuration)}.
*
* <p>
* Additionally, parses the property {@value HTTP_WEBSOCKETS} into {@link Builder#websockets(WebsocketsSupport)} and
* {@value HTTP_HTTP1} into {@link Builder#http1(Boolean)}.
* Additionally, parses the property {@value HTTP_WEBSOCKETS} into {@link Builder#websockets(WebsocketsSupport)},
* {@value HTTP_HTTP1} into {@link Builder#http1(Boolean)}, {@value HTTP_STREAM_TIMEOUT} into
* {@link Builder#http2StreamIdleTimeout(long)}, and {@value HTTP_COMPRESSION} into
* {@link Builder#httpCompression(Boolean)}
*
* @param config the config
* @return the builder
Expand All @@ -85,6 +89,7 @@ public static Builder buildFromConfig(Configuration config) {
final Builder builder = ServerConfig.buildFromConfig(builder(), config);
String httpWebsockets = config.getStringWithDefault(HTTP_WEBSOCKETS, null);
String httpHttp1 = config.getStringWithDefault(HTTP_HTTP1, null);
String httpCompression = config.getStringWithDefault(HTTP_COMPRESSION, null);
String h2StreamIdleTimeout = config.getStringWithDefault(HTTP_STREAM_TIMEOUT, null);
if (httpWebsockets != null) {
switch (httpWebsockets.toLowerCase()) {
Expand All @@ -109,6 +114,9 @@ public static Builder buildFromConfig(Configuration config) {
if (h2StreamIdleTimeout != null) {
builder.http2StreamIdleTimeout(Long.parseLong(h2StreamIdleTimeout));
}
if (httpCompression != null) {
builder.httpCompression(Boolean.parseBoolean(httpCompression));
}
return builder;
}

Expand All @@ -135,6 +143,12 @@ public int port() {

public abstract OptionalLong http2StreamIdleTimeout();

/**
* Include HTTP compression.
*/
@Nullable
public abstract Boolean httpCompression();

/**
* How long can a stream be idle in milliseconds before it should be shut down. Non-positive values disable this
* feature. Default is zero.
Expand Down Expand Up @@ -177,12 +191,22 @@ public final boolean http1OrDefault() {
return true;
}

/**
* Returns {@link #httpCompression()} if explicitly set, otherwise returns {@code true}.
*/
public final boolean httpCompressionOrDefault() {
final Boolean httpCompression = httpCompression();
return httpCompression == null || httpCompression;
}

public interface Builder extends ServerConfig.Builder<JettyConfig, Builder> {

Builder websockets(WebsocketsSupport websockets);

Builder http1(Boolean http1);

Builder httpCompression(Boolean httpCompression);

Builder http2StreamIdleTimeout(long timeoutInMillis);
}
}

0 comments on commit 556de7c

Please sign in to comment.