From b343343b0ceb83a61e8580745d8914f1daaf64ce Mon Sep 17 00:00:00 2001 From: daguimu Date: Wed, 29 Apr 2026 15:58:23 +0800 Subject: [PATCH] Make tunnel-client max HTTP content length configurable (#3034) The tunnel-client `ProxyClient` aggregates HTTP responses with a fixed 10 MB cap (`ArthasConstants.MAX_HTTP_CONTENT_LENGTH`). When a user downloads a JFR recording larger than 10 MB through the tunnel-server, Netty's `HttpObjectAggregator` aborts with `TooLongHttpContentException` and the browser receives only the literal text "error". Introduce a system property `arthas.tunnel.client.max-http-content-length` that overrides the limit for the tunnel-client HTTP aggregator only. Default behavior (10 MB) is unchanged; operators can raise the cap when streaming large recordings without affecting the WebSocket frame or object-view size limits that share the legacy constant. Closes #3034 --- .../taobao/arthas/common/ArthasConstants.java | 29 ++++++++ tunnel-client/pom.xml | 10 +++ .../arthas/tunnel/client/ProxyClient.java | 3 +- .../arthas/common/ArthasConstantsTest.java | 67 +++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 tunnel-client/src/test/java/com/taobao/arthas/common/ArthasConstantsTest.java diff --git a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java index 78cf8adc8ff..608c6019ddb 100644 --- a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java +++ b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java @@ -16,6 +16,35 @@ public class ArthasConstants { public static final int MAX_HTTP_CONTENT_LENGTH = 1024 * 1024 * 10; + /** + * System property to override the max HTTP content length used by the + * tunnel-client when proxying responses (e.g. JFR recording downloads) + * back to the tunnel-server. The value is in bytes; default is + * {@link #MAX_HTTP_CONTENT_LENGTH} (10 MB). See issue #3034. + */ + public static final String TUNNEL_CLIENT_MAX_HTTP_CONTENT_LENGTH_PROPERTY = "arthas.tunnel.client.max-http-content-length"; + + /** + * Resolve the configured tunnel-client max HTTP content length. Falls back + * to {@link #MAX_HTTP_CONTENT_LENGTH} when the system property is unset, + * non-numeric, or non-positive. + */ + public static int getTunnelClientMaxHttpContentLength() { + String value = System.getProperty(TUNNEL_CLIENT_MAX_HTTP_CONTENT_LENGTH_PROPERTY); + if (value == null || value.isEmpty()) { + return MAX_HTTP_CONTENT_LENGTH; + } + try { + int parsed = Integer.parseInt(value.trim()); + if (parsed > 0) { + return parsed; + } + } catch (NumberFormatException ignore) { + // fall through to default + } + return MAX_HTTP_CONTENT_LENGTH; + } + public static final String ARTHAS_OUTPUT = "arthas-output"; public static final String APP_NAME = "app-name"; diff --git a/tunnel-client/pom.xml b/tunnel-client/pom.xml index 968eeb74b67..506ce5bd039 100644 --- a/tunnel-client/pom.xml +++ b/tunnel-client/pom.xml @@ -57,6 +57,16 @@ netty-codec-http + + org.junit.vintage + junit-vintage-engine + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java index 33b93e0b8d6..c7c42f49967 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java @@ -60,7 +60,8 @@ public SimpleHttpResponse query(String targetUrl) throws InterruptedException { @Override protected void initChannel(LocalChannel ch) { ChannelPipeline p = ch.pipeline(); - p.addLast(new HttpClientCodec(), new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH), + p.addLast(new HttpClientCodec(), + new HttpObjectAggregator(ArthasConstants.getTunnelClientMaxHttpContentLength()), new HttpProxyClientHandler(httpResponsePromise)); } }); diff --git a/tunnel-client/src/test/java/com/taobao/arthas/common/ArthasConstantsTest.java b/tunnel-client/src/test/java/com/taobao/arthas/common/ArthasConstantsTest.java new file mode 100644 index 00000000000..f33f9756408 --- /dev/null +++ b/tunnel-client/src/test/java/com/taobao/arthas/common/ArthasConstantsTest.java @@ -0,0 +1,67 @@ +package com.taobao.arthas.common; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the configurable tunnel-client max HTTP content length introduced + * for issue #3034 ("Unable to download JFR recordings larger than 10MB via + * Tunnel Server"). + */ +public class ArthasConstantsTest { + + private static final String PROPERTY = ArthasConstants.TUNNEL_CLIENT_MAX_HTTP_CONTENT_LENGTH_PROPERTY; + + private String originalValue; + + @Before + public void saveProperty() { + originalValue = System.getProperty(PROPERTY); + System.clearProperty(PROPERTY); + } + + @After + public void restoreProperty() { + if (originalValue == null) { + System.clearProperty(PROPERTY); + } else { + System.setProperty(PROPERTY, originalValue); + } + } + + @Test + public void defaultsToTenMegabytesWhenPropertyMissing() { + assertEquals(ArthasConstants.MAX_HTTP_CONTENT_LENGTH, + ArthasConstants.getTunnelClientMaxHttpContentLength()); + } + + @Test + public void honoursConfiguredPropertyValue() { + int oneHundredMb = 100 * 1024 * 1024; + System.setProperty(PROPERTY, Integer.toString(oneHundredMb)); + + assertEquals(oneHundredMb, ArthasConstants.getTunnelClientMaxHttpContentLength()); + } + + @Test + public void fallsBackToDefaultWhenPropertyIsNotANumber() { + System.setProperty(PROPERTY, "not-a-number"); + + assertEquals(ArthasConstants.MAX_HTTP_CONTENT_LENGTH, + ArthasConstants.getTunnelClientMaxHttpContentLength()); + } + + @Test + public void fallsBackToDefaultWhenPropertyIsNotPositive() { + System.setProperty(PROPERTY, "0"); + assertEquals(ArthasConstants.MAX_HTTP_CONTENT_LENGTH, + ArthasConstants.getTunnelClientMaxHttpContentLength()); + + System.setProperty(PROPERTY, "-1"); + assertEquals(ArthasConstants.MAX_HTTP_CONTENT_LENGTH, + ArthasConstants.getTunnelClientMaxHttpContentLength()); + } +}