Skip to content

Commit 64ad6df

Browse files
committed
test(http): add real JSON-RPC integration test and clean up SizeLimitHandler tests
Add testJsonRpcSizeLimitIntegration() in JsonrpcServiceTest using the Spring-injected FullNodeJsonRpcHttpService (real JsonRpcServlet + jsonrpc4j) to verify SizeLimitHandler does not introduce regressions. Covers: normal request passthrough, Content-Length oversized 413, and chunked oversized behavior (200 empty body due to RateLimiterServlet absorbing BadMessageException). Clean up SizeLimitHandlerTest: remove 3 redundant testJsonRpcBody* tests that used BroadCatchServlet (cannot represent real jsonrpc4j chain), rename TestJsonRpcService to SecondHttpService, remove banner-style ruler comments, fix stale EchoServlet Javadoc reference, and remove HTML tags from Javadoc.
1 parent 6f0a0f0 commit 64ad6df

2 files changed

Lines changed: 105 additions & 68 deletions

File tree

framework/src/test/java/org/tron/common/jetty/SizeLimitHandlerTest.java

Lines changed: 31 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -32,31 +32,28 @@
3232
import org.tron.core.config.args.Args;
3333

3434
/**
35-
* Tests the {@link org.eclipse.jetty.server.handler.SizeLimitHandler} body-size
36-
* enforcement configured in {@link HttpService initContextHandler()}.
35+
* Tests {@link org.eclipse.jetty.server.handler.SizeLimitHandler} body-size
36+
* enforcement configured in {@link HttpService#initContextHandler()}.
3737
*
38-
* <p>Covers:</p>
39-
* <ul>
40-
* <li>Bodies within the limit are accepted ({@code 200}).</li>
41-
* <li>Bodies exceeding the limit are rejected ({@code 413}).</li>
42-
* <li>The limit counts raw UTF-8 <em>bytes</em>, not Java {@code char}s.</li>
43-
* <li>HTTP and JSON-RPC services use independent size limits.</li>
44-
* <li>Default values are {@code GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE} (4 MB).</li>
45-
* </ul>
38+
* Covers: accept/reject by size, UTF-8 byte counting, independent limits
39+
* across HttpService instances, chunked transfer, and zero-limit behavior.
40+
*
41+
* Real JsonRpcServlet integration is tested separately in
42+
* {@code JsonrpcServiceTest#testJsonRpcSizeLimitIntegration}.
4643
*/
4744
@Slf4j
4845
public class SizeLimitHandlerTest {
4946

5047
private static final int HTTP_MAX_BODY_SIZE = 1024;
51-
private static final int JSONRPC_MAX_BODY_SIZE = 512;
48+
private static final int SECOND_SERVICE_MAX_BODY_SIZE = 512;
5249

5350
@ClassRule
5451
public static final TemporaryFolder temporaryFolder = new TemporaryFolder();
5552

56-
private static TestHttpService httpService;
57-
private static TestJsonRpcService jsonRpcService;
58-
private static URI httpServerUri;
59-
private static URI jsonRpcServerUri;
53+
private static TestHttpService httpService;
54+
private static SecondHttpService secondService;
55+
private static URI httpServerUri;
56+
private static URI secondServerUri;
6057
private static CloseableHttpClient client;
6158

6259
/**
@@ -96,17 +93,17 @@ protected void addServlet(ServletContextHandler context) {
9693
}
9794
}
9895

99-
/** Minimal concrete {@link HttpService} simulating a JSON-RPC service. */
100-
static class TestJsonRpcService extends HttpService {
101-
TestJsonRpcService(int port, long maxRequestSize) {
96+
/** Second HttpService instance with a different size limit, for independence tests. */
97+
static class SecondHttpService extends HttpService {
98+
SecondHttpService(int port, long maxRequestSize) {
10299
this.port = port;
103100
this.contextPath = "/";
104101
this.maxRequestSize = maxRequestSize;
105102
}
106103

107104
@Override
108105
protected void addServlet(ServletContextHandler context) {
109-
context.addServlet(new ServletHolder(new BroadCatchServlet()), "/jsonrpc");
106+
context.addServlet(new ServletHolder(new BroadCatchServlet()), "/*");
110107
}
111108
}
112109

@@ -115,17 +112,17 @@ public static void setup() throws Exception {
115112
Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()},
116113
TestConstants.TEST_CONF);
117114
Args.getInstance().setHttpMaxMessageSize(HTTP_MAX_BODY_SIZE);
118-
Args.getInstance().setJsonRpcMaxMessageSize(JSONRPC_MAX_BODY_SIZE);
115+
Args.getInstance().setJsonRpcMaxMessageSize(SECOND_SERVICE_MAX_BODY_SIZE);
119116

120117
int httpPort = PublicMethod.chooseRandomPort();
121118
httpService = new TestHttpService(httpPort, HTTP_MAX_BODY_SIZE);
122119
httpService.start().get(10, TimeUnit.SECONDS);
123120
httpServerUri = new URI(String.format("http://localhost:%d/", httpPort));
124121

125-
int jsonRpcPort = PublicMethod.chooseRandomPort();
126-
jsonRpcService = new TestJsonRpcService(jsonRpcPort, JSONRPC_MAX_BODY_SIZE);
127-
jsonRpcService.start().get(10, TimeUnit.SECONDS);
128-
jsonRpcServerUri = new URI(String.format("http://localhost:%d/jsonrpc", jsonRpcPort));
122+
int secondPort = PublicMethod.chooseRandomPort();
123+
secondService = new SecondHttpService(secondPort, SECOND_SERVICE_MAX_BODY_SIZE);
124+
secondService.start().get(10, TimeUnit.SECONDS);
125+
secondServerUri = new URI(String.format("http://localhost:%d/", secondPort));
129126

130127
client = HttpClients.createDefault();
131128
}
@@ -142,16 +139,14 @@ public static void teardown() throws Exception {
142139
httpService.stop();
143140
}
144141
} finally {
145-
if (jsonRpcService != null) {
146-
jsonRpcService.stop();
142+
if (secondService != null) {
143+
secondService.stop();
147144
}
148145
}
149146
Args.clearParam();
150147
}
151148
}
152149

153-
// -- HTTP service body-size tests -------------------------------------------
154-
155150
@Test
156151
public void testHttpBodyWithinLimit() throws Exception {
157152
Assert.assertEquals(200, post(httpServerUri, new StringEntity("small body")));
@@ -169,40 +164,16 @@ public void testHttpBodyAtExactLimit() throws Exception {
169164
post(httpServerUri, new StringEntity(repeat('b', HTTP_MAX_BODY_SIZE))));
170165
}
171166

172-
// -- JSON-RPC service body-size tests ---------------------------------------
173-
174-
@Test
175-
public void testJsonRpcBodyWithinLimit() throws Exception {
176-
Assert.assertEquals(200,
177-
post(jsonRpcServerUri, new StringEntity("{\"method\":\"eth_blockNumber\"}")));
178-
}
179-
180-
@Test
181-
public void testJsonRpcBodyExceedsLimit() throws Exception {
182-
Assert.assertEquals(413,
183-
post(jsonRpcServerUri, new StringEntity(repeat('x', JSONRPC_MAX_BODY_SIZE + 1))));
184-
}
185-
186-
@Test
187-
public void testJsonRpcBodyAtExactLimit() throws Exception {
188-
Assert.assertEquals(200,
189-
post(jsonRpcServerUri, new StringEntity(repeat('c', JSONRPC_MAX_BODY_SIZE))));
190-
}
191-
192-
// -- Independent limit tests ------------------------------------------------
193-
194167
@Test
195-
public void testHttpAndJsonRpcHaveIndependentLimits() throws Exception {
196-
// A body that exceeds JSON-RPC limit but is within HTTP limit
197-
String body = repeat('d', JSONRPC_MAX_BODY_SIZE + 100);
168+
public void testTwoServicesHaveIndependentLimits() throws Exception {
169+
// A body that exceeds secondService limit but is within httpService limit
170+
String body = repeat('d', SECOND_SERVICE_MAX_BODY_SIZE + 100);
198171
Assert.assertTrue(body.length() < HTTP_MAX_BODY_SIZE);
199172

200173
Assert.assertEquals(200, post(httpServerUri, new StringEntity(body)));
201-
Assert.assertEquals(413, post(jsonRpcServerUri, new StringEntity(body)));
174+
Assert.assertEquals(413, post(secondServerUri, new StringEntity(body)));
202175
}
203176

204-
// -- UTF-8 byte counting test -----------------------------------------------
205-
206177
@Test
207178
public void testLimitIsBasedOnBytesNotCharacters() throws Exception {
208179
// Each CJK character is 3 UTF-8 bytes; 342 chars x 3 = 1026 bytes > 1024
@@ -212,10 +183,8 @@ public void testLimitIsBasedOnBytesNotCharacters() throws Exception {
212183
Assert.assertEquals(413, post(httpServerUri, new StringEntity(cjk, "UTF-8")));
213184
}
214185

215-
// -- Chunked (no Content-Length) transfer tests ------------------------------
216-
217186
/**
218-
* Chunked request within the limit should succeed (EchoServlet).
187+
* Chunked request within the limit should succeed.
219188
* InputStreamEntity with size=-1 sends chunked Transfer-Encoding (no Content-Length).
220189
*/
221190
@Test
@@ -228,11 +197,11 @@ public void testChunkedBodyWithinLimit() throws Exception {
228197
/**
229198
* Chunked oversized body hitting a servlet with broad catch(Exception).
230199
*
231-
* <p>SizeLimitHandler's LimitInterceptor throws BadMessageException during
200+
* SizeLimitHandler's LimitInterceptor throws BadMessageException during
232201
* streaming read, but the servlet's catch(Exception) absorbs it and returns
233202
* 200 + error JSON instead of 413. This matches real TRON servlet behavior.
234203
*
235-
* <p>OOM protection still works: the body read is truncated at the limit.
204+
* OOM protection still works: the body read is truncated at the limit.
236205
*/
237206
@Test
238207
public void testChunkedBodyExceedsLimit() throws Exception {
@@ -252,8 +221,6 @@ public void testChunkedBodyExceedsLimit() throws Exception {
252221
body.contains("Error"));
253222
}
254223

255-
// -- Zero-limit behavior test -----------------------------------------------
256-
257224
/**
258225
* When maxRequestSize is 0, SizeLimitHandler treats it as "reject all bodies > 0 bytes".
259226
* Jetty's logic: {@code _requestLimit >= 0 && size > _requestLimit} — 0 >= 0 is true,
@@ -278,8 +245,6 @@ public void testZeroLimitRejectsAllBodies() throws Exception {
278245
}
279246
}
280247

281-
// -- checkBodySize vs SizeLimitHandler consistency tests --------------------
282-
283248
/**
284249
* For pure ASCII JSON (the normal TRON API case), wire bytes and
285250
* {@code body.getBytes().length} (what {@code Util.checkBodySize()} measures)
@@ -320,7 +285,7 @@ public void testWireBytesMatchCheckBodySizeForUtf8Json() throws Exception {
320285
/**
321286
* When the body contains {@code \r\n} line endings, {@code lines().collect()}
322287
* normalizes them to {@code \n} (on Linux) or the platform line separator.
323-
* This makes {@code checkBodySize} measure <em>fewer</em> bytes than the wire —
288+
* This makes {@code checkBodySize} measure fewer bytes than the wire —
324289
* a safe direction: checkBodySize never rejects what SizeLimitHandler accepts.
325290
*/
326291
@Test
@@ -338,8 +303,6 @@ public void testCheckBodySizeSafeDirectionWithNewlines() throws Exception {
338303
wireBytes, servletBytes, wireBytes - servletBytes);
339304
}
340305

341-
// -- helpers ----------------------------------------------------------------
342-
343306
/** POSTs with the given entity and returns the response body as a string. */
344307
private String postForBody(URI uri, HttpEntity entity) throws Exception {
345308
HttpPost req = new HttpPost(uri);

framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
import com.google.gson.JsonObject;
99
import com.google.protobuf.ByteString;
1010
import io.prometheus.client.CollectorRegistry;
11+
import java.io.ByteArrayInputStream;
12+
import java.lang.reflect.Field;
1113
import java.util.ArrayList;
1214
import java.util.Collections;
1315
import java.util.List;
1416
import javax.annotation.Resource;
1517
import lombok.extern.slf4j.Slf4j;
1618
import org.apache.http.client.methods.CloseableHttpResponse;
1719
import org.apache.http.client.methods.HttpPost;
20+
import org.apache.http.entity.InputStreamEntity;
1821
import org.apache.http.entity.StringEntity;
1922
import org.apache.http.impl.client.CloseableHttpClient;
2023
import org.apache.http.impl.client.HttpClients;
@@ -25,6 +28,7 @@
2528
import org.junit.Test;
2629
import org.tron.common.BaseTest;
2730
import org.tron.common.TestConstants;
31+
import org.tron.common.application.HttpService;
2832
import org.tron.common.parameter.CommonParameter;
2933
import org.tron.common.prometheus.Metrics;
3034
import org.tron.common.utils.ByteArray;
@@ -1099,4 +1103,74 @@ public void testWeb3ClientVersion() {
10991103
Assert.fail();
11001104
}
11011105
}
1106+
1107+
/**
1108+
* Verifies SizeLimitHandler integration with the real JsonRpcServlet + jsonrpc4j stack.
1109+
*
1110+
* Covers: normal request no regression, Content-Length oversized 413,
1111+
* and chunked oversized handled gracefully (body truncated, 200 + empty body
1112+
* because RateLimiterServlet absorbs the BadMessageException).
1113+
*/
1114+
@Test
1115+
public void testJsonRpcSizeLimitIntegration() {
1116+
long testLimit = 1024;
1117+
try {
1118+
Field field = HttpService.class.getDeclaredField("maxRequestSize");
1119+
field.setAccessible(true);
1120+
long originalLimit = field.getLong(fullNodeJsonRpcHttpService);
1121+
field.setLong(fullNodeJsonRpcHttpService, testLimit);
1122+
1123+
fullNodeJsonRpcHttpService.start();
1124+
String url = "http://127.0.0.1:"
1125+
+ CommonParameter.getInstance().getJsonRpcHttpFullNodePort() + "/jsonrpc";
1126+
1127+
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
1128+
// Normal JSON-RPC request passes through SizeLimitHandler
1129+
JsonObject req = new JsonObject();
1130+
req.addProperty("jsonrpc", "2.0");
1131+
req.addProperty("method", "web3_clientVersion");
1132+
req.addProperty("id", 1);
1133+
1134+
HttpPost post = new HttpPost(url);
1135+
post.addHeader("Content-Type", "application/json");
1136+
post.setEntity(new StringEntity(req.toString()));
1137+
CloseableHttpResponse resp = httpClient.execute(post);
1138+
Assert.assertEquals(200, resp.getStatusLine().getStatusCode());
1139+
String body = EntityUtils.toString(resp.getEntity());
1140+
Assert.assertTrue("Normal JSON-RPC response should contain result",
1141+
body.contains("result"));
1142+
resp.close();
1143+
1144+
// Oversized request with Content-Length → 413 before JsonRpcServlet
1145+
HttpPost overPost = new HttpPost(url);
1146+
overPost.addHeader("Content-Type", "application/json");
1147+
overPost.setEntity(new StringEntity(
1148+
new String(new char[(int) testLimit + 1]).replace('\0', 'x')));
1149+
resp = httpClient.execute(overPost);
1150+
Assert.assertEquals(413, resp.getStatusLine().getStatusCode());
1151+
resp.close();
1152+
1153+
// Chunked oversized → BadMessageException thrown during body read,
1154+
// absorbed by RateLimiterServlet catch(Exception) → 200 with empty body.
1155+
// Body read IS truncated at the limit — OOM protection effective.
1156+
byte[] chunkedData = new String(new char[(int) testLimit * 2])
1157+
.replace('\0', 'x').getBytes("UTF-8");
1158+
HttpPost chunkedPost = new HttpPost(url);
1159+
chunkedPost.setEntity(new InputStreamEntity(
1160+
new ByteArrayInputStream(chunkedData), -1));
1161+
resp = httpClient.execute(chunkedPost);
1162+
Assert.assertEquals(200, resp.getStatusLine().getStatusCode());
1163+
body = EntityUtils.toString(resp.getEntity());
1164+
Assert.assertTrue("Chunked oversized should return empty body"
1165+
+ " (RateLimiterServlet absorbs BadMessageException)", body.isEmpty());
1166+
resp.close();
1167+
} finally {
1168+
field.setLong(fullNodeJsonRpcHttpService, originalLimit);
1169+
}
1170+
} catch (Exception e) {
1171+
Assert.fail(e.getMessage());
1172+
} finally {
1173+
fullNodeJsonRpcHttpService.stop();
1174+
}
1175+
}
11021176
}

0 commit comments

Comments
 (0)