Merge pull request #9903 from eclipse/jetty-10.0.x-9895-MessageTooLarge

Issue #9895 - ensure callback is failed after error in PerMessageDeflateExtension
This commit is contained in:
Lachlan 2023-06-16 08:00:13 +10:00 committed by GitHub
commit 5b1e7bd549
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 2 deletions

View File

@ -464,16 +464,23 @@ public class PerMessageDeflateExtension extends AbstractExtension implements Dem
chunk.setPayload(payload);
chunk.setFin(frame.isFin() && complete);
boolean succeedCallback = complete;
// If we are complete we return true, then DemandingFlusher.process() will null out the Frame and Callback.
// The application may decide to hold onto the buffer and delay completing the callback, so we need to capture
// references to these in the payloadCallback and not rely on state of the flusher which may have moved on.
// This flusher could be failed while the application already has the payloadCallback, so we need protection against
// the flusher failing and the application completing the callback, that's why we use the payload AtomicReference.
boolean completeCallback = complete;
AtomicReference<ByteBuffer> payloadRef = _payloadRef;
Callback payloadCallback = Callback.from(() ->
{
getBufferPool().release(payloadRef.getAndSet(null));
if (succeedCallback)
if (completeCallback)
callback.succeeded();
}, t ->
{
getBufferPool().release(payloadRef.getAndSet(null));
if (completeCallback)
callback.failed(t);
failFlusher(t);
});

View File

@ -33,6 +33,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -92,6 +93,27 @@ public class LargeDeflateTest
assertThat(message, is(sentMessage));
}
@Test
void testDeflateLargerThanMaxMessage() throws Exception
{
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
upgradeRequest.addExtensions("permessage-deflate");
EventSocket clientSocket = new EventSocket();
ByteBuffer message = largePayloads();
Session session = _client.connect(clientSocket, URI.create("ws://localhost:" + _connector.getLocalPort() + "/ws"), upgradeRequest).get();
// Set the maxBinaryMessageSize on the server to be lower than the size of the message.
assertTrue(_serverSocket.openLatch.await(5, TimeUnit.SECONDS));
_serverSocket.session.setMaxBinaryMessageSize(message.remaining() - 1024);
session.getRemote().sendBytes(message);
assertTrue(clientSocket.closeLatch.await(5, TimeUnit.SECONDS));
assertTrue(_serverSocket.closeLatch.await(5, TimeUnit.SECONDS));
assertThat(_serverSocket.closeCode, is(StatusCode.MESSAGE_TOO_LARGE));
assertThat(_serverSocket.closeReason, containsString("Binary message too large"));
}
private static ByteBuffer largePayloads()
{
var bytes = new byte[4 * 1024 * 1024];