Skip to content

Commit d02c9a7

Browse files
authored
Prevent ThreadContext header leak when sending response backport(#68649) (#85865)
We need to stash thread context in DefaultRestChannel before we call channel.sendResponse because when calling this method, our execution might be delayed and the thread be reused for another task - like sending another response. And it would see thread context from the initial “delayed” work. This commit also expands the assertions on the empty thread context to make sure it does not contains response headers. closes #68278
1 parent b622c83 commit d02c9a7

File tree

4 files changed

+18
-1
lines changed

4 files changed

+18
-1
lines changed

docs/changelog/68649.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 68649
2+
summary: Prevent `ThreadContext` header leak when sending response
3+
area: Infra/Core
4+
type: bug
5+
issues:
6+
- 68278

modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpRequestHandler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import org.elasticsearch.ExceptionsHelper;
1616
import org.elasticsearch.http.HttpPipelinedRequest;
17+
import org.elasticsearch.transport.Transports;
1718

1819
@ChannelHandler.Sharable
1920
class Netty4HttpRequestHandler extends SimpleChannelInboundHandler<HttpPipelinedRequest> {
@@ -26,6 +27,8 @@ class Netty4HttpRequestHandler extends SimpleChannelInboundHandler<HttpPipelined
2627

2728
@Override
2829
protected void channelRead0(ChannelHandlerContext ctx, HttpPipelinedRequest httpRequest) {
30+
assert Transports.assertDefaultThreadContext(serverTransport.getThreadPool().getThreadContext());
31+
assert Transports.assertTransportThread();
2932
final Netty4HttpChannel channel = ctx.channel().attr(Netty4HttpServerTransport.HTTP_CHANNEL_KEY).get();
3033
boolean success = false;
3134
try {
@@ -41,6 +44,8 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpPipelinedRequest http
4144
@Override
4245
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
4346
ExceptionsHelper.maybeDieOnAnotherThread(cause);
47+
assert Transports.assertDefaultThreadContext(serverTransport.getThreadPool().getThreadContext());
48+
4449
Netty4HttpChannel channel = ctx.channel().attr(Netty4HttpServerTransport.HTTP_CHANNEL_KEY).get();
4550
if (cause instanceof Error) {
4651
serverTransport.onException(channel, new Exception(cause));

server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,8 @@ private static ActionListener<Void> earlyResponseListener(HttpRequest request, H
478478
return NO_OP;
479479
}
480480
}
481+
482+
public ThreadPool getThreadPool() {
483+
return threadPool;
484+
}
481485
}

server/src/main/java/org/elasticsearch/http/DefaultRestChannel.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ public void sendResponse(RestResponse restResponse) {
131131
addCookies(httpResponse);
132132

133133
ActionListener<Void> listener = ActionListener.wrap(() -> Releasables.close(toClose));
134-
httpChannel.sendResponse(httpResponse, listener);
134+
try (ThreadContext.StoredContext existing = threadContext.stashContext()) {
135+
httpChannel.sendResponse(httpResponse, listener);
136+
}
135137
success = true;
136138
} finally {
137139
if (success == false) {

0 commit comments

Comments
 (0)