Skip to content

Commit

Permalink
Explicitly sets the content length to zero when a 204 or related stat…
Browse files Browse the repository at this point in the history
…us code is returned to avoid validation errors at a later time if chunked encoding is selected. New test.
  • Loading branch information
spericas committed Oct 16, 2024
1 parent 8f9f04b commit 57b1722
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ static void routing(HttpRules rules) {
res.streamFilter(os -> os); // forces filter codepath
res.status(Status.NO_CONTENT_204);
res.send();
}).post("hello_stream", (req, res) -> {
try (var out = res.outputStream()) {
res.status(Status.NO_CONTENT_204);
out.flush();
}
});
}

Expand All @@ -68,4 +73,13 @@ void gzipEncodeEmptyEntityFilter() {
.request();
assertThat(res.status().code(), is(204));
}

@Test
void gzipEncodeEmptyEntityStream() {
Http1ClientResponse res = client.post("hello_stream")
.header(HeaderNames.CONTENT_TYPE, "application/json")
.header(HeaderNames.ACCEPT_ENCODING, "gzip")
.request();
assertThat(res.status().code(), is(204));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ static void nonEntityBytes(ServerResponseHeaders headers,

status = status == null ? Status.OK_200 : status;

if (status.code() == Status.NO_CONTENT_204.code()
|| status.code() == Status.RESET_CONTENT_205.code()
|| status.code() == Status.NOT_MODIFIED_304.code()) {
if (isNoEntityStatus(status)) {
// https://www.rfc-editor.org/rfc/rfc9110#status.204
// A 204 response is terminated by the end of the header section; it cannot contain content or trailers
// ditto for 205, and 304
Expand Down Expand Up @@ -431,6 +429,13 @@ private OutputStream outputStream(boolean skipEncoders) {
return outputStreamFilter == null ? encodedOutputStream : outputStreamFilter.apply(encodedOutputStream);
}

private static boolean isNoEntityStatus(Status status) {
int code = status.code();
return code == Status.NO_CONTENT_204.code()
|| code == Status.RESET_CONTENT_205.code()
|| code == Status.NOT_MODIFIED_304.code();
}

static class BlockingOutputStream extends OutputStream {
private final ServerResponseHeaders headers;
private final WritableHeaders<?> trailers;
Expand Down Expand Up @@ -693,9 +698,15 @@ private void sendFirstChunkOnly() {
}

private void sendHeadersAndPrepare() {
Status usedStatus = status.get();

if (headers.contains(HeaderNames.CONTENT_LENGTH)) {
contentLength = headers.contentLength().orElse(-1);
isChunked = false;
} else if (isNoEntityStatus(usedStatus)) {
// force content length to zero to prevent validation errors
headers.set(HeaderValues.CONTENT_LENGTH_ZERO);
contentLength = 0;
} else {
contentLength = -1;
// Add chunked encoding, if there is no other transfer-encoding headers
Expand All @@ -710,7 +721,6 @@ private void sendHeadersAndPrepare() {
}

// at this moment, we must send headers
Status usedStatus = status.get();
sendListener.status(ctx, usedStatus);
sendListener.headers(ctx, headers);
BufferData bufferData = BufferData.growing(256);
Expand Down

0 comments on commit 57b1722

Please sign in to comment.