diff options
author | Bjørn Christian Seime <bjorncs@vespa.ai> | 2023-11-10 11:22:24 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@vespa.ai> | 2023-11-10 11:22:24 +0100 |
commit | 5dc460f5f42c01e687773da74c4d688f2aa6cea3 (patch) | |
tree | 3fa8b7872c0d04adefdbd35caf7908ae8f2ff5cf /container-core | |
parent | 83b1ccd36dd5df2e43307aab19adc07b41c94c9f (diff) |
Limit max payload size based on heap size
Diffstat (limited to 'container-core')
3 files changed, 27 insertions, 6 deletions
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 |