|
1 | 1 | package org.tron.common.jetty; |
2 | 2 |
|
3 | 3 | import java.io.ByteArrayInputStream; |
| 4 | +import java.io.ByteArrayOutputStream; |
4 | 5 | import java.io.IOException; |
| 6 | +import java.io.InputStream; |
| 7 | +import java.net.Socket; |
5 | 8 | import java.net.URI; |
| 9 | +import java.nio.charset.StandardCharsets; |
6 | 10 | import java.util.concurrent.TimeUnit; |
7 | 11 | import java.util.stream.Collectors; |
8 | 12 | import javax.servlet.http.HttpServlet; |
|
33 | 37 |
|
34 | 38 | /** |
35 | 39 | * Tests {@link org.eclipse.jetty.server.handler.SizeLimitHandler} body-size |
36 | | - * enforcement configured in {@link HttpService#initContextHandler()}. |
| 40 | + * enforcement configured in {@link HttpService}. |
37 | 41 | * |
38 | 42 | * Covers: accept/reject by size, UTF-8 byte counting, independent limits |
39 | 43 | * across HttpService instances, chunked transfer, and zero-limit behavior. |
@@ -152,10 +156,69 @@ public void testHttpBodyWithinLimit() throws Exception { |
152 | 156 | Assert.assertEquals(200, post(httpServerUri, new StringEntity("small body"))); |
153 | 157 | } |
154 | 158 |
|
| 159 | + /** |
| 160 | + * An oversized request must return 413 carrying the short, uniform bad-message |
| 161 | + * page produced by the custom ErrorHandler in {@link HttpService} - |
| 162 | + * not the default Jetty error page that leaks the exception stack, class name |
| 163 | + * and the internal request size. |
| 164 | + */ |
155 | 165 | @Test |
156 | 166 | public void testHttpBodyExceedsLimit() throws Exception { |
157 | | - Assert.assertEquals(413, |
158 | | - post(httpServerUri, new StringEntity(repeat('a', HTTP_MAX_BODY_SIZE + 1)))); |
| 167 | + HttpPost req = new HttpPost(httpServerUri); |
| 168 | + req.setEntity(new StringEntity(repeat('a', HTTP_MAX_BODY_SIZE + 1))); |
| 169 | + HttpResponse resp = client.execute(req); |
| 170 | + |
| 171 | + String body = EntityUtils.toString(resp.getEntity()); |
| 172 | + |
| 173 | + // return value: 413 |
| 174 | + Assert.assertEquals(413, resp.getStatusLine().getStatusCode()); |
| 175 | + |
| 176 | + // returned page: short uniform bad-message page with the generic reason |
| 177 | + Assert.assertTrue("should render the short bad-message page", |
| 178 | + body.contains("Bad Message 413")); |
| 179 | + Assert.assertTrue("reason should be the generic status message", |
| 180 | + body.contains("Payload Too Large")); |
| 181 | + |
| 182 | + // must NOT leak Jetty internals |
| 183 | + Assert.assertFalse("must not leak exception class / stack frames", |
| 184 | + body.contains("org.eclipse.jetty")); |
| 185 | + } |
| 186 | + |
| 187 | + /** |
| 188 | + * A malformed Content-Length is rejected by the HTTP parser (onBadMessage -> |
| 189 | + * ErrorHandler.badMessageError()), a different path from the 413 dispatch handler. |
| 190 | + * Confirms the custom ErrorHandler leaves other 4xx untouched: still the default |
| 191 | + * 400 bad-message page, not rerouted through the 413 branch. |
| 192 | + */ |
| 193 | + @Test |
| 194 | + public void testBadContentLengthReturnsDefault400() throws Exception { |
| 195 | + String raw = "POST / HTTP/1.1\r\n" |
| 196 | + + "Host: localhost\r\n" |
| 197 | + + "Content-Length: +450\r\n" |
| 198 | + + "\r\n"; |
| 199 | + String resp = sendRaw(httpServerUri, raw); |
| 200 | + |
| 201 | + Assert.assertTrue("expected 400, got: " + firstLine(resp), resp.startsWith("HTTP/1.1 400")); |
| 202 | + Assert.assertTrue("should be the default bad-message page", resp.contains("Bad Message 400")); |
| 203 | + Assert.assertFalse("must not be rerouted through the 413 branch", |
| 204 | + resp.contains("Payload Too Large")); |
| 205 | + } |
| 206 | + |
| 207 | + /** |
| 208 | + * A request-line URI longer than the request header buffer is rejected by the HTTP |
| 209 | + * parser (414), again via badMessageError(), not the 413 dispatch handler. Confirms it |
| 210 | + * is unaffected by the custom ErrorHandler. |
| 211 | + */ |
| 212 | + @Test |
| 213 | + public void testOversizedUriReturnsDefault414() throws Exception { |
| 214 | + String raw = "GET /" + repeat('a', 9000) + " HTTP/1.1\r\n" // request line > default 8KB |
| 215 | + + "Host: localhost\r\n" |
| 216 | + + "\r\n"; |
| 217 | + String resp = sendRaw(httpServerUri, raw); |
| 218 | + |
| 219 | + Assert.assertTrue("expected 414, got: " + firstLine(resp), resp.startsWith("HTTP/1.1 414")); |
| 220 | + Assert.assertFalse("must not be rerouted through the 413 branch", |
| 221 | + resp.contains("Payload Too Large")); |
159 | 222 | } |
160 | 223 |
|
161 | 224 | @Test |
@@ -324,4 +387,26 @@ private int post(URI uri, HttpEntity entity) throws Exception { |
324 | 387 | private static String repeat(char c, int n) { |
325 | 388 | return new String(new char[n]).replace('\0', c); |
326 | 389 | } |
| 390 | + |
| 391 | + /** Sends a raw HTTP request over a socket and returns the full response (until EOF). */ |
| 392 | + private static String sendRaw(URI uri, String rawRequest) throws Exception { |
| 393 | + try (Socket socket = new Socket(uri.getHost(), uri.getPort())) { |
| 394 | + socket.getOutputStream().write(rawRequest.getBytes(StandardCharsets.US_ASCII)); |
| 395 | + socket.getOutputStream().flush(); |
| 396 | + ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| 397 | + InputStream in = socket.getInputStream(); |
| 398 | + byte[] buf = new byte[4096]; |
| 399 | + int n; |
| 400 | + while ((n = in.read(buf)) != -1) { |
| 401 | + out.write(buf, 0, n); |
| 402 | + } |
| 403 | + return new String(out.toByteArray(), StandardCharsets.UTF_8); |
| 404 | + } |
| 405 | + } |
| 406 | + |
| 407 | + /** First line (status line) of a raw HTTP response. */ |
| 408 | + private static String firstLine(String resp) { |
| 409 | + int idx = resp.indexOf("\r\n"); |
| 410 | + return idx < 0 ? resp : resp.substring(0, idx); |
| 411 | + } |
327 | 412 | } |
0 commit comments