Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.tron.core.services.filter;

import com.google.common.base.Strings;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
Expand All @@ -9,6 +8,8 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpStatus;
import org.tron.common.prometheus.MetricKeys;
import org.tron.common.prometheus.MetricLabels;
import org.tron.common.prometheus.Metrics;
Expand Down Expand Up @@ -66,6 +67,10 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
}
MetricsUtil.meterMark(MetricsKey.NET_API_QPS, 1);
MetricsUtil.meterMark(MetricsKey.NET_API_FAIL_QPS, 1);
if (e instanceof BadMessageException
&& ((BadMessageException) e).getCode() == HttpStatus.PAYLOAD_TOO_LARGE_413) {
throw (BadMessageException) e;
}
}
}

Expand All @@ -75,4 +80,3 @@ public void destroy() {
}



Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.http.BadMessageException;
import org.springframework.beans.factory.annotation.Autowired;
import org.tron.common.parameter.RateLimiterInitialization;
import org.tron.common.prometheus.MetricKeys;
Expand Down Expand Up @@ -133,7 +134,7 @@ protected void service(HttpServletRequest req, HttpServletResponse resp)
resp.getWriter()
.println(Util.printErrorMsg(new IllegalAccessException("lack of computing resources")));
}
} catch (ServletException | IOException e) {
} catch (ServletException | IOException | BadMessageException e) {
throw e;
} catch (Exception unexpected) {
logger.error("Http Api {}, Method:{}. Error:", url, req.getMethod(), unexpected);
Expand All @@ -149,4 +150,4 @@ protected void service(HttpServletRequest req, HttpServletResponse resp)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.util.encoders.Hex;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -1572,8 +1573,7 @@ public void testWeb3ClientVersion() {
* Verifies SizeLimitHandler integration with the real JsonRpcServlet + jsonrpc4j stack.
*
* Covers: normal request no regression, Content-Length oversized 413,
* and chunked oversized handled gracefully (body truncated, 200 + empty body
* because jsonrpc4j absorbs the BadMessageException).
* and chunked oversized 413 during streaming body reads.
*/
@Test
public void testJsonRpcSizeLimitIntegration() {
Expand Down Expand Up @@ -1609,22 +1609,20 @@ public void testJsonRpcSizeLimitIntegration() {
overPost.setEntity(new StringEntity(
new String(new char[(int) testLimit + 1]).replace('\0', 'x')));
resp = httpClient.execute(overPost);
Assert.assertEquals(413, resp.getStatusLine().getStatusCode());
Assert.assertEquals(HttpStatus.PAYLOAD_TOO_LARGE_413,
resp.getStatusLine().getStatusCode());
resp.close();

// Chunked oversized -> BadMessageException thrown during body read,
// absorbed by jsonrpc4j catch(Exception) -> 200 with empty body.
// Chunked oversized -> BadMessageException thrown during body read.
// Body read IS truncated at the limit - OOM protection effective.
byte[] chunkedData = new String(new char[(int) testLimit * 2])
.replace('\0', 'x').getBytes("UTF-8");
HttpPost chunkedPost = new HttpPost(url);
chunkedPost.setEntity(new InputStreamEntity(
new ByteArrayInputStream(chunkedData), -1));
resp = httpClient.execute(chunkedPost);
Assert.assertEquals(200, resp.getStatusLine().getStatusCode());
body = EntityUtils.toString(resp.getEntity());
Assert.assertTrue("Chunked oversized should return empty body"
+ " (jsonrpc4j absorbs BadMessageException)", body.isEmpty());
Assert.assertEquals(HttpStatus.PAYLOAD_TOO_LARGE_413,
resp.getStatusLine().getStatusCode());
resp.close();
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.tron.core.services.filter;

import static org.junit.Assert.assertThrows;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

public class HttpInterceptorTest {

private final HttpInterceptor interceptor = new HttpInterceptor();

@Test
public void testOversizedBadMessagePropagates() {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/jsonrpc");
request.setServletPath("/jsonrpc");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = (req, resp) -> {
throw new BadMessageException(HttpStatus.PAYLOAD_TOO_LARGE_413,
"Request body is too large");
};

BadMessageException e = assertThrows(BadMessageException.class,
() -> interceptor.doFilter(request, response, chain));

org.junit.Assert.assertEquals(HttpStatus.PAYLOAD_TOO_LARGE_413, e.getCode());
}

@Test
public void testNonOversizedExceptionIsStillSwallowed() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/jsonrpc");
request.setServletPath("/jsonrpc");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = (req, resp) -> {
throw new ServletException("expected");
};

interceptor.doFilter(request, response, chain);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -69,6 +71,14 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
}
}

static class OversizedRequestServlet extends RateLimiterServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
throw new BadMessageException(HttpStatus.PAYLOAD_TOO_LARGE_413,
"Request body is too large");
}
}

/**
* GlobalRateLimiter's static initializer calls Args.getInstance().getRateLimiterGlobalQps().
* Without Args being initialized the default QPS is 0, causing RateLimiter.create(0) to throw.
Expand Down Expand Up @@ -250,4 +260,24 @@ public void testNullRateLimiterConsultsOnlyGlobal() throws Exception {
globalMock.verify(() -> GlobalRateLimiter.acquirePermit(any()), times(1));
}
}

@Test
public void testOversizedRequestBadMessagePropagates() throws Exception {
OversizedRequestServlet oversizedServlet = new OversizedRequestServlet();
Field f = RateLimiterServlet.class.getDeclaredField("container");
f.setAccessible(true);
f.set(oversizedServlet, container);
MockHttpServletRequest postRequest = new MockHttpServletRequest("POST", "/jsonrpc");
postRequest.setRemoteAddr("10.0.0.1");
postRequest.setServletPath("/jsonrpc");

try (MockedStatic<GlobalRateLimiter> globalMock = mockStatic(GlobalRateLimiter.class)) {
globalMock.when(() -> GlobalRateLimiter.acquirePermit(any())).thenReturn(true);

BadMessageException e = assertThrows(BadMessageException.class,
() -> oversizedServlet.service(postRequest, response));

assertEquals(HttpStatus.PAYLOAD_TOO_LARGE_413, e.getCode());
}
}
}
Loading