From fde00e00acf6214ad849d04ddb0e24e69a5144c1 Mon Sep 17 00:00:00 2001 From: Maxim Nesen Date: Thu, 23 Jan 2025 17:09:51 +0100 Subject: [PATCH] Netty Expect 100-Continue fix Signed-off-by: Maxim Nesen --- .../JerseyExpectContinueHandler.java | 21 +++++++++---------- .../netty/connector/NettyConnector.java | 11 ++++++---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java index 390bddf81f..ae2ea03cfd 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -26,6 +26,7 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.LastHttpContent; import org.glassfish.jersey.client.ClientRequest; import javax.ws.rs.ProcessingException; @@ -40,6 +41,8 @@ public class JerseyExpectContinueHandler extends ChannelInboundHandlerAdapter { private boolean isExpected; + private boolean expectReceived; + private static final List statusesToBeConsidered = Arrays.asList(HttpResponseStatus.CONTINUE, HttpResponseStatus.UNAUTHORIZED, HttpResponseStatus.EXPECTATION_FAILED, HttpResponseStatus.METHOD_NOT_ALLOWED, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE); @@ -56,10 +59,12 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception if (!HttpResponseStatus.CONTINUE.equals(response.status())) { ctx.fireChannelRead(msg); //bypass the message to the next handler in line } else { - ctx.pipeline().remove(JerseyExpectContinueHandler.class); + expectReceived = true; } + } else if (expectReceived && msg instanceof LastHttpContent) { + ctx.pipeline().remove(JerseyExpectContinueHandler.class); } else { - if (!isExpected) { + if (!isExpected || expectedFuture.isDone()) { ctx.pipeline().remove(JerseyExpectContinueHandler.class); } ctx.fireChannelRead(msg); //bypass the message to the next handler in line @@ -93,9 +98,7 @@ CompletableFuture processExpect100ContinueRequest(HttpReques // Expect:100-Continue either is not supported or is turned off : null; isExpected = expect100ContinueFuture != null; - if (!isExpected) { - ch.pipeline().remove(JerseyExpectContinueHandler.class); - } else { + if (isExpected) { final HttpResponseStatus status = expectedFuture .get(timeout, TimeUnit.MILLISECONDS); @@ -119,8 +122,4 @@ private void processExpectationStatus(HttpResponseStatus status) .UNEXPECTED_VALUE_FOR_EXPECT_100_CONTINUE_STATUSES(status.code()), null); } } - - boolean isExpected() { - return isExpected; - } -} \ No newline at end of file +} diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java index 9523ee66fb..3c1309a545 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java @@ -64,7 +64,6 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; @@ -255,6 +254,8 @@ protected void execute(final ClientRequest jerseyRequest, final Set redirec } } + final JerseyExpectContinueHandler expect100ContinueHandler = new JerseyExpectContinueHandler(); + if (chan == null) { Integer connectTimeout = jerseyRequest.resolveProperty(ClientProperties.CONNECT_TIMEOUT, 0); Bootstrap b = new Bootstrap(); @@ -327,12 +328,15 @@ protected void initChannel(SocketChannel ch) throws Exception { final Integer maxInitialLineLength = ClientProperties.getValue(config.getProperties(), NettyClientProperties.MAX_INITIAL_LINE_LENGTH, NettyClientProperties.DEFAULT_INITIAL_LINE_LENGTH); +/* final Long aggregatorContentLength = ClientProperties.getValue(config.getProperties(), ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, ClientProperties.DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE); +*/ p.addLast(new HttpClientCodec(maxInitialLineLength, maxHeaderSize, maxChunkSize)); - p.addLast(new HttpObjectAggregator(aggregatorContentLength.intValue())); +// p.addLast(new HttpObjectAggregator(aggregatorContentLength.intValue())); + p.addLast(EXPECT_100_CONTINUE_HANDLER, expect100ContinueHandler); p.addLast(new ChunkedWriteHandler()); p.addLast(new HttpContentDecompressor()); } @@ -361,11 +365,10 @@ protected void initChannel(SocketChannel ch) throws Exception { final Channel ch = chan; JerseyClientHandler clientHandler = new JerseyClientHandler(jerseyRequest, responseAvailable, responseDone, redirectUriHistory, this); - final JerseyExpectContinueHandler expect100ContinueHandler = new JerseyExpectContinueHandler(); + // read timeout makes sense really as an inactivity timeout ch.pipeline().addLast(READ_TIMEOUT_HANDLER, new IdleStateHandler(0, 0, timeout, TimeUnit.MILLISECONDS)); - ch.pipeline().addLast(EXPECT_100_CONTINUE_HANDLER, expect100ContinueHandler); ch.pipeline().addLast(REQUEST_HANDLER, clientHandler); responseDone.whenComplete((_r, th) -> {