Skip to content

Commit

Permalink
Cache Content-Length when it is about to send the FullHttpResponse (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
violetagg authored Jul 17, 2024
1 parent 5b7488f commit 8fe4229
Showing 1 changed file with 48 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import reactor.util.context.Context;

import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
import static io.netty.handler.codec.http.DefaultHttpHeadersFactory.trailersFactory;
import static io.netty.handler.codec.http.HttpUtil.isTransferEncodingChunked;
import static reactor.netty.ReactorNetty.format;
import static reactor.netty.http.server.HttpServerFormDecoderProvider.DEFAULT_FORM_DECODER_SPEC;
Expand Down Expand Up @@ -135,7 +136,7 @@ class HttpServerOperations extends HttpOperations<HttpServerRequest, HttpServerR
String path;
Future<?> requestTimeoutFuture;
Consumer<? super HttpHeaders> trailerHeadersConsumer;
HttpMessage fullHttpResponse;
FullHttpResponse fullHttpResponse;

volatile Context currentContext;

Expand Down Expand Up @@ -535,7 +536,7 @@ public NettyOutbound send(Publisher<? extends ByteBuf> source) {
.flatMap(b -> {
if (!hasSentHeaders()) {
try {
fullHttpResponse = prepareHttpMessage(b);
fullHttpResponse = prepareFullHttpResponse(b);

afterMarkSentHeaders();
}
Expand Down Expand Up @@ -570,7 +571,7 @@ public NettyOutbound sendObject(Object message) {
return new PostHeadersNettyOutbound(Mono.create(sink -> {
if (!hasSentHeaders()) {
try {
fullHttpResponse = prepareHttpMessage(b);
fullHttpResponse = prepareFullHttpResponse(b);

afterMarkSentHeaders();
}
Expand Down Expand Up @@ -685,7 +686,7 @@ public Mono<Void> then() {
int contentLength = HttpUtil.getContentLength(msg, -1);
if (contentLength == 0 || isContentAlwaysEmpty()) {
last = true;
msg = newFullBodyMessage(Unpooled.EMPTY_BUFFER);
msg = newFullHttpResponse(Unpooled.EMPTY_BUFFER, contentLength);
}
else if (contentLength > 0) {
responseHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING);
Expand Down Expand Up @@ -950,6 +951,48 @@ void terminateInternal() {
terminate();
}

final FullHttpResponse prepareFullHttpResponse(ByteBuf buffer) {
int contentLength = HttpUtil.getContentLength(outboundHttpMessage(), -1);
if (contentLength == 0 || isContentAlwaysEmpty()) {
if (log.isDebugEnabled()) {
log.debug(format(channel(), "Dropped HTTP content, since response has " +
"1. [Content-Length: 0] or 2. there must be no content: {}"), buffer);
}
buffer.release();
return newFullHttpResponse(Unpooled.EMPTY_BUFFER, contentLength);
}
else {
return newFullHttpResponse(buffer, contentLength);
}
}

final FullHttpResponse newFullHttpResponse(ByteBuf body, int contentLength) {
if (!HttpMethod.HEAD.equals(method())) {
responseHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING);
int code = status().code();
if (!(HttpResponseStatus.NOT_MODIFIED.code() == code ||
HttpResponseStatus.NO_CONTENT.code() == code)) {

if (contentLength == -1) {
responseHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, body.readableBytes());
}
}
}
// For HEAD requests:
// - if there is Transfer-Encoding and Content-Length, Transfer-Encoding will be removed
// - if there is only Transfer-Encoding, it will be kept and not replaced by
// Content-Length: body.readableBytes()
// For HEAD requests, the I/O handler may decide to provide only the headers and complete
// the response. In that case body will be EMPTY_BUFFER and if we set Content-Length: 0,
// this will not be correct
// https://github.com/reactor/reactor-netty/issues/1333
else if (contentLength != -1) {
responseHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING);
}

return new DefaultFullHttpResponse(version(), status(), body, responseHeaders, trailersFactory().newHeaders());
}

static long requestsCounter(Channel channel) {
HttpServerOperations ops = Connection.from(channel).as(HttpServerOperations.class);

Expand Down Expand Up @@ -1006,7 +1049,7 @@ else if (cause instanceof TooLongHttpHeaderException) {
else {
status = HttpResponseStatus.BAD_REQUEST;
}
HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
response.headers()
.setInt(HttpHeaderNames.CONTENT_LENGTH, 0)
.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
Expand Down

0 comments on commit 8fe4229

Please sign in to comment.