Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Early Rejection of Large Requests Based on Content-Length Header #6032

Merged
merged 4 commits into from
Jan 9, 2025

Conversation

yzfeng2020
Copy link
Contributor

@yzfeng2020 yzfeng2020 commented Dec 12, 2024

Motivation:

Currently, large requests are rejected only after the transferred bytes exceed the configured limit. This behavior is sub-optimal for requests that include a valid Content-Length header indicating the request is already too large. By rejecting such requests earlier—when the header is read—we can improve resource utilization and reduce unnecessary processing.

Modifications:

  • Added a field to ContentTooLargeException to indicate when the exception is raised during header processing.
  • Implemented content-length-based early rejection in Http1ObjectDecoder and Http2ObjectDecoder.
    Result:

Result:

@yzfeng2020 yzfeng2020 force-pushed the early-reject-large-req branch from fd7fe9e to 0a25899 Compare December 12, 2024 08:20
@@ -600,8 +600,8 @@ void testTimeoutAfterPartialContentWithPooling(WebClient client) throws Exceptio
void testTooLargeContentToNonExistentService(WebClient client) {
final byte[] content = new byte[(int) MAX_CONTENT_LENGTH + 1];
final AggregatedHttpResponse res = client.post("/non-existent", content).aggregate().join();
assertThat(res.status()).isSameAs(HttpStatus.NOT_FOUND);
assertThat(res.contentUtf8()).startsWith("Status: 404\n");
assertThat(res.status()).isSameAs(HttpStatus.REQUEST_ENTITY_TOO_LARGE);
Copy link
Contributor Author

@yzfeng2020 yzfeng2020 Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i had to change this test as it's rejected earlier now, is there a way to fix it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To simulate a client request with no content-length header, we can probably make a transfer-encoding request.

e.g.

val req = HttpRequest.streaming(POST, "/non-existent);
client.execute(req)...
req.write(content)
req.close();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense, updated!

@@ -472,6 +472,11 @@ public Boolean allowSemicolonInPathComponent() {
return false;
}

@Override
public Boolean allowLargeRequestEarlyRejection() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to guard this feature under a flag..

@yzfeng2020 yzfeng2020 marked this pull request as ready for review December 13, 2024 08:55
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, left some minor comments

@yzfeng2020 yzfeng2020 force-pushed the early-reject-large-req branch from 36bb54d to c114764 Compare December 20, 2024 06:27
@yzfeng2020 yzfeng2020 changed the title early reject Early Rejection of Large Requests Based on Content-Length Header Dec 20, 2024
@yzfeng2020 yzfeng2020 force-pushed the early-reject-large-req branch from 06f9c52 to 990f4cb Compare December 20, 2024 09:42
@yzfeng2020 yzfeng2020 requested a review from jrhee17 December 20, 2024 09:43
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍 👍

@yzfeng2020
Copy link
Contributor Author

@ikhoon @minwoox happy new year! I hope you can review this PR when you have time, thanks

@minwoox minwoox added the defect label Jan 2, 2025
@minwoox minwoox added this to the 1.32.0 milestone Jan 2, 2025
Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yzfeng2020 Happy new year! 😆

@yzfeng2020
Copy link
Contributor Author

hey @ikhoon gentle ping if you can take a look! :)

Comment on lines +284 to +286
if (maxRequestLength > 0 && contentLength > maxRequestLength) {
abortLargeRequest(ctx, req, id, endOfStream, keepAliveHandler, true);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure if it is necessary to pass the content-length exceeding request to the service chains. Should we fail() the request instead? What do you think @minwoox?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, it was implemented this way to meet a demand for logging content-length-exceeding requests via LoggingService.

This feature could still be useful for identifying such requests on the server side, provided it doesn't cause a performance issue. 🤔

Copy link
Contributor

@ikhoon ikhoon Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't worried about performance, but I was considering the race condition between the response sent by the user and the aborted response.

That case would be rare, so I think it can be ignored.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, let's revisit this if we encounter any issue with this. 😉

@minwoox minwoox merged commit 1157370 into line:main Jan 9, 2025
14 checks passed
@minwoox
Copy link
Contributor

minwoox commented Jan 9, 2025

@yzfeng2020 Thanks! 👍 👍 👍

ikhoon pushed a commit that referenced this pull request Jan 14, 2025
Motivation:

 Currently, large requests are rejected only after the transferred bytes exceed the configured limit. This behavior is sub-optimal for requests that include a valid Content-Length header indicating the request is already too large. By rejecting such requests earlier—when the header is read—we can improve resource utilization and reduce unnecessary processing.

Modifications:

- Added a field to ContentTooLargeException to indicate when the exception is raised during header processing.
- Implemented content-length-based early rejection in Http1ObjectDecoder and Http2ObjectDecoder.
Result:


Result:

- Closes #5880
- Requests with a Content-Length header exceeding the allowed limit can now be rejected early in the request flow, reducing wasted resources and improving efficiency.

<!--
Visit this URL to learn more about how to write a pull request description:
https://armeria.dev/community/developer-guide#how-to-write-pull-request-description
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Early rejection for requests exceeding configured maxRequestLength
4 participants