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

Respond with 408 status when the server didn't receive the request fully #5680

Merged
merged 5 commits into from
May 31, 2024

Conversation

minwoox
Copy link
Member

@minwoox minwoox commented May 16, 2024

Motivation:
Currently, the server always responds with a 503 status if a RequestTimeoutException is raised. However, according to the RFC 9110, the correct response in this case should be a 408 status code. https://httpwg.org/specs/rfc9110.html#status.408

The 408 (Request Timeout) status code indicates that the server did not receive a complete request message within the time that it was prepared to wait.

Modification:

  • Introduced DecodedHttpRequest.isClosedSuccefully() to check if the request was received fully.
  • Updated the server to send a 408 response when a request times out and the service didn't receive the request fully.

Result:

…lly.

Motivation:
Currently, the server always responds with a 503 status if a `RequestTimeoutException` is raised.
However, according to the RFC 9110, the correct response in this case should be a 408 status code.
https://httpwg.org/specs/rfc9110.html#status.408
```
The 408 (Request Timeout) status code indicates that the server did not receive a complete request message within the time that it was prepared to wait.
```

Modification:
- Introduced `DecodedHttpRequest.isNormallyClosed()` to check if the request was received fully.
- Updated the server to send a 408 response when a request times out and the service didn't receive the request fully.

Result:
- The server now returns a 408 status if a service didn't receive the request fully and the request times out.
- Issue line#5579 has been closed.
@minwoox minwoox added the defect label May 16, 2024
@minwoox minwoox added this to the 1.29.0 milestone May 16, 2024
@@ -482,10 +482,8 @@ public CompletableFuture<List<T>> collect(EventExecutor executor, SubscriptionOp
}

private static SubscriptionImpl noopSubscription() {
final DefaultStreamMessage<?> streamMessage = new DefaultStreamMessage<>();
Copy link
Member Author

Choose a reason for hiding this comment

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

We shouldn't use DefaultStreamMessage because subscription.request is called without subscribing.

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.

Left an optional comment, but looks good! Thanks @minwoox 🙇 👍 🙇

Copy link
Contributor

@ikhoon ikhoon 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!

@@ -88,6 +88,8 @@ static DecodedHttpRequest of(boolean endOfStream, EventLoop eventLoop, int id, i

void close(Throwable cause);

boolean isNormallyClosed();
Copy link
Contributor

Choose a reason for hiding this comment

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

I know what normally means, but it doesn't really hit me. How about?

Suggested change
boolean isNormallyClosed();
boolean isClosedSuccessfully();

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good name. 👍

Comment on lines 92 to 94
if (ctx.method() == HttpMethod.GET) {
// Consider GET requests as having received the request fully.
status = HttpStatus.SERVICE_UNAVAILABLE;
Copy link
Contributor

Choose a reason for hiding this comment

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

My understanding is that this branch is used for performance optimization. Because, generally, request.isNormallyClosed() should be true for GET method.

Copy link
Member Author

Choose a reason for hiding this comment

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

A GET request is allowed to have a request body, thus isNormallyClosed might not be called.
https://stackoverflow.com/questions/978061/http-get-with-request-body

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason that we should return 503 even if a request hasn’t fully received for a GET instead of 408?

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought we could consider a GET request as having received the request fully.
Do you prefer to return 408 instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought returning 408 is more consistent behavior because that does not rely on HTTP method but check if a request is fully received.

Copy link
Member Author

Choose a reason for hiding this comment

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

Alright then let me return 408 instead. For the record, a GET request that doesn't have any request body might get 408 as well because this DefaultServerErrorHandler can be called before isNormalClosed() is called.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think, in most cases, isNormalClosed() is true when a DecodedHttpRequest is created for a GET request. endOfStream would be true with a GET request having an empty body. As a result, EmptyContentDecodedHttpRequest will be created for which isNormalClosed() is always true.

Copy link
Member Author

Choose a reason for hiding this comment

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

That is true. I thought an HTTP/1.1 request was handled differently but it was not after all.

@minwoox minwoox merged commit 2112f50 into line:main May 31, 2024
15 checks passed
@minwoox minwoox deleted the 408_RequestTimeout branch May 31, 2024 06:55
@minwoox
Copy link
Member Author

minwoox commented May 31, 2024

Thanks for reviewing. 😉

Dogacel pushed a commit to Dogacel/armeria that referenced this pull request Jun 8, 2024
…lly (line#5680)

Motivation:
Currently, the server always responds with a 503 status if a `RequestTimeoutException` is raised. However, according to the RFC 9110, the correct response in this case should be a 408 status code. https://httpwg.org/specs/rfc9110.html#status.408
```
The 408 (Request Timeout) status code indicates that the server did not receive a complete request message within the time that it was prepared to wait.
```

Modification:
- Introduced `DecodedHttpRequest.isNormallyClosed()` to check if the request was received fully.
- Updated the server to send a 408 response when a request times out and the service didn't receive the request fully.

Result:
- The server now returns a 408 status if a service didn't receive the request fully and the request times out.
- Issue line#5579 has been closed.
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.

3 participants