aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@vespa.ai>2023-11-10 11:22:24 +0100
committerBjørn Christian Seime <bjorncs@vespa.ai>2023-11-10 11:22:24 +0100
commit5dc460f5f42c01e687773da74c4d688f2aa6cea3 (patch)
tree3fa8b7872c0d04adefdbd35caf7908ae8f2ff5cf
parent83b1ccd36dd5df2e43307aab19adc07b41c94c9f (diff)
Limit max payload size based on heap size
-rw-r--r--configserver/src/main/resources/configserver-app/services.xml3
-rw-r--r--container-core/abi-spec.json2
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java27
-rw-r--r--container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def4
4 files changed, 29 insertions, 7 deletions
diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml
index eacb3d30568..3d7f52dc343 100644
--- a/configserver/src/main/resources/configserver-app/services.xml
+++ b/configserver/src/main/resources/configserver-app/services.xml
@@ -145,7 +145,8 @@
<server port="19071" id="configserver">
<config name="jdisc.http.connector">
<!-- Limit max request content size accepted -->
- <maxContentSize>2000000000</maxContentSize> <!-- ~2GB -->
+ <maxContentSize>0</maxContentSize> <!-- 0 => scale based on max heap size -->
+ <maxContentSizeErrorMessageTemplate>Application package is too large (%1$d > %2$d bytes). Try increasing heap as described in https://docs.vespa.ai/en/performance/container-tuning.html#config-server-and-config-proxy.</maxContentSizeErrorMessageTemplate>
</config>
</server>
<preprocess:include file='http-server.xml' required='false' />
diff --git a/container-core/abi-spec.json b/container-core/abi-spec.json
index f29c4c2a8f7..aae5ea31fbb 100644
--- a/container-core/abi-spec.json
+++ b/container-core/abi-spec.json
@@ -1086,6 +1086,7 @@
"public com.yahoo.jdisc.http.ConnectorConfig$Builder responseHeaderSize(int)",
"public com.yahoo.jdisc.http.ConnectorConfig$Builder acceptQueueSize(int)",
"public com.yahoo.jdisc.http.ConnectorConfig$Builder maxContentSize(long)",
+ "public com.yahoo.jdisc.http.ConnectorConfig$Builder maxContentSizeErrorMessageTemplate(java.lang.String)",
"public com.yahoo.jdisc.http.ConnectorConfig$Builder reuseAddress(boolean)",
"public com.yahoo.jdisc.http.ConnectorConfig$Builder idleTimeout(double)",
"public com.yahoo.jdisc.http.ConnectorConfig$Builder tcpKeepAliveEnabled(boolean)",
@@ -1471,6 +1472,7 @@
"public int responseHeaderSize()",
"public int acceptQueueSize()",
"public long maxContentSize()",
+ "public java.lang.String maxContentSizeErrorMessageTemplate()",
"public boolean reuseAddress()",
"public double idleTimeout()",
"public boolean tcpKeepAliveEnabled()",
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java
index 0c498822ea9..5a90987ed32 100644
--- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java
@@ -4,6 +4,8 @@ package com.yahoo.jdisc.http.server.jetty;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.handler.CompletionHandler;
import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.http.ConnectorConfig;
+import com.yahoo.text.Text;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
@@ -105,14 +107,28 @@ class ServletRequestReader {
Janitor janitor,
RequestMetricReporter metricReporter) {
this.req = Objects.requireNonNull(req);
- long maxContentSize = RequestUtils.getConnector(req).connectorConfig().maxContentSize();
+ var cfg = RequestUtils.getConnector(req).connectorConfig();
+ long maxContentSize = resolveMaxContentSize(cfg);
+ var msgTemplate = resolveMaxContentSizeErrorMessage(cfg);
this.requestContentChannel = maxContentSize >= 0
- ? new ByteLimitedContentChannel(Objects.requireNonNull(requestContentChannel), maxContentSize)
+ ? new ByteLimitedContentChannel(Objects.requireNonNull(requestContentChannel), maxContentSize, msgTemplate)
: Objects.requireNonNull(requestContentChannel);
this.janitor = Objects.requireNonNull(janitor);
this.metricReporter = Objects.requireNonNull(metricReporter);
}
+ private static String resolveMaxContentSizeErrorMessage(ConnectorConfig cfg) {
+ return cfg.maxContentSizeErrorMessageTemplate().strip();
+ }
+
+ private static long resolveMaxContentSize(ConnectorConfig cfg) {
+ // Scale based on max heap size if 0
+ long maxContentSize = cfg.maxContentSize() != 0
+ ? cfg.maxContentSize() : Math.min(Runtime.getRuntime().maxMemory() / 2, Integer.MAX_VALUE);
+ log.fine(() -> Text.format("maxContentSize=%d", maxContentSize));
+ return maxContentSize;
+ }
+
/** Register read listener to start reading request data */
void start() {
try {
@@ -268,12 +284,14 @@ class ServletRequestReader {
private static class ByteLimitedContentChannel implements ContentChannel {
private final long maxContentSize;
+ private final String messageTemplate;
private final AtomicLong bytesWritten = new AtomicLong();
private final ContentChannel delegate;
- ByteLimitedContentChannel(ContentChannel delegate, long maxContentSize) {
+ ByteLimitedContentChannel(ContentChannel delegate, long maxContentSize, String messageTemplate) {
this.delegate = delegate;
this.maxContentSize = maxContentSize;
+ this.messageTemplate = messageTemplate;
}
@Override
@@ -281,8 +299,7 @@ class ServletRequestReader {
long written = bytesWritten.addAndGet(buf.remaining());
if (written > maxContentSize) {
handler.failed(new RequestException(
- Response.Status.REQUEST_TOO_LONG,
- "Request content length %d exceeds limit of %d bytes".formatted(written, maxContentSize)));
+ Response.Status.REQUEST_TOO_LONG, messageTemplate.formatted(written, maxContentSize)));
return;
}
delegate.write(buf, handler);
diff --git a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def
index b4a513e0de8..d2f081fe7d5 100644
--- a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def
+++ b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def
@@ -22,9 +22,11 @@ responseHeaderSize int default=65536
# The accept queue size (also known as accept backlog).
acceptQueueSize int default=0
-# Max content size allowed for requests. Set to -1 to disable.
+# Max content size allowed for requests. Set to -1 to disable. Set to 0 for auto (50% of heap size, max 2GB).
maxContentSize long default=-1
+maxContentSizeErrorMessageTemplate string default="Request content length %1$d exceeds limit of %2$d bytes"
+
# Whether the server socket reuses addresses.
reuseAddress bool default=true