Release HTTP Request Body Earlier (#57094) (#57110)

We don't need to hold on to the request body past the beginning of sending
the response. There is no need to keep a reference to it until after the response
has been sent fully and we can eagerly release it here.
Note, this can be optimized further to release the contents even earlier but for now
this is an easy increment to saving some memory on the IO pool.
This commit is contained in:
Armin Braun 2020-05-25 13:00:19 +02:00 committed by GitHub
parent 9fa60f7367
commit 56401d3f66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 31 additions and 25 deletions

View File

@ -196,7 +196,7 @@ public class Netty4HttpRequest implements HttpRequest {
@Override
public Netty4HttpResponse createResponse(RestStatus status, BytesReference content) {
return new Netty4HttpResponse(this, status, content);
return new Netty4HttpResponse(request.headers(), request.protocolVersion(), status, content);
}
@Override

View File

@ -20,7 +20,9 @@
package org.elasticsearch.http.netty4;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.http.HttpResponse;
import org.elasticsearch.rest.RestStatus;
@ -28,11 +30,11 @@ import org.elasticsearch.transport.netty4.Netty4Utils;
public class Netty4HttpResponse extends DefaultFullHttpResponse implements HttpResponse {
private final Netty4HttpRequest request;
private final HttpHeaders requestHeaders;
Netty4HttpResponse(Netty4HttpRequest request, RestStatus status, BytesReference content) {
super(request.nettyRequest().protocolVersion(), HttpResponseStatus.valueOf(status.getStatus()), Netty4Utils.toByteBuf(content));
this.request = request;
Netty4HttpResponse(HttpHeaders requestHeaders, HttpVersion version, RestStatus status, BytesReference content) {
super(version, HttpResponseStatus.valueOf(status.getStatus()), Netty4Utils.toByteBuf(content));
this.requestHeaders = requestHeaders;
}
@Override
@ -45,8 +47,8 @@ public class Netty4HttpResponse extends DefaultFullHttpResponse implements HttpR
return headers().contains(name);
}
public Netty4HttpRequest getRequest() {
return request;
public HttpHeaders requestHeaders() {
return requestHeaders;
}
}

View File

@ -93,20 +93,20 @@ public class Netty4CorsHandler extends ChannelDuplexHandler {
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
assert msg instanceof Netty4HttpResponse : "Invalid message type: " + msg.getClass();
Netty4HttpResponse response = (Netty4HttpResponse) msg;
setCorsResponseHeaders(response.getRequest().nettyRequest(), response, config);
setCorsResponseHeaders(response.requestHeaders(), response, config);
ctx.write(response, promise);
}
public static void setCorsResponseHeaders(HttpRequest request, HttpResponse resp, CorsHandler.Config config) {
public static void setCorsResponseHeaders(HttpHeaders headers, HttpResponse resp, CorsHandler.Config config) {
if (!config.isCorsSupportEnabled()) {
return;
}
String originHeader = request.headers().get(HttpHeaderNames.ORIGIN);
String originHeader = headers.get(HttpHeaderNames.ORIGIN);
if (!Strings.isNullOrEmpty(originHeader)) {
final String originHeaderVal;
if (config.isAnyOriginSupported()) {
originHeaderVal = ANY_ORIGIN;
} else if (config.isOriginAllowed(originHeader) || isSameOrigin(originHeader, request.headers().get(HttpHeaderNames.HOST))) {
} else if (config.isOriginAllowed(originHeader) || isSameOrigin(originHeader, headers.get(HttpHeaderNames.HOST))) {
originHeaderVal = originHeader;
} else {
originHeaderVal = null;

View File

@ -194,7 +194,7 @@ public class NioHttpRequest implements HttpRequest {
@Override
public NioHttpResponse createResponse(RestStatus status, BytesReference content) {
return new NioHttpResponse(this, status, content);
return new NioHttpResponse(request.headers(), request.protocolVersion(), status, content);
}
@Override

View File

@ -20,18 +20,20 @@
package org.elasticsearch.http.nio;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.http.HttpResponse;
import org.elasticsearch.rest.RestStatus;
public class NioHttpResponse extends DefaultFullHttpResponse implements HttpResponse {
private final NioHttpRequest request;
private final HttpHeaders requestHeaders;
NioHttpResponse(NioHttpRequest request, RestStatus status, BytesReference content) {
super(request.nettyRequest().protocolVersion(), HttpResponseStatus.valueOf(status.getStatus()), ByteBufUtils.toByteBuf(content));
this.request = request;
NioHttpResponse(HttpHeaders requestHeaders, HttpVersion version, RestStatus status, BytesReference content) {
super(version, HttpResponseStatus.valueOf(status.getStatus()), ByteBufUtils.toByteBuf(content));
this.requestHeaders = requestHeaders;
}
@Override
@ -44,7 +46,7 @@ public class NioHttpResponse extends DefaultFullHttpResponse implements HttpResp
return headers().contains(name);
}
public NioHttpRequest getRequest() {
return request;
public HttpHeaders requestHeaders() {
return requestHeaders;
}
}

View File

@ -50,7 +50,7 @@ import java.util.stream.Collectors;
public class NioCorsHandler extends ChannelDuplexHandler {
public static final String ANY_ORIGIN = "*";
private static Pattern SCHEME_PATTERN = Pattern.compile("^https?://");
private static final Pattern SCHEME_PATTERN = Pattern.compile("^https?://");
private final CorsHandler.Config config;
private NioHttpRequest request;
@ -94,20 +94,20 @@ public class NioCorsHandler extends ChannelDuplexHandler {
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
assert msg instanceof NioHttpResponse : "Invalid message type: " + msg.getClass();
NioHttpResponse response = (NioHttpResponse) msg;
setCorsResponseHeaders(response.getRequest().nettyRequest(), response, config);
setCorsResponseHeaders(response.requestHeaders(), response, config);
ctx.write(response, promise);
}
public static void setCorsResponseHeaders(HttpRequest request, HttpResponse resp, CorsHandler.Config config) {
public static void setCorsResponseHeaders(HttpHeaders headers, HttpResponse resp, CorsHandler.Config config) {
if (!config.isCorsSupportEnabled()) {
return;
}
String originHeader = request.headers().get(HttpHeaderNames.ORIGIN);
String originHeader = headers.get(HttpHeaderNames.ORIGIN);
if (!Strings.isNullOrEmpty(originHeader)) {
final String originHeaderVal;
if (config.isAnyOriginSupported()) {
originHeaderVal = ANY_ORIGIN;
} else if (config.isOriginAllowed(originHeader) || isSameOrigin(originHeader, request.headers().get(HttpHeaderNames.HOST))) {
} else if (config.isOriginAllowed(originHeader) || isSameOrigin(originHeader, headers.get(HttpHeaderNames.HOST))) {
originHeaderVal = originHeader;
} else {
originHeaderVal = null;

View File

@ -83,8 +83,10 @@ public class DefaultRestChannel extends AbstractRestChannel implements RestChann
@Override
public void sendResponse(RestResponse restResponse) {
final ArrayList<Releasable> toClose = new ArrayList<>(4);
toClose.add(httpRequest::release);
// We're sending a response so we know we won't be needing the request content again and release it
Releasables.closeWhileHandlingException(httpRequest::release);
final ArrayList<Releasable> toClose = new ArrayList<>(3);
if (isCloseConnection()) {
toClose.add(() -> CloseableChannel.closeChannel(httpChannel));
}