Skip to content

Commit 5b8f0b6

Browse files
committed
Configure Buffer pools for Jetty server
This circumvents jetty/jetty.project#12670
1 parent 115f8c3 commit 5b8f0b6

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

http-server/src/main/java/io/airlift/http/server/HttpServer.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.airlift.http.server.jetty.MonitoredQueuedThreadPoolMBean;
2323
import io.airlift.log.Logger;
2424
import io.airlift.node.NodeInfo;
25+
import io.airlift.units.DataSize;
2526
import jakarta.annotation.PostConstruct;
2627
import jakarta.annotation.PreDestroy;
2728
import jakarta.servlet.Filter;
@@ -34,6 +35,8 @@
3435
import org.eclipse.jetty.http2.server.AuthorityCustomizer;
3536
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
3637
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
38+
import org.eclipse.jetty.io.ArrayByteBufferPool;
39+
import org.eclipse.jetty.io.ByteBufferPool;
3740
import org.eclipse.jetty.io.ConnectionStatistics;
3841
import org.eclipse.jetty.jmx.MBeanContainer;
3942
import org.eclipse.jetty.server.ConnectionFactory;
@@ -65,6 +68,7 @@
6568
import java.util.EnumSet;
6669
import java.util.List;
6770
import java.util.Optional;
71+
import java.util.OptionalLong;
6872
import java.util.Set;
6973
import java.util.concurrent.Executor;
7074
import java.util.concurrent.ScheduledExecutorService;
@@ -75,6 +79,7 @@
7579
import static com.google.common.base.Verify.verify;
7680
import static com.google.common.collect.ImmutableList.toImmutableList;
7781
import static io.airlift.concurrent.Threads.daemonThreadsNamed;
82+
import static java.lang.Math.max;
7883
import static java.lang.Math.toIntExact;
7984
import static java.time.temporal.ChronoUnit.DAYS;
8085
import static java.util.Collections.list;
@@ -140,7 +145,12 @@ public HttpServer(
140145
log.info("Virtual threads support is enabled");
141146
threadPool.setVirtualThreadsExecutor(executor);
142147
}
143-
server = new Server(threadPool);
148+
149+
int maxBufferSize = toIntExact(max(
150+
toSafeBytes(config.getMaxRequestHeaderSize()).orElse(65536),
151+
toSafeBytes(config.getMaxResponseHeaderSize()).orElse(65536)));
152+
153+
server = new Server(threadPool, null, createByteBufferPool(maxBufferSize, config));
144154
this.monitoredQueuedThreadPoolMBean = new MonitoredQueuedThreadPoolMBean(threadPool);
145155

146156
boolean showStackTrace = config.isShowStackTrace();
@@ -274,14 +284,25 @@ public HttpServer(
274284
}
275285

276286
server.setHandler(statsHandler);
277-
278287
ErrorHandler errorHandler = new ErrorHandler();
279288
errorHandler.setShowMessageInTitle(showStackTrace);
280289
errorHandler.setShowStacks(showStackTrace);
281290
errorHandler.setDefaultResponseMimeType(TEXT_PLAIN.asString());
282291
server.setErrorHandler(errorHandler);
283292
}
284293

294+
private ByteBufferPool createByteBufferPool(int maxBufferSize, HttpServerConfig config)
295+
{
296+
return new ArrayByteBufferPool.Quadratic(
297+
0,
298+
maxBufferSize,
299+
Integer.MAX_VALUE,
300+
config.getMaxHeapMemory().map(DataSize::toBytes)
301+
.orElse(0L), // Use default heuristics for max heap memory
302+
config.getMaxDirectMemory().map(DataSize::toBytes)
303+
.orElse(0L)); // Use default heuristics for max direct memory
304+
}
305+
285306
private ConnectionFactory[] insecureFactories(HttpServerConfig config, HttpConfiguration httpConfiguration)
286307
{
287308
HttpConnectionFactory http1 = new HttpConnectionFactory(httpConfiguration);
@@ -473,4 +494,13 @@ private static ServerConnector createServerConnector(
473494
connector.open(channel);
474495
return connector;
475496
}
497+
498+
private static OptionalLong toSafeBytes(DataSize dataSize)
499+
{
500+
if (dataSize == null) {
501+
return OptionalLong.empty();
502+
}
503+
504+
return OptionalLong.of(dataSize.toBytes());
505+
}
476506
}

http-server/src/main/java/io/airlift/http/server/HttpServerConfig.java

+39
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@
1717

1818
import io.airlift.configuration.Config;
1919
import io.airlift.configuration.ConfigDescription;
20+
import io.airlift.configuration.ConfigHidden;
2021
import io.airlift.configuration.DefunctConfig;
2122
import io.airlift.units.DataSize;
2223
import io.airlift.units.Duration;
2324
import io.airlift.units.MaxDataSize;
2425
import io.airlift.units.MinDataSize;
26+
import jakarta.validation.constraints.AssertTrue;
2527
import jakarta.validation.constraints.Min;
2628
import jakarta.validation.constraints.NotNull;
2729

30+
import java.util.Optional;
31+
2832
import static io.airlift.http.server.HttpServerConfig.ProcessForwardedMode.ACCEPT;
2933
import static io.airlift.http.server.HttpServerConfig.ProcessForwardedMode.REJECT;
3034
import static io.airlift.units.DataSize.Unit.KILOBYTE;
@@ -94,6 +98,9 @@ public class HttpServerConfig
9498

9599
private boolean compressionEnabled = true;
96100

101+
private Optional<DataSize> maxHeapMemory = Optional.empty();
102+
private Optional<DataSize> maxDirectMemory = Optional.empty();
103+
97104
public boolean isHttpEnabled()
98105
{
99106
return httpEnabled;
@@ -473,6 +480,38 @@ public HttpServerConfig setCompressionEnabled(boolean compressionEnabled)
473480
return this;
474481
}
475482

483+
public Optional<@MinDataSize("16MB") DataSize> getMaxHeapMemory()
484+
{
485+
return maxHeapMemory;
486+
}
487+
488+
@Config("http-server.max-heap-memory")
489+
@ConfigHidden
490+
public HttpServerConfig setMaxHeapMemory(DataSize maxHeapMemory)
491+
{
492+
this.maxHeapMemory = Optional.ofNullable(maxHeapMemory);
493+
return this;
494+
}
495+
496+
public Optional<@MinDataSize("16MB") DataSize> getMaxDirectMemory()
497+
{
498+
return maxDirectMemory;
499+
}
500+
501+
@Config("http-server.max-direct-memory")
502+
@ConfigHidden
503+
public HttpServerConfig setMaxDirectMemory(DataSize maxDirectMemory)
504+
{
505+
this.maxDirectMemory = Optional.ofNullable(maxDirectMemory);
506+
return this;
507+
}
508+
509+
@AssertTrue(message = "either both http-server.max-heap-memory and http-server.max-direct-memory are set or none of them")
510+
public boolean eitherBothMemorySettingsAreSetOrNone()
511+
{
512+
return maxHeapMemory.isPresent() == maxDirectMemory.isPresent();
513+
}
514+
476515
public enum ProcessForwardedMode
477516
{
478517
ACCEPT,

http-server/src/test/java/io/airlift/http/server/TestHttpServerConfig.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ public void testDefaults()
6767
.setHttp2InputBufferSize(DataSize.of(8, KILOBYTE))
6868
.setHttp2InitialStreamReceiveWindowSize(DataSize.of(16, MEGABYTE))
6969
.setHttp2StreamIdleTimeout(new Duration(15, SECONDS))
70-
.setCompressionEnabled(true));
70+
.setCompressionEnabled(true)
71+
.setMaxHeapMemory(null)
72+
.setMaxDirectMemory(null));
7173
}
7274

7375
@Test
@@ -103,6 +105,8 @@ public void testExplicitPropertyMappings()
103105
.put("http-server.http2.input-buffer-size", "4MB")
104106
.put("http-server.http2.stream-idle-timeout", "23s")
105107
.put("http-server.compression.enabled", "false")
108+
.put("http-server.max-heap-memory", "127GB")
109+
.put("http-server.max-direct-memory", "129GB")
106110
.build();
107111

108112
HttpServerConfig expected = new HttpServerConfig()
@@ -134,7 +138,9 @@ public void testExplicitPropertyMappings()
134138
.setHttp2InitialStreamReceiveWindowSize(DataSize.of(4, MEGABYTE))
135139
.setHttp2InputBufferSize(DataSize.of(4, MEGABYTE))
136140
.setHttp2StreamIdleTimeout(new Duration(23, SECONDS))
137-
.setCompressionEnabled(false);
141+
.setCompressionEnabled(false)
142+
.setMaxHeapMemory(DataSize.of(127, GIGABYTE))
143+
.setMaxDirectMemory(DataSize.of(129, GIGABYTE));
138144

139145
assertFullMapping(properties, expected);
140146
}

0 commit comments

Comments
 (0)