diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/client-io-arch.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/client-io-arch.adoc index 1a23358bf2c..abc80009d30 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/client-io-arch.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/client-io-arch.adoc @@ -33,7 +33,7 @@ The `ClientConnector` primarily wraps the link:{javadoc-url}/org/eclipse/jetty/i * a thread pool (in form of an `java.util.concurrent.Executor`) * a scheduler (in form of `org.eclipse.jetty.util.thread.Scheduler`) -* a byte buffer pool (in form of `org.eclipse.jetty.io.ByteBufferPool`) +* a byte buffer pool (in form of `org.eclipse.jetty.io.RetainableByteBufferPool`) * a TLS factory (in form of `org.eclipse.jetty.util.ssl.SslContextFactory.Client`) The `ClientConnector` is where you want to set those components after you have configured them. diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java index 8a3e69f75b1..9f2c92c2b4e 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java @@ -72,7 +72,7 @@ public class HTTP2Docs } // Get the content buffer. - ByteBuffer byteBuffer = data.frame().getData(); + ByteBuffer byteBuffer = data.frame().getByteBuffer(); // Unwrap the Data object, converting it to a Chunk. // The Data.release() semantic is maintained in the completion of the Callback. diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java index e75148d384b..3cbb717b391 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java @@ -387,7 +387,7 @@ public class ClientConnectorDocs // Wrap the "telnet" ClientConnectionFactory with the SslClientConnectionFactory. connectionFactory = new SslClientConnectionFactory(clientConnector.getSslContextFactory(), - clientConnector.getByteBufferPool(), clientConnector.getExecutor(), connectionFactory); + clientConnector.getRetainableByteBufferPool(), clientConnector.getExecutor(), connectionFactory); // We will obtain a SslConnection now. CompletableFuture connectionPromise = new Promise.Completable<>(); diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java index 9612d5204e5..90857287c2f 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java @@ -63,7 +63,6 @@ import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2 import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2; import org.eclipse.jetty.http3.client.HTTP3Client; import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.Content; @@ -334,14 +333,13 @@ public class HTTPClientDocs // An event happens in some other class, in some other thread. class ContentPublisher { - void publish(ByteBufferPool bufferPool, byte[] bytes, boolean lastContent) + void publish(byte[] bytes, boolean lastContent) { // Wrap the bytes into a new ByteBuffer. ByteBuffer buffer = ByteBuffer.wrap(bytes); - // Offer the content, and release the ByteBuffer - // to the pool when the Callback is completed. - content.write(buffer, Callback.from(() -> bufferPool.release(buffer))); + // Write the content. + content.write(buffer, Callback.NOOP); // Close AsyncRequestContent when all the content is arrived. if (lastContent) diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java index 940138f7df9..7797bfc87a5 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java @@ -254,7 +254,7 @@ public class HTTP2ClientDocs } // Get the content buffer. - ByteBuffer buffer = data.frame().getData(); + ByteBuffer buffer = data.frame().getByteBuffer(); // Consume the buffer, here - as an example - just log it. System.getLogger("http2").log(INFO, "Consuming buffer {0}", buffer); @@ -368,7 +368,7 @@ public class HTTP2ClientDocs } // The pushed stream "response" content bytes. - ByteBuffer buffer = data.frame().getData(); + ByteBuffer buffer = data.frame().getByteBuffer(); // Consume the buffer and release the Data object. data.release(); diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java index 291048af5af..4852c39e829 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java @@ -159,7 +159,7 @@ public class HTTP2ServerDocs } // Get the content buffer. - ByteBuffer buffer = data.frame().getData(); + ByteBuffer buffer = data.frame().getByteBuffer(); // Consume the buffer, here - as an example - just log it. System.getLogger("http2").log(INFO, "Consuming buffer {0}", buffer); diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java index 801d6e13acd..8e70421dc53 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java @@ -52,7 +52,7 @@ public abstract class AbstractConnectorHttpClientTransport extends AbstractHttpC { HttpClient httpClient = getHttpClient(); connector.setBindAddress(httpClient.getBindAddress()); - connector.setByteBufferPool(httpClient.getByteBufferPool()); + connector.setRetainableByteBufferPool(httpClient.getRetainableByteBufferPool()); connector.setConnectBlocking(httpClient.isConnectBlocking()); connector.setConnectTimeout(Duration.ofMillis(httpClient.getConnectTimeout())); connector.setExecutor(httpClient.getExecutor()); diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java index 537a74f540e..88e734451e1 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java @@ -20,6 +20,7 @@ import java.util.Map; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.RetainableByteBuffer; /** * {@link ContentDecoder} decodes content bytes of a response. @@ -29,21 +30,14 @@ import org.eclipse.jetty.http.HttpHeader; public interface ContentDecoder { /** - *

Decodes the bytes in the given {@code buffer} and returns decoded bytes, if any.

+ *

Decodes the bytes in the given {@code buffer} and returns the decoded bytes.

+ *

The returned {@link RetainableByteBuffer} containing the decoded bytes may + * be empty and must be released via {@link RetainableByteBuffer#release()}.

* * @param buffer the buffer containing encoded bytes - * @return a buffer containing decoded bytes, if any + * @return a buffer containing decoded bytes that must be released */ - public abstract ByteBuffer decode(ByteBuffer buffer); - - /** - *

Releases the ByteBuffer returned by {@link #decode(ByteBuffer)}.

- * - * @param decoded the ByteBuffer returned by {@link #decode(ByteBuffer)} - */ - public default void release(ByteBuffer decoded) - { - } + public abstract RetainableByteBuffer decode(ByteBuffer buffer); /** * Factory for {@link ContentDecoder}s; subclasses must implement {@link #newContentDecoder()}. diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java index 304f2d3765e..aa09fa56785 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java @@ -13,9 +13,8 @@ package org.eclipse.jetty.client; -import java.nio.ByteBuffer; - -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; /** * {@link ContentDecoder} for the "gzip" encoding. @@ -34,13 +33,13 @@ public class GZIPContentDecoder extends org.eclipse.jetty.http.GZIPContentDecode this(null, bufferSize); } - public GZIPContentDecoder(ByteBufferPool byteBufferPool, int bufferSize) + public GZIPContentDecoder(RetainableByteBufferPool retainableByteBufferPool, int bufferSize) { - super(byteBufferPool, bufferSize); + super(retainableByteBufferPool, bufferSize); } @Override - protected boolean decodedChunk(ByteBuffer chunk) + protected boolean decodedChunk(RetainableByteBuffer chunk) { super.decodedChunk(chunk); return true; @@ -51,8 +50,8 @@ public class GZIPContentDecoder extends org.eclipse.jetty.http.GZIPContentDecode */ public static class Factory extends ContentDecoder.Factory { + private final RetainableByteBufferPool retainableByteBufferPool; private final int bufferSize; - private final ByteBufferPool byteBufferPool; public Factory() { @@ -64,22 +63,22 @@ public class GZIPContentDecoder extends org.eclipse.jetty.http.GZIPContentDecode this(null, bufferSize); } - public Factory(ByteBufferPool byteBufferPool) + public Factory(RetainableByteBufferPool retainableByteBufferPool) { - this(byteBufferPool, DEFAULT_BUFFER_SIZE); + this(retainableByteBufferPool, DEFAULT_BUFFER_SIZE); } - public Factory(ByteBufferPool byteBufferPool, int bufferSize) + public Factory(RetainableByteBufferPool retainableByteBufferPool, int bufferSize) { super("gzip"); - this.byteBufferPool = byteBufferPool; + this.retainableByteBufferPool = retainableByteBufferPool; this.bufferSize = bufferSize; } @Override public ContentDecoder newContentDecoder() { - return new GZIPContentDecoder(byteBufferPool, bufferSize); + return new GZIPContentDecoder(retainableByteBufferPool, bufferSize); } } } diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 881b797fdbf..ee7660f96f7 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -49,10 +49,8 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Fields; @@ -201,11 +199,9 @@ public class HttpClient extends ContainerLifeCycle int maxBucketSize = executor instanceof ThreadPool.SizedThreadPool ? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2 : ProcessorUtils.availableProcessors() * 2; - ByteBufferPool byteBufferPool = getByteBufferPool(); - if (byteBufferPool == null) - setByteBufferPool(new MappedByteBufferPool(2048, maxBucketSize)); - if (getBean(RetainableByteBufferPool.class) == null) - addBean(new ArrayRetainableByteBufferPool(0, 2048, 65536, maxBucketSize)); + RetainableByteBufferPool retainableByteBufferPool = getRetainableByteBufferPool(); + if (retainableByteBufferPool == null) + setRetainableByteBufferPool(new ArrayRetainableByteBufferPool(0, 2048, 65536, maxBucketSize)); Scheduler scheduler = getScheduler(); if (scheduler == null) { @@ -224,7 +220,7 @@ public class HttpClient extends ContainerLifeCycle handlers.put(new ProxyAuthenticationProtocolHandler(this)); handlers.put(new UpgradeProtocolHandler()); - decoderFactories.put(new GZIPContentDecoder.Factory(byteBufferPool)); + decoderFactories.put(new GZIPContentDecoder.Factory(retainableByteBufferPool)); cookieManager = newCookieManager(); cookieStore = cookieManager.getCookieStore(); @@ -650,19 +646,19 @@ public class HttpClient extends ContainerLifeCycle } /** - * @return the {@link ByteBufferPool} of this HttpClient + * @return the {@link RetainableByteBufferPool} of this HttpClient */ - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return connector.getByteBufferPool(); + return connector.getRetainableByteBufferPool(); } /** - * @param byteBufferPool the {@link ByteBufferPool} of this HttpClient + * @param retainableByteBufferPool the {@link RetainableByteBufferPool} of this HttpClient */ - public void setByteBufferPool(ByteBufferPool byteBufferPool) + public void setRetainableByteBufferPool(RetainableByteBufferPool retainableByteBufferPool) { - connector.setByteBufferPool(byteBufferPool); + connector.setRetainableByteBufferPool(retainableByteBufferPool); } /** @@ -1156,6 +1152,6 @@ public class HttpClient extends ContainerLifeCycle { if (sslContextFactory == null) sslContextFactory = getSslContextFactory(); - return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory); + return new SslClientConnectionFactory(sslContextFactory, getRetainableByteBufferPool(), getExecutor(), connectionFactory); } } diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/internal/HttpReceiver.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/internal/HttpReceiver.java index 0bc75047e44..575bcab02ba 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/internal/HttpReceiver.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/internal/HttpReceiver.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.client.internal; import java.net.URI; -import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -27,8 +26,8 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.content.ContentSourceTransformer; -import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.SerializedInvoker; @@ -578,17 +577,17 @@ public abstract class HttpReceiver _chunk.retain(); if (LOG.isDebugEnabled()) LOG.debug("decoding: {}", _chunk); - ByteBuffer decodedBuffer = _decoder.decode(_chunk.getByteBuffer()); + RetainableByteBuffer decodedBuffer = _decoder.decode(_chunk.getByteBuffer()); if (LOG.isDebugEnabled()) - LOG.debug("decoded: {}", BufferUtil.toDetailString(decodedBuffer)); + LOG.debug("decoded: {}", decodedBuffer); - if (BufferUtil.hasContent(decodedBuffer)) + if (decodedBuffer != null && decodedBuffer.hasRemaining()) { // The decoded ByteBuffer is a transformed "copy" of the // compressed one, so it has its own reference counter. if (LOG.isDebugEnabled()) LOG.debug("returning decoded content"); - return Content.Chunk.from(decodedBuffer, false, _decoder::release); + return Content.Chunk.asChunk(decodedBuffer.getByteBuffer(), false, decodedBuffer); } else { diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpReceiverOverHTTP.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpReceiverOverHTTP.java index 3d81b80a0e1..c1ebf22175c 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpReceiverOverHTTP.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpReceiverOverHTTP.java @@ -66,7 +66,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res parser.setHeaderCacheSize(httpTransport.getHeaderCacheSize()); parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive()); } - retainableByteBufferPool = httpClient.getByteBufferPool().asRetainableByteBufferPool(); + retainableByteBufferPool = httpClient.getRetainableByteBufferPool(); } void receive() @@ -169,7 +169,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res protected ByteBuffer getResponseBuffer() { - return networkBuffer == null ? null : networkBuffer.getBuffer(); + return networkBuffer == null ? null : networkBuffer.getByteBuffer(); } private void acquireNetworkBuffer() @@ -222,7 +222,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res HttpClient client = getHttpDestination().getHttpClient(); upgradeBuffer = BufferUtil.allocate(networkBuffer.remaining(), client.isUseInputDirectByteBuffers()); BufferUtil.clearToFill(upgradeBuffer); - BufferUtil.put(networkBuffer.getBuffer(), upgradeBuffer); + BufferUtil.put(networkBuffer.getByteBuffer(), upgradeBuffer); BufferUtil.flipToFlush(upgradeBuffer, 0); } releaseNetworkBuffer(); @@ -245,7 +245,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res while (true) { if (LOG.isDebugEnabled()) - LOG.debug("Parsing {} in {}", BufferUtil.toDetailString(networkBuffer.getBuffer()), this); + LOG.debug("Parsing {} in {}", BufferUtil.toDetailString(networkBuffer.getByteBuffer()), this); // Always parse even empty buffers to advance the parser. if (parse()) { @@ -269,7 +269,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res reacquireNetworkBuffer(); // The networkBuffer may have been reacquired. - int read = endPoint.fill(networkBuffer.getBuffer()); + int read = endPoint.fill(networkBuffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("Read {} bytes in {} from {} in {}", read, networkBuffer, endPoint, this); @@ -309,7 +309,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res { while (true) { - boolean handle = parser.parseNext(networkBuffer.getBuffer()); + boolean handle = parser.parseNext(networkBuffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("Parse result={} on {}", handle, this); Runnable action = getAndSetAction(null); @@ -347,7 +347,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res if (getHttpChannel().isTunnel(method, status)) return true; - if (networkBuffer.isEmpty()) + if (!networkBuffer.hasRemaining()) return false; if (!HttpStatus.isInformational(status)) @@ -359,7 +359,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res return false; } - if (networkBuffer.isEmpty()) + if (!networkBuffer.hasRemaining()) return false; } } diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java index b7ae70d47cb..b9bf13897ce 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java @@ -23,9 +23,10 @@ import org.eclipse.jetty.client.internal.HttpSender; import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.MetaData; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingCallback; @@ -41,7 +42,7 @@ public class HttpSenderOverHTTP extends HttpSender private final HttpGenerator generator = new HttpGenerator(); private HttpExchange exchange; private MetaData.Request metaData; - private ByteBuffer contentBuffer; + private ByteBuffer contentByteBuffer; private boolean lastContent; private Callback callback; private boolean shutdown; @@ -63,7 +64,7 @@ public class HttpSenderOverHTTP extends HttpSender try { this.exchange = exchange; - this.contentBuffer = contentBuffer; + this.contentByteBuffer = contentBuffer; this.lastContent = lastContent; this.callback = callback; HttpRequest request = exchange.getRequest(); @@ -92,7 +93,7 @@ public class HttpSenderOverHTTP extends HttpSender try { this.exchange = exchange; - this.contentBuffer = contentBuffer; + this.contentByteBuffer = contentBuffer; this.lastContent = lastContent; this.callback = callback; if (LOG.isDebugEnabled()) @@ -144,8 +145,8 @@ public class HttpSenderOverHTTP extends HttpSender private class HeadersCallback extends IteratingCallback { - private ByteBuffer headerBuffer; - private ByteBuffer chunkBuffer; + private RetainableByteBuffer headerBuffer; + private RetainableByteBuffer chunkBuffer; private boolean generated; private HeadersCallback() @@ -157,52 +158,54 @@ public class HttpSenderOverHTTP extends HttpSender protected Action process() throws Exception { HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient(); - ByteBufferPool byteBufferPool = httpClient.getByteBufferPool(); + RetainableByteBufferPool bufferPool = httpClient.getRetainableByteBufferPool(); boolean useDirectByteBuffers = httpClient.isUseOutputDirectByteBuffers(); while (true) { - HttpGenerator.Result result = generator.generateRequest(metaData, headerBuffer, chunkBuffer, contentBuffer, lastContent); + ByteBuffer headerByteBuffer = headerBuffer == null ? null : headerBuffer.getByteBuffer(); + ByteBuffer chunkByteBuffer = chunkBuffer == null ? null : chunkBuffer.getByteBuffer(); + HttpGenerator.Result result = generator.generateRequest(metaData, headerByteBuffer, chunkByteBuffer, contentByteBuffer, lastContent); if (LOG.isDebugEnabled()) LOG.debug("Generated headers ({} bytes), chunk ({} bytes), content ({} bytes) - {}/{} for {}", - headerBuffer == null ? -1 : headerBuffer.remaining(), - chunkBuffer == null ? -1 : chunkBuffer.remaining(), - contentBuffer == null ? -1 : contentBuffer.remaining(), + headerByteBuffer == null ? -1 : headerByteBuffer.remaining(), + chunkByteBuffer == null ? -1 : chunkByteBuffer.remaining(), + contentByteBuffer == null ? -1 : contentByteBuffer.remaining(), result, generator, exchange.getRequest()); switch (result) { case NEED_HEADER: { - headerBuffer = byteBufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers); + headerBuffer = bufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers); break; } case HEADER_OVERFLOW: { - httpClient.getByteBufferPool().release(headerBuffer); + headerBuffer.release(); headerBuffer = null; throw new IllegalArgumentException("Request header too large"); } case NEED_CHUNK: { - chunkBuffer = byteBufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers); + chunkBuffer = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers); break; } case NEED_CHUNK_TRAILER: { - chunkBuffer = byteBufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers); + chunkBuffer = bufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers); break; } case FLUSH: { EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint(); - if (headerBuffer == null) - headerBuffer = BufferUtil.EMPTY_BUFFER; - if (chunkBuffer == null) - chunkBuffer = BufferUtil.EMPTY_BUFFER; - if (contentBuffer == null) - contentBuffer = BufferUtil.EMPTY_BUFFER; - long bytes = headerBuffer.remaining() + chunkBuffer.remaining() + contentBuffer.remaining(); + if (headerByteBuffer == null) + headerByteBuffer = BufferUtil.EMPTY_BUFFER; + if (chunkByteBuffer == null) + chunkByteBuffer = BufferUtil.EMPTY_BUFFER; + if (contentByteBuffer == null) + contentByteBuffer = BufferUtil.EMPTY_BUFFER; + long bytes = headerByteBuffer.remaining() + chunkByteBuffer.remaining() + contentByteBuffer.remaining(); getHttpChannel().getHttpConnection().addBytesOut(bytes); - endPoint.write(this, headerBuffer, chunkBuffer, contentBuffer); + endPoint.write(this, headerByteBuffer, chunkByteBuffer, contentByteBuffer); generated = true; return Action.SCHEDULED; } @@ -263,21 +266,19 @@ public class HttpSenderOverHTTP extends HttpSender private void release() { - HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient(); - ByteBufferPool bufferPool = httpClient.getByteBufferPool(); - if (!BufferUtil.isTheEmptyBuffer(headerBuffer)) - bufferPool.release(headerBuffer); + if (headerBuffer != null) + headerBuffer.release(); headerBuffer = null; - if (!BufferUtil.isTheEmptyBuffer(chunkBuffer)) - bufferPool.release(chunkBuffer); + if (chunkBuffer != null) + chunkBuffer.release(); chunkBuffer = null; - contentBuffer = null; + contentByteBuffer = null; } } private class ContentCallback extends IteratingCallback { - private ByteBuffer chunkBuffer; + private RetainableByteBuffer chunkBuffer; public ContentCallback() { @@ -288,14 +289,15 @@ public class HttpSenderOverHTTP extends HttpSender protected Action process() throws Exception { HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient(); - ByteBufferPool bufferPool = httpClient.getByteBufferPool(); + RetainableByteBufferPool bufferPool = httpClient.getRetainableByteBufferPool(); boolean useDirectByteBuffers = httpClient.isUseOutputDirectByteBuffers(); while (true) { - HttpGenerator.Result result = generator.generateRequest(null, null, chunkBuffer, contentBuffer, lastContent); + ByteBuffer chunkByteBuffer = chunkBuffer == null ? null : chunkBuffer.getByteBuffer(); + HttpGenerator.Result result = generator.generateRequest(null, null, chunkByteBuffer, contentByteBuffer, lastContent); if (LOG.isDebugEnabled()) LOG.debug("Generated content ({} bytes, last={}) - {}/{}", - contentBuffer == null ? -1 : contentBuffer.remaining(), + contentByteBuffer == null ? -1 : contentByteBuffer.remaining(), lastContent, result, generator); switch (result) { @@ -312,10 +314,10 @@ public class HttpSenderOverHTTP extends HttpSender case FLUSH: { EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint(); - if (chunkBuffer != null) - endPoint.write(this, chunkBuffer, contentBuffer); + if (chunkByteBuffer != null) + endPoint.write(this, chunkByteBuffer, contentByteBuffer); else - endPoint.write(this, contentBuffer); + endPoint.write(this, contentByteBuffer); return Action.SCHEDULED; } case SHUTDOWN_OUT: @@ -350,11 +352,10 @@ public class HttpSenderOverHTTP extends HttpSender private void release() { - HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient(); - ByteBufferPool bufferPool = httpClient.getByteBufferPool(); - bufferPool.release(chunkBuffer); + if (chunkBuffer != null) + chunkBuffer.release(); chunkBuffer = null; - contentBuffer = null; + contentByteBuffer = null; } } } diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java index 3083aaad235..c9824032702 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java @@ -27,9 +27,9 @@ import java.util.zip.GZIPOutputStream; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.Callback; @@ -242,9 +242,9 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest } }); - ByteBufferPool pool = client.getByteBufferPool(); - assumeTrue(pool instanceof MappedByteBufferPool); - MappedByteBufferPool bufferPool = (MappedByteBufferPool)pool; + RetainableByteBufferPool pool = client.getRetainableByteBufferPool(); + assumeTrue(pool instanceof ArrayRetainableByteBufferPool); + ArrayRetainableByteBufferPool bufferPool = (ArrayRetainableByteBufferPool)pool; ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scenario.getScheme()) @@ -254,9 +254,9 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest assertEquals(HttpStatus.OK_200, response.getStatus()); assertArrayEquals(content, response.getContent()); - long directMemory = bufferPool.getMemory(true); + long directMemory = bufferPool.getDirectMemory(); assertThat(directMemory, lessThan((long)content.length)); - long heapMemory = bufferPool.getMemory(false); + long heapMemory = bufferPool.getHeapMemory(); assertThat(heapMemory, lessThan((long)content.length)); } diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java index 28374c541c3..74cd82f04e0 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java @@ -44,9 +44,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ArrayByteBufferPool; import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.Connection; @@ -632,7 +630,7 @@ public class HttpClientTLSTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(connector.getRetainableByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected int networkFill(ByteBuffer input) throws IOException @@ -666,12 +664,12 @@ public class HttpClientTLSTest { if (sslContextFactory == null) sslContextFactory = getSslContextFactory(); - return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory) + return new SslClientConnectionFactory(sslContextFactory, getRetainableByteBufferPool(), getExecutor(), connectionFactory) { @Override - protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) + protected SslConnection newSslConnection(RetainableByteBufferPool bufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(byteBufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(bufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected int networkFill(ByteBuffer input) throws IOException @@ -707,50 +705,6 @@ public class HttpClientTLSTest assertEquals(0, clientBytes.get()); } - protected class TestRetained extends ArrayRetainableByteBufferPool - { - private final ByteBufferPool _pool; - - public TestRetained(ByteBufferPool pool, int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - super(0, factor, maxCapacity, maxBucketSize, retainedHeapMemory, retainedDirectMemory); - _pool = pool; - } - - @Override - protected ByteBuffer allocate(int capacity) - { - return _pool.acquire(capacity, false); - } - - @Override - protected ByteBuffer allocateDirect(int capacity) - { - return _pool.acquire(capacity, true); - } - - @Override - protected void removed(RetainableByteBuffer retainedBuffer) - { - _pool.release(retainedBuffer.getBuffer()); - } - - @Override - public Pool poolFor(int capacity, boolean direct) - { - return super.poolFor(capacity, direct); - } - } - - private class TestByteBufferPool extends ArrayByteBufferPool - { - @Override - protected RetainableByteBufferPool newRetainableByteBufferPool(int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - return new TestRetained(this, factor, maxCapacity, maxBucketSize, retainedHeapMemory, retainedDirectMemory); - } - } - @Test public void testEncryptedInputBufferRepooling() throws Exception { @@ -759,9 +713,6 @@ public class HttpClientTLSTest serverThreads.setName("server"); server = new Server(serverThreads); - ArrayByteBufferPool byteBufferPool = new TestByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = byteBufferPool.asRetainableByteBufferPool(); - server.addBean(byteBufferPool); HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.addCustomizer(new SecureRequestCustomizer()); HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); @@ -770,9 +721,8 @@ public class HttpClientTLSTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + RetainableByteBufferPool bufferPool = connector.getRetainableByteBufferPool(); + return new SslConnection(bufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected int networkFill(ByteBuffer input) throws IOException @@ -803,11 +753,12 @@ public class HttpClientTLSTest assertThrows(Exception.class, () -> client.newRequest("localhost", connector.getLocalPort()).scheme(HttpScheme.HTTPS.asString()).send()); - Pool bucket = ((TestRetained)retainableByteBufferPool).poolFor(16 * 1024 + 1, connector.getConnectionFactory(HttpConnectionFactory.class).isUseInputDirectByteBuffers()); + ArrayRetainableByteBufferPool bufferPool = (ArrayRetainableByteBufferPool)server.getRetainableByteBufferPool(); + Pool bucket = bufferPool.poolFor(16 * 1024 + 1, connector.getConnectionFactory(HttpConnectionFactory.class).isUseInputDirectByteBuffers()); assertEquals(1, bucket.size()); assertEquals(1, bucket.getIdleCount()); - long count = ssl.isDirectBuffersForDecryption() ? byteBufferPool.getDirectByteBufferCount() : byteBufferPool.getHeapByteBufferCount(); + long count = ssl.isDirectBuffersForDecryption() ? bufferPool.getDirectByteBufferCount() : bufferPool.getHeapByteBufferCount(); assertEquals(1, count); } @@ -817,26 +768,28 @@ public class HttpClientTLSTest SslContextFactory.Server serverTLSFactory = createServerSslContextFactory(); QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); - server = new Server(serverThreads); - List leakedBuffers = new CopyOnWriteArrayList<>(); - ArrayByteBufferPool byteBufferPool = new ArrayByteBufferPool() + List leakedBuffers = new CopyOnWriteArrayList<>(); + RetainableByteBufferPool bufferPool = new RetainableByteBufferPool.Wrapper(new ArrayRetainableByteBufferPool()) { @Override - public ByteBuffer acquire(int size, boolean direct) + public RetainableByteBuffer acquire(int size, boolean direct) { - ByteBuffer acquired = super.acquire(size, direct); - leakedBuffers.add(acquired); - return acquired; - } - - @Override - public void release(ByteBuffer buffer) - { - leakedBuffers.remove(buffer); - super.release(buffer); + RetainableByteBuffer.Wrapper buffer = new RetainableByteBuffer.Wrapper(super.acquire(size, direct)) + { + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + leakedBuffers.remove(this); + return released; + } + }; + leakedBuffers.add(buffer); + return buffer; } }; - server.addBean(byteBufferPool); + server = new Server(serverThreads, null, bufferPool); HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.addCustomizer(new SecureRequestCustomizer()); HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); @@ -845,9 +798,8 @@ public class HttpClientTLSTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + RetainableByteBufferPool bufferPool = connector.getRetainableByteBufferPool(); + return new SslConnection(bufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected boolean networkFlush(ByteBuffer output) throws IOException @@ -875,7 +827,7 @@ public class HttpClientTLSTest assertThrows(Exception.class, () -> client.newRequest("localhost", connector.getLocalPort()).scheme(HttpScheme.HTTPS.asString()).send()); - byteBufferPool.asRetainableByteBufferPool().clear(); + bufferPool.clear(); await().atMost(5, TimeUnit.SECONDS).until(() -> leakedBuffers, is(empty())); } @@ -886,26 +838,28 @@ public class HttpClientTLSTest SslContextFactory.Server serverTLSFactory = createServerSslContextFactory(); QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); - server = new Server(serverThreads); - List leakedBuffers = new CopyOnWriteArrayList<>(); - ArrayByteBufferPool byteBufferPool = new ArrayByteBufferPool() + List leakedBuffers = new CopyOnWriteArrayList<>(); + RetainableByteBufferPool bufferPool = new RetainableByteBufferPool.Wrapper(new ArrayRetainableByteBufferPool()) { @Override - public ByteBuffer acquire(int size, boolean direct) + public RetainableByteBuffer acquire(int size, boolean direct) { - ByteBuffer acquired = super.acquire(size, direct); - leakedBuffers.add(acquired); - return acquired; - } - - @Override - public void release(ByteBuffer buffer) - { - leakedBuffers.remove(buffer); - super.release(buffer); + RetainableByteBuffer.Wrapper buffer = new RetainableByteBuffer.Wrapper(super.acquire(size, direct)) + { + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + leakedBuffers.remove(this); + return released; + } + }; + leakedBuffers.add(buffer); + return buffer; } }; - server.addBean(byteBufferPool); + server = new Server(serverThreads, null, bufferPool); HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.addCustomizer(new SecureRequestCustomizer()); HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); @@ -915,9 +869,8 @@ public class HttpClientTLSTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + RetainableByteBufferPool bufferPool = connector.getRetainableByteBufferPool(); + return new SslConnection(bufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected boolean networkFlush(ByteBuffer output) throws IOException @@ -934,7 +887,7 @@ public class HttpClientTLSTest server.setHandler(new EmptyServerHandler() { @Override - protected void service(Request request, Response response) throws Throwable + protected void service(Request request, Response response) { failFlush.set(true); EndPoint endPoint = request.getConnectionMetaData().getConnection().getEndPoint(); @@ -959,7 +912,7 @@ public class HttpClientTLSTest assertThrows(Exception.class, () -> client.newRequest("localhost", connector.getLocalPort()).scheme(HttpScheme.HTTPS.asString()).send()); - byteBufferPool.asRetainableByteBufferPool().clear(); + bufferPool.clear(); await().atMost(5, TimeUnit.SECONDS).until(() -> leakedBuffers, is(empty())); } @@ -970,26 +923,28 @@ public class HttpClientTLSTest SslContextFactory.Server serverTLSFactory = createServerSslContextFactory(); QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); - server = new Server(serverThreads); - List leakedBuffers = new CopyOnWriteArrayList<>(); - ArrayByteBufferPool byteBufferPool = new ArrayByteBufferPool() + List leakedBuffers = new CopyOnWriteArrayList<>(); + RetainableByteBufferPool bufferPool = new RetainableByteBufferPool.Wrapper(new ArrayRetainableByteBufferPool()) { @Override - public ByteBuffer acquire(int size, boolean direct) + public RetainableByteBuffer acquire(int size, boolean direct) { - ByteBuffer acquired = super.acquire(size, direct); - leakedBuffers.add(acquired); - return acquired; - } - - @Override - public void release(ByteBuffer buffer) - { - leakedBuffers.remove(buffer); - super.release(buffer); + RetainableByteBuffer.Wrapper buffer = new RetainableByteBuffer.Wrapper(super.acquire(size, direct)) + { + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + leakedBuffers.remove(this); + return released; + } + }; + leakedBuffers.add(buffer); + return buffer; } }; - server.addBean(byteBufferPool); + server = new Server(serverThreads, null, bufferPool); HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.addCustomizer(new SecureRequestCustomizer()); HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); @@ -999,9 +954,8 @@ public class HttpClientTLSTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + RetainableByteBufferPool bufferPool = connector.getRetainableByteBufferPool(); + return new SslConnection(bufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected boolean networkFlush(ByteBuffer output) throws IOException @@ -1018,7 +972,7 @@ public class HttpClientTLSTest server.setHandler(new EmptyServerHandler() { @Override - protected void service(Request request, Response response) throws Throwable + protected void service(Request request, Response response) { failFlush.set(true); EndPoint endPoint = request.getConnectionMetaData().getConnection().getEndPoint(); @@ -1043,7 +997,7 @@ public class HttpClientTLSTest assertThrows(Exception.class, () -> client.newRequest("localhost", connector.getLocalPort()).scheme(HttpScheme.HTTPS.asString()).send()); - byteBufferPool.asRetainableByteBufferPool().clear(); + bufferPool.clear(); await().atMost(5, TimeUnit.SECONDS).until(() -> leakedBuffers, is(empty())); } @@ -1063,7 +1017,7 @@ public class HttpClientTLSTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(connector.getRetainableByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected int networkFill(ByteBuffer input) throws IOException @@ -1098,12 +1052,12 @@ public class HttpClientTLSTest { if (sslContextFactory == null) sslContextFactory = getSslContextFactory(); - return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory) + return new SslClientConnectionFactory(sslContextFactory, getRetainableByteBufferPool(), getExecutor(), connectionFactory) { @Override - protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) + protected SslConnection newSslConnection(RetainableByteBufferPool bufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(byteBufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(bufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected int networkFill(ByteBuffer input) throws IOException @@ -1160,12 +1114,12 @@ public class HttpClientTLSTest { if (sslContextFactory == null) sslContextFactory = getSslContextFactory(); - return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory) + return new SslClientConnectionFactory(sslContextFactory, getRetainableByteBufferPool(), getExecutor(), connectionFactory) { @Override - protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) + protected SslConnection newSslConnection(RetainableByteBufferPool bufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(byteBufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(bufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected SSLEngineResult wrap(SSLEngine sslEngine, ByteBuffer[] input, ByteBuffer output) throws SSLException @@ -1204,7 +1158,7 @@ public class HttpClientTLSTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(connector.getRetainableByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuffer output) throws SSLException @@ -1240,12 +1194,12 @@ public class HttpClientTLSTest { if (sslContextFactory == null) sslContextFactory = getSslContextFactory(); - return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory) + return new SslClientConnectionFactory(sslContextFactory, getRetainableByteBufferPool(), getExecutor(), connectionFactory) { @Override - protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) + protected SslConnection newSslConnection(RetainableByteBufferPool bufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(byteBufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(bufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected SSLEngineResult wrap(SSLEngine sslEngine, ByteBuffer[] input, ByteBuffer output) throws SSLException diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslConnectionTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslConnectionTest.java index b519f1af452..bfbd8a1e8a1 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslConnectionTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslConnectionTest.java @@ -19,10 +19,10 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLHandshakeException; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.ByteArrayEndPoint; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.BufferUtil; @@ -43,13 +43,13 @@ public class SslConnectionTest sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.start(); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); QueuedThreadPool threadPool = new QueuedThreadPool(); threadPool.start(); ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); SSLEngine sslEngine = sslContextFactory.newSSLEngine(); sslEngine.setUseClientMode(false); - SslConnection sslConnection = new SslConnection(byteBufferPool, threadPool, endPoint, sslEngine); + SslConnection sslConnection = new SslConnection(bufferPool, threadPool, endPoint, sslEngine); EndPoint sslEndPoint = sslConnection.getDecryptedEndPoint(); sslEndPoint.setConnection(new AbstractConnection(sslEndPoint, threadPool) { diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java index c43a57d58c2..198d054346f 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java @@ -283,7 +283,7 @@ public class MultiPartRequestContentTest extends AbstractHttpClientServerTest }); MultiPartRequestContent multiPart = new MultiPartRequestContent(); - PathRequestContent content = new PathRequestContent(contentType, tmpPath, client.getByteBufferPool().asRetainableByteBufferPool()); + PathRequestContent content = new PathRequestContent(contentType, tmpPath, client.getRetainableByteBufferPool()); content.setUseDirectByteBuffers(client.isUseOutputDirectByteBuffers()); multiPart.addPart(new MultiPart.ContentSourcePart(name, tmpPath.getFileName().toString(), null, content)); multiPart.close(); diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty.xml b/jetty-core/jetty-deploy/src/test/resources/jetty.xml index 5afa3f4249a..48fb93c25f0 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty.xml +++ b/jetty-core/jetty-deploy/src/test/resources/jetty.xml @@ -42,8 +42,8 @@ - diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpChannelOverFCGI.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpChannelOverFCGI.java index f45e1b762e3..13276396f49 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpChannelOverFCGI.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpChannelOverFCGI.java @@ -21,12 +21,13 @@ import org.eclipse.jetty.client.internal.HttpExchange; import org.eclipse.jetty.client.internal.HttpReceiver; import org.eclipse.jetty.client.internal.HttpSender; import org.eclipse.jetty.fcgi.generator.Flusher; -import org.eclipse.jetty.fcgi.generator.Generator; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.IdleTimeout; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -161,9 +162,9 @@ public class HttpChannelOverFCGI extends HttpChannel release(); } - protected void flush(Generator.Result... results) + protected void flush(RetainableByteBufferPool.Accumulator accumulator, Callback callback) { - flusher.flush(results); + flusher.flush(accumulator, callback); } void receive() diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpConnectionOverFCGI.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpConnectionOverFCGI.java index d91360e1505..7fe15be9075 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpConnectionOverFCGI.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpConnectionOverFCGI.java @@ -81,7 +81,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements IConne this.parser = new ClientParser(new ResponseListener()); requests.addLast(0); HttpClient client = destination.getHttpClient(); - this.networkByteBufferPool = client.getByteBufferPool().asRetainableByteBufferPool(); + this.networkByteBufferPool = client.getRetainableByteBufferPool(); } public HttpDestination getHttpDestination() @@ -161,14 +161,14 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements IConne { while (true) { - if (parse(networkBuffer.getBuffer())) + if (parse(networkBuffer.getByteBuffer())) return false; if (networkBuffer.isRetained()) reacquireNetworkBuffer(); // The networkBuffer may have been reacquired. - int read = endPoint.fill(networkBuffer.getBuffer()); + int read = endPoint.fill(networkBuffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("Read {} bytes from {}", read, endPoint); diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpSenderOverFCGI.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpSenderOverFCGI.java index 1ebf7391793..fd5fa644162 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpSenderOverFCGI.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/transport/internal/HttpSenderOverFCGI.java @@ -26,10 +26,10 @@ import org.eclipse.jetty.client.internal.HttpSender; import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.fcgi.client.transport.HttpClientTransportOverFCGI; import org.eclipse.jetty.fcgi.generator.ClientGenerator; -import org.eclipse.jetty.fcgi.generator.Generator; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Jetty; import org.eclipse.jetty.util.StringUtil; @@ -42,7 +42,7 @@ public class HttpSenderOverFCGI extends HttpSender { super(channel); HttpClient httpClient = channel.getHttpDestination().getHttpClient(); - this.generator = new ClientGenerator(httpClient.getByteBufferPool(), httpClient.isUseOutputDirectByteBuffers()); + this.generator = new ClientGenerator(httpClient.getRetainableByteBufferPool(), httpClient.isUseOutputDirectByteBuffers()); } @Override @@ -98,18 +98,18 @@ public class HttpSenderOverFCGI extends HttpSender HttpClientTransportOverFCGI transport = (HttpClientTransportOverFCGI)getHttpChannel().getHttpDestination().getHttpClient().getTransport(); transport.customize(request, fcgiHeaders); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); int id = getHttpChannel().getRequest(); if (contentBuffer.hasRemaining() || lastContent) { - Generator.Result headersResult = generator.generateRequestHeaders(id, fcgiHeaders, Callback.NOOP); - Generator.Result contentResult = generator.generateRequestContent(id, contentBuffer, lastContent, callback); - getHttpChannel().flush(headersResult, contentResult); + generator.generateRequestHeaders(accumulator, id, fcgiHeaders); + generator.generateRequestContent(accumulator, id, contentBuffer, lastContent); } else { - Generator.Result headersResult = generator.generateRequestHeaders(id, fcgiHeaders, callback); - getHttpChannel().flush(headersResult); + generator.generateRequestHeaders(accumulator, id, fcgiHeaders); } + getHttpChannel().flush(accumulator, callback); } @Override @@ -117,9 +117,10 @@ public class HttpSenderOverFCGI extends HttpSender { if (contentBuffer.hasRemaining() || lastContent) { + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); int request = getHttpChannel().getRequest(); - Generator.Result result = generator.generateRequestContent(request, contentBuffer, lastContent, callback); - getHttpChannel().flush(result); + generator.generateRequestContent(accumulator, request, contentBuffer, lastContent); + getHttpChannel().flush(accumulator, callback); } else { diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java index 205f1329468..7688c3fff65 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java @@ -22,9 +22,9 @@ import java.util.List; import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.Callback; public class ClientGenerator extends Generator { @@ -33,17 +33,17 @@ public class ClientGenerator extends Generator // 0x7F_FF - 4 (the 4 is to make room for the name (or value) length). public static final int MAX_PARAM_LENGTH = 0x7F_FF - 4; - public ClientGenerator(ByteBufferPool byteBufferPool) + public ClientGenerator(RetainableByteBufferPool bufferPool) { - this(byteBufferPool, true); + this(bufferPool, true); } - public ClientGenerator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers) + public ClientGenerator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers) { - super(byteBufferPool, useDirectByteBuffers); + super(bufferPool, useDirectByteBuffers); } - public Result generateRequestHeaders(int request, HttpFields fields, Callback callback) + public void generateRequestHeaders(RetainableByteBufferPool.Accumulator accumulator, int request, HttpFields fields) { request &= 0xFF_FF; @@ -79,30 +79,31 @@ public class ClientGenerator extends Generator // One FCGI_BEGIN_REQUEST + N FCGI_PARAMS + one last FCGI_PARAMS - ByteBuffer beginRequestBuffer = acquire(16); - BufferUtil.clearToFill(beginRequestBuffer); - Result result = new Result(getByteBufferPool(), callback); - result = result.append(beginRequestBuffer, true); + RetainableByteBuffer beginBuffer = getRetainableByteBufferPool().acquire(16, isUseDirectByteBuffers()); + accumulator.append(beginBuffer); + ByteBuffer beginByteBuffer = beginBuffer.getByteBuffer(); + BufferUtil.clearToFill(beginByteBuffer); // Generate the FCGI_BEGIN_REQUEST frame - beginRequestBuffer.putInt(0x01_01_00_00 + request); - beginRequestBuffer.putInt(0x00_08_00_00); + beginByteBuffer.putInt(0x01_01_00_00 + request); + beginByteBuffer.putInt(0x00_08_00_00); // Hardcode RESPONDER role and KEEP_ALIVE flag - beginRequestBuffer.putLong(0x00_01_01_00_00_00_00_00L); - BufferUtil.flipToFlush(beginRequestBuffer, 0); + beginByteBuffer.putLong(0x00_01_01_00_00_00_00_00L); + BufferUtil.flipToFlush(beginByteBuffer, 0); int index = 0; while (fieldsLength > 0) { int capacity = 8 + Math.min(maxCapacity, fieldsLength); - ByteBuffer buffer = acquire(capacity); - BufferUtil.clearToFill(buffer); - result = result.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(capacity, isUseDirectByteBuffers()); + accumulator.append(buffer); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); // Generate the FCGI_PARAMS frame - buffer.putInt(0x01_04_00_00 + request); - buffer.putShort((short)0); - buffer.putShort((short)0); + byteBuffer.putInt(0x01_04_00_00 + request); + byteBuffer.putShort((short)0); + byteBuffer.putShort((short)0); capacity -= 8; int length = 0; @@ -117,10 +118,10 @@ public class ClientGenerator extends Generator if (required > capacity) break; - putParamLength(buffer, nameLength); - putParamLength(buffer, valueLength); - buffer.put(nameBytes); - buffer.put(valueBytes); + putParamLength(byteBuffer, nameLength); + putParamLength(byteBuffer, valueLength); + byteBuffer.put(nameBytes); + byteBuffer.put(valueBytes); length += required; fieldsLength -= required; @@ -128,20 +129,19 @@ public class ClientGenerator extends Generator index += 2; } - buffer.putShort(4, (short)length); - BufferUtil.flipToFlush(buffer, 0); + byteBuffer.putShort(4, (short)length); + BufferUtil.flipToFlush(byteBuffer, 0); } - ByteBuffer lastParamsBuffer = acquire(8); - BufferUtil.clearToFill(lastParamsBuffer); - result = result.append(lastParamsBuffer, true); + RetainableByteBuffer lastBuffer = getRetainableByteBufferPool().acquire(8, isUseDirectByteBuffers()); + accumulator.append(lastBuffer); + ByteBuffer lastByteBuffer = lastBuffer.getByteBuffer(); + BufferUtil.clearToFill(lastByteBuffer); // Generate the last FCGI_PARAMS frame - lastParamsBuffer.putInt(0x01_04_00_00 + request); - lastParamsBuffer.putInt(0x00_00_00_00); - BufferUtil.flipToFlush(lastParamsBuffer, 0); - - return result; + lastByteBuffer.putInt(0x01_04_00_00 + request); + lastByteBuffer.putInt(0x00_00_00_00); + BufferUtil.flipToFlush(lastByteBuffer, 0); } private int putParamLength(ByteBuffer buffer, int length) @@ -159,8 +159,8 @@ public class ClientGenerator extends Generator return length > 127 ? 4 : 1; } - public Result generateRequestContent(int request, ByteBuffer content, boolean lastContent, Callback callback) + public void generateRequestContent(RetainableByteBufferPool.Accumulator accumulator, int request, ByteBuffer content, boolean lastContent) { - return generateContent(request, content, false, lastContent, callback, FCGI.FrameType.STDIN); + generateContent(accumulator, request, content, lastContent, FCGI.FrameType.STDIN); } } diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java index 49b2865f3a3..58ebcb0e74d 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java @@ -15,9 +15,12 @@ package org.eclipse.jetty.fcgi.generator; import java.nio.ByteBuffer; import java.util.ArrayDeque; +import java.util.List; import java.util.Queue; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.thread.AutoLock; import org.slf4j.Logger; @@ -28,7 +31,7 @@ public class Flusher private static final Logger LOG = LoggerFactory.getLogger(Flusher.class); private final AutoLock lock = new AutoLock(); - private final Queue queue = new ArrayDeque<>(); + private final Queue queue = new ArrayDeque<>(); private final IteratingCallback flushCallback = new FlushCallback(); private final EndPoint endPoint; @@ -37,24 +40,21 @@ public class Flusher this.endPoint = endPoint; } - public void flush(Generator.Result... results) + public void flush(RetainableByteBufferPool.Accumulator accumulator, Callback callback) { - for (Generator.Result result : results) - { - offer(result); - } + offer(new Entry(accumulator, callback)); flushCallback.iterate(); } - private void offer(Generator.Result result) + private void offer(Entry entry) { try (AutoLock ignored = lock.lock()) { - queue.offer(result); + queue.offer(entry); } } - private Generator.Result poll() + private Entry poll() { try (AutoLock ignored = lock.lock()) { @@ -64,35 +64,32 @@ public class Flusher public void shutdown() { - flush(new ShutdownResult()); + flush(null, Callback.from(() -> + { + if (LOG.isDebugEnabled()) + LOG.debug("Shutting down {}", endPoint); + endPoint.shutdownOutput(); + })); } private class FlushCallback extends IteratingCallback { - private Generator.Result active; + private Entry active; @Override protected Action process() throws Exception { // Look if other writes are needed. - Generator.Result result = poll(); - if (result == null) + Entry entry = poll(); + if (entry == null) { // No more writes to do, return. return Action.IDLE; } - // Attempt to gather another result. - // Most often there is another result in the - // queue so this is a real optimization because - // it sends both results in just one TCP packet. - Generator.Result other = poll(); - if (other != null) - result = result.join(other); - - active = result; - ByteBuffer[] buffers = result.getByteBuffers(); - endPoint.write(this, buffers); + active = entry; + List buffers = entry.accumulator.getByteBuffers(); + endPoint.write(this, buffers.toArray(ByteBuffer[]::new)); return Action.SCHEDULED; } @@ -121,38 +118,30 @@ public class Flusher while (true) { - Generator.Result result = poll(); - if (result == null) + Entry entry = poll(); + if (entry == null) break; - result.failed(x); + entry.failed(x); } } } - private class ShutdownResult extends Generator.Result + private record Entry(RetainableByteBufferPool.Accumulator accumulator, Callback callback) implements Callback { - private ShutdownResult() - { - super(null, null); - } - @Override public void succeeded() { - shutdown(); + if (accumulator != null) + accumulator.release(); + callback.succeeded(); } @Override public void failed(Throwable x) { - shutdown(); - } - - private void shutdown() - { - if (LOG.isDebugEnabled()) - LOG.debug("Shutting down {}", endPoint); - endPoint.shutdownOutput(); + if (accumulator != null) + accumulator.release(); + callback.failed(x); } } } diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java index 72e5ae2d513..9031b2c8eed 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java @@ -14,150 +14,69 @@ package org.eclipse.jetty.fcgi.generator; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; import org.eclipse.jetty.fcgi.FCGI; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.Callback; public class Generator { public static final int MAX_CONTENT_LENGTH = 0xFF_FF; - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private final boolean useDirectByteBuffers; - public Generator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers) + public Generator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers) { - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; this.useDirectByteBuffers = useDirectByteBuffers; } - ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return byteBufferPool; + return bufferPool; } - ByteBuffer acquire(int capacity) + public boolean isUseDirectByteBuffers() { - return byteBufferPool.acquire(capacity, useDirectByteBuffers); + return useDirectByteBuffers; } - protected Result generateContent(int id, ByteBuffer content, boolean recycle, boolean lastContent, Callback callback, FCGI.FrameType frameType) + protected void generateContent(RetainableByteBufferPool.Accumulator accumulator, int id, ByteBuffer content, boolean lastContent, FCGI.FrameType frameType) { id &= 0xFF_FF; int contentLength = content == null ? 0 : content.remaining(); - Result result = new Result(byteBufferPool, callback); while (contentLength > 0 || lastContent) { - ByteBuffer buffer = acquire(8); - BufferUtil.clearToFill(buffer); - result = result.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(8, isUseDirectByteBuffers()); + accumulator.append(buffer); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); - // Generate the frame header - buffer.put((byte)0x01); - buffer.put((byte)frameType.code); - buffer.putShort((short)id); + // Generate the frame header. + byteBuffer.put((byte)0x01); + byteBuffer.put((byte)frameType.code); + byteBuffer.putShort((short)id); int length = Math.min(MAX_CONTENT_LENGTH, contentLength); - buffer.putShort((short)length); - buffer.putShort((short)0); - BufferUtil.flipToFlush(buffer, 0); + byteBuffer.putShort((short)length); + byteBuffer.putShort((short)0); + BufferUtil.flipToFlush(byteBuffer, 0); if (contentLength == 0) break; - // Slice the content to avoid copying + // Slice the content to avoid copying. int limit = content.limit(); content.limit(content.position() + length); ByteBuffer slice = content.slice(); - // Don't recycle the slice - result = result.append(slice, false); + // Don't recycle the slice. + accumulator.append(RetainableByteBuffer.wrap(slice)); content.position(content.limit()); content.limit(limit); contentLength -= length; - // Recycle the content buffer if needed - if (recycle && contentLength == 0) - result = result.append(content, true); - } - - return result; - } - - // TODO: rewrite this class in light of ByteBufferPool.Lease. - public static class Result implements Callback - { - private final List callbacks = new ArrayList<>(2); - private final List buffers = new ArrayList<>(8); - private final List recycles = new ArrayList<>(8); - private final ByteBufferPool byteBufferPool; - - public Result(ByteBufferPool byteBufferPool, Callback callback) - { - this.byteBufferPool = byteBufferPool; - this.callbacks.add(callback); - } - - public Result append(ByteBuffer buffer, boolean recycle) - { - if (buffer != null) - { - buffers.add(buffer); - recycles.add(recycle); - } - return this; - } - - public Result join(Result that) - { - callbacks.addAll(that.callbacks); - buffers.addAll(that.buffers); - recycles.addAll(that.recycles); - return this; - } - - public ByteBuffer[] getByteBuffers() - { - return buffers.toArray(new ByteBuffer[0]); - } - - @Override - @SuppressWarnings("ForLoopReplaceableByForEach") - public void succeeded() - { - recycle(); - for (int i = 0; i < callbacks.size(); ++i) - { - Callback callback = callbacks.get(i); - if (callback != null) - callback.succeeded(); - } - } - - @Override - @SuppressWarnings("ForLoopReplaceableByForEach") - public void failed(Throwable x) - { - recycle(); - for (int i = 0; i < callbacks.size(); ++i) - { - Callback callback = callbacks.get(i); - if (callback != null) - callback.failed(x); - } - } - - protected void recycle() - { - for (int i = 0; i < buffers.size(); ++i) - { - ByteBuffer buffer = buffers.get(i); - if (recycles.get(i)) - byteBufferPool.release(buffer); - } } } } diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java index cd75698f92c..de752e0335f 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java @@ -23,9 +23,9 @@ import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.Callback; public class ServerGenerator extends Generator { @@ -35,18 +35,18 @@ public class ServerGenerator extends Generator private final boolean sendStatus200; - public ServerGenerator(ByteBufferPool byteBufferPool) + public ServerGenerator(RetainableByteBufferPool bufferPool) { - this(byteBufferPool, true, true); + this(bufferPool, true, true); } - public ServerGenerator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers, boolean sendStatus200) + public ServerGenerator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers, boolean sendStatus200) { - super(byteBufferPool, useDirectByteBuffers); + super(bufferPool, useDirectByteBuffers); this.sendStatus200 = sendStatus200; } - public Result generateResponseHeaders(int request, int code, String reason, HttpFields fields, Callback callback) + public void generateResponseHeaders(RetainableByteBufferPool.Accumulator accumulator, int request, int code, String reason, HttpFields fields) { request &= 0xFF_FF; @@ -83,50 +83,48 @@ public class ServerGenerator extends Generator // End of headers length += EOL.length; - ByteBuffer buffer = acquire(length); - BufferUtil.clearToFill(buffer); + ByteBuffer byteBuffer = BufferUtil.allocate(length, isUseDirectByteBuffers()); + BufferUtil.clearToFill(byteBuffer); for (int i = 0; i < bytes.size(); i += 2) { - buffer.put(bytes.get(i)).put(COLON).put(bytes.get(i + 1)).put(EOL); + byteBuffer.put(bytes.get(i)).put(COLON).put(bytes.get(i + 1)).put(EOL); } - buffer.put(EOL); + byteBuffer.put(EOL); - BufferUtil.flipToFlush(buffer, 0); + BufferUtil.flipToFlush(byteBuffer, 0); - return generateContent(request, buffer, true, false, callback, FCGI.FrameType.STDOUT); + generateContent(accumulator, request, byteBuffer, false, FCGI.FrameType.STDOUT); } - public Result generateResponseContent(int request, ByteBuffer content, boolean lastContent, boolean aborted, Callback callback) + public void generateResponseContent(RetainableByteBufferPool.Accumulator accumulator, int request, ByteBuffer content, boolean lastContent, boolean aborted) { if (aborted) { - Result result = new Result(getByteBufferPool(), callback); if (lastContent) - result.append(generateEndRequest(request, true), true); + accumulator.append(generateEndRequest(request, true)); else - result.append(BufferUtil.EMPTY_BUFFER, false); - return result; + accumulator.append(RetainableByteBuffer.wrap(BufferUtil.EMPTY_BUFFER)); } else { - Result result = generateContent(request, content, false, lastContent, callback, FCGI.FrameType.STDOUT); + generateContent(accumulator, request, content, lastContent, FCGI.FrameType.STDOUT); if (lastContent) - result.append(generateEndRequest(request, false), true); - return result; + accumulator.append(generateEndRequest(request, false)); } } - private ByteBuffer generateEndRequest(int request, boolean aborted) + private RetainableByteBuffer generateEndRequest(int request, boolean aborted) { request &= 0xFF_FF; - ByteBuffer endRequestBuffer = acquire(16); - BufferUtil.clearToFill(endRequestBuffer); - endRequestBuffer.putInt(0x01_03_00_00 + request); - endRequestBuffer.putInt(0x00_08_00_00); - endRequestBuffer.putInt(aborted ? 1 : 0); - endRequestBuffer.putInt(0); - BufferUtil.flipToFlush(endRequestBuffer, 0); + RetainableByteBuffer endRequestBuffer = getRetainableByteBufferPool().acquire(16, isUseDirectByteBuffers()); + ByteBuffer byteBuffer = endRequestBuffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + byteBuffer.putInt(0x01_03_00_00 + request); + byteBuffer.putInt(0x00_08_00_00); + byteBuffer.putInt(aborted ? 1 : 0); + byteBuffer.putInt(0); + BufferUtil.flipToFlush(byteBuffer, 0); return endRequestBuffer; } } diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/generator/ClientGeneratorTest.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/generator/ClientGeneratorTest.java index 27a65fd10ad..4ffe773ba97 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/generator/ClientGeneratorTest.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/generator/ClientGeneratorTest.java @@ -21,8 +21,8 @@ import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.fcgi.parser.ServerParser; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,50 +31,51 @@ import static org.junit.jupiter.api.Assertions.assertFalse; public class ClientGeneratorTest { @Test - public void testGenerateRequestHeaders() throws Exception + public void testGenerateRequestHeaders() { HttpFields.Mutable fields = HttpFields.build(); // Short name, short value final String shortShortName = "REQUEST_METHOD"; - final String shortShortValue = "GET"; + String shortShortValue = "GET"; fields.put(new HttpField(shortShortName, shortShortValue)); // Short name, long value final String shortLongName = "REQUEST_URI"; // Be sure it's longer than 127 chars to test the large value - final String shortLongValue = "/api/0.6/map?bbox=-64.217736,-31.456810,-64.187736,-31.432322,filler=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + String shortLongValue = "/api/0.6/map?bbox=-64.217736,-31.456810,-64.187736,-31.432322,filler=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; fields.put(new HttpField(shortLongName, shortLongValue)); // Long name, short value // Be sure it's longer than 127 chars to test the large name final String longShortName = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210"; - final String longShortValue = "api.openstreetmap.org"; + String longShortValue = "api.openstreetmap.org"; fields.put(new HttpField(longShortName, longShortValue)); // Long name, long value char[] chars = new char[ClientGenerator.MAX_PARAM_LENGTH]; Arrays.fill(chars, 'z'); - final String longLongName = new String(chars); - final String longLongValue = new String(chars); + String longLongName = new String(chars); + String longLongValue = new String(chars); fields.put(new HttpField(longLongName, longLongValue)); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - ClientGenerator generator = new ClientGenerator(byteBufferPool); - final int id = 13; - Generator.Result result = generator.generateRequestHeaders(id, fields, null); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + ClientGenerator generator = new ClientGenerator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + int id = 13; + generator.generateRequestHeaders(accumulator, id, fields); // Use the fundamental theorem of arithmetic to test the results. // This way we know onHeader() has been called the right number of // times with the right arguments, and so onHeaders(). - final int[] primes = new int[]{2, 3, 5, 7, 11}; + int[] primes = new int[]{2, 3, 5, 7, 11}; int value = 1; for (int prime : primes) { value *= prime; } - final AtomicInteger params = new AtomicInteger(1); + AtomicInteger params = new AtomicInteger(1); ServerParser parser = new ServerParser(new ServerParser.Listener.Adapter() { @Override @@ -83,23 +84,27 @@ public class ClientGeneratorTest assertEquals(id, request); switch (field.getName()) { - case shortShortName: + case shortShortName -> + { assertEquals(shortShortValue, field.getValue()); params.set(params.get() * primes[0]); - break; - case shortLongName: + } + case shortLongName -> + { assertEquals(shortLongValue, field.getValue()); params.set(params.get() * primes[1]); - break; - case longShortName: + } + case longShortName -> + { assertEquals(longShortValue, field.getValue()); params.set(params.get() * primes[2]); - break; - default: + } + default -> + { assertEquals(longLongName, field.getName()); assertEquals(longLongValue, field.getValue()); params.set(params.get() * primes[3]); - break; + } } } @@ -112,7 +117,7 @@ public class ClientGeneratorTest } }); - for (ByteBuffer buffer : result.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); @@ -122,17 +127,18 @@ public class ClientGeneratorTest // Parse again byte by byte params.set(1); - for (ByteBuffer buffer : result.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { buffer.flip(); while (buffer.hasRemaining()) { parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()})); } - assertFalse(buffer.hasRemaining()); } assertEquals(value, params.get()); + + accumulator.release(); } @Test @@ -147,16 +153,17 @@ public class ClientGeneratorTest testGenerateRequestContent(128 * 1024); } - private void testGenerateRequestContent(final int contentLength) throws Exception + private void testGenerateRequestContent(int contentLength) throws Exception { ByteBuffer content = ByteBuffer.allocate(contentLength); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - ClientGenerator generator = new ClientGenerator(byteBufferPool); - final int id = 13; - Generator.Result result = generator.generateRequestContent(id, content, true, null); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + ClientGenerator generator = new ClientGenerator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + int id = 13; + generator.generateRequestContent(accumulator, id, content, true); - final AtomicInteger totalLength = new AtomicInteger(); + AtomicInteger totalLength = new AtomicInteger(); ServerParser parser = new ServerParser(new ServerParser.Listener.Adapter() { @Override @@ -175,21 +182,22 @@ public class ClientGeneratorTest } }); - for (ByteBuffer buffer : result.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); } // Parse again one byte at a time - for (ByteBuffer buffer : result.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { buffer.flip(); while (buffer.hasRemaining()) { parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()})); } - assertFalse(buffer.hasRemaining()); } + + accumulator.release(); } } diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java index cd673377953..64f2b48e52a 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java @@ -19,12 +19,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.fcgi.FCGI; -import org.eclipse.jetty.fcgi.generator.Generator; import org.eclipse.jetty.fcgi.generator.ServerGenerator; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -34,32 +33,33 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ClientParserTest { @Test - public void testParseResponseHeaders() throws Exception + public void testParseResponseHeaders() { - final int id = 13; + int id = 13; HttpFields.Mutable fields = HttpFields.build(); - final int statusCode = 200; - final String statusMessage = "OK"; - final String contentTypeName = "Content-Type"; - final String contentTypeValue = "text/html;charset=utf-8"; + int statusCode = 200; + String statusMessage = "OK"; + String contentTypeName = "Content-Type"; + String contentTypeValue = "text/html;charset=utf-8"; fields.put(contentTypeName, contentTypeValue); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - ServerGenerator generator = new ServerGenerator(byteBufferPool); - Generator.Result result = generator.generateResponseHeaders(id, statusCode, statusMessage, fields, null); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + ServerGenerator generator = new ServerGenerator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateResponseHeaders(accumulator, id, statusCode, statusMessage, fields); // Use the fundamental theorem of arithmetic to test the results. // This way we know onHeader() has been called the right number of // times with the right arguments, and so onHeaders(). - final int[] primes = new int[]{2, 3, 5}; + int[] primes = new int[]{2, 3, 5}; int value = 1; for (int prime : primes) { value *= prime; } - final AtomicInteger params = new AtomicInteger(1); + AtomicInteger params = new AtomicInteger(1); ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter() { @Override @@ -74,14 +74,10 @@ public class ClientParserTest public void onHeader(int request, HttpField field) { assertEquals(id, request); - switch (field.getName()) + if (field.getName().equals(contentTypeName)) { - case contentTypeName: - assertEquals(contentTypeValue, field.getValue().toLowerCase(Locale.ENGLISH)); - params.set(params.get() * primes[1]); - break; - default: - break; + assertEquals(contentTypeValue, field.getValue().toLowerCase(Locale.ENGLISH)); + params.set(params.get() * primes[1]); } } @@ -94,28 +90,31 @@ public class ClientParserTest } }); - for (ByteBuffer buffer : result.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); } assertEquals(value, params.get()); + + accumulator.release(); } @Test public void testParseNoResponseContent() throws Exception { - final int id = 13; + int id = 13; HttpFields fields = HttpFields.build() .put("Content-Length", "0"); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - ServerGenerator generator = new ServerGenerator(byteBufferPool); - Generator.Result result1 = generator.generateResponseHeaders(id, 200, "OK", fields, null); - Generator.Result result2 = generator.generateResponseContent(id, null, true, false, null); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + ServerGenerator generator = new ServerGenerator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateResponseHeaders(accumulator, id, 200, "OK", fields); + generator.generateResponseContent(accumulator, id, null, true, false); - final AtomicInteger verifier = new AtomicInteger(); + AtomicInteger verifier = new AtomicInteger(); ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter() { @Override @@ -134,40 +133,38 @@ public class ClientParserTest } }); - for (ByteBuffer buffer : result1.getByteBuffers()) - { - parser.parse(buffer); - assertFalse(buffer.hasRemaining()); - } - for (ByteBuffer buffer : result2.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); } assertEquals(3, verifier.get()); + + accumulator.release(); } @Test public void testParseSmallResponseContent() throws Exception { - final int id = 13; + int id = 13; HttpFields.Mutable fields = HttpFields.build(); ByteBuffer content = ByteBuffer.wrap(new byte[1024]); - final int contentLength = content.remaining(); + int contentLength = content.remaining(); - final int code = 200; - final String contentTypeName = "Content-Length"; - final String contentTypeValue = String.valueOf(contentLength); + int code = 200; + String contentTypeName = "Content-Length"; + String contentTypeValue = String.valueOf(contentLength); fields.put(contentTypeName, contentTypeValue); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - ServerGenerator generator = new ServerGenerator(byteBufferPool); - Generator.Result result1 = generator.generateResponseHeaders(id, code, "OK", fields, null); - Generator.Result result2 = generator.generateResponseContent(id, content, true, false, null); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + ServerGenerator generator = new ServerGenerator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateResponseHeaders(accumulator, id, code, "OK", fields); + generator.generateResponseContent(accumulator, id, content, true, false); - final AtomicInteger verifier = new AtomicInteger(); + AtomicInteger verifier = new AtomicInteger(); ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter() { @Override @@ -187,41 +184,39 @@ public class ClientParserTest } }); - for (ByteBuffer buffer : result1.getByteBuffers()) - { - parser.parse(buffer); - assertFalse(buffer.hasRemaining()); - } - for (ByteBuffer buffer : result2.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); } assertEquals(5, verifier.get()); + + accumulator.release(); } @Test public void testParseLargeResponseContent() throws Exception { - final int id = 13; + int id = 13; HttpFields.Mutable fields = HttpFields.build(); ByteBuffer content = ByteBuffer.wrap(new byte[128 * 1024]); - final int contentLength = content.remaining(); + int contentLength = content.remaining(); - final int code = 200; - final String contentTypeName = "Content-Length"; - final String contentTypeValue = String.valueOf(contentLength); + int code = 200; + String contentTypeName = "Content-Length"; + String contentTypeValue = String.valueOf(contentLength); fields.put(contentTypeName, contentTypeValue); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - ServerGenerator generator = new ServerGenerator(byteBufferPool); - Generator.Result result1 = generator.generateResponseHeaders(id, code, "OK", fields, null); - Generator.Result result2 = generator.generateResponseContent(id, content, true, false, null); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + ServerGenerator generator = new ServerGenerator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateResponseHeaders(accumulator, id, code, "OK", fields); + generator.generateResponseContent(accumulator, id, content, true, false); - final AtomicInteger totalLength = new AtomicInteger(); - final AtomicBoolean verifier = new AtomicBoolean(); + AtomicInteger totalLength = new AtomicInteger(); + AtomicBoolean verifier = new AtomicBoolean(); ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter() { @Override @@ -241,17 +236,14 @@ public class ClientParserTest } }); - for (ByteBuffer buffer : result1.getByteBuffers()) - { - parser.parse(buffer); - assertFalse(buffer.hasRemaining()); - } - for (ByteBuffer buffer : result2.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); } assertTrue(verifier.get()); + + accumulator.release(); } } diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/HttpStreamOverFCGI.java b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/HttpStreamOverFCGI.java index 885d6924c87..964ade042da 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/HttpStreamOverFCGI.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/HttpStreamOverFCGI.java @@ -18,7 +18,6 @@ import java.util.Locale; import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.fcgi.generator.Flusher; -import org.eclipse.jetty.fcgi.generator.Generator; import org.eclipse.jetty.fcgi.generator.ServerGenerator; import org.eclipse.jetty.http.HostPortHttpField; import org.eclipse.jetty.http.HttpField; @@ -30,6 +29,7 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpStream; import org.eclipse.jetty.util.BufferUtil; @@ -229,8 +229,9 @@ public class HttpStreamOverFCGI implements HttpStream { if (last) { - Generator.Result result = generateResponseContent(true, BufferUtil.EMPTY_BUFFER, callback); - flusher.flush(result); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generateResponseContent(accumulator, true, BufferUtil.EMPTY_BUFFER); + flusher.flush(accumulator, callback); } else { @@ -240,8 +241,9 @@ public class HttpStreamOverFCGI implements HttpStream } else { - Generator.Result result = generateResponseContent(last, content, callback); - flusher.flush(result); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generateResponseContent(accumulator, last, content); + flusher.flush(accumulator, callback); } if (last && _shutdown) @@ -258,40 +260,42 @@ public class HttpStreamOverFCGI implements HttpStream boolean shutdown = _shutdown = info.getFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); + RetainableByteBufferPool bufferPool = _generator.getRetainableByteBufferPool(); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); Flusher flusher = _connection.getFlusher(); if (head) { if (last) { - Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP); - Generator.Result contentResult = generateResponseContent(true, BufferUtil.EMPTY_BUFFER, callback); - flusher.flush(headersResult, contentResult); + generateResponseHeaders(accumulator, info); + generateResponseContent(accumulator, true, BufferUtil.EMPTY_BUFFER); + flusher.flush(accumulator, callback); } else { - Generator.Result headersResult = generateResponseHeaders(info, callback); - flusher.flush(headersResult); + generateResponseHeaders(accumulator, info); + flusher.flush(accumulator, callback); } } else { - Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP); - Generator.Result contentResult = generateResponseContent(last, content, callback); - flusher.flush(headersResult, contentResult); + generateResponseHeaders(accumulator, info); + generateResponseContent(accumulator, last, content); + flusher.flush(accumulator, callback); } if (last && shutdown) flusher.shutdown(); } - private Generator.Result generateResponseHeaders(MetaData.Response info, Callback callback) + private void generateResponseHeaders(RetainableByteBufferPool.Accumulator accumulator, MetaData.Response info) { - return _generator.generateResponseHeaders(_id, info.getStatus(), info.getReason(), info.getFields(), callback); + _generator.generateResponseHeaders(accumulator, _id, info.getStatus(), info.getReason(), info.getFields()); } - private Generator.Result generateResponseContent(boolean last, ByteBuffer buffer, Callback callback) + private void generateResponseContent(RetainableByteBufferPool.Accumulator accumulator, boolean last, ByteBuffer buffer) { - return _generator.generateResponseContent(_id, buffer, last, _aborted, callback); + _generator.generateResponseContent(accumulator, _id, buffer, last, _aborted); } @Override diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java index c6bc575bfc0..c1c6fc75eba 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java @@ -63,7 +63,7 @@ public class ServerFCGIConnection extends AbstractConnection implements Connecti { super(endPoint, connector.getExecutor()); this.connector = connector; - this.networkByteBufferPool = connector.getByteBufferPool().asRetainableByteBufferPool(); + this.networkByteBufferPool = connector.getRetainableByteBufferPool(); this.flusher = new Flusher(endPoint); this.configuration = configuration; this.sendStatus200 = sendStatus200; @@ -212,7 +212,7 @@ public class ServerFCGIConnection extends AbstractConnection implements Connecti LOG.debug("Read {} bytes from {} {}", read, getEndPoint(), this); if (read > 0) { - if (parse(networkBuffer.getBuffer())) + if (parse(networkBuffer.getByteBuffer())) return; } else if (read == 0) @@ -252,7 +252,7 @@ public class ServerFCGIConnection extends AbstractConnection implements Connecti // See also HttpConnection.parseAndFillForContent(). while (stream != null) { - if (parse(networkBuffer.getBuffer())) + if (parse(networkBuffer.getByteBuffer())) return; // Check if the request was completed by the parsing. if (stream == null) @@ -281,7 +281,7 @@ public class ServerFCGIConnection extends AbstractConnection implements Connecti { try { - return getEndPoint().fill(networkBuffer.getBuffer()); + return getEndPoint().fill(networkBuffer.getByteBuffer()); } catch (Throwable x) { @@ -333,7 +333,7 @@ public class ServerFCGIConnection extends AbstractConnection implements Connecti if (stream != null) throw new UnsupportedOperationException("FastCGI Multiplexing"); HttpChannel channel = httpChannelFactory.newHttpChannel(ServerFCGIConnection.this); - ServerGenerator generator = new ServerGenerator(connector.getByteBufferPool(), isUseOutputDirectByteBuffers(), sendStatus200); + ServerGenerator generator = new ServerGenerator(connector.getRetainableByteBufferPool(), isUseOutputDirectByteBuffers(), sendStatus200); stream = new HttpStreamOverFCGI(ServerFCGIConnection.this, generator, channel, request); channel.setHttpStream(stream); if (LOG.isDebugEnabled()) diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java index 6c202372cbb..d35e81805b5 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java @@ -21,10 +21,9 @@ import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.LeakTrackingConnectionPool; import org.eclipse.jetty.fcgi.client.transport.HttpClientTransportOverFCGI; import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.ClientConnector; -import org.eclipse.jetty.io.LeakTrackingByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.Server; @@ -39,8 +38,8 @@ import static org.hamcrest.MatcherAssert.assertThat; public abstract class AbstractHttpClientServerTest { - private LeakTrackingByteBufferPool serverBufferPool; - protected ByteBufferPool clientBufferPool; + private RetainableByteBufferPool serverBufferPool; + protected RetainableByteBufferPool clientBufferPool; private final AtomicLong connectionLeaks = new AtomicLong(); protected Server server; protected ServerConnector connector; @@ -53,7 +52,8 @@ public abstract class AbstractHttpClientServerTest serverThreads.setName("server"); server = new Server(serverThreads); ServerFCGIConnectionFactory fcgiConnectionFactory = new ServerFCGIConnectionFactory(new HttpConfiguration()); - serverBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + // TODO: restore leak tracking. + serverBufferPool = new ArrayRetainableByteBufferPool(); connector = new ServerConnector(server, null, null, serverBufferPool, 1, Math.max(1, ProcessorUtils.availableProcessors() / 2), fcgiConnectionFactory); server.addConnector(connector); @@ -65,9 +65,10 @@ public abstract class AbstractHttpClientServerTest QueuedThreadPool clientThreads = new QueuedThreadPool(); clientThreads.setName("client"); clientConnector.setExecutor(clientThreads); + // TODO: restore leak tracking. if (clientBufferPool == null) - clientBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); - clientConnector.setByteBufferPool(clientBufferPool); + clientBufferPool = new ArrayRetainableByteBufferPool(); + clientConnector.setRetainableByteBufferPool(clientBufferPool); HttpClientTransport transport = new HttpClientTransportOverFCGI(clientConnector, ""); transport.setConnectionPoolFactory(destination -> new LeakTrackingConnectionPool(destination, client.getMaxConnectionsPerDestination()) { @@ -86,22 +87,6 @@ public abstract class AbstractHttpClientServerTest { System.gc(); - if (serverBufferPool != null) - { - assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), Matchers.is(0L)); - assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), Matchers.is(0L)); - assertThat("Server BufferPool - leaked removes", serverBufferPool.getLeakedRemoves(), Matchers.is(0L)); - assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), Matchers.is(0L)); - } - - if ((clientBufferPool != null) && (clientBufferPool instanceof LeakTrackingByteBufferPool pool)) - { - assertThat("Client BufferPool - leaked acquires", pool.getLeakedAcquires(), Matchers.is(0L)); - assertThat("Client BufferPool - leaked releases", pool.getLeakedReleases(), Matchers.is(0L)); - assertThat("Client BufferPool - leaked removes", pool.getLeakedRemoves(), Matchers.is(0L)); - assertThat("Client BufferPool - unreleased", pool.getLeakedResources(), Matchers.is(0L)); - } - assertThat("Connection Leaks", connectionLeaks.get(), Matchers.is(0L)); if (client != null) diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java index b33600070d3..11924579772 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java @@ -36,7 +36,6 @@ import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpChannel; @@ -400,11 +399,6 @@ public class HttpClientTest extends AbstractHttpClientServerTest @Test public void testGZIPContentEncoding() throws Exception { - // GZIPContentDecoder returns to application pooled - // buffers, which is fine, but in this test they will - // appear as "leaked", so we use a normal ByteBufferPool. - clientBufferPool = new MappedByteBufferPool.Tagged(); - byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; start(new Handler.Abstract() { diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/GZIPContentDecoder.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/GZIPContentDecoder.java index 7a84fc3ca5d..b176ba3bb82 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/GZIPContentDecoder.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/GZIPContentDecoder.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.http; -import java.nio.Buffer; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -21,7 +20,8 @@ import java.util.zip.DataFormatException; import java.util.zip.Inflater; import java.util.zip.ZipException; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.component.Destroyable; import org.eclipse.jetty.util.compression.InflaterPool; @@ -36,8 +36,8 @@ public class GZIPContentDecoder implements Destroyable // Unsigned Integer Max == 2^32 private static final long UINT_MAX = 0xFFFFFFFFL; - private final List _inflateds = new ArrayList<>(); - private final ByteBufferPool _pool; + private final List _inflateds = new ArrayList<>(); + private final RetainableByteBufferPool _pool; private final int _bufferSize; private InflaterPool.Entry _inflaterEntry; private Inflater _inflater; @@ -45,7 +45,7 @@ public class GZIPContentDecoder implements Destroyable private int _size; private long _value; private byte _flags; - private ByteBuffer _inflated; + private RetainableByteBuffer _inflated; public GZIPContentDecoder() { @@ -57,24 +57,24 @@ public class GZIPContentDecoder implements Destroyable this(null, bufferSize); } - public GZIPContentDecoder(ByteBufferPool pool, int bufferSize) + public GZIPContentDecoder(RetainableByteBufferPool retainableByteBufferPool, int bufferSize) { - this(new InflaterPool(0, true), pool, bufferSize); + this(new InflaterPool(0, true), retainableByteBufferPool, bufferSize); } - public GZIPContentDecoder(InflaterPool inflaterPool, ByteBufferPool pool, int bufferSize) + public GZIPContentDecoder(InflaterPool inflaterPool, RetainableByteBufferPool retainableByteBufferPool, int bufferSize) { _inflaterEntry = inflaterPool.acquire(); _inflater = _inflaterEntry.get(); _bufferSize = bufferSize; - _pool = pool; + _pool = retainableByteBufferPool != null ? retainableByteBufferPool : new RetainableByteBufferPool.NonPooling(); reset(); } /** *

Inflates compressed data from a buffer.

- *

The buffers returned by this method should be released - * via {@link #release(ByteBuffer)}.

+ *

The {@link RetainableByteBuffer} returned by this method + * must be released via {@link RetainableByteBuffer#release()}.

*

This method may fully consume the input buffer, but return * only a chunk of the inflated bytes, to allow applications to * consume the inflated chunk before performing further inflation, @@ -83,21 +83,21 @@ public class GZIPContentDecoder implements Destroyable * it's already fully consumed) and that will produce another * chunk of inflated bytes. Termination happens when the input * buffer is fully consumed, and the returned buffer is empty.

- *

See {@link #decodedChunk(ByteBuffer)} to perform inflating + *

See {@link #decodedChunk(RetainableByteBuffer)} to perform inflating * in a non-blocking way that allows to apply backpressure.

* * @param compressed the buffer containing compressed data. * @return a buffer containing inflated data. */ - public ByteBuffer decode(ByteBuffer compressed) + public RetainableByteBuffer decode(ByteBuffer compressed) { decodeChunks(compressed); if (_inflateds.isEmpty()) { - if (BufferUtil.isEmpty(_inflated) || _state == State.CRC || _state == State.ISIZE) - return BufferUtil.EMPTY_BUFFER; - ByteBuffer result = _inflated; + if ((_inflated == null || !_inflated.hasRemaining()) || _state == State.CRC || _state == State.ISIZE) + return acquire(0); + RetainableByteBuffer result = _inflated; _inflated = null; return result; } @@ -105,12 +105,12 @@ public class GZIPContentDecoder implements Destroyable { _inflateds.add(_inflated); _inflated = null; - int length = _inflateds.stream().mapToInt(Buffer::remaining).sum(); - ByteBuffer result = acquire(length); - for (ByteBuffer buffer : _inflateds) + int length = _inflateds.stream().mapToInt(RetainableByteBuffer::remaining).sum(); + RetainableByteBuffer result = acquire(length); + for (RetainableByteBuffer buffer : _inflateds) { - BufferUtil.append(result, buffer); - release(buffer); + BufferUtil.append(result.getByteBuffer(), buffer.getByteBuffer()); + buffer.release(); } _inflateds.clear(); return result; @@ -132,38 +132,26 @@ public class GZIPContentDecoder implements Destroyable * should return, allowing to consume the inflated chunk and apply * backpressure */ - protected boolean decodedChunk(ByteBuffer chunk) + protected boolean decodedChunk(RetainableByteBuffer chunk) { - if (_inflated == null) - { - _inflated = chunk; - } - else - { - if (BufferUtil.space(_inflated) >= chunk.remaining()) - { - BufferUtil.append(_inflated, chunk); - release(chunk); - } - else - { - _inflateds.add(_inflated); - _inflated = chunk; - } - } + // Retain the chunk because it is stored for later use. + chunk.retain(); + if (_inflated != null) + _inflateds.add(_inflated); + _inflated = chunk; return false; } /** *

Inflates compressed data.

*

Inflation continues until the compressed block end is reached, there is no - * more compressed data or a call to {@link #decodedChunk(ByteBuffer)} returns true.

+ * more compressed data or a call to {@link #decodedChunk(RetainableByteBuffer)} returns true.

* * @param compressed the buffer of compressed data to inflate */ protected void decodeChunks(ByteBuffer compressed) { - ByteBuffer buffer = null; + RetainableByteBuffer buffer = null; try { while (true) @@ -211,9 +199,10 @@ public class GZIPContentDecoder implements Destroyable try { - int pos = BufferUtil.flipToFill(buffer); - _inflater.inflate(buffer); - BufferUtil.flipToFlush(buffer, pos); + ByteBuffer decoded = buffer.getByteBuffer(); + int pos = BufferUtil.flipToFill(decoded); + _inflater.inflate(decoded); + BufferUtil.flipToFlush(decoded, pos); } catch (DataFormatException x) { @@ -222,9 +211,10 @@ public class GZIPContentDecoder implements Destroyable if (buffer.hasRemaining()) { - ByteBuffer decoded = buffer; + boolean stop = decodedChunk(buffer); + buffer.release(); buffer = null; - if (decodedChunk(decoded)) + if (stop) return; } else if (_inflater.needsInput()) @@ -395,7 +385,7 @@ public class GZIPContentDecoder implements Destroyable finally { if (buffer != null) - release(buffer); + buffer.release(); } } @@ -430,23 +420,8 @@ public class GZIPContentDecoder implements Destroyable * @param capacity capacity of the ByteBuffer to acquire * @return a heap buffer of the configured capacity either from the pool or freshly allocated. */ - public ByteBuffer acquire(int capacity) + public RetainableByteBuffer acquire(int capacity) { - return _pool == null ? BufferUtil.allocate(capacity) : _pool.acquire(capacity, false); - } - - /** - *

Releases an allocated buffer.

- *

This method calls {@link ByteBufferPool#release(ByteBuffer)} if a buffer pool has - * been configured.

- *

This method should be called once for all buffers returned from {@link #decode(ByteBuffer)} - * or passed to {@link #decodedChunk(ByteBuffer)}.

- * - * @param buffer the buffer to release. - */ - public void release(ByteBuffer buffer) - { - if (_pool != null && !BufferUtil.isTheEmptyBuffer(buffer)) - _pool.release(buffer); + return _pool.acquire(capacity, false); } } diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java index cce551591d8..120e26ee903 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java @@ -30,9 +30,9 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NoopByteBufferPool; import org.eclipse.jetty.io.Retainable; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.NanoTime; import org.eclipse.jetty.util.StringUtil; @@ -69,16 +69,16 @@ public class CachingHttpContentFactory implements HttpContent.Factory private final HttpContent.Factory _authority; private final ConcurrentHashMap _cache = new ConcurrentHashMap<>(); private final AtomicLong _cachedSize = new AtomicLong(); - private final ByteBufferPool _byteBufferPool; + private final RetainableByteBufferPool _bufferPool; private int _maxCachedFileSize = DEFAULT_MAX_CACHED_FILE_SIZE; private int _maxCachedFiles = DEFAULT_MAX_CACHED_FILES; private long _maxCacheSize = DEFAULT_MAX_CACHE_SIZE; private boolean _useDirectByteBuffers = true; - public CachingHttpContentFactory(HttpContent.Factory authority, ByteBufferPool byteBufferPool) + public CachingHttpContentFactory(HttpContent.Factory authority, RetainableByteBufferPool bufferPool) { _authority = authority; - _byteBufferPool = (byteBufferPool == null) ? new NoopByteBufferPool() : byteBufferPool; + _bufferPool = bufferPool != null ? bufferPool : new RetainableByteBufferPool.NonPooling(); } protected ConcurrentMap getCache() @@ -294,7 +294,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory protected class CachedHttpContent extends HttpContent.Wrapper implements CachingHttpContent { - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private final String _cacheKey; private final HttpField _etagField; private final long _contentLengthValue; @@ -327,21 +327,23 @@ public class CachingHttpContentFactory implements HttpContent.Factory boolean isValid = true; // Read the content into memory if the HttpContent does not already have a buffer. + RetainableByteBuffer buffer; ByteBuffer byteBuffer = httpContent.getByteBuffer(); if (byteBuffer == null) { + buffer = _bufferPool.acquire((int)_contentLengthValue, _useDirectByteBuffers); try { if (_contentLengthValue <= _maxCachedFileSize) { - byteBuffer = _byteBufferPool.acquire((int)_contentLengthValue, _useDirectByteBuffers); try (ReadableByteChannel readableByteChannel = httpContent.getResource().newReadableByteChannel()) { + byteBuffer = buffer.getByteBuffer(); int read = BufferUtil.readFrom(readableByteChannel, byteBuffer); if (read != _contentLengthValue) { - _byteBufferPool.release(byteBuffer); - byteBuffer = null; + buffer.release(); + buffer = null; isValid = false; } } @@ -349,17 +351,19 @@ public class CachingHttpContentFactory implements HttpContent.Factory } catch (Throwable t) { + if (buffer != null) + buffer.release(); + buffer = null; isValid = false; - if (byteBuffer != null) - { - _byteBufferPool.release(byteBuffer); - byteBuffer = null; - } LOG.warn("Failed to read Resource", t); } } + else + { + buffer = RetainableByteBuffer.wrap(byteBuffer); + } - _buffer = byteBuffer; + _buffer = buffer; _isValid = isValid; _bytesOccupied = httpContent.getBytesOccupied(); _lastModifiedValue = httpContent.getLastModifiedValue(); @@ -381,7 +385,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory @Override public ByteBuffer getByteBuffer() { - return _buffer == null ? null : _buffer.asReadOnlyBuffer(); + return _buffer == null ? null : _buffer.getByteBuffer().asReadOnlyBuffer(); } @Override @@ -420,7 +424,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory if (_referenceCount.release()) { if (_buffer != null) - _byteBufferPool.release(_buffer); + _buffer.release(); super.release(); } } diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ValidatingCachingHttpContentFactory.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ValidatingCachingHttpContentFactory.java index 4581b0d4144..b3dea606cf7 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ValidatingCachingHttpContentFactory.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ValidatingCachingHttpContentFactory.java @@ -19,7 +19,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.NanoTime; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.thread.Scheduler; @@ -55,13 +55,13 @@ public class ValidatingCachingHttpContentFactory extends CachingHttpContentFacto * * @param authority the wrapped {@link HttpContent.Factory} to use. * @param validationPeriod time between filesystem checks in ms to see if an {@link HttpContent} is still valid (-1 never validate, 0 always validate). - * @param byteBufferPool the {@link org.eclipse.jetty.io.ByteBufferPool} to use. + * @param bufferPool the {@link org.eclipse.jetty.io.RetainableByteBufferPool} to use. */ public ValidatingCachingHttpContentFactory(@Name("authority") HttpContent.Factory authority, @Name("validationPeriod") long validationPeriod, - @Name("byteBufferPool") ByteBufferPool byteBufferPool) + @Name("bufferPool") RetainableByteBufferPool bufferPool) { - this(authority, validationPeriod, byteBufferPool, null, -1, -1); + this(authority, validationPeriod, bufferPool, null, -1, -1); } /** @@ -70,19 +70,19 @@ public class ValidatingCachingHttpContentFactory extends CachingHttpContentFacto * * @param authority the wrapped {@link HttpContent.Factory} to use. * @param validationPeriod time between filesystem checks in ms to see if an {@link HttpContent} is still valid (-1 never validate, 0 always validate). - * @param byteBufferPool the {@link org.eclipse.jetty.io.ByteBufferPool} to use. + * @param bufferPool the {@link org.eclipse.jetty.io.RetainableByteBufferPool} to use. * @param scheduler scheduler to use for the sweeper, can be null to not use sweeper. * @param sweepPeriod time between runs of the sweeper in ms (if 0 never sweep for invalid entries). * @param idleTimeout amount of time in ms an entry can be unused before evicted by the sweeper (if 0 never evict unused entries). */ public ValidatingCachingHttpContentFactory(@Name("authority") HttpContent.Factory authority, @Name("validationPeriod") long validationPeriod, - @Name("byteBufferPool") ByteBufferPool byteBufferPool, + @Name("byteBufferPool") RetainableByteBufferPool bufferPool, @Name("scheduler") Scheduler scheduler, @Name("sweepPeriod") long sweepPeriod, @Name("idleTimeout") long idleTimeout) { - super(authority, byteBufferPool); + super(authority, bufferPool); _validationTime = validationPeriod; _scheduler = scheduler; _sweepDelay = sweepPeriod; diff --git a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/GZIPContentDecoderTest.java b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/GZIPContentDecoderTest.java index b0f2916f43c..75e4161d423 100644 --- a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/GZIPContentDecoderTest.java +++ b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/GZIPContentDecoderTest.java @@ -23,7 +23,9 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,28 +41,29 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class GZIPContentDecoderTest { - private ArrayByteBufferPool pool; - private AtomicInteger buffers = new AtomicInteger(0); + private final AtomicInteger counter = new AtomicInteger(); + private RetainableByteBufferPool pool; @BeforeEach public void before() { - buffers.set(0); - pool = new ArrayByteBufferPool() + pool = new RetainableByteBufferPool.Wrapper(new ArrayRetainableByteBufferPool()) { - @Override - public ByteBuffer acquire(int size, boolean direct) + public RetainableByteBuffer acquire(int size, boolean direct) { - buffers.incrementAndGet(); - return super.acquire(size, direct); - } - - @Override - public void release(ByteBuffer buffer) - { - buffers.decrementAndGet(); - super.release(buffer); + counter.incrementAndGet(); + return new RetainableByteBuffer.Wrapper(super.acquire(size, direct)) + { + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + counter.decrementAndGet(); + return released; + } + }; } }; } @@ -68,7 +71,7 @@ public class GZIPContentDecoderTest @AfterEach public void after() { - assertEquals(0, buffers.get()); + assertThat(counter.get(), is(0)); } @Test @@ -126,8 +129,9 @@ public class GZIPContentDecoderTest byte[] bytes = baos.toByteArray(); GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048); - ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); + RetainableByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); assertEquals(0, decoded.remaining()); + decoded.release(); } @Test @@ -142,9 +146,9 @@ public class GZIPContentDecoderTest byte[] bytes = baos.toByteArray(); GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048); - ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); - assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); - decoder.release(decoded); + RetainableByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString()); + decoded.release(); } @Test @@ -165,11 +169,12 @@ public class GZIPContentDecoderTest System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048); - ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); - assertEquals(0, decoded.capacity()); + RetainableByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); + assertEquals(0, decoded.remaining()); + decoded.release(); decoded = decoder.decode(ByteBuffer.wrap(bytes2)); - assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); - decoder.release(decoded); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString()); + decoded.release(); } @Test @@ -190,14 +195,14 @@ public class GZIPContentDecoderTest System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048); - ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); - assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); + RetainableByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString()); assertFalse(decoder.isFinished()); - decoder.release(decoded); + decoded.release(); decoded = decoder.decode(ByteBuffer.wrap(bytes2)); assertEquals(0, decoded.remaining()); assertTrue(decoder.isFinished()); - decoder.release(decoded); + decoded.release(); } @Test @@ -218,12 +223,12 @@ public class GZIPContentDecoderTest System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048); - ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); - assertEquals(0, decoded.capacity()); - decoder.release(decoded); + RetainableByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); + assertEquals(0, decoded.remaining()); + decoded.release(); decoded = decoder.decode(ByteBuffer.wrap(bytes2)); - assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); - decoder.release(decoded); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString()); + decoded.release(); } @Test @@ -249,16 +254,16 @@ public class GZIPContentDecoderTest GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048); ByteBuffer buffer = ByteBuffer.wrap(bytes); - ByteBuffer decoded = decoder.decode(buffer); - assertEquals(data1, StandardCharsets.UTF_8.decode(decoded).toString()); + RetainableByteBuffer decoded = decoder.decode(buffer); + assertEquals(data1, StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString()); assertTrue(decoder.isFinished()); assertTrue(buffer.hasRemaining()); - decoder.release(decoded); + decoded.release(); decoded = decoder.decode(buffer); - assertEquals(data2, StandardCharsets.UTF_8.decode(decoded).toString()); + assertEquals(data2, StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString()); assertTrue(decoder.isFinished()); assertFalse(buffer.hasRemaining()); - decoder.release(decoded); + decoded.release(); } @Test @@ -280,9 +285,9 @@ public class GZIPContentDecoderTest ByteBuffer buffer = ByteBuffer.wrap(bytes); while (buffer.hasRemaining()) { - ByteBuffer decoded = decoder.decode(buffer); - result += StandardCharsets.UTF_8.decode(decoded).toString(); - decoder.release(decoded); + RetainableByteBuffer decoded = decoder.decode(buffer); + result += StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString(); + decoded.release(); } assertEquals(data, result); } @@ -306,10 +311,10 @@ public class GZIPContentDecoderTest ByteBuffer buffer = ByteBuffer.wrap(bytes); while (buffer.hasRemaining()) { - ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(new byte[]{buffer.get()})); + RetainableByteBuffer decoded = decoder.decode(ByteBuffer.wrap(new byte[]{buffer.get()})); if (decoded.hasRemaining()) - result += StandardCharsets.UTF_8.decode(decoded).toString(); - decoder.release(decoded); + result += StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString(); + decoded.release(); } assertEquals(data, result); assertTrue(decoder.isFinished()); @@ -341,10 +346,10 @@ public class GZIPContentDecoderTest ByteBuffer buffer = ByteBuffer.wrap(bytes); while (buffer.hasRemaining()) { - ByteBuffer decoded = decoder.decode(buffer); + RetainableByteBuffer decoded = decoder.decode(buffer); if (decoded.hasRemaining()) - result += StandardCharsets.UTF_8.decode(decoded).toString(); - decoder.release(decoded); + result += StandardCharsets.UTF_8.decode(decoded.getByteBuffer()).toString(); + decoded.release(); if (decoder.isFinished()) break; } @@ -414,12 +419,12 @@ public class GZIPContentDecoderTest ByteBuffer buf = ByteBuffer.wrap(b, off, len); while (buf.hasRemaining()) { - ByteBuffer decoded = decoder.decode(buf); + RetainableByteBuffer decoded = decoder.decode(buf); if (decoded.hasRemaining()) { decodedByteCount += decoded.remaining(); } - decoder.release(decoded); + decoded.release(); } } diff --git a/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java index 7bd52127165..23fd9c2cfa9 100644 --- a/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java +++ b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java @@ -90,7 +90,7 @@ public class HttpClientTransportOverHTTP2 extends AbstractHttpClientTransport HttpClient httpClient = getHttpClient(); client.setExecutor(httpClient.getExecutor()); client.setScheduler(httpClient.getScheduler()); - client.setByteBufferPool(httpClient.getByteBufferPool()); + client.setRetainableByteBufferPool(httpClient.getRetainableByteBufferPool()); client.setConnectTimeout(httpClient.getConnectTimeout()); client.setIdleTimeout(httpClient.getIdleTimeout()); client.setInputBufferSize(httpClient.getResponseBufferSize()); diff --git a/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/internal/HttpReceiverOverHTTP2.java b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/internal/HttpReceiverOverHTTP2.java index d802c1f1507..f935a79265d 100644 --- a/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/internal/HttpReceiverOverHTTP2.java +++ b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/internal/HttpReceiverOverHTTP2.java @@ -76,7 +76,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel. DataFrame frame = data.frame(); boolean last = frame.remaining() == 0 && frame.isEndStream(); if (!last) - return Content.Chunk.asChunk(frame.getData(), last, data); + return Content.Chunk.asChunk(frame.getByteBuffer(), last, data); data.release(); responseSuccess(getHttpExchange(), null); return Content.Chunk.EOF; diff --git a/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java b/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java index 368d3009d60..2d488a23e8c 100644 --- a/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java +++ b/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java @@ -28,9 +28,9 @@ import org.eclipse.jetty.http2.FlowControlStrategy; import org.eclipse.jetty.http2.api.Session; import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.SettingsFrame; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.annotation.ManagedAttribute; @@ -153,14 +153,14 @@ public class HTTP2Client extends ContainerLifeCycle connector.setScheduler(scheduler); } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return connector.getByteBufferPool(); + return connector.getRetainableByteBufferPool(); } - public void setByteBufferPool(ByteBufferPool bufferPool) + public void setRetainableByteBufferPool(RetainableByteBufferPool bufferPool) { - connector.setByteBufferPool(bufferPool); + connector.setRetainableByteBufferPool(bufferPool); } public FlowControlStrategy.Factory getFlowControlStrategyFactory() @@ -435,7 +435,7 @@ public class HTTP2Client extends ContainerLifeCycle { if (isUseALPN()) factory = new ALPNClientConnectionFactory(getExecutor(), factory, getProtocols()); - factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), factory); + factory = new SslClientConnectionFactory(sslContextFactory, getRetainableByteBufferPool(), getExecutor(), factory); } return factory; } diff --git a/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index 3d665b1575f..6937d0db6e4 100644 --- a/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -28,7 +28,6 @@ import org.eclipse.jetty.http2.internal.HTTP2Connection; import org.eclipse.jetty.http2.internal.HTTP2Session; import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -49,14 +48,14 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory public Connection newConnection(EndPoint endPoint, Map context) { HTTP2Client client = (HTTP2Client)context.get(CLIENT_CONTEXT_KEY); - ByteBufferPool byteBufferPool = client.getByteBufferPool(); + RetainableByteBufferPool bufferPool = client.getRetainableByteBufferPool(); Executor executor = client.getExecutor(); Scheduler scheduler = client.getScheduler(); Session.Listener listener = (Session.Listener)context.get(SESSION_LISTENER_CONTEXT_KEY); @SuppressWarnings("unchecked") Promise promise = (Promise)context.get(SESSION_PROMISE_CONTEXT_KEY); - Generator generator = new Generator(byteBufferPool, client.getMaxDynamicTableSize(), client.getMaxHeaderBlockFragment()); + Generator generator = new Generator(bufferPool, client.getMaxDynamicTableSize(), client.getMaxHeaderBlockFragment()); FlowControlStrategy flowControl = client.getFlowControlStrategyFactory().newFlowControlStrategy(); HTTP2ClientSession session = new HTTP2ClientSession(scheduler, endPoint, generator, listener, flowControl); session.setMaxRemoteStreams(client.getMaxConcurrentPushedStreams()); @@ -64,13 +63,11 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory if (streamIdleTimeout > 0) session.setStreamIdleTimeout(streamIdleTimeout); - Parser parser = new Parser(byteBufferPool, session, 4096, 8192); + Parser parser = new Parser(bufferPool, session, 4096, 8192); parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - RetainableByteBufferPool retainableByteBufferPool = byteBufferPool.asRetainableByteBufferPool(); - - HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, + HTTP2ClientConnection connection = new HTTP2ClientConnection(client, bufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); connection.setUseInputDirectByteBuffers(client.isUseInputDirectByteBuffers()); connection.setUseOutputDirectByteBuffers(client.isUseOutputDirectByteBuffers()); diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java index d8003f51fb0..a4cf8c828e1 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java @@ -313,10 +313,10 @@ public interface Stream *

It is always guaranteed that invoking {@link Stream#demand()} * from within this method will not cause a {@link StackOverflowError}.

*

Typical usage:

- *
+         * 
{@code
          * class MyStreamListener implements Stream.Listener
          * {
-         *     @Override
+         *     @Override
          *     public void onDataAvailable(Stream stream)
          *     {
          *         // Read a chunk of the content.
@@ -329,7 +329,7 @@ public interface Stream
          *         else
          *         {
          *             // Process the content.
-         *             process(data.getByteBuffer());
+         *             process(data.frame().getByteBuffer());
          *             // Notify that the content has been consumed.
          *             data.release();
          *             if (!data.frame().isEndStream())
@@ -340,7 +340,7 @@ public interface Stream
          *         }
          *     }
          * }
-         * 
+ * }
* * @param stream the stream * @see Stream#demand() diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/frames/DataFrame.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/frames/DataFrame.java index 9939a502189..3c34edf3ba3 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/frames/DataFrame.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/frames/DataFrame.java @@ -39,7 +39,7 @@ public class DataFrame extends StreamFrame this.padding = padding; } - public ByteBuffer getData() + public ByteBuffer getByteBuffer() { return data; } @@ -68,12 +68,12 @@ public class DataFrame extends StreamFrame @Override public DataFrame withStreamId(int streamId) { - return new DataFrame(streamId, getData(), isEndStream()); + return new DataFrame(streamId, getByteBuffer(), isEndStream()); } @Override public String toString() { - return String.format("%s#%d{length:%d,end=%b}", super.toString(), getStreamId(), data.remaining(), endStream); + return String.format("%s#%d{length:%d,end=%b}", super.toString(), getStreamId(), remaining(), isEndStream()); } } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java index bf7ee9e5b0e..7199617f238 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java @@ -455,7 +455,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. public ByteBuffer getBuffer() { - return delegate.getBuffer(); + return delegate.getByteBuffer(); } public boolean isRetained() @@ -494,7 +494,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private void put(ByteBuffer source) { - BufferUtil.append(delegate.getBuffer(), source); + BufferUtil.append(delegate.getByteBuffer(), source); } } } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java index dc960759ca4..7316190c78a 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java @@ -30,8 +30,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.http2.hpack.HpackException; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EofException; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.component.Dumpable; @@ -51,7 +51,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable private final Queue pendingEntries = new ArrayDeque<>(); private final Collection processedEntries = new ArrayList<>(); private final HTTP2Session session; - private final ByteBufferPool.Lease lease; + private final RetainableByteBufferPool.Accumulator accumulator; private InvocationType invocationType = InvocationType.NON_BLOCKING; private Throwable terminated; private Entry stalledEntry; @@ -59,7 +59,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable public HTTP2Flusher(HTTP2Session session) { this.session = session; - this.lease = new ByteBufferPool.Lease(session.getGenerator().getByteBufferPool()); + this.accumulator = new RetainableByteBufferPool.Accumulator(); } @Override @@ -213,7 +213,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable try { - if (entry.generate(lease)) + if (entry.generate(accumulator)) { if (LOG.isDebugEnabled()) LOG.debug("Generated {} frame bytes for {}", entry.getFrameBytesGenerated(), entry); @@ -266,7 +266,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable break; int writeThreshold = session.getWriteThreshold(); - if (lease.getTotalLength() >= writeThreshold) + if (accumulator.getTotalLength() >= writeThreshold) { if (LOG.isDebugEnabled()) LOG.debug("Write threshold {} exceeded", writeThreshold); @@ -274,7 +274,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable } } - List byteBuffers = lease.getByteBuffers(); + List byteBuffers = accumulator.getByteBuffers(); if (byteBuffers.isEmpty()) { finish(); @@ -284,7 +284,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable if (LOG.isDebugEnabled()) LOG.debug("Writing {} buffers ({} bytes) - entries processed/pending {}/{}: {}/{}", byteBuffers.size(), - lease.getTotalLength(), + accumulator.getTotalLength(), processedEntries.size(), pendingEntries.size(), processedEntries, @@ -308,7 +308,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable { if (LOG.isDebugEnabled()) LOG.debug("Written {} buffers - entries processed/pending {}/{}: {}/{}", - lease.getByteBuffers().size(), + accumulator.getByteBuffers().size(), processedEntries.size(), pendingEntries.size(), processedEntries, @@ -319,7 +319,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable private void finish() { - lease.recycle(); + accumulator.release(); processedEntries.forEach(Entry::succeeded); processedEntries.clear(); @@ -349,7 +349,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable @Override protected void onCompleteFailure(Throwable x) { - lease.recycle(); + accumulator.release(); Throwable closed; Set allEntries; @@ -440,7 +440,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable return 0; } - protected abstract boolean generate(ByteBufferPool.Lease lease) throws HpackException; + protected abstract boolean generate(RetainableByteBufferPool.Accumulator accumulator) throws HpackException; public abstract long onFlushed(long bytes) throws IOException; diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java index 9f21c12c23f..27c5079cdd8 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java @@ -57,9 +57,9 @@ import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.http2.hpack.HpackException; import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.CyclicTimeouts; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.AtomicBiInteger; import org.eclipse.jetty.util.Atomics; @@ -1206,9 +1206,9 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session } @Override - protected boolean generate(ByteBufferPool.Lease lease) throws HpackException + protected boolean generate(RetainableByteBufferPool.Accumulator accumulator) throws HpackException { - frameBytes = generator.control(lease, frame); + frameBytes = generator.control(accumulator, frame); beforeSend(); return true; } @@ -1320,7 +1320,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session } @Override - protected boolean generate(ByteBufferPool.Lease lease) + protected boolean generate(RetainableByteBufferPool.Accumulator accumulator) { int dataRemaining = getDataBytesRemaining(); @@ -1334,7 +1334,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session // Only one DATA frame is generated. DataFrame dataFrame = (DataFrame)frame; - int frameBytes = generator.data(lease, dataFrame, length); + int frameBytes = generator.data(accumulator, dataFrame, length); this.frameBytes += frameBytes; this.frameRemaining += frameBytes; diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2StreamEndPoint.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2StreamEndPoint.java index c7661179bfe..d402410432a 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2StreamEndPoint.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2StreamEndPoint.java @@ -215,7 +215,7 @@ public abstract class HTTP2StreamEndPoint implements EndPoint private int fillFromData(Stream.Data data, ByteBuffer sink) { int length = 0; - ByteBuffer source = data.frame().getData(); + ByteBuffer source = data.frame().getByteBuffer(); boolean hasContent = source.hasRemaining(); if (hasContent) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/DataGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/DataGenerator.java index 5de967599a5..bd13aa83ee3 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/DataGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/DataGenerator.java @@ -19,7 +19,8 @@ import org.eclipse.jetty.http2.frames.DataFrame; import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class DataGenerator @@ -31,12 +32,12 @@ public class DataGenerator this.headerGenerator = headerGenerator; } - public int generate(ByteBufferPool.Lease lease, DataFrame frame, int maxLength) + public int generate(RetainableByteBufferPool.Accumulator accumulator, DataFrame frame, int maxLength) { - return generateData(lease, frame.getStreamId(), frame.getData(), frame.isEndStream(), maxLength); + return generateData(accumulator, frame.getStreamId(), frame.getByteBuffer(), frame.isEndStream(), maxLength); } - public int generateData(ByteBufferPool.Lease lease, int streamId, ByteBuffer data, boolean last, int maxLength) + public int generateData(RetainableByteBufferPool.Accumulator accumulator, int streamId, ByteBuffer data, boolean last, int maxLength) { if (streamId < 0) throw new IllegalArgumentException("Invalid stream id: " + streamId); @@ -46,7 +47,7 @@ public class DataGenerator int length = Math.min(dataLength, Math.min(maxFrameSize, maxLength)); if (length == dataLength) { - generateFrame(lease, streamId, data, last); + generateFrame(accumulator, streamId, data, last); } else { @@ -56,12 +57,12 @@ public class DataGenerator ByteBuffer slice = data.slice(); data.position(newLimit); data.limit(limit); - generateFrame(lease, streamId, slice, false); + generateFrame(accumulator, streamId, slice, false); } return Frame.HEADER_LENGTH + length; } - private void generateFrame(ByteBufferPool.Lease lease, int streamId, ByteBuffer data, boolean last) + private void generateFrame(RetainableByteBufferPool.Accumulator accumulator, int streamId, ByteBuffer data, boolean last) { int length = data.remaining(); @@ -69,11 +70,11 @@ public class DataGenerator if (last) flags |= Flags.END_STREAM; - ByteBuffer header = headerGenerator.generate(lease, FrameType.DATA, Frame.HEADER_LENGTH + length, length, flags, streamId); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); + RetainableByteBuffer header = headerGenerator.generate(FrameType.DATA, Frame.HEADER_LENGTH + length, length, flags, streamId); + BufferUtil.flipToFlush(header.getByteBuffer(), 0); + accumulator.append(header); // Skip empty data buffers. if (data.remaining() > 0) - lease.append(data, false); + accumulator.append(RetainableByteBuffer.wrap(data)); } } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/FrameGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/FrameGenerator.java index 0c0b6bfe4b1..0386b0a5b2c 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/FrameGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/FrameGenerator.java @@ -20,7 +20,9 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.hpack.HpackEncoder; import org.eclipse.jetty.http2.hpack.HpackException; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; public abstract class FrameGenerator { @@ -31,11 +33,11 @@ public abstract class FrameGenerator this.headerGenerator = headerGenerator; } - public abstract int generate(ByteBufferPool.Lease lease, Frame frame) throws HpackException; + public abstract int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) throws HpackException; - protected ByteBuffer generateHeader(ByteBufferPool.Lease lease, FrameType frameType, int length, int flags, int streamId) + protected RetainableByteBuffer generateHeader(FrameType frameType, int length, int flags, int streamId) { - return headerGenerator.generate(lease, frameType, Frame.HEADER_LENGTH + length, length, flags, streamId); + return headerGenerator.generate(frameType, Frame.HEADER_LENGTH + length, length, flags, streamId); } public int getMaxFrameSize() @@ -48,17 +50,19 @@ public abstract class FrameGenerator return headerGenerator.isUseDirectByteBuffers(); } - protected ByteBuffer encode(HpackEncoder encoder, ByteBufferPool.Lease lease, MetaData metaData, int maxFrameSize) throws HpackException + protected RetainableByteBuffer encode(HpackEncoder encoder, MetaData metaData, int maxFrameSize) throws HpackException { - ByteBuffer hpacked = lease.acquire(maxFrameSize, isUseDirectByteBuffers()); + RetainableByteBuffer hpacked = headerGenerator.getRetainableByteBufferPool().acquire(maxFrameSize, isUseDirectByteBuffers()); try { - encoder.encode(hpacked, metaData); + ByteBuffer byteBuffer = hpacked.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + encoder.encode(byteBuffer, metaData); return hpacked; } catch (HpackException x) { - lease.release(hpacked); + hpacked.release(); throw x; } } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/Generator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/Generator.java index aa7d3928125..86ba49cb5f3 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/Generator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/Generator.java @@ -18,31 +18,31 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.hpack.HpackEncoder; import org.eclipse.jetty.http2.hpack.HpackException; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class Generator { - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private final HeaderGenerator headerGenerator; private final HpackEncoder hpackEncoder; private final FrameGenerator[] generators; private final DataGenerator dataGenerator; - public Generator(ByteBufferPool byteBufferPool) + public Generator(RetainableByteBufferPool bufferPool) { - this(byteBufferPool, 4096, 0); + this(bufferPool, 4096, 0); } - public Generator(ByteBufferPool byteBufferPool, int maxDynamicTableSize, int maxHeaderBlockFragment) + public Generator(RetainableByteBufferPool bufferPool, int maxDynamicTableSize, int maxHeaderBlockFragment) { - this(byteBufferPool, true, maxDynamicTableSize, maxHeaderBlockFragment); + this(bufferPool, true, maxDynamicTableSize, maxHeaderBlockFragment); } - public Generator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers, int maxDynamicTableSize, int maxHeaderBlockFragment) + public Generator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers, int maxDynamicTableSize, int maxHeaderBlockFragment) { - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; - headerGenerator = new HeaderGenerator(useDirectByteBuffers); + headerGenerator = new HeaderGenerator(bufferPool, useDirectByteBuffers); hpackEncoder = new HpackEncoder(maxDynamicTableSize); this.generators = new FrameGenerator[FrameType.values().length]; @@ -61,9 +61,9 @@ public class Generator this.dataGenerator = new DataGenerator(headerGenerator); } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return byteBufferPool; + return bufferPool; } public void setValidateHpackEncoding(boolean validateEncoding) @@ -81,14 +81,14 @@ public class Generator headerGenerator.setMaxFrameSize(maxFrameSize); } - public int control(ByteBufferPool.Lease lease, Frame frame) throws HpackException + public int control(RetainableByteBufferPool.Accumulator accumulator, Frame frame) throws HpackException { - return generators[frame.getType().getType()].generate(lease, frame); + return generators[frame.getType().getType()].generate(accumulator, frame); } - public int data(ByteBufferPool.Lease lease, DataFrame frame, int maxLength) + public int data(RetainableByteBufferPool.Accumulator accumulator, DataFrame frame, int maxLength) { - return dataGenerator.generate(lease, frame, maxLength); + return dataGenerator.generate(accumulator, frame, maxLength); } public void setMaxHeaderListSize(int value) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/GoAwayGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/GoAwayGenerator.java index 8468d29db22..5f3b6d3ec93 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/GoAwayGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/GoAwayGenerator.java @@ -20,7 +20,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.frames.GoAwayFrame; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class GoAwayGenerator extends FrameGenerator @@ -31,13 +32,13 @@ public class GoAwayGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { GoAwayFrame goAwayFrame = (GoAwayFrame)frame; - return generateGoAway(lease, goAwayFrame.getLastStreamId(), goAwayFrame.getError(), goAwayFrame.getPayload()); + return generateGoAway(accumulator, goAwayFrame.getLastStreamId(), goAwayFrame.getError(), goAwayFrame.getPayload()); } - public int generateGoAway(ByteBufferPool.Lease lease, int lastStreamId, int error, byte[] payload) + public int generateGoAway(RetainableByteBufferPool.Accumulator accumulator, int lastStreamId, int error, byte[] payload) { if (lastStreamId < 0) lastStreamId = 0; @@ -51,16 +52,17 @@ public class GoAwayGenerator extends FrameGenerator payload = Arrays.copyOfRange(payload, 0, maxPayloadLength); int length = fixedLength + (payload != null ? payload.length : 0); - ByteBuffer header = generateHeader(lease, FrameType.GO_AWAY, length, Flags.NONE, 0); + RetainableByteBuffer header = generateHeader(FrameType.GO_AWAY, length, Flags.NONE, 0); + ByteBuffer byteBuffer = header.getByteBuffer(); - header.putInt(lastStreamId); - header.putInt(error); + byteBuffer.putInt(lastStreamId); + byteBuffer.putInt(error); if (payload != null) - header.put(payload); + byteBuffer.put(payload); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(header); return Frame.HEADER_LENGTH + length; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeaderGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeaderGenerator.java index 1d1592c19e9..ccdebbdd907 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeaderGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeaderGenerator.java @@ -17,38 +17,49 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; public class HeaderGenerator { private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH; + private final RetainableByteBufferPool bufferPool; private final boolean useDirectByteBuffers; - public HeaderGenerator() + public HeaderGenerator(RetainableByteBufferPool bufferPool) { - this(true); + this(bufferPool, true); } - public HeaderGenerator(boolean useDirectByteBuffers) + public HeaderGenerator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers) { + this.bufferPool = bufferPool; this.useDirectByteBuffers = useDirectByteBuffers; } + public RetainableByteBufferPool getRetainableByteBufferPool() + { + return bufferPool; + } + public boolean isUseDirectByteBuffers() { return useDirectByteBuffers; } - public ByteBuffer generate(ByteBufferPool.Lease lease, FrameType frameType, int capacity, int length, int flags, int streamId) + public RetainableByteBuffer generate(FrameType frameType, int capacity, int length, int flags, int streamId) { - ByteBuffer header = lease.acquire(capacity, isUseDirectByteBuffers()); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(capacity, isUseDirectByteBuffers()); + ByteBuffer header = buffer.getByteBuffer(); + BufferUtil.clearToFill(header); header.put((byte)((length & 0x00_FF_00_00) >>> 16)); header.put((byte)((length & 0x00_00_FF_00) >>> 8)); header.put((byte)((length & 0x00_00_00_FF))); header.put((byte)frameType.getType()); header.put((byte)flags); header.putInt(streamId); - return header; + return buffer; } public int getMaxFrameSize() diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeadersGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeadersGenerator.java index c1afa0a46ee..57d9dcd4443 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeadersGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/HeadersGenerator.java @@ -23,7 +23,8 @@ import org.eclipse.jetty.http2.frames.PriorityFrame; import org.eclipse.jetty.http2.hpack.HpackEncoder; import org.eclipse.jetty.http2.hpack.HpackException; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class HeadersGenerator extends FrameGenerator @@ -46,13 +47,13 @@ public class HeadersGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) throws HpackException + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) throws HpackException { HeadersFrame headersFrame = (HeadersFrame)frame; - return generateHeaders(lease, headersFrame.getStreamId(), headersFrame.getMetaData(), headersFrame.getPriority(), headersFrame.isEndStream()); + return generateHeaders(accumulator, headersFrame.getStreamId(), headersFrame.getMetaData(), headersFrame.getPriority(), headersFrame.isEndStream()); } - public int generateHeaders(ByteBufferPool.Lease lease, int streamId, MetaData metaData, PriorityFrame priority, boolean endStream) throws HpackException + public int generateHeaders(RetainableByteBufferPool.Accumulator accumulator, int streamId, MetaData metaData, PriorityFrame priority, boolean endStream) throws HpackException { if (streamId < 0) throw new IllegalArgumentException("Invalid stream id: " + streamId); @@ -62,12 +63,13 @@ public class HeadersGenerator extends FrameGenerator if (priority != null) flags = Flags.PRIORITY; - ByteBuffer hpacked = encode(encoder, lease, metaData, getMaxFrameSize()); - int hpackedLength = hpacked.position(); - BufferUtil.flipToFlush(hpacked, 0); + RetainableByteBuffer hpack = encode(encoder, metaData, getMaxFrameSize()); + ByteBuffer hpackByteBuffer = hpack.getByteBuffer(); + int hpackLength = hpackByteBuffer.position(); + BufferUtil.flipToFlush(hpackByteBuffer, 0); // Split into CONTINUATION frames if necessary. - if (maxHeaderBlockFragment > 0 && hpackedLength > maxHeaderBlockFragment) + if (maxHeaderBlockFragment > 0 && hpackLength > maxHeaderBlockFragment) { if (endStream) flags |= Flags.END_STREAM; @@ -76,35 +78,38 @@ public class HeadersGenerator extends FrameGenerator if (priority != null) length += PriorityFrame.PRIORITY_LENGTH; - ByteBuffer header = generateHeader(lease, FrameType.HEADERS, length, flags, streamId); - generatePriority(header, priority); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); - hpacked.limit(maxHeaderBlockFragment); - lease.append(hpacked.slice(), false); + RetainableByteBuffer header = generateHeader(FrameType.HEADERS, length, flags, streamId); + ByteBuffer headerByteBuffer = header.getByteBuffer(); + generatePriority(headerByteBuffer, priority); + BufferUtil.flipToFlush(headerByteBuffer, 0); + accumulator.append(header); + hpackByteBuffer.limit(maxHeaderBlockFragment); + accumulator.append(RetainableByteBuffer.wrap(hpackByteBuffer.slice())); int totalLength = Frame.HEADER_LENGTH + length; int position = maxHeaderBlockFragment; int limit = position + maxHeaderBlockFragment; - while (limit < hpackedLength) + while (limit < hpackLength) { - hpacked.position(position).limit(limit); - header = generateHeader(lease, FrameType.CONTINUATION, maxHeaderBlockFragment, Flags.NONE, streamId); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); - lease.append(hpacked.slice(), false); + hpackByteBuffer.position(position).limit(limit); + header = generateHeader(FrameType.CONTINUATION, maxHeaderBlockFragment, Flags.NONE, streamId); + headerByteBuffer = header.getByteBuffer(); + BufferUtil.flipToFlush(headerByteBuffer, 0); + accumulator.append(header); + accumulator.append(RetainableByteBuffer.wrap(hpackByteBuffer.slice())); position += maxHeaderBlockFragment; limit += maxHeaderBlockFragment; totalLength += Frame.HEADER_LENGTH + maxHeaderBlockFragment; } - hpacked.position(position).limit(hpackedLength); - header = generateHeader(lease, FrameType.CONTINUATION, hpacked.remaining(), Flags.END_HEADERS, streamId); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); - lease.append(hpacked, true); - totalLength += Frame.HEADER_LENGTH + hpacked.remaining(); + hpackByteBuffer.position(position).limit(hpackLength); + header = generateHeader(FrameType.CONTINUATION, hpack.remaining(), Flags.END_HEADERS, streamId); + headerByteBuffer = header.getByteBuffer(); + BufferUtil.flipToFlush(headerByteBuffer, 0); + accumulator.append(header); + accumulator.append(hpack); + totalLength += Frame.HEADER_LENGTH + hpack.remaining(); return totalLength; } @@ -114,15 +119,16 @@ public class HeadersGenerator extends FrameGenerator if (endStream) flags |= Flags.END_STREAM; - int length = hpackedLength; + int length = hpackLength; if (priority != null) length += PriorityFrame.PRIORITY_LENGTH; - ByteBuffer header = generateHeader(lease, FrameType.HEADERS, length, flags, streamId); - generatePriority(header, priority); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); - lease.append(hpacked, true); + RetainableByteBuffer header = generateHeader(FrameType.HEADERS, length, flags, streamId); + ByteBuffer headerByteBuffer = header.getByteBuffer(); + generatePriority(headerByteBuffer, priority); + BufferUtil.flipToFlush(headerByteBuffer, 0); + accumulator.append(header); + accumulator.append(hpack); return Frame.HEADER_LENGTH + length; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/NoOpGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/NoOpGenerator.java index 388bb582cbc..0361b466dc9 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/NoOpGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/NoOpGenerator.java @@ -14,7 +14,7 @@ package org.eclipse.jetty.http2.internal.generator; import org.eclipse.jetty.http2.frames.Frame; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class NoOpGenerator extends FrameGenerator { @@ -24,7 +24,7 @@ public class NoOpGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { return 0; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PingGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PingGenerator.java index fd12c31433d..9299aa77168 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PingGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PingGenerator.java @@ -19,7 +19,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.frames.PingFrame; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class PingGenerator extends FrameGenerator @@ -30,23 +31,24 @@ public class PingGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { PingFrame pingFrame = (PingFrame)frame; - return generatePing(lease, pingFrame.getPayload(), pingFrame.isReply()); + return generatePing(accumulator, pingFrame.getPayload(), pingFrame.isReply()); } - public int generatePing(ByteBufferPool.Lease lease, byte[] payload, boolean reply) + public int generatePing(RetainableByteBufferPool.Accumulator accumulator, byte[] payload, boolean reply) { if (payload.length != PingFrame.PING_LENGTH) throw new IllegalArgumentException("Invalid payload length: " + payload.length); - ByteBuffer header = generateHeader(lease, FrameType.PING, PingFrame.PING_LENGTH, reply ? Flags.ACK : Flags.NONE, 0); + RetainableByteBuffer header = generateHeader(FrameType.PING, PingFrame.PING_LENGTH, reply ? Flags.ACK : Flags.NONE, 0); + ByteBuffer byteBuffer = header.getByteBuffer(); - header.put(payload); + byteBuffer.put(payload); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(header); return Frame.HEADER_LENGTH + PingFrame.PING_LENGTH; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PrefaceGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PrefaceGenerator.java index b2cddd59286..941ab927073 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PrefaceGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PrefaceGenerator.java @@ -17,7 +17,8 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.PrefaceFrame; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class PrefaceGenerator extends FrameGenerator { @@ -27,9 +28,9 @@ public class PrefaceGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { - lease.append(ByteBuffer.wrap(PrefaceFrame.PREFACE_BYTES), false); + accumulator.append(RetainableByteBuffer.wrap(ByteBuffer.wrap(PrefaceFrame.PREFACE_BYTES))); return PrefaceFrame.PREFACE_BYTES.length; } } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PriorityGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PriorityGenerator.java index 8ddb988de0c..707052a007c 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PriorityGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PriorityGenerator.java @@ -19,7 +19,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.frames.PriorityFrame; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class PriorityGenerator extends FrameGenerator @@ -30,18 +31,19 @@ public class PriorityGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { PriorityFrame priorityFrame = (PriorityFrame)frame; - return generatePriority(lease, priorityFrame.getStreamId(), priorityFrame.getParentStreamId(), priorityFrame.getWeight(), priorityFrame.isExclusive()); + return generatePriority(accumulator, priorityFrame.getStreamId(), priorityFrame.getParentStreamId(), priorityFrame.getWeight(), priorityFrame.isExclusive()); } - public int generatePriority(ByteBufferPool.Lease lease, int streamId, int parentStreamId, int weight, boolean exclusive) + public int generatePriority(RetainableByteBufferPool.Accumulator accumulator, int streamId, int parentStreamId, int weight, boolean exclusive) { - ByteBuffer header = generateHeader(lease, FrameType.PRIORITY, PriorityFrame.PRIORITY_LENGTH, Flags.NONE, streamId); - generatePriorityBody(header, streamId, parentStreamId, weight, exclusive); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); + RetainableByteBuffer header = generateHeader(FrameType.PRIORITY, PriorityFrame.PRIORITY_LENGTH, Flags.NONE, streamId); + ByteBuffer byteBuffer = header.getByteBuffer(); + generatePriorityBody(byteBuffer, streamId, parentStreamId, weight, exclusive); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(header); return Frame.HEADER_LENGTH + PriorityFrame.PRIORITY_LENGTH; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PushPromiseGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PushPromiseGenerator.java index a100a5d173a..230cb8fbf8a 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PushPromiseGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/PushPromiseGenerator.java @@ -22,7 +22,8 @@ import org.eclipse.jetty.http2.frames.PushPromiseFrame; import org.eclipse.jetty.http2.hpack.HpackEncoder; import org.eclipse.jetty.http2.hpack.HpackException; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class PushPromiseGenerator extends FrameGenerator @@ -36,13 +37,13 @@ public class PushPromiseGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) throws HpackException + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) throws HpackException { PushPromiseFrame pushPromiseFrame = (PushPromiseFrame)frame; - return generatePushPromise(lease, pushPromiseFrame.getStreamId(), pushPromiseFrame.getPromisedStreamId(), pushPromiseFrame.getMetaData()); + return generatePushPromise(accumulator, pushPromiseFrame.getStreamId(), pushPromiseFrame.getPromisedStreamId(), pushPromiseFrame.getMetaData()); } - public int generatePushPromise(ByteBufferPool.Lease lease, int streamId, int promisedStreamId, MetaData metaData) throws HpackException + public int generatePushPromise(RetainableByteBufferPool.Accumulator accumulator, int streamId, int promisedStreamId, MetaData metaData) throws HpackException { if (streamId < 0) throw new IllegalArgumentException("Invalid stream id: " + streamId); @@ -54,19 +55,21 @@ public class PushPromiseGenerator extends FrameGenerator int extraSpace = 4; maxFrameSize -= extraSpace; - ByteBuffer hpacked = encode(encoder, lease, metaData, maxFrameSize); - int hpackedLength = hpacked.position(); - BufferUtil.flipToFlush(hpacked, 0); + RetainableByteBuffer hpack = encode(encoder, metaData, maxFrameSize); + ByteBuffer hpackByteBuffer = hpack.getByteBuffer(); + int hpackLength = hpackByteBuffer.position(); + BufferUtil.flipToFlush(hpackByteBuffer, 0); - int length = hpackedLength + extraSpace; + int length = hpackLength + extraSpace; int flags = Flags.END_HEADERS; - ByteBuffer header = generateHeader(lease, FrameType.PUSH_PROMISE, length, flags, streamId); - header.putInt(promisedStreamId); - BufferUtil.flipToFlush(header, 0); + RetainableByteBuffer header = generateHeader(FrameType.PUSH_PROMISE, length, flags, streamId); + ByteBuffer headerByteBuffer = header.getByteBuffer(); + headerByteBuffer.putInt(promisedStreamId); + BufferUtil.flipToFlush(headerByteBuffer, 0); - lease.append(header, true); - lease.append(hpacked, true); + accumulator.append(header); + accumulator.append(hpack); return Frame.HEADER_LENGTH + length; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/ResetGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/ResetGenerator.java index ed55d23d5cd..df8960c54f2 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/ResetGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/ResetGenerator.java @@ -19,7 +19,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.frames.ResetFrame; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class ResetGenerator extends FrameGenerator @@ -30,21 +31,22 @@ public class ResetGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { ResetFrame resetFrame = (ResetFrame)frame; - return generateReset(lease, resetFrame.getStreamId(), resetFrame.getError()); + return generateReset(accumulator, resetFrame.getStreamId(), resetFrame.getError()); } - public int generateReset(ByteBufferPool.Lease lease, int streamId, int error) + public int generateReset(RetainableByteBufferPool.Accumulator accumulator, int streamId, int error) { if (streamId < 0) throw new IllegalArgumentException("Invalid stream id: " + streamId); - ByteBuffer header = generateHeader(lease, FrameType.RST_STREAM, ResetFrame.RESET_LENGTH, Flags.NONE, streamId); - header.putInt(error); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); + RetainableByteBuffer header = generateHeader(FrameType.RST_STREAM, ResetFrame.RESET_LENGTH, Flags.NONE, streamId); + ByteBuffer byteBuffer = header.getByteBuffer(); + byteBuffer.putInt(error); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(header); return Frame.HEADER_LENGTH + ResetFrame.RESET_LENGTH; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/SettingsGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/SettingsGenerator.java index e31fe3f5756..ddbee07a498 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/SettingsGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/SettingsGenerator.java @@ -20,7 +20,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.frames.SettingsFrame; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class SettingsGenerator extends FrameGenerator @@ -31,13 +32,13 @@ public class SettingsGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { SettingsFrame settingsFrame = (SettingsFrame)frame; - return generateSettings(lease, settingsFrame.getSettings(), settingsFrame.isReply()); + return generateSettings(accumulator, settingsFrame.getSettings(), settingsFrame.isReply()); } - public int generateSettings(ByteBufferPool.Lease lease, Map settings, boolean reply) + public int generateSettings(RetainableByteBufferPool.Accumulator accumulator, Map settings, boolean reply) { // Two bytes for the identifier, four bytes for the value. int entryLength = 2 + 4; @@ -45,16 +46,17 @@ public class SettingsGenerator extends FrameGenerator if (length > getMaxFrameSize()) throw new IllegalArgumentException("Invalid settings, too big"); - ByteBuffer header = generateHeader(lease, FrameType.SETTINGS, length, reply ? Flags.ACK : Flags.NONE, 0); + RetainableByteBuffer header = generateHeader(FrameType.SETTINGS, length, reply ? Flags.ACK : Flags.NONE, 0); + ByteBuffer byteBuffer = header.getByteBuffer(); for (Map.Entry entry : settings.entrySet()) { - header.putShort(entry.getKey().shortValue()); - header.putInt(entry.getValue()); + byteBuffer.putShort(entry.getKey().shortValue()); + byteBuffer.putInt(entry.getValue()); } - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(header); return Frame.HEADER_LENGTH + length; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/WindowUpdateGenerator.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/WindowUpdateGenerator.java index 960dcd20bb9..d2012ef1eed 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/WindowUpdateGenerator.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/generator/WindowUpdateGenerator.java @@ -19,7 +19,8 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class WindowUpdateGenerator extends FrameGenerator @@ -30,21 +31,22 @@ public class WindowUpdateGenerator extends FrameGenerator } @Override - public int generate(ByteBufferPool.Lease lease, Frame frame) + public int generate(RetainableByteBufferPool.Accumulator accumulator, Frame frame) { WindowUpdateFrame windowUpdateFrame = (WindowUpdateFrame)frame; - return generateWindowUpdate(lease, windowUpdateFrame.getStreamId(), windowUpdateFrame.getWindowDelta()); + return generateWindowUpdate(accumulator, windowUpdateFrame.getStreamId(), windowUpdateFrame.getWindowDelta()); } - public int generateWindowUpdate(ByteBufferPool.Lease lease, int streamId, int windowUpdate) + public int generateWindowUpdate(RetainableByteBufferPool.Accumulator accumulator, int streamId, int windowUpdate) { if (windowUpdate < 0) throw new IllegalArgumentException("Invalid window update: " + windowUpdate); - ByteBuffer header = generateHeader(lease, FrameType.WINDOW_UPDATE, WindowUpdateFrame.WINDOW_UPDATE_LENGTH, Flags.NONE, streamId); - header.putInt(windowUpdate); - BufferUtil.flipToFlush(header, 0); - lease.append(header, true); + RetainableByteBuffer header = generateHeader(FrameType.WINDOW_UPDATE, WindowUpdateFrame.WINDOW_UPDATE_LENGTH, Flags.NONE, streamId); + ByteBuffer byteBuffer = header.getByteBuffer(); + byteBuffer.putInt(windowUpdate); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(header); return Frame.HEADER_LENGTH + WindowUpdateFrame.WINDOW_UPDATE_LENGTH; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ContinuationBodyParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ContinuationBodyParser.java index 7ec64d869b7..5315b8ce67a 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ContinuationBodyParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ContinuationBodyParser.java @@ -20,6 +20,7 @@ import org.eclipse.jetty.http2.frames.ContinuationFrame; import org.eclipse.jetty.http2.frames.HeadersFrame; import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.Flags; +import org.eclipse.jetty.io.RetainableByteBuffer; public class ContinuationBodyParser extends BodyParser { @@ -100,9 +101,9 @@ public class ContinuationBodyParser extends BodyParser private boolean onHeaders(ByteBuffer buffer) { - ByteBuffer headerBlock = headerBlockFragments.complete(); - MetaData metaData = headerBlockParser.parse(headerBlock, headerBlock.remaining()); - headerBlockFragments.getByteBufferPool().release(headerBlock); + RetainableByteBuffer headerBlock = headerBlockFragments.complete(); + MetaData metaData = headerBlockParser.parse(headerBlock.getByteBuffer(), headerBlock.remaining()); + headerBlock.release(); if (metaData == null) return true; if (metaData == HeaderBlockParser.SESSION_FAILURE) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockFragments.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockFragments.java index 8e4e892002c..e247606a0a3 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockFragments.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockFragments.java @@ -16,24 +16,21 @@ package org.eclipse.jetty.http2.internal.parser; import java.nio.ByteBuffer; import org.eclipse.jetty.http2.frames.PriorityFrame; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; public class HeaderBlockFragments { - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private PriorityFrame priorityFrame; private boolean endStream; private int streamId; - private ByteBuffer storage; + private RetainableByteBuffer storage; - public HeaderBlockFragments(ByteBufferPool byteBufferPool) + public HeaderBlockFragments(RetainableByteBufferPool bufferPool) { - this.byteBufferPool = byteBufferPool; - } - - public ByteBufferPool getByteBufferPool() - { - return byteBufferPool; + this.bufferPool = bufferPool; } public void storeFragment(ByteBuffer fragment, int length, boolean last) @@ -41,27 +38,28 @@ public class HeaderBlockFragments if (storage == null) { int space = last ? length : length * 2; - storage = byteBufferPool.acquire(space, fragment.isDirect()); - storage.clear(); + storage = bufferPool.acquire(space, fragment.isDirect()); + BufferUtil.flipToFill(storage.getByteBuffer()); } // Grow the storage if necessary. if (storage.remaining() < length) { + ByteBuffer byteBuffer = storage.getByteBuffer(); int space = last ? length : length * 2; - int capacity = storage.position() + space; - ByteBuffer newStorage = byteBufferPool.acquire(capacity, storage.isDirect()); - newStorage.clear(); - storage.flip(); - newStorage.put(storage); - byteBufferPool.release(storage); + int capacity = byteBuffer.position() + space; + RetainableByteBuffer newStorage = bufferPool.acquire(capacity, storage.isDirect()); + BufferUtil.flipToFill(newStorage.getByteBuffer()); + byteBuffer.flip(); + newStorage.getByteBuffer().put(byteBuffer); + storage.release(); storage = newStorage; } // Copy the fragment into the storage. int limit = fragment.limit(); fragment.limit(fragment.position() + length); - storage.put(fragment); + storage.getByteBuffer().put(fragment); fragment.limit(limit); } @@ -85,11 +83,11 @@ public class HeaderBlockFragments this.endStream = endStream; } - public ByteBuffer complete() + public RetainableByteBuffer complete() { - ByteBuffer result = storage; + RetainableByteBuffer result = storage; storage = null; - result.flip(); + result.getByteBuffer().flip(); return result; } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockParser.java index 8ab18235422..2c8baf850a8 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/HeaderBlockParser.java @@ -20,7 +20,8 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.hpack.HpackDecoder; import org.eclipse.jetty.http2.hpack.HpackException; import org.eclipse.jetty.http2.internal.ErrorCode; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,15 +33,15 @@ public class HeaderBlockParser private static final Logger LOG = LoggerFactory.getLogger(HeaderBlockParser.class); private final HeaderParser headerParser; - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private final HpackDecoder hpackDecoder; private final BodyParser notifier; - private ByteBuffer blockBuffer; + private RetainableByteBuffer blockBuffer; - public HeaderBlockParser(HeaderParser headerParser, ByteBufferPool byteBufferPool, HpackDecoder hpackDecoder, BodyParser notifier) + public HeaderBlockParser(HeaderParser headerParser, RetainableByteBufferPool bufferPool, HpackDecoder hpackDecoder, BodyParser notifier) { this.headerParser = headerParser; - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; this.hpackDecoder = hpackDecoder; this.notifier = notifier; } @@ -61,17 +62,19 @@ public class HeaderBlockParser // If they are not all available, accumulate them. // When all are available, decode them. - int accumulated = blockBuffer == null ? 0 : blockBuffer.position(); + ByteBuffer byteBuffer = blockBuffer == null ? null : blockBuffer.getByteBuffer(); + int accumulated = byteBuffer == null ? 0 : byteBuffer.position(); int remaining = blockLength - accumulated; if (buffer.remaining() < remaining) { if (blockBuffer == null) { - blockBuffer = byteBufferPool.acquire(blockLength, buffer.isDirect()); - BufferUtil.clearToFill(blockBuffer); + blockBuffer = bufferPool.acquire(blockLength, buffer.isDirect()); + byteBuffer = blockBuffer.getByteBuffer(); + BufferUtil.flipToFill(byteBuffer); } - blockBuffer.put(buffer); + byteBuffer.put(buffer); return null; } else @@ -79,11 +82,11 @@ public class HeaderBlockParser int limit = buffer.limit(); buffer.limit(buffer.position() + remaining); ByteBuffer toDecode; - if (blockBuffer != null) + if (byteBuffer != null) { - blockBuffer.put(buffer); - BufferUtil.flipToFlush(blockBuffer, 0); - toDecode = blockBuffer; + byteBuffer.put(buffer); + BufferUtil.flipToFlush(byteBuffer, 0); + toDecode = byteBuffer; } else { @@ -121,7 +124,7 @@ public class HeaderBlockParser if (blockBuffer != null) { - byteBufferPool.release(blockBuffer); + blockBuffer.release(); blockBuffer = null; } } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/Parser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/Parser.java index e5fd3fd74a1..9e616da7b61 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/Parser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/Parser.java @@ -31,7 +31,7 @@ import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.http2.hpack.HpackDecoder; import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +44,7 @@ public class Parser { private static final Logger LOG = LoggerFactory.getLogger(Parser.class); - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private final Listener listener; private final HeaderParser headerParser; private final HpackDecoder hpackDecoder; @@ -55,14 +55,14 @@ public class Parser private boolean continuation; private State state = State.HEADER; - public Parser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize) + public Parser(RetainableByteBufferPool bufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize) { - this(byteBufferPool, listener, maxDynamicTableSize, maxHeaderSize, RateControl.NO_RATE_CONTROL); + this(bufferPool, listener, maxDynamicTableSize, maxHeaderSize, RateControl.NO_RATE_CONTROL); } - public Parser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize, RateControl rateControl) + public Parser(RetainableByteBufferPool bufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize, RateControl rateControl) { - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; this.listener = listener; this.headerParser = new HeaderParser(rateControl == null ? RateControl.NO_RATE_CONTROL : rateControl); this.hpackDecoder = new HpackDecoder(maxDynamicTableSize, maxHeaderSize); @@ -73,8 +73,8 @@ public class Parser { Listener listener = wrapper.apply(this.listener); unknownBodyParser = new UnknownBodyParser(headerParser, listener); - HeaderBlockParser headerBlockParser = new HeaderBlockParser(headerParser, byteBufferPool, hpackDecoder, unknownBodyParser); - HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(byteBufferPool); + HeaderBlockParser headerBlockParser = new HeaderBlockParser(headerParser, bufferPool, hpackDecoder, unknownBodyParser); + HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(bufferPool); bodyParsers[FrameType.DATA.getType()] = new DataBodyParser(headerParser, listener); bodyParsers[FrameType.HEADERS.getType()] = new HeadersBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments); bodyParsers[FrameType.PRIORITY.getType()] = new PriorityBodyParser(headerParser, listener); diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ServerParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ServerParser.java index d7dbfa98095..8023fd4a8a4 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ServerParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/internal/parser/ServerParser.java @@ -19,7 +19,7 @@ import org.eclipse.jetty.http2.RateControl; import org.eclipse.jetty.http2.frames.FrameType; import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.Flags; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,9 +33,9 @@ public class ServerParser extends Parser private State state = State.PREFACE; private boolean notifyPreface = true; - public ServerParser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize, RateControl rateControl) + public ServerParser(RetainableByteBufferPool bufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize, RateControl rateControl) { - super(byteBufferPool, listener, maxDynamicTableSize, maxHeaderSize, rateControl); + super(bufferPool, listener, maxDynamicTableSize, maxHeaderSize, rateControl); this.listener = listener; this.prefaceParser = new PrefaceParser(listener); } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java index 1c68695415c..e8d691a978f 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java @@ -29,8 +29,8 @@ import org.eclipse.jetty.http2.internal.Flags; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.HeadersGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -42,11 +42,11 @@ public class ContinuationParseTest @Test public void testParseOneByteAtATime() throws Exception { - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder()); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(bufferPool), new HpackEncoder()); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) @@ -71,10 +71,10 @@ public class ContinuationParseTest .put("User-Agent", "Jetty"); MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields, -1); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateHeaders(lease, streamId, metaData, null, true); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateHeaders(accumulator, streamId, metaData, null, true); - List byteBuffers = lease.getByteBuffers(); + List byteBuffers = accumulator.getByteBuffers(); assertEquals(2, byteBuffers.size()); ByteBuffer headersBody = byteBuffers.remove(1); @@ -131,7 +131,7 @@ public class ContinuationParseTest byteBuffers.add(headersBody.slice()); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/DataGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/DataGenerateParseTest.java index 23f17926dc6..e0da7e4a542 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/DataGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/DataGenerateParseTest.java @@ -22,8 +22,8 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.generator.DataGenerator; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.junit.jupiter.api.Test; @@ -34,7 +34,7 @@ public class DataGenerateParseTest { private final byte[] smallContent = new byte[128]; private final byte[] largeContent = new byte[128 * 1024]; - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); public DataGenerateParseTest() { @@ -62,7 +62,7 @@ public class DataGenerateParseTest DataFrame frame = frames.get(0); assertTrue(frame.getStreamId() != 0); assertTrue(frame.isEndStream()); - assertEquals(content, frame.getData()); + assertEquals(content, frame.getByteBuffer()); } @Test @@ -77,7 +77,7 @@ public class DataGenerateParseTest DataFrame frame = frames.get(i - 1); assertTrue(frame.getStreamId() != 0); assertEquals(i == frames.size(), frame.isEndStream()); - aggregate.put(frame.getData()); + aggregate.put(frame.getByteBuffer()); } aggregate.flip(); assertEquals(content, aggregate); @@ -85,10 +85,10 @@ public class DataGenerateParseTest private List testGenerateParse(ByteBuffer data) { - DataGenerator generator = new DataGenerator(new HeaderGenerator()); + DataGenerator generator = new DataGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onData(DataFrame frame) @@ -101,19 +101,19 @@ public class DataGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); ByteBuffer slice = data.slice(); int generated = 0; while (true) { - generated += generator.generateData(lease, 13, slice, true, slice.remaining()); + generated += generator.generateData(accumulator, 13, slice, true, slice.remaining()); generated -= Frame.HEADER_LENGTH; if (generated == data.remaining()) break; } frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); } @@ -125,10 +125,10 @@ public class DataGenerateParseTest @Test public void testGenerateParseOneByteAtATime() { - DataGenerator generator = new DataGenerator(new HeaderGenerator()); + DataGenerator generator = new DataGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onData(DataFrame frame) @@ -141,20 +141,20 @@ public class DataGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); ByteBuffer data = ByteBuffer.wrap(largeContent); ByteBuffer slice = data.slice(); int generated = 0; while (true) { - generated += generator.generateData(lease, 13, slice, true, slice.remaining()); + generated += generator.generateData(accumulator, 13, slice, true, slice.remaining()); generated -= Frame.HEADER_LENGTH; if (generated == data.remaining()) break; } frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java index b52e9e35e52..47b80c64c6a 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java @@ -24,8 +24,8 @@ import org.eclipse.jetty.http2.WindowRateControl; import org.eclipse.jetty.http2.hpack.HpackEncoder; import org.eclipse.jetty.http2.internal.Flags; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -33,7 +33,7 @@ import static org.hamcrest.Matchers.lessThan; public class FrameFloodTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); // Frame structure: // | Len0 | Len1 | Len2 | Type | Flags | StreamID0 |StreamID1 |StreamID2 |StreamID3 | Payload... | @@ -72,7 +72,7 @@ public class FrameFloodTest public void testInvalidHeadersFrameFlood() throws Exception { // Invalid MetaData (no method, no scheme, etc). - MetaData.Request metadata = new MetaData.Request(null, (String)null, null, null, HttpVersion.HTTP_2, null, -1); + MetaData.Request metadata = new MetaData.Request(null, null, null, null, HttpVersion.HTTP_2, null, -1); HpackEncoder encoder = new HpackEncoder(); ByteBuffer buffer = ByteBuffer.allocate(1024); encoder.encode(buffer, metadata); @@ -123,7 +123,7 @@ public class FrameFloodTest private void testFrameFlood(byte[] preamble, byte[] bytes) { AtomicBoolean failed = new AtomicBoolean(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onConnectionFailure(int error, String reason) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/GoAwayGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/GoAwayGenerateParseTest.java index df27cdc3417..15d5c4fa5d1 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/GoAwayGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/GoAwayGenerateParseTest.java @@ -22,8 +22,8 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.generator.GoAwayGenerator; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -32,15 +32,15 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class GoAwayGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParse() throws Exception { - GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator()); + GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onGoAway(GoAwayFrame frame) @@ -56,11 +56,11 @@ public class GoAwayGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateGoAway(lease, lastStreamId, error, null); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateGoAway(accumulator, lastStreamId, error, null); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -79,10 +79,10 @@ public class GoAwayGenerateParseTest @Test public void testGenerateParseOneByteAtATime() throws Exception { - GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator()); + GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onGoAway(GoAwayFrame frame) @@ -100,11 +100,11 @@ public class GoAwayGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateGoAway(lease, lastStreamId, error, payload); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateGoAway(accumulator, lastStreamId, error, payload); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java index 90f89f05cd0..6e1fe0d61aa 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java @@ -28,8 +28,8 @@ import org.eclipse.jetty.http2.hpack.HpackEncoder; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.HeadersGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -38,12 +38,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class HeadersGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParse() throws Exception { - HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder()); + HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(bufferPool), new HpackEncoder()); int streamId = 13; HttpFields fields = HttpFields.build() @@ -52,7 +52,7 @@ public class HeadersGenerateParseTest MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields, -1); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) @@ -65,12 +65,12 @@ public class HeadersGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true); - generator.generateHeaders(lease, streamId, metaData, priorityFrame, true); + generator.generateHeaders(accumulator, streamId, metaData, priorityFrame, true); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -102,10 +102,10 @@ public class HeadersGenerateParseTest @Test public void testGenerateParseOneByteAtATime() throws Exception { - HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder()); + HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(bufferPool), new HpackEncoder()); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) @@ -124,12 +124,12 @@ public class HeadersGenerateParseTest .put("User-Agent", "Jetty"); MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields, -1); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true); - generator.generateHeaders(lease, streamId, metaData, priorityFrame, true); + generator.generateHeaders(accumulator, streamId, metaData, priorityFrame, true); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { buffer = buffer.slice(); while (buffer.hasRemaining()) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersTooLargeParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersTooLargeParseTest.java index 141176f4efd..c049a51f48c 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersTooLargeParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersTooLargeParseTest.java @@ -28,8 +28,8 @@ import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.HeadersGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -37,7 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class HeadersTooLargeParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testProtocolErrorURITooLong() throws HpackException @@ -64,10 +64,10 @@ public class HeadersTooLargeParseTest private void assertProtocolError(int maxHeaderSize, MetaData.Request metaData) throws HpackException { - HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder()); + HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(bufferPool), new HpackEncoder()); AtomicInteger failure = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onConnectionFailure(int error, String reason) @@ -78,11 +78,11 @@ public class HeadersTooLargeParseTest parser.init(UnaryOperator.identity()); int streamId = 48; - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true); - int len = generator.generateHeaders(lease, streamId, metaData, priorityFrame, true); + int len = generator.generateHeaders(accumulator, streamId, metaData, priorityFrame, true); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining() && failure.get() == 0) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/MaxFrameSizeParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/MaxFrameSizeParseTest.java index 478a6f8e4f5..126151780fb 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/MaxFrameSizeParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/MaxFrameSizeParseTest.java @@ -19,15 +19,15 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class MaxFrameSizeParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testMaxFrameSize() @@ -35,7 +35,7 @@ public class MaxFrameSizeParseTest int maxFrameLength = Frame.DEFAULT_MAX_LENGTH + 16; AtomicInteger failure = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onConnectionFailure(int error, String reason) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java index 39f1f8c0d3b..99bd496e8ca 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java @@ -22,8 +22,8 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.PingGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.NanoTime; import org.junit.jupiter.api.Test; @@ -33,15 +33,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class PingGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParse() throws Exception { - PingGenerator generator = new PingGenerator(new HeaderGenerator()); + PingGenerator generator = new PingGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onPing(PingFrame frame) @@ -57,11 +57,11 @@ public class PingGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generatePing(lease, payload, true); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generatePing(accumulator, payload, true); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -79,10 +79,10 @@ public class PingGenerateParseTest @Test public void testGenerateParseOneByteAtATime() throws Exception { - PingGenerator generator = new PingGenerator(new HeaderGenerator()); + PingGenerator generator = new PingGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onPing(PingFrame frame) @@ -98,11 +98,11 @@ public class PingGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generatePing(lease, payload, true); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generatePing(accumulator, payload, true); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -120,10 +120,10 @@ public class PingGenerateParseTest @Test public void testPayloadAsLong() throws Exception { - PingGenerator generator = new PingGenerator(new HeaderGenerator()); + PingGenerator generator = new PingGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onPing(PingFrame frame) @@ -133,11 +133,11 @@ public class PingGenerateParseTest }, 4096, 8192); parser.init(UnaryOperator.identity()); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); PingFrame ping = new PingFrame(NanoTime.now(), true); - generator.generate(lease, ping); + generator.generate(accumulator, ping); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java index 8d048fbaa00..27a372243a6 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java @@ -21,23 +21,23 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.PriorityGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class PriorityGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParse() throws Exception { - PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator()); + PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onPriority(PriorityFrame frame) @@ -55,11 +55,11 @@ public class PriorityGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generatePriority(lease, streamId, parentStreamId, weight, exclusive); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generatePriority(accumulator, streamId, parentStreamId, weight, exclusive); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -79,10 +79,10 @@ public class PriorityGenerateParseTest @Test public void testGenerateParseOneByteAtATime() throws Exception { - PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator()); + PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onPriority(PriorityFrame frame) @@ -100,11 +100,11 @@ public class PriorityGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generatePriority(lease, streamId, parentStreamId, weight, exclusive); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generatePriority(accumulator, streamId, parentStreamId, weight, exclusive); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PushPromiseGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PushPromiseGenerateParseTest.java index 812d4ff2f89..ce5b61a8610 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PushPromiseGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/PushPromiseGenerateParseTest.java @@ -28,8 +28,8 @@ import org.eclipse.jetty.http2.hpack.HpackEncoder; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.PushPromiseGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -37,15 +37,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class PushPromiseGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParse() throws Exception { - PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder()); + PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(bufferPool), new HpackEncoder()); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onPushPromise(PushPromiseFrame frame) @@ -65,11 +65,11 @@ public class PushPromiseGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generatePushPromise(lease, streamId, promisedStreamId, metaData); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generatePushPromise(accumulator, streamId, promisedStreamId, metaData); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -95,10 +95,10 @@ public class PushPromiseGenerateParseTest @Test public void testGenerateParseOneByteAtATime() throws Exception { - PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder()); + PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(bufferPool), new HpackEncoder()); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onPushPromise(PushPromiseFrame frame) @@ -118,11 +118,11 @@ public class PushPromiseGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generatePushPromise(lease, streamId, promisedStreamId, metaData); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generatePushPromise(accumulator, streamId, promisedStreamId, metaData); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -134,7 +134,7 @@ public class PushPromiseGenerateParseTest PushPromiseFrame frame = frames.get(0); assertEquals(streamId, frame.getStreamId()); assertEquals(promisedStreamId, frame.getPromisedStreamId()); - MetaData.Request request = (MetaData.Request)frame.getMetaData(); + MetaData.Request request = frame.getMetaData(); assertEquals(metaData.getMethod(), request.getMethod()); assertEquals(metaData.getURI(), request.getURI()); for (int j = 0; j < fields.size(); ++j) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ResetGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ResetGenerateParseTest.java index 5bbbb44c109..4ed17c70e9d 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ResetGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ResetGenerateParseTest.java @@ -21,23 +21,23 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.ResetGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class ResetGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParse() throws Exception { - ResetGenerator generator = new ResetGenerator(new HeaderGenerator()); + ResetGenerator generator = new ResetGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onReset(ResetFrame frame) @@ -53,11 +53,11 @@ public class ResetGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateReset(lease, streamId, error); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateReset(accumulator, streamId, error); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -75,10 +75,10 @@ public class ResetGenerateParseTest @Test public void testGenerateParseOneByteAtATime() throws Exception { - ResetGenerator generator = new ResetGenerator(new HeaderGenerator()); + ResetGenerator generator = new ResetGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onReset(ResetFrame frame) @@ -94,11 +94,11 @@ public class ResetGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateReset(lease, streamId, error); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateReset(accumulator, streamId, error); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java index a5689245051..735d074418c 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java @@ -26,8 +26,8 @@ import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.SettingsGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -35,7 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class SettingsGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParseNoSettings() @@ -69,10 +69,10 @@ public class SettingsGenerateParseTest private List testGenerateParse(Map settings) { - SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); + SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator(bufferPool)); List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onSettings(SettingsFrame frame) @@ -85,11 +85,11 @@ public class SettingsGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateSettings(lease, settings, true); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateSettings(accumulator, settings, true); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -104,10 +104,10 @@ public class SettingsGenerateParseTest @Test public void testGenerateParseInvalidSettings() { - SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); + SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator(bufferPool)); AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onConnectionFailure(int error, String reason) @@ -119,13 +119,13 @@ public class SettingsGenerateParseTest Map settings1 = new HashMap<>(); settings1.put(13, 17); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateSettings(lease, settings1, true); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateSettings(accumulator, settings1, true); // Modify the length of the frame to make it invalid - ByteBuffer bytes = lease.getByteBuffers().get(0); + ByteBuffer bytes = accumulator.getByteBuffers().get(0); bytes.putShort(1, (short)(bytes.getShort(1) - 1)); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -139,10 +139,10 @@ public class SettingsGenerateParseTest @Test public void testGenerateParseOneByteAtATime() { - SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); + SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator(bufferPool)); List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onSettings(SettingsFrame frame) @@ -160,11 +160,11 @@ public class SettingsGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateSettings(lease, settings1, true); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateSettings(accumulator, settings1, true); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -184,10 +184,10 @@ public class SettingsGenerateParseTest @Test public void testGenerateParseTooManyDifferentSettingsInOneFrame() { - SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); + SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator(bufferPool)); AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onConnectionFailure(int error, String reason) @@ -205,10 +205,10 @@ public class SettingsGenerateParseTest settings.put(i + 10, i); } - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateSettings(lease, settings, false); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateSettings(accumulator, settings, false); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -227,7 +227,7 @@ public class SettingsGenerateParseTest int maxSettingsKeys = pairs / 2; AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter(), 4096, 8192); parser.setMaxSettingsKeys(maxSettingsKeys); parser.setMaxFrameLength(Frame.DEFAULT_MAX_LENGTH); parser.init(listener -> new Parser.Listener.Wrapper(listener) @@ -265,10 +265,10 @@ public class SettingsGenerateParseTest @Test public void testGenerateParseTooManySettingsInMultipleFrames() { - SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); + SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator(bufferPool)); AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onConnectionFailure(int error, String reason) @@ -283,13 +283,13 @@ public class SettingsGenerateParseTest Map settings = new HashMap<>(); settings.put(13, 17); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); for (int i = 0; i < maxSettingsKeys + 1; ++i) { - generator.generateSettings(lease, settings, false); + generator.generateSettings(accumulator, settings, false); } - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/UnknownParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/UnknownParseTest.java index bd55440be64..68336fb08cf 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/UnknownParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/UnknownParseTest.java @@ -21,8 +21,8 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,7 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; public class UnknownParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testParse() @@ -48,7 +48,7 @@ public class UnknownParseTest public void testInvalidFrameSize() { AtomicInteger failure = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter(), 4096, 8192); parser.init(listener -> new Parser.Listener.Wrapper(listener) { @Override @@ -73,7 +73,7 @@ public class UnknownParseTest private void testParse(Function fn) { AtomicBoolean failure = new AtomicBoolean(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onConnectionFailure(int error, String reason) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/WindowUpdateGenerateParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/WindowUpdateGenerateParseTest.java index f87496558c9..21a165397b9 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/WindowUpdateGenerateParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/WindowUpdateGenerateParseTest.java @@ -21,23 +21,23 @@ import java.util.function.UnaryOperator; import org.eclipse.jetty.http2.internal.generator.HeaderGenerator; import org.eclipse.jetty.http2.internal.generator.WindowUpdateGenerator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class WindowUpdateGenerateParseTest { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testGenerateParse() throws Exception { - WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator()); + WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onWindowUpdate(WindowUpdateFrame frame) @@ -53,11 +53,11 @@ public class WindowUpdateGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateWindowUpdate(lease, streamId, windowUpdate); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateWindowUpdate(accumulator, streamId, windowUpdate); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { @@ -75,10 +75,10 @@ public class WindowUpdateGenerateParseTest @Test public void testGenerateParseOneByteAtATime() throws Exception { - WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator()); + WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator(bufferPool)); final List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onWindowUpdate(WindowUpdateFrame frame) @@ -94,11 +94,11 @@ public class WindowUpdateGenerateParseTest // Iterate a few times to be sure generator and parser are properly reset. for (int i = 0; i < 2; ++i) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateWindowUpdate(lease, streamId, windowUpdate); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.generateWindowUpdate(accumulator, streamId, windowUpdate); frames.clear(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { while (buffer.hasRemaining()) { diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index b49b9cccaed..b85935bc9ee 100644 --- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -271,7 +271,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne { ServerSessionListener listener = newSessionListener(connector, endPoint); - Generator generator = new Generator(connector.getByteBufferPool(), isUseOutputDirectByteBuffers(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment()); + Generator generator = new Generator(connector.getRetainableByteBufferPool(), isUseOutputDirectByteBuffers(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment()); FlowControlStrategy flowControl = getFlowControlStrategyFactory().newFlowControlStrategy(); HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, generator, listener, flowControl); session.setMaxLocalStreams(getMaxConcurrentStreams()); @@ -291,7 +291,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - RetainableByteBufferPool retainableByteBufferPool = connector.getByteBufferPool().asRetainableByteBufferPool(); + RetainableByteBufferPool retainableByteBufferPool = connector.getRetainableByteBufferPool(); HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector, endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); @@ -305,7 +305,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne private ServerParser newServerParser(Connector connector, ServerParser.Listener listener, RateControl rateControl) { - return new ServerParser(connector.getByteBufferPool(), listener, getMaxDynamicTableSize(), getHttpConfiguration().getRequestHeaderSize(), rateControl); + return new ServerParser(connector.getRetainableByteBufferPool(), listener, getMaxDynamicTableSize(), getHttpConfiguration().getRequestHeaderSize(), rateControl); } @ManagedObject("The container of HTTP/2 sessions") diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java index 06d4026956f..2d0162cc4fb 100644 --- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java +++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java @@ -253,7 +253,7 @@ public class HttpStreamOverHTTP2 implements HttpStream, HTTP2Channel.Server data.release(); return Content.Chunk.EOF; } - return Content.Chunk.asChunk(frame.getData(), frame.isEndStream(), data); + return Content.Chunk.asChunk(frame.getByteBuffer(), frame.isEndStream(), data); } @Override diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractServerTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractServerTest.java index aa6fd997ee3..cbf4b859cbb 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractServerTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractServerTest.java @@ -29,8 +29,8 @@ import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.internal.parser.Parser; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; @@ -42,7 +42,7 @@ import org.junit.jupiter.api.AfterEach; public class AbstractServerTest { protected ServerConnector connector; - protected ByteBufferPool byteBufferPool; + protected RetainableByteBufferPool bufferPool; protected Generator generator; protected Server server; protected String path; @@ -68,8 +68,8 @@ public class AbstractServerTest connector = new ServerConnector(server, connectionFactory); server.addConnector(connector); path = "/test"; - byteBufferPool = new MappedByteBufferPool(); - generator = new Generator(byteBufferPool); + bufferPool = new ArrayRetainableByteBufferPool(); + generator = new Generator(bufferPool); } protected MetaData.Request newRequest(String method, HttpFields fields) diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/BadURITest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/BadURITest.java index 0607b266261..8cf88772aea 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/BadURITest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/BadURITest.java @@ -31,7 +31,7 @@ import org.eclipse.jetty.http2.frames.PrefaceFrame; import org.eclipse.jetty.http2.frames.SettingsFrame; import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.Request; @@ -97,8 +97,8 @@ public class BadURITest } }); - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - Generator generator = new Generator(byteBufferPool); + RetainableByteBufferPool bufferPool = connector.getRetainableByteBufferPool(); + Generator generator = new Generator(bufferPool); // Craft a request with a bad URI, it will not hit the Handler. MetaData.Request metaData1 = new MetaData.Request( @@ -111,15 +111,15 @@ public class BadURITest HttpFields.EMPTY, -1 ); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); - generator.control(lease, new HeadersFrame(1, metaData1, null, true)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); + generator.control(accumulator, new HeadersFrame(1, metaData1, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } @@ -128,7 +128,7 @@ public class BadURITest Thread.sleep(1000); // Send a second request and verify that it hits the Handler. - lease.recycle(); + accumulator.release(); MetaData.Request metaData2 = new MetaData.Request( HttpMethod.GET.asString(), HttpScheme.HTTP.asString(), @@ -138,8 +138,8 @@ public class BadURITest HttpFields.EMPTY, -1 ); - generator.control(lease, new HeadersFrame(3, metaData2, null, true)); - for (ByteBuffer buffer : lease.getByteBuffers()) + generator.control(accumulator, new HeadersFrame(3, metaData2, null, true)); + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/CloseTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/CloseTest.java index f82498cf7f0..9ecb271995b 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/CloseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/CloseTest.java @@ -37,7 +37,7 @@ import org.eclipse.jetty.http2.frames.SettingsFrame; import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.HTTP2Session; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -74,21 +74,21 @@ public class CloseTest extends AbstractServerTest } }); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) @@ -135,17 +135,17 @@ public class CloseTest extends AbstractServerTest } }); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); - generator.control(lease, new GoAwayFrame(1, ErrorCode.NO_ERROR.code, "OK".getBytes(StandardCharsets.UTF_8))); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new GoAwayFrame(1, ErrorCode.NO_ERROR.code, "OK".getBytes(StandardCharsets.UTF_8))); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } @@ -153,7 +153,7 @@ public class CloseTest extends AbstractServerTest // Don't close the connection; the server should close. final CountDownLatch responseLatch = new CountDownLatch(1); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) @@ -202,23 +202,23 @@ public class CloseTest extends AbstractServerTest }); connector.setIdleTimeout(idleTimeout); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } final CountDownLatch responseLatch = new CountDownLatch(1); final CountDownLatch closeLatch = new CountDownLatch(1); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/DataDemandTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/DataDemandTest.java index 2d3e98b9526..64da0dda26a 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/DataDemandTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/DataDemandTest.java @@ -35,8 +35,8 @@ import org.eclipse.jetty.http2.frames.DataFrame; import org.eclipse.jetty.http2.frames.HeadersFrame; import org.eclipse.jetty.http2.internal.HTTP2Session; import org.eclipse.jetty.http2.internal.generator.Generator; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.Promise; @@ -119,7 +119,7 @@ public class DataDemandTest extends AbstractTest await().atMost(1, TimeUnit.SECONDS).until(() -> serverQueue.size() == count.get() + 1); count.incrementAndGet(); long sum = serverQueue.stream() - .mapToLong(data -> data.frame().getData().remaining()) + .mapToLong(data -> data.frame().getByteBuffer().remaining()) .sum(); if (sum == length) break; @@ -155,7 +155,7 @@ public class DataDemandTest extends AbstractTest await().atMost(1, TimeUnit.SECONDS).until(() -> clientQueue.size() == count.get() + 1); count.incrementAndGet(); long sum = clientQueue.stream() - .mapToLong(data -> data.frame().getData().remaining()) + .mapToLong(data -> data.frame().getByteBuffer().remaining()) .sum(); if (sum == length) break; @@ -365,17 +365,17 @@ public class DataDemandTest extends AbstractTest // Generate a lot of small DATA frames and write them in a single // write so that the server will continuously be notified and demand, // which will test that it won't throw StackOverflowError. - MappedByteBufferPool byteBufferPool = new MappedByteBufferPool(); - Generator generator = new Generator(byteBufferPool); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + Generator generator = new Generator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); for (int i = 512; i >= 0; --i) - generator.data(lease, new DataFrame(clientStream.getId(), ByteBuffer.allocate(1), i == 0), 1); + generator.data(accumulator, new DataFrame(clientStream.getId(), ByteBuffer.allocate(1), i == 0), 1); // Since this is a naked write, we need to wait that the // client finishes writing the SETTINGS reply to the server // during connection initialization, or we risk a WritePendingException. Thread.sleep(1000); - ((HTTP2Session)clientStream.getSession()).getEndPoint().write(Callback.NOOP, lease.getByteBuffers().toArray(new ByteBuffer[0])); + ((HTTP2Session)clientStream.getSession()).getEndPoint().write(Callback.NOOP, accumulator.getByteBuffers().toArray(ByteBuffer[]::new)); assertTrue(latch.await(15, TimeUnit.SECONDS)); } diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/FlowControlStrategyTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/FlowControlStrategyTest.java index b23bbb3c850..bc1a11a3a5a 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/FlowControlStrategyTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/FlowControlStrategyTest.java @@ -51,7 +51,7 @@ import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.HTTP2Session; import org.eclipse.jetty.http2.internal.HTTP2Stream; import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -667,7 +667,7 @@ public abstract class FlowControlStrategyTest Stream.Data data = stream.readData(); DataFrame frame = data.frame(); int remaining = frame.remaining(); - frame.getData().get(bytes, received, remaining); + frame.getByteBuffer().get(bytes, received, remaining); this.received += remaining; data.release(); if (frame.isEndStream()) @@ -737,7 +737,7 @@ public abstract class FlowControlStrategyTest public void onDataAvailable(Stream stream) { Stream.Data data = stream.readData(); - responseContent.put(data.frame().getData()); + responseContent.put(data.frame().getByteBuffer()); data.release(); if (data.frame().isEndStream()) latch.countDown(); @@ -844,10 +844,10 @@ public abstract class FlowControlStrategyTest // Now the client is supposed to not send more frames. // If it does, the connection must be closed. HTTP2Session http2Session = (HTTP2Session)session; - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(connector.getByteBufferPool()); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); ByteBuffer extraData = ByteBuffer.allocate(1024); - http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining()); - List buffers = lease.getByteBuffers(); + http2Session.getGenerator().data(accumulator, new DataFrame(stream.getId(), extraData, true), extraData.remaining()); + List buffers = accumulator.getByteBuffers(); http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[0])); // Expect the connection to be closed. @@ -949,10 +949,10 @@ public abstract class FlowControlStrategyTest // Now the client is supposed to not send more frames. // If it does, the connection must be closed. HTTP2Session http2Session = (HTTP2Session)session; - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(connector.getByteBufferPool()); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); ByteBuffer extraData = ByteBuffer.allocate(1024); - http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining()); - List buffers = lease.getByteBuffers(); + http2Session.getGenerator().data(accumulator, new DataFrame(stream.getId(), extraData, true), extraData.remaining()); + List buffers = accumulator.getByteBuffers(); http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[0])); // Expect the connection to be closed. diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java index ae23a8b12e7..c9d7d7d080d 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java @@ -38,10 +38,10 @@ import org.eclipse.jetty.http2.frames.PrefaceFrame; import org.eclipse.jetty.http2.frames.SettingsFrame; import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.internal.parser.Parser; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.ServerConnector; @@ -150,13 +150,13 @@ public class HTTP2CServerTest extends AbstractServerTest assertTrue(upgrade.toString().startsWith("HTTP/1.1 101 ")); - byteBufferPool = new MappedByteBufferPool(); - generator = new Generator(byteBufferPool); + bufferPool = new ArrayRetainableByteBufferPool(); + generator = new Generator(bufferPool); final AtomicReference headersRef = new AtomicReference<>(); final AtomicReference dataRef = new AtomicReference<>(); final AtomicReference latchRef = new AtomicReference<>(new CountDownLatch(2)); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) @@ -186,7 +186,7 @@ public class HTTP2CServerTest extends AbstractServerTest DataFrame responseData = dataRef.get(); assertNotNull(responseData); - String content = BufferUtil.toString(responseData.getData()); + String content = BufferUtil.toString(responseData.getByteBuffer()); // The upgrade request is seen as HTTP/1.1. assertThat(content, containsString("Hello from Jetty using HTTP/2.0")); @@ -196,12 +196,12 @@ public class HTTP2CServerTest extends AbstractServerTest headersRef.set(null); dataRef.set(null); latchRef.set(new CountDownLatch(2)); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:" + connector.getLocalPort()), "/two", HttpVersion.HTTP_2, HttpFields.EMPTY, -1); - generator.control(lease, new HeadersFrame(3, metaData, null, true)); - for (ByteBuffer buffer : lease.getByteBuffers()) + generator.control(accumulator, new HeadersFrame(3, metaData, null, true)); + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } @@ -219,7 +219,7 @@ public class HTTP2CServerTest extends AbstractServerTest responseData = dataRef.get(); assertNotNull(responseData); - content = BufferUtil.toString(responseData.getData()); + content = BufferUtil.toString(responseData.getByteBuffer()); assertThat(content, containsString("Hello from Jetty using HTTP/2.0")); assertThat(content, containsString("uri=/two")); @@ -231,28 +231,28 @@ public class HTTP2CServerTest extends AbstractServerTest { final CountDownLatch latch = new CountDownLatch(3); - byteBufferPool = new MappedByteBufferPool(); - generator = new Generator(byteBufferPool); + bufferPool = new ArrayRetainableByteBufferPool(); + generator = new Generator(bufferPool); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:" + connector.getLocalPort()), "/test", HttpVersion.HTTP_2, HttpFields.EMPTY, -1); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { client.setSoTimeout(5000); OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } final AtomicReference headersRef = new AtomicReference<>(); final AtomicReference dataRef = new AtomicReference<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onSettings(SettingsFrame frame) @@ -288,7 +288,7 @@ public class HTTP2CServerTest extends AbstractServerTest DataFrame responseData = dataRef.get(); assertNotNull(responseData); - String s = BufferUtil.toString(responseData.getData()); + String s = BufferUtil.toString(responseData.getByteBuffer()); assertThat(s, containsString("Hello from Jetty using HTTP/2.0")); assertThat(s, containsString("uri=/test")); @@ -328,18 +328,18 @@ public class HTTP2CServerTest extends AbstractServerTest // Now send an HTTP/2 direct request, which // will have the PRI * HTTP/2.0 preface. - byteBufferPool = new MappedByteBufferPool(); - generator = new Generator(byteBufferPool); + bufferPool = new ArrayRetainableByteBufferPool(); + generator = new Generator(bufferPool); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); try (Socket client = new Socket("localhost", connector.getLocalPort())) { client.setSoTimeout(5000); OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2ServerTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2ServerTest.java index 106d8f7d3dd..9b1a83bbfd7 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2ServerTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2ServerTest.java @@ -47,8 +47,9 @@ import org.eclipse.jetty.http2.internal.Flags; import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.internal.parser.Parser; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ManagedSelector; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.SocketChannelEndPoint; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.Handler; @@ -84,19 +85,19 @@ public class HTTP2ServerTest extends AbstractServerTest // No preface bytes. MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } - final CountDownLatch latch = new CountDownLatch(1); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + CountDownLatch latch = new CountDownLatch(1); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onGoAway(GoAwayFrame frame) @@ -115,7 +116,7 @@ public class HTTP2ServerTest extends AbstractServerTest @Test public void testRequestResponseNoContent() throws Exception { - final CountDownLatch latch = new CountDownLatch(3); + CountDownLatch latch = new CountDownLatch(3); startServer(new Handler.Abstract() { @Override @@ -127,22 +128,22 @@ public class HTTP2ServerTest extends AbstractServerTest } }); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } - final AtomicReference frameRef = new AtomicReference<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + AtomicReference frameRef = new AtomicReference<>(); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onSettings(SettingsFrame frame) @@ -173,8 +174,8 @@ public class HTTP2ServerTest extends AbstractServerTest @Test public void testRequestResponseContent() throws Exception { - final byte[] content = "Hello, world!".getBytes(StandardCharsets.UTF_8); - final CountDownLatch latch = new CountDownLatch(4); + byte[] content = "Hello, world!".getBytes(StandardCharsets.UTF_8); + CountDownLatch latch = new CountDownLatch(4); startServer(new Handler.Abstract() { @Override @@ -186,23 +187,23 @@ public class HTTP2ServerTest extends AbstractServerTest } }); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } - final AtomicReference headersRef = new AtomicReference<>(); - final AtomicReference dataRef = new AtomicReference<>(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + AtomicReference headersRef = new AtomicReference<>(); + AtomicReference dataRef = new AtomicReference<>(); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onSettings(SettingsFrame frame) @@ -237,7 +238,7 @@ public class HTTP2ServerTest extends AbstractServerTest DataFrame responseData = dataRef.get(); assertNotNull(responseData); - assertArrayEquals(content, BufferUtil.toArray(responseData.getData())); + assertArrayEquals(content, BufferUtil.toArray(responseData.getByteBuffer())); } } @@ -254,23 +255,23 @@ public class HTTP2ServerTest extends AbstractServerTest } }); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); - generator.control(lease, new PingFrame(new byte[8], false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); + generator.control(accumulator, new PingFrame(new byte[8], false)); // Modify the length of the frame to a wrong one. - lease.getByteBuffers().get(2).putShort(0, (short)7); + accumulator.getByteBuffers().get(2).putShort(0, (short)7); - final CountDownLatch latch = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onGoAway(GoAwayFrame frame) @@ -300,23 +301,23 @@ public class HTTP2ServerTest extends AbstractServerTest } }); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); - generator.control(lease, new PingFrame(new byte[8], false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); + generator.control(accumulator, new PingFrame(new byte[8], false)); // Modify the streamId of the frame to non zero. - lease.getByteBuffers().get(2).putInt(4, 1); + accumulator.getByteBuffers().get(2).putInt(4, 1); - final CountDownLatch latch = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onGoAway(GoAwayFrame frame) @@ -336,8 +337,8 @@ public class HTTP2ServerTest extends AbstractServerTest @Test public void testCommitFailure() throws Exception { - final long delay = 1000; - final AtomicBoolean broken = new AtomicBoolean(); + long delay = 1000; + AtomicBoolean broken = new AtomicBoolean(); startServer(new Handler.Abstract() { @Override @@ -373,22 +374,22 @@ public class HTTP2ServerTest extends AbstractServerTest server.addConnector(connector2); server.start(); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector2.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } // The server will close the connection abruptly since it // cannot write and therefore cannot even send the GO_AWAY. - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter(), 4096, 8192); parser.init(UnaryOperator.identity()); boolean closed = parseResponse(client, parser, 2 * delay); assertTrue(closed); @@ -413,22 +414,22 @@ public class HTTP2ServerTest extends AbstractServerTest } }); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } output.flush(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter(), 4096, 8192); parser.init(UnaryOperator.identity()); boolean closed = parseResponse(client, parser); @@ -442,12 +443,12 @@ public class HTTP2ServerTest extends AbstractServerTest { testRequestWithContinuationFrames(null, () -> { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); - return lease; + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); + return accumulator; }); } @@ -457,12 +458,12 @@ public class HTTP2ServerTest extends AbstractServerTest PriorityFrame priority = new PriorityFrame(1, 13, 200, true); testRequestWithContinuationFrames(priority, () -> { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, priority, true)); - return lease; + generator.control(accumulator, new HeadersFrame(1, metaData, priority, true)); + return accumulator; }); } @@ -471,19 +472,19 @@ public class HTTP2ServerTest extends AbstractServerTest { testRequestWithContinuationFrames(null, () -> { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); // Take the HeadersFrame header and set the length to zero. - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); ByteBuffer headersFrameHeader = buffers.get(2); headersFrameHeader.put(0, (byte)0); headersFrameHeader.putShort(1, (short)0); // Insert a CONTINUATION frame header for the body of the HEADERS frame. - lease.insert(3, buffers.get(4).slice(), false); - return lease; + accumulator.insert(3, RetainableByteBuffer.wrap(buffers.get(4).slice())); + return accumulator; }); } @@ -493,19 +494,19 @@ public class HTTP2ServerTest extends AbstractServerTest PriorityFrame priority = new PriorityFrame(1, 13, 200, true); testRequestWithContinuationFrames(null, () -> { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, priority, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, priority, true)); // Take the HeadersFrame header and set the length to just the priority frame. - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); ByteBuffer headersFrameHeader = buffers.get(2); headersFrameHeader.put(0, (byte)0); headersFrameHeader.putShort(1, (short)PriorityFrame.PRIORITY_LENGTH); // Insert a CONTINUATION frame header for the body of the HEADERS frame. - lease.insert(3, buffers.get(4).slice(), false); - return lease; + accumulator.insert(3, RetainableByteBuffer.wrap(buffers.get(4).slice())); + return accumulator; }); } @@ -514,13 +515,13 @@ public class HTTP2ServerTest extends AbstractServerTest { testRequestWithContinuationFrames(null, () -> { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); // Take the ContinuationFrame header, duplicate it, and set the length to zero. - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); ByteBuffer continuationFrameHeader = buffers.get(4); ByteBuffer duplicate = ByteBuffer.allocate(continuationFrameHeader.remaining()); duplicate.put(continuationFrameHeader).flip(); @@ -528,8 +529,8 @@ public class HTTP2ServerTest extends AbstractServerTest continuationFrameHeader.put(0, (byte)0); continuationFrameHeader.putShort(1, (short)0); // Insert a CONTINUATION frame header for the body of the previous CONTINUATION frame. - lease.insert(5, duplicate, false); - return lease; + accumulator.insert(5, RetainableByteBuffer.wrap(duplicate)); + return accumulator; }); } @@ -538,13 +539,13 @@ public class HTTP2ServerTest extends AbstractServerTest { testRequestWithContinuationFrames(null, () -> { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY); - generator.control(lease, new HeadersFrame(1, metaData, null, true)); + generator.control(accumulator, new HeadersFrame(1, metaData, null, true)); // Take the last CONTINUATION frame and reset the flag. - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); ByteBuffer continuationFrameHeader = buffers.get(buffers.size() - 2); continuationFrameHeader.put(4, (byte)0); // Add a last, empty, CONTINUATION frame. @@ -554,14 +555,14 @@ public class HTTP2ServerTest extends AbstractServerTest (byte)Flags.END_HEADERS, 0, 0, 0, 1 // Stream ID }); - lease.append(last, false); - return lease; + accumulator.append(RetainableByteBuffer.wrap(last)); + return accumulator; }); } - private void testRequestWithContinuationFrames(PriorityFrame priorityFrame, Callable frames) throws Exception + private void testRequestWithContinuationFrames(PriorityFrame priorityFrame, Callable frames) throws Exception { - final CountDownLatch serverLatch = new CountDownLatch(1); + CountDownLatch serverLatch = new CountDownLatch(1); startServer(new ServerSessionListener() { @Override @@ -585,14 +586,14 @@ public class HTTP2ServerTest extends AbstractServerTest return null; } }); - generator = new Generator(byteBufferPool, 4096, 4); + generator = new Generator(bufferPool, 4096, 4); - ByteBufferPool.Lease lease = frames.call(); + RetainableByteBufferPool.Accumulator accumulator = frames.call(); try (Socket client = new Socket("localhost", connector.getLocalPort())) { OutputStream output = client.getOutputStream(); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } @@ -600,8 +601,8 @@ public class HTTP2ServerTest extends AbstractServerTest assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); - final CountDownLatch clientLatch = new CountDownLatch(1); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + CountDownLatch clientLatch = new CountDownLatch(1); + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onHeaders(HeadersFrame frame) diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2Test.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2Test.java index 39aa89c05f2..ff84ba9863a 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2Test.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2Test.java @@ -201,7 +201,7 @@ public class HTTP2Test extends AbstractTest Stream.Data data = stream.readData(); DataFrame frame = data.frame(); assertTrue(frame.isEndStream()); - assertEquals(ByteBuffer.wrap(content), frame.getData()); + assertEquals(ByteBuffer.wrap(content), frame.getByteBuffer()); data.release(); latch.countDown(); } @@ -593,10 +593,10 @@ public class HTTP2Test extends AbstractTest DataFrame data1 = new DataFrame(stream.getId(), ByteBuffer.allocate(1024), false) { @Override - public ByteBuffer getData() + public ByteBuffer getByteBuffer() { sleep(2 * sleep); - return super.getData(); + return super.getByteBuffer(); } }; DataFrame data2 = new DataFrame(stream.getId(), BufferUtil.EMPTY_BUFFER, true); diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java index 20ab9759301..915673de3af 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java @@ -67,10 +67,10 @@ import org.eclipse.jetty.http2.internal.HTTP2Session; import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.internal.parser.ServerParser; import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.Request; @@ -111,7 +111,7 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest assertTrue(http2Client.isStarted()); assertSame(httpClient.getExecutor(), http2Client.getExecutor()); assertSame(httpClient.getScheduler(), http2Client.getScheduler()); - assertSame(httpClient.getByteBufferPool(), http2Client.getByteBufferPool()); + assertSame(httpClient.getRetainableByteBufferPool(), http2Client.getRetainableByteBufferPool()); assertEquals(httpClient.getConnectTimeout(), http2Client.getConnectTimeout()); assertEquals(httpClient.getIdleTimeout(), http2Client.getIdleTimeout()); assertEquals(httpClient.isUseInputDirectByteBuffers(), http2Client.isUseInputDirectByteBuffers()); @@ -541,9 +541,9 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest resultLatch.countDown(); }); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - Generator generator = new Generator(byteBufferPool); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + Generator generator = new Generator(bufferPool); try (Socket socket = server.accept()) { @@ -551,7 +551,7 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest OutputStream output = socket.getOutputStream(); InputStream input = socket.getInputStream(); - ServerParser parser = new ServerParser(byteBufferPool, new ServerParser.Listener.Adapter() + ServerParser parser = new ServerParser(bufferPool, new ServerParser.Listener.Adapter() { @Override public void onPreface() @@ -559,9 +559,9 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest try { // Server's preface. - generator.control(lease, new SettingsFrame(new HashMap<>(), false)); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), false)); // Reply to client's SETTINGS. - generator.control(lease, new SettingsFrame(new HashMap<>(), true)); + generator.control(accumulator, new SettingsFrame(new HashMap<>(), true)); writeFrames(); } catch (HpackException x) @@ -578,7 +578,7 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest // Response. MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, HttpFields.EMPTY); HeadersFrame response = new HeadersFrame(request.getStreamId(), metaData, null, true); - generator.control(lease, response); + generator.control(accumulator, response); writeFrames(); } catch (HpackException x) @@ -592,11 +592,11 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest try { // Write the frames. - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { output.write(BufferUtil.toArray(buffer)); } - lease.recycle(); + accumulator.release(); } catch (Throwable x) { diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/InterleavingTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/InterleavingTest.java index 37e040aad78..bab2e80b58c 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/InterleavingTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/InterleavingTest.java @@ -158,7 +158,7 @@ public class InterleavingTest extends AbstractTest if (dataFrame.isEndStream()) ++finished; - BufferUtil.writeTo(dataFrame.getData(), contents.get(streamId)); + BufferUtil.writeTo(dataFrame.getByteBuffer(), contents.get(streamId)); data.release(); } diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/PrefaceTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/PrefaceTest.java index 306b634d46b..dd57d7b906b 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/PrefaceTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/PrefaceTest.java @@ -50,9 +50,9 @@ import org.eclipse.jetty.http2.internal.ErrorCode; import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.internal.parser.Parser; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -148,26 +148,26 @@ public class PrefaceTest extends AbstractTest } }); - ByteBufferPool byteBufferPool = http2Client.getByteBufferPool(); + RetainableByteBufferPool bufferPool = http2Client.getRetainableByteBufferPool(); try (SocketChannel socket = SocketChannel.open()) { socket.connect(new InetSocketAddress("localhost", connector.getLocalPort())); - Generator generator = new Generator(byteBufferPool); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); + Generator generator = new Generator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); Map clientSettings = new HashMap<>(); clientSettings.put(SettingsFrame.ENABLE_PUSH, 0); - generator.control(lease, new SettingsFrame(clientSettings, false)); + generator.control(accumulator, new SettingsFrame(clientSettings, false)); // The PING frame just to make sure the client stops reading. - generator.control(lease, new PingFrame(true)); + generator.control(accumulator, new PingFrame(true)); - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); socket.write(buffers.toArray(new ByteBuffer[0])); Queue settings = new ArrayDeque<>(); AtomicBoolean closed = new AtomicBoolean(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onSettings(SettingsFrame frame) @@ -183,7 +183,7 @@ public class PrefaceTest extends AbstractTest }, 4096, 8192); parser.init(UnaryOperator.identity()); - ByteBuffer buffer = byteBufferPool.acquire(1024, true); + ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { BufferUtil.clearToFill(buffer); @@ -247,7 +247,7 @@ public class PrefaceTest extends AbstractTest }); server.start(); - ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); try (SocketChannel socket = SocketChannel.open()) { socket.connect(new InetSocketAddress("localhost", connector.getLocalPort())); @@ -268,7 +268,7 @@ public class PrefaceTest extends AbstractTest assertTrue(serverSettingsLatch.get().await(5, TimeUnit.SECONDS)); // The 101 response is the reply to the client preface SETTINGS frame. - ByteBuffer buffer = byteBufferPool.acquire(1024, true); + ByteBuffer buffer = ByteBuffer.allocateDirect(1024); http1: while (true) { @@ -295,13 +295,13 @@ public class PrefaceTest extends AbstractTest serverSettingsLatch.set(new CountDownLatch(1)); // After the 101, the client must send the connection preface. - Generator generator = new Generator(byteBufferPool); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); + Generator generator = new Generator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); Map clientSettings = new HashMap<>(); clientSettings.put(SettingsFrame.ENABLE_PUSH, 1); - generator.control(lease, new SettingsFrame(clientSettings, false)); - List buffers = lease.getByteBuffers(); + generator.control(accumulator, new SettingsFrame(clientSettings, false)); + List buffers = accumulator.getByteBuffers(); socket.write(buffers.toArray(new ByteBuffer[0])); // However, we should not call onPreface() again. @@ -311,7 +311,7 @@ public class PrefaceTest extends AbstractTest CountDownLatch clientSettingsLatch = new CountDownLatch(1); AtomicBoolean responded = new AtomicBoolean(); - Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + Parser parser = new Parser(bufferPool, new Parser.Listener.Adapter() { @Override public void onSettings(SettingsFrame frame) diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/RawHTTP2ProxyTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/RawHTTP2ProxyTest.java index c95b1414ba3..01a0d675ec9 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/RawHTTP2ProxyTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/RawHTTP2ProxyTest.java @@ -223,7 +223,7 @@ public class RawHTTP2ProxyTest DataFrame frame = data.frame(); if (LOGGER.isDebugEnabled()) LOGGER.debug("CLIENT received {}", frame); - assertEquals(buffer1.slice(), frame.getData()); + assertEquals(buffer1.slice(), frame.getByteBuffer()); data.release(); latch1.countDown(); if (!data.frame().isEndStream()) @@ -461,7 +461,7 @@ public class RawHTTP2ProxyTest case DATA -> { DataFrame clientToProxyFrame = (DataFrame)frameInfo.frame; - DataFrame proxyToServerFrame = new DataFrame(proxyToServerStream.getId(), clientToProxyFrame.getData(), clientToProxyFrame.isEndStream()); + DataFrame proxyToServerFrame = new DataFrame(proxyToServerStream.getId(), clientToProxyFrame.getByteBuffer(), clientToProxyFrame.isEndStream()); proxyToServerStream.data(proxyToServerFrame, this); yield Action.SCHEDULED; } @@ -609,7 +609,7 @@ public class RawHTTP2ProxyTest case DATA -> { DataFrame clientToProxyFrame = (DataFrame)frameInfo.frame; - DataFrame proxyToServerFrame = new DataFrame(serverToProxyStream.getId(), clientToProxyFrame.getData(), clientToProxyFrame.isEndStream()); + DataFrame proxyToServerFrame = new DataFrame(serverToProxyStream.getId(), clientToProxyFrame.getByteBuffer(), clientToProxyFrame.isEndStream()); proxyToClientStream.data(proxyToServerFrame, this); yield Action.SCHEDULED; } diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamCountTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamCountTest.java index 82e5220abf2..cad58d6b2bd 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamCountTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamCountTest.java @@ -33,7 +33,7 @@ import org.eclipse.jetty.http2.frames.ResetFrame; import org.eclipse.jetty.http2.frames.SettingsFrame; import org.eclipse.jetty.http2.internal.HTTP2Session; import org.eclipse.jetty.http2.internal.generator.Generator; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FuturePromise; @@ -201,10 +201,10 @@ public class StreamCountTest extends AbstractTest HeadersFrame frame3 = new HeadersFrame(streamId3, metaData, null, false); DataFrame data3 = new DataFrame(streamId3, BufferUtil.EMPTY_BUFFER, true); Generator generator = ((HTTP2Session)session).getGenerator(); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(generator.getByteBufferPool()); - generator.control(lease, frame3); - generator.data(lease, data3, data3.remaining()); - ((HTTP2Session)session).getEndPoint().write(Callback.NOOP, lease.getByteBuffers().toArray(new ByteBuffer[0])); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, frame3); + generator.data(accumulator, data3, data3.remaining()); + ((HTTP2Session)session).getEndPoint().write(Callback.NOOP, accumulator.getByteBuffers().toArray(ByteBuffer[]::new)); // Expect 2 RST_STREAM frames. assertTrue(sessionResetLatch.await(5, TimeUnit.SECONDS)); diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamResetTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamResetTest.java index 38b22b006ef..1eee6657b24 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamResetTest.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/StreamResetTest.java @@ -64,8 +64,8 @@ import org.eclipse.jetty.http2.internal.generator.Generator; import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; import org.eclipse.jetty.io.AbstractEndPoint; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.Handler; @@ -671,7 +671,7 @@ public class StreamResetTest extends AbstractTest Stream.Data data = stream.readData(); dataQueue.offer(data); // Do not consume the data yet. - if (received.addAndGet(data.frame().getData().remaining()) == windowSize) + if (received.addAndGet(data.frame().getByteBuffer().remaining()) == windowSize) latch.countDown(); else stream.demand(); @@ -724,7 +724,7 @@ public class StreamResetTest extends AbstractTest Stream.Data data = stream.readData(); dataList.add(data); // Do not release to stall the flow control window. - if (received.addAndGet(data.frame().getData().remaining()) == windowSize) + if (received.addAndGet(data.frame().getByteBuffer().remaining()) == windowSize) latch.countDown(); else stream.demand(); @@ -784,7 +784,7 @@ public class StreamResetTest extends AbstractTest Stream.Data data = stream.readData(); dataQueue.offer(data); // Do not consume the data yet. - if (received.addAndGet(data.frame().getData().remaining()) == windowSize) + if (received.addAndGet(data.frame().getByteBuffer().remaining()) == windowSize) latch.countDown(); else stream.demand(); @@ -887,30 +887,30 @@ public class StreamResetTest extends AbstractTest } }); - ByteBufferPool byteBufferPool = http2Client.getByteBufferPool(); + RetainableByteBufferPool bufferPool = http2Client.getRetainableByteBufferPool(); try (SocketChannel socket = SocketChannel.open()) { String host = "localhost"; int port = connector.getLocalPort(); socket.connect(new InetSocketAddress(host, port)); - Generator generator = new Generator(byteBufferPool); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); + Generator generator = new Generator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); Map clientSettings = new HashMap<>(); // Max stream HTTP/2 flow control window. clientSettings.put(SettingsFrame.INITIAL_WINDOW_SIZE, Integer.MAX_VALUE); - generator.control(lease, new SettingsFrame(clientSettings, false)); + generator.control(accumulator, new SettingsFrame(clientSettings, false)); // Max session HTTP/2 flow control window. - generator.control(lease, new WindowUpdateFrame(0, Integer.MAX_VALUE - FlowControlStrategy.DEFAULT_WINDOW_SIZE)); + generator.control(accumulator, new WindowUpdateFrame(0, Integer.MAX_VALUE - FlowControlStrategy.DEFAULT_WINDOW_SIZE)); HttpURI uri = HttpURI.from("http", host, port, "/"); MetaData.Request request = new MetaData.Request(HttpMethod.GET.asString(), uri, HttpVersion.HTTP_2, HttpFields.EMPTY); int streamId = 3; HeadersFrame headersFrame = new HeadersFrame(streamId, request, null, true); - generator.control(lease, headersFrame); + generator.control(accumulator, headersFrame); - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); socket.write(buffers.toArray(new ByteBuffer[0])); // Wait until the server is TCP congested. @@ -918,9 +918,9 @@ public class StreamResetTest extends AbstractTest WriteFlusher flusher = flusherRef.get(); waitUntilTCPCongested(flusher); - lease.recycle(); - generator.control(lease, new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code)); - buffers = lease.getByteBuffers(); + accumulator.release(); + generator.control(accumulator, new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code)); + buffers = accumulator.getByteBuffers(); socket.write(buffers.toArray(new ByteBuffer[0])); assertTrue(writeLatch1.await(5, TimeUnit.SECONDS)); @@ -978,29 +978,29 @@ public class StreamResetTest extends AbstractTest } }); - ByteBufferPool byteBufferPool = http2Client.getByteBufferPool(); + RetainableByteBufferPool bufferPool = http2Client.getRetainableByteBufferPool(); try (SocketChannel socket = SocketChannel.open()) { String host = "localhost"; int port = connector.getLocalPort(); socket.connect(new InetSocketAddress(host, port)); - Generator generator = new Generator(byteBufferPool); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.control(lease, new PrefaceFrame()); + Generator generator = new Generator(bufferPool); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + generator.control(accumulator, new PrefaceFrame()); Map clientSettings = new HashMap<>(); // Max stream HTTP/2 flow control window. clientSettings.put(SettingsFrame.INITIAL_WINDOW_SIZE, Integer.MAX_VALUE); - generator.control(lease, new SettingsFrame(clientSettings, false)); + generator.control(accumulator, new SettingsFrame(clientSettings, false)); // Max session HTTP/2 flow control window. - generator.control(lease, new WindowUpdateFrame(0, Integer.MAX_VALUE - FlowControlStrategy.DEFAULT_WINDOW_SIZE)); + generator.control(accumulator, new WindowUpdateFrame(0, Integer.MAX_VALUE - FlowControlStrategy.DEFAULT_WINDOW_SIZE)); HttpURI uri = HttpURI.from("http", host, port, "/1"); MetaData.Request request = new MetaData.Request(HttpMethod.GET.asString(), uri, HttpVersion.HTTP_2, HttpFields.EMPTY); HeadersFrame headersFrame = new HeadersFrame(3, request, null, true); - generator.control(lease, headersFrame); + generator.control(accumulator, headersFrame); - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); socket.write(buffers.toArray(new ByteBuffer[0])); waitUntilTCPCongested(exchanger.exchange(null)); @@ -1010,15 +1010,15 @@ public class StreamResetTest extends AbstractTest request = new MetaData.Request(HttpMethod.GET.asString(), uri, HttpVersion.HTTP_2, HttpFields.EMPTY); int streamId = 5; headersFrame = new HeadersFrame(streamId, request, null, true); - generator.control(lease, headersFrame); - buffers = lease.getByteBuffers(); + generator.control(accumulator, headersFrame); + buffers = accumulator.getByteBuffers(); socket.write(buffers.toArray(new ByteBuffer[0])); assertTrue(requestLatch1.await(5, TimeUnit.SECONDS)); // Now reset the second request, which has not started writing yet. - lease.recycle(); - generator.control(lease, new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code)); - buffers = lease.getByteBuffers(); + accumulator.release(); + generator.control(accumulator, new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code)); + buffers = accumulator.getByteBuffers(); socket.write(buffers.toArray(new ByteBuffer[0])); // Wait to be sure that the server processed the reset. Thread.sleep(1000); diff --git a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java index ccf4346e4ab..54487f5c073 100644 --- a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java +++ b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java @@ -69,7 +69,7 @@ public class HttpClientTransportOverHTTP3 extends AbstractHttpClientTransport im ClientConnector clientConnector = this.client.getClientConnector(); clientConnector.setExecutor(httpClient.getExecutor()); clientConnector.setScheduler(httpClient.getScheduler()); - clientConnector.setByteBufferPool(httpClient.getByteBufferPool()); + clientConnector.setRetainableByteBufferPool(httpClient.getRetainableByteBufferPool()); clientConnector.setConnectTimeout(Duration.ofMillis(httpClient.getConnectTimeout())); clientConnector.setConnectBlocking(httpClient.isConnectBlocking()); clientConnector.setBindAddress(httpClient.getBindAddress()); diff --git a/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java b/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java index 5fd2d2a5889..07c003cbe8b 100644 --- a/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java +++ b/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.http3.internal.MessageFlusher; import org.eclipse.jetty.http3.internal.UnidirectionalStreamConnection; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.eclipse.jetty.http3.qpack.QpackEncoder; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.client.ClientProtocolSession; import org.eclipse.jetty.quic.client.ClientQuicSession; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; @@ -63,7 +64,8 @@ public class ClientHTTP3Session extends ClientProtocolSession long encoderStreamId = getQuicSession().newStreamId(StreamType.CLIENT_UNIDIRECTIONAL); QuicStreamEndPoint encoderEndPoint = openInstructionEndPoint(encoderStreamId); InstructionFlusher encoderInstructionFlusher = new InstructionFlusher(quicSession, encoderEndPoint, EncoderStreamConnection.STREAM_TYPE); - this.encoder = new QpackEncoder(new InstructionHandler(encoderInstructionFlusher), configuration.getMaxBlockedStreams()); + RetainableByteBufferPool bufferPool = quicSession.getRetainableByteBufferPool(); + this.encoder = new QpackEncoder(bufferPool, new InstructionHandler(encoderInstructionFlusher), configuration.getMaxBlockedStreams()); addBean(encoder); if (LOG.isDebugEnabled()) LOG.debug("created encoder stream #{} on {}", encoderStreamId, encoderEndPoint); @@ -71,7 +73,7 @@ public class ClientHTTP3Session extends ClientProtocolSession long decoderStreamId = getQuicSession().newStreamId(StreamType.CLIENT_UNIDIRECTIONAL); QuicStreamEndPoint decoderEndPoint = openInstructionEndPoint(decoderStreamId); InstructionFlusher decoderInstructionFlusher = new InstructionFlusher(quicSession, decoderEndPoint, DecoderStreamConnection.STREAM_TYPE); - this.decoder = new QpackDecoder(new InstructionHandler(decoderInstructionFlusher), configuration.getMaxResponseHeadersSize()); + this.decoder = new QpackDecoder(bufferPool, new InstructionHandler(decoderInstructionFlusher), configuration.getMaxResponseHeadersSize()); addBean(decoder); if (LOG.isDebugEnabled()) LOG.debug("created decoder stream #{} on {}", decoderStreamId, decoderEndPoint); @@ -83,7 +85,7 @@ public class ClientHTTP3Session extends ClientProtocolSession if (LOG.isDebugEnabled()) LOG.debug("created control stream #{} on {}", controlStreamId, controlEndPoint); - this.messageFlusher = new MessageFlusher(quicSession.getByteBufferPool(), encoder, configuration.getMaxRequestHeadersSize(), configuration.isUseOutputDirectByteBuffers()); + this.messageFlusher = new MessageFlusher(quicSession.getRetainableByteBufferPool(), encoder, configuration.getMaxRequestHeadersSize(), configuration.isUseOutputDirectByteBuffers()); addBean(messageFlusher); } @@ -197,7 +199,7 @@ public class ClientHTTP3Session extends ClientProtocolSession private void openUnidirectionalStreamEndPoint(QuicStreamEndPoint endPoint) { - UnidirectionalStreamConnection connection = new UnidirectionalStreamConnection(endPoint, getQuicSession().getExecutor(), getQuicSession().getByteBufferPool(), encoder, decoder, session); + UnidirectionalStreamConnection connection = new UnidirectionalStreamConnection(endPoint, getQuicSession().getExecutor(), getQuicSession().getRetainableByteBufferPool(), encoder, decoder, session); endPoint.setConnection(connection); endPoint.opened(); } diff --git a/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3StreamConnection.java index b225dd79ff4..15cdbf20033 100644 --- a/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3StreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3StreamConnection.java @@ -21,6 +21,6 @@ public class ClientHTTP3StreamConnection extends HTTP3StreamConnection { public ClientHTTP3StreamConnection(QuicStreamEndPoint endPoint, ClientHTTP3Session session, MessageParser parser) { - super(endPoint, session.getQuicSession().getExecutor(), session.getQuicSession().getByteBufferPool(), parser); + super(endPoint, session.getQuicSession().getExecutor(), session.getQuicSession().getRetainableByteBufferPool(), parser); } } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlFlusher.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlFlusher.java index f75ab50316f..15e36b7fe97 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlFlusher.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlFlusher.java @@ -21,7 +21,8 @@ import java.util.Queue; import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.internal.generator.ControlGenerator; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.QuicSession; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; import org.eclipse.jetty.util.Callback; @@ -37,9 +38,9 @@ public class ControlFlusher extends IteratingCallback private final AutoLock lock = new AutoLock(); private final Queue queue = new ArrayDeque<>(); - private final ByteBufferPool.Lease lease; - private final ControlGenerator generator; private final QuicStreamEndPoint endPoint; + private final ControlGenerator generator; + private final RetainableByteBufferPool.Accumulator accumulator; private boolean initialized; private Throwable terminated; private List entries; @@ -47,15 +48,16 @@ public class ControlFlusher extends IteratingCallback public ControlFlusher(QuicSession session, QuicStreamEndPoint endPoint, boolean useDirectByteBuffers) { - this.lease = new ByteBufferPool.Lease(session.getByteBufferPool()); this.endPoint = endPoint; - this.generator = new ControlGenerator(useDirectByteBuffers); + RetainableByteBufferPool bufferPool = session.getRetainableByteBufferPool(); + this.generator = new ControlGenerator(bufferPool, useDirectByteBuffers); + this.accumulator = new RetainableByteBufferPool.Accumulator(); } public boolean offer(Frame frame, Callback callback) { Throwable closed; - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { closed = terminated; if (closed == null) @@ -70,7 +72,7 @@ public class ControlFlusher extends IteratingCallback @Override protected Action process() { - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { if (queue.isEmpty()) return Action.IDLE; @@ -83,7 +85,7 @@ public class ControlFlusher extends IteratingCallback for (Entry entry : entries) { - generator.generate(lease, endPoint.getStreamId(), entry.frame, null); + generator.generate(accumulator, endPoint.getStreamId(), entry.frame, null); invocationType = Invocable.combine(invocationType, entry.callback.getInvocationType()); } @@ -93,12 +95,12 @@ public class ControlFlusher extends IteratingCallback ByteBuffer buffer = ByteBuffer.allocate(VarLenInt.length(ControlStreamConnection.STREAM_TYPE)); VarLenInt.encode(buffer, ControlStreamConnection.STREAM_TYPE); buffer.flip(); - lease.insert(0, buffer, false); + accumulator.insert(0, RetainableByteBuffer.wrap(buffer)); } - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); if (LOG.isDebugEnabled()) - LOG.debug("writing {} buffers ({} bytes) on {}", buffers.size(), lease.getTotalLength(), this); + LOG.debug("writing {} buffers ({} bytes) on {}", buffers.size(), accumulator.getTotalLength(), this); endPoint.write(this, buffers.toArray(ByteBuffer[]::new)); return Action.SCHEDULED; } @@ -109,7 +111,7 @@ public class ControlFlusher extends IteratingCallback if (LOG.isDebugEnabled()) LOG.debug("succeeded to write {} on {}", entries, this); - lease.recycle(); + accumulator.release(); entries.forEach(e -> e.callback.succeeded()); entries.clear(); @@ -125,11 +127,11 @@ public class ControlFlusher extends IteratingCallback if (LOG.isDebugEnabled()) LOG.debug("failed to write {} on {}", entries, this, failure); - lease.recycle(); + accumulator.release(); List allEntries = new ArrayList<>(entries); entries.clear(); - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { terminated = failure; allEntries.addAll(queue); @@ -157,21 +159,7 @@ public class ControlFlusher extends IteratingCallback return String.format("%s#%s", super.toString(), endPoint.getStreamId()); } - private static class Entry + private record Entry(Frame frame, Callback callback) { - private final Frame frame; - private final Callback callback; - - private Entry(Frame frame, Callback callback) - { - this.frame = frame; - this.callback = callback; - } - - @Override - public String toString() - { - return frame.toString(); - } } } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlStreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlStreamConnection.java index 0003d604b77..7e0aa7baab8 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlStreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlStreamConnection.java @@ -18,9 +18,10 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.http3.internal.parser.ControlParser; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,15 +32,15 @@ public class ControlStreamConnection extends AbstractConnection implements Conne public static final long STREAM_TYPE = 0x00; private static final Logger LOG = LoggerFactory.getLogger(ControlStreamConnection.class); - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private final ControlParser parser; private boolean useInputDirectByteBuffers = true; - private ByteBuffer buffer; + private RetainableByteBuffer buffer; - public ControlStreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, ControlParser parser) + public ControlStreamConnection(EndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, ControlParser parser) { super(endPoint, executor); - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; this.parser = parser; } @@ -57,17 +58,18 @@ public class ControlStreamConnection extends AbstractConnection implements Conne public void onUpgradeTo(ByteBuffer upgrade) { int capacity = Math.max(upgrade.remaining(), getInputBufferSize()); - buffer = byteBufferPool.acquire(capacity, isUseInputDirectByteBuffers()); - int position = BufferUtil.flipToFill(buffer); - buffer.put(upgrade); - BufferUtil.flipToFlush(buffer, position); + buffer = bufferPool.acquire(capacity, isUseInputDirectByteBuffers()); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + int position = BufferUtil.flipToFill(byteBuffer); + byteBuffer.put(upgrade); + BufferUtil.flipToFlush(byteBuffer, position); } @Override public void onOpen() { super.onOpen(); - if (BufferUtil.hasContent(buffer)) + if (buffer != null && buffer.hasRemaining()) onFillable(); else fillInterested(); @@ -79,28 +81,28 @@ public class ControlStreamConnection extends AbstractConnection implements Conne try { if (buffer == null) - buffer = byteBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); - + buffer = bufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); + ByteBuffer byteBuffer = buffer.getByteBuffer(); while (true) { // Parse first in case of bytes from the upgrade. - parser.parse(buffer); + parser.parse(byteBuffer); // Then read from the EndPoint. - int filled = getEndPoint().fill(buffer); + int filled = getEndPoint().fill(byteBuffer); if (LOG.isDebugEnabled()) LOG.debug("filled {} on {}", filled, this); if (filled == 0) { - byteBufferPool.release(buffer); + buffer.release(); buffer = null; fillInterested(); break; } else if (filled < 0) { - byteBufferPool.release(buffer); + buffer.release(); buffer = null; getEndPoint().close(); break; @@ -111,7 +113,7 @@ public class ControlStreamConnection extends AbstractConnection implements Conne { if (LOG.isDebugEnabled()) LOG.debug("could not process control stream {}", getEndPoint(), x); - byteBufferPool.release(buffer); + buffer.release(); buffer = null; getEndPoint().close(x); } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/DecoderStreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/DecoderStreamConnection.java index 1c58aa6b2fa..cfff5bc350b 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/DecoderStreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/DecoderStreamConnection.java @@ -18,8 +18,8 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.http3.qpack.QpackEncoder; import org.eclipse.jetty.http3.qpack.QpackException; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class DecoderStreamConnection extends InstructionStreamConnection { @@ -28,9 +28,9 @@ public class DecoderStreamConnection extends InstructionStreamConnection private final QpackEncoder encoder; - public DecoderStreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, QpackEncoder encoder) + public DecoderStreamConnection(EndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, QpackEncoder encoder) { - super(endPoint, executor, byteBufferPool); + super(endPoint, executor, bufferPool); this.encoder = encoder; } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/EncoderStreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/EncoderStreamConnection.java index 98985ea7c60..fbc29ce3f23 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/EncoderStreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/EncoderStreamConnection.java @@ -18,8 +18,8 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.eclipse.jetty.http3.qpack.QpackException; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class EncoderStreamConnection extends InstructionStreamConnection { @@ -28,9 +28,9 @@ public class EncoderStreamConnection extends InstructionStreamConnection private final QpackDecoder decoder; - public EncoderStreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, QpackDecoder decoder) + public EncoderStreamConnection(EndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, QpackDecoder decoder) { - super(endPoint, executor, byteBufferPool); + super(endPoint, executor, bufferPool); this.decoder = decoder; } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3StreamConnection.java index 21766cc042a..bafd858f8b4 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3StreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3StreamConnection.java @@ -27,7 +27,6 @@ import org.eclipse.jetty.http3.frames.HeadersFrame; import org.eclipse.jetty.http3.internal.parser.MessageParser; import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; @@ -41,7 +40,7 @@ public abstract class HTTP3StreamConnection extends AbstractConnection private static final ByteBuffer EMPTY_DATA_FRAME = ByteBuffer.allocate(2); private final AtomicReference action = new AtomicReference<>(); - private final RetainableByteBufferPool buffers; + private final RetainableByteBufferPool bufferPool; private final MessageParser parser; private boolean useInputDirectByteBuffers = true; private HTTP3Stream stream; @@ -49,10 +48,10 @@ public abstract class HTTP3StreamConnection extends AbstractConnection private boolean applicationMode; private boolean remotelyClosed; - public HTTP3StreamConnection(QuicStreamEndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, MessageParser parser) + public HTTP3StreamConnection(QuicStreamEndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, MessageParser parser) { super(endPoint, executor); - this.buffers = byteBufferPool.asRetainableByteBufferPool(); + this.bufferPool = bufferPool; this.parser = parser; parser.init(MessageListener::new); } @@ -239,7 +238,7 @@ public abstract class HTTP3StreamConnection extends AbstractConnection { if (networkBuffer == null) { - networkBuffer = buffers.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); + networkBuffer = bufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); if (LOG.isDebugEnabled()) LOG.debug("acquired {}", networkBuffer); } @@ -270,7 +269,7 @@ public abstract class HTTP3StreamConnection extends AbstractConnection while (true) { - ByteBuffer byteBuffer = networkBuffer.getBuffer(); + ByteBuffer byteBuffer = networkBuffer.getByteBuffer(); MessageParser.Result result = parser.parse(byteBuffer); if (LOG.isDebugEnabled()) LOG.debug("parsed {} on {} with buffer {}", result, this, networkBuffer); @@ -280,11 +279,11 @@ public abstract class HTTP3StreamConnection extends AbstractConnection if (networkBuffer.isRetained()) { networkBuffer.release(); - RetainableByteBuffer newBuffer = buffers.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); + RetainableByteBuffer newBuffer = bufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); if (LOG.isDebugEnabled()) LOG.debug("reacquired {} for retained {}", newBuffer, networkBuffer); networkBuffer = newBuffer; - byteBuffer = networkBuffer.getBuffer(); + byteBuffer = networkBuffer.getByteBuffer(); } int filled = fill(byteBuffer); diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionFlusher.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionFlusher.java index 9f746b0a5ec..d6808aa9523 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionFlusher.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionFlusher.java @@ -20,9 +20,11 @@ import java.util.List; import java.util.Queue; import org.eclipse.jetty.http3.qpack.Instruction; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.QuicSession; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.thread.AutoLock; import org.slf4j.Logger; @@ -38,7 +40,8 @@ public class InstructionFlusher extends IteratingCallback private final AutoLock lock = new AutoLock(); private final Queue queue = new ArrayDeque<>(); - private final ByteBufferPool.Lease lease; + private final RetainableByteBufferPool bufferPool; + private final RetainableByteBufferPool.Accumulator accumulator; private final QuicStreamEndPoint endPoint; private final long streamType; private boolean initialized; @@ -46,7 +49,8 @@ public class InstructionFlusher extends IteratingCallback public InstructionFlusher(QuicSession session, QuicStreamEndPoint endPoint, long streamType) { - this.lease = new ByteBufferPool.Lease(session.getByteBufferPool()); + this.bufferPool = session.getRetainableByteBufferPool(); + this.accumulator = new RetainableByteBufferPool.Accumulator(); this.endPoint = endPoint; this.streamType = streamType; } @@ -54,7 +58,7 @@ public class InstructionFlusher extends IteratingCallback public boolean offer(List instructions) { Throwable closed; - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { closed = terminated; if (closed == null) @@ -67,7 +71,7 @@ public class InstructionFlusher extends IteratingCallback protected Action process() { List instructions; - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { if (queue.isEmpty()) return Action.IDLE; @@ -78,20 +82,22 @@ public class InstructionFlusher extends IteratingCallback if (LOG.isDebugEnabled()) LOG.debug("flushing {} on {}", instructions, this); - instructions.forEach(i -> i.encode(lease)); + instructions.forEach(i -> i.encode(accumulator)); if (!initialized) { initialized = true; - ByteBuffer buffer = ByteBuffer.allocate(VarLenInt.length(streamType)); - VarLenInt.encode(buffer, streamType); - buffer.flip(); - lease.insert(0, buffer, false); + RetainableByteBuffer buffer = bufferPool.acquire(VarLenInt.length(streamType), false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + VarLenInt.encode(byteBuffer, streamType); + byteBuffer.flip(); + accumulator.insert(0, buffer); } - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); if (LOG.isDebugEnabled()) - LOG.debug("writing {} buffers ({} bytes) on {}", buffers.size(), lease.getTotalLength(), this); + LOG.debug("writing {} buffers ({} bytes) on {}", buffers.size(), accumulator.getTotalLength(), this); endPoint.write(this, buffers.toArray(ByteBuffer[]::new)); return Action.SCHEDULED; } @@ -100,9 +106,9 @@ public class InstructionFlusher extends IteratingCallback public void succeeded() { if (LOG.isDebugEnabled()) - LOG.debug("succeeded to write {} on {}", lease.getByteBuffers(), this); + LOG.debug("succeeded to write {} buffers on {}", accumulator.getByteBuffers().size(), this); - lease.recycle(); + accumulator.release(); super.succeeded(); } @@ -111,11 +117,11 @@ public class InstructionFlusher extends IteratingCallback protected void onCompleteFailure(Throwable failure) { if (LOG.isDebugEnabled()) - LOG.debug("failed to write {} on {}", lease.getByteBuffers(), this, failure); + LOG.debug("failed to write {} buffers on {}", accumulator.getByteBuffers().size(), this, failure); - lease.recycle(); + accumulator.release(); - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { terminated = failure; queue.clear(); diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionStreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionStreamConnection.java index 24eaeed82b5..2f96607226e 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionStreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionStreamConnection.java @@ -18,9 +18,10 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.http3.qpack.QpackException; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,14 +29,14 @@ import org.slf4j.LoggerFactory; public abstract class InstructionStreamConnection extends AbstractConnection implements Connection.UpgradeTo { private static final Logger LOG = LoggerFactory.getLogger(InstructionStreamConnection.class); - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private boolean useInputDirectByteBuffers = true; - private ByteBuffer buffer; + private RetainableByteBuffer buffer; - public InstructionStreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool) + public InstructionStreamConnection(EndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool) { super(endPoint, executor); - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; } public boolean isUseInputDirectByteBuffers() @@ -52,17 +53,18 @@ public abstract class InstructionStreamConnection extends AbstractConnection imp public void onUpgradeTo(ByteBuffer upgrade) { int capacity = Math.max(upgrade.remaining(), getInputBufferSize()); - buffer = byteBufferPool.acquire(capacity, isUseInputDirectByteBuffers()); - int position = BufferUtil.flipToFill(buffer); - buffer.put(upgrade); - BufferUtil.flipToFlush(buffer, position); + buffer = bufferPool.acquire(capacity, isUseInputDirectByteBuffers()); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + int position = BufferUtil.flipToFill(byteBuffer); + byteBuffer.put(upgrade); + BufferUtil.flipToFlush(byteBuffer, position); } @Override public void onOpen() { super.onOpen(); - if (BufferUtil.hasContent(buffer)) + if (buffer != null && buffer.hasRemaining()) onFillable(); else fillInterested(); @@ -74,28 +76,28 @@ public abstract class InstructionStreamConnection extends AbstractConnection imp try { if (buffer == null) - buffer = byteBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); - + buffer = bufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); + ByteBuffer byteBuffer = buffer.getByteBuffer(); while (true) { // Parse first in case of bytes from the upgrade. - parseInstruction(buffer); + parseInstruction(byteBuffer); // Then read from the EndPoint. - int filled = getEndPoint().fill(buffer); + int filled = getEndPoint().fill(byteBuffer); if (LOG.isDebugEnabled()) LOG.debug("filled {} on {}", filled, this); if (filled == 0) { - byteBufferPool.release(buffer); + buffer.release(); buffer = null; fillInterested(); break; } else if (filled < 0) { - byteBufferPool.release(buffer); + buffer.release(); buffer = null; getEndPoint().close(); break; @@ -106,7 +108,7 @@ public abstract class InstructionStreamConnection extends AbstractConnection imp { if (LOG.isDebugEnabled()) LOG.debug("could not process decoder stream {}", getEndPoint(), x); - byteBufferPool.release(buffer); + buffer.release(); buffer = null; getEndPoint().close(x); } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/MessageFlusher.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/MessageFlusher.java index ab8be1802ee..d19405c0414 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/MessageFlusher.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/MessageFlusher.java @@ -21,7 +21,7 @@ import java.util.Queue; import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.internal.generator.MessageGenerator; import org.eclipse.jetty.http3.qpack.QpackEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingCallback; @@ -35,19 +35,19 @@ public class MessageFlusher extends IteratingCallback private final AutoLock lock = new AutoLock(); private final Queue entries = new ArrayDeque<>(); - private final ByteBufferPool.Lease lease; + private final RetainableByteBufferPool.Accumulator accumulator; private final MessageGenerator generator; private Entry entry; - public MessageFlusher(ByteBufferPool byteBufferPool, QpackEncoder encoder, int maxHeadersLength, boolean useDirectByteBuffers) + public MessageFlusher(RetainableByteBufferPool bufferPool, QpackEncoder encoder, int maxHeadersLength, boolean useDirectByteBuffers) { - this.lease = new ByteBufferPool.Lease(byteBufferPool); - this.generator = new MessageGenerator(encoder, maxHeadersLength, useDirectByteBuffers); + this.accumulator = new RetainableByteBufferPool.Accumulator(); + this.generator = new MessageGenerator(bufferPool, encoder, maxHeadersLength, useDirectByteBuffers); } public boolean offer(QuicStreamEndPoint endPoint, Frame frame, Callback callback) { - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { entries.offer(new Entry(endPoint, frame, callback)); } @@ -57,7 +57,7 @@ public class MessageFlusher extends IteratingCallback @Override protected Action process() { - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { entry = entries.poll(); if (entry == null) @@ -75,14 +75,14 @@ public class MessageFlusher extends IteratingCallback return Action.SCHEDULED; } - int generated = generator.generate(lease, entry.endPoint.getStreamId(), frame, this::failed); + int generated = generator.generate(accumulator, entry.endPoint.getStreamId(), frame, this::failed); if (generated < 0) return Action.SCHEDULED; QuicStreamEndPoint endPoint = entry.endPoint; - List buffers = lease.getByteBuffers(); + List buffers = accumulator.getByteBuffers(); if (LOG.isDebugEnabled()) - LOG.debug("writing {} buffers ({} bytes) for stream #{} on {}", buffers.size(), lease.getTotalLength(), endPoint.getStreamId(), this); + LOG.debug("writing {} buffers ({} bytes) for stream #{} on {}", buffers.size(), accumulator.getTotalLength(), endPoint.getStreamId(), this); endPoint.write(this, buffers, Frame.isLast(frame)); return Action.SCHEDULED; @@ -94,7 +94,7 @@ public class MessageFlusher extends IteratingCallback if (LOG.isDebugEnabled()) LOG.debug("succeeded to write {} on {}", entry, this); - lease.recycle(); + accumulator.release(); entry.callback.succeeded(); entry = null; @@ -108,7 +108,7 @@ public class MessageFlusher extends IteratingCallback if (LOG.isDebugEnabled()) LOG.debug("failed to write {} on {}", entry, this, x); - lease.recycle(); + accumulator.release(); entry.callback.failed(x); entry = null; diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java index 536668c0ffe..6bf48e18a6d 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java @@ -21,11 +21,11 @@ import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.eclipse.jetty.http3.qpack.QpackEncoder; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; import org.eclipse.jetty.quic.common.StreamType; -import org.eclipse.jetty.util.BufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,18 +33,18 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement { private static final Logger LOG = LoggerFactory.getLogger(UnidirectionalStreamConnection.class); - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private final QpackEncoder encoder; private final QpackDecoder decoder; private final ParserListener listener; private final VarLenInt parser = new VarLenInt(); private boolean useInputDirectByteBuffers = true; - private ByteBuffer buffer; + private RetainableByteBuffer buffer; - public UnidirectionalStreamConnection(QuicStreamEndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, QpackEncoder encoder, QpackDecoder decoder, ParserListener listener) + public UnidirectionalStreamConnection(QuicStreamEndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, QpackEncoder encoder, QpackDecoder decoder, ParserListener listener) { super(endPoint, executor); - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; this.encoder = encoder; this.decoder = decoder; this.listener = listener; @@ -78,8 +78,8 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement { int remaining = buffer.remaining(); ByteBuffer copy = buffer.isDirect() ? ByteBuffer.allocateDirect(remaining) : ByteBuffer.allocate(remaining); - copy.put(buffer); - byteBufferPool.release(buffer); + copy.put(buffer.getByteBuffer()); + buffer.release(); buffer = null; copy.flip(); return copy; @@ -91,28 +91,28 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement try { if (buffer == null) - buffer = byteBufferPool.acquire(2048, isUseInputDirectByteBuffers()); - + buffer = bufferPool.acquire(2048, isUseInputDirectByteBuffers()); + ByteBuffer byteBuffer = buffer.getByteBuffer(); while (true) { - int filled = getEndPoint().fill(buffer); + int filled = getEndPoint().fill(byteBuffer); if (LOG.isDebugEnabled()) - LOG.debug("filled {} on {}: {}", filled, this, BufferUtil.toDetailString(buffer)); + LOG.debug("filled {} on {}: {}", filled, this, buffer); if (filled > 0) { - if (parser.decode(buffer, this::detectAndUpgrade)) + if (parser.decode(byteBuffer, this::detectAndUpgrade)) break; } else if (filled == 0) { - byteBufferPool.release(buffer); + buffer.release(); fillInterested(); break; } else { - byteBufferPool.release(buffer); + buffer.release(); buffer = null; getEndPoint().close(); break; @@ -123,7 +123,7 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement { if (LOG.isDebugEnabled()) LOG.debug("could not process stream {}", getEndPoint(), x); - byteBufferPool.release(buffer); + buffer.release(); buffer = null; getEndPoint().close(x); } @@ -134,7 +134,7 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement if (streamType == ControlStreamConnection.STREAM_TYPE) { ControlParser parser = new ControlParser(listener); - ControlStreamConnection newConnection = new ControlStreamConnection(getEndPoint(), getExecutor(), byteBufferPool, parser); + ControlStreamConnection newConnection = new ControlStreamConnection(getEndPoint(), getExecutor(), bufferPool, parser); newConnection.setInputBufferSize(getInputBufferSize()); newConnection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); if (LOG.isDebugEnabled()) @@ -143,7 +143,7 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement } else if (streamType == EncoderStreamConnection.STREAM_TYPE) { - EncoderStreamConnection newConnection = new EncoderStreamConnection(getEndPoint(), getExecutor(), byteBufferPool, decoder); + EncoderStreamConnection newConnection = new EncoderStreamConnection(getEndPoint(), getExecutor(), bufferPool, decoder); newConnection.setInputBufferSize(getInputBufferSize()); newConnection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); if (LOG.isDebugEnabled()) @@ -152,7 +152,7 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement } else if (streamType == DecoderStreamConnection.STREAM_TYPE) { - DecoderStreamConnection newConnection = new DecoderStreamConnection(getEndPoint(), getExecutor(), byteBufferPool, encoder); + DecoderStreamConnection newConnection = new DecoderStreamConnection(getEndPoint(), getExecutor(), bufferPool, encoder); newConnection.setInputBufferSize(getInputBufferSize()); newConnection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); if (LOG.isDebugEnabled()) diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/CancelPushGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/CancelPushGenerator.java index 464476625e8..50928293195 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/CancelPushGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/CancelPushGenerator.java @@ -16,12 +16,17 @@ package org.eclipse.jetty.http3.internal.generator; import java.util.function.Consumer; import org.eclipse.jetty.http3.frames.Frame; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class CancelPushGenerator extends FrameGenerator { + public CancelPushGenerator(RetainableByteBufferPool bufferPool) + { + super(bufferPool); + } + @Override - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { return 0; } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/ControlGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/ControlGenerator.java index 002d7fc2b29..c1347dbe438 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/ControlGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/ControlGenerator.java @@ -17,22 +17,22 @@ import java.util.function.Consumer; import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.FrameType; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class ControlGenerator { private final FrameGenerator[] generators = new FrameGenerator[FrameType.maxType() + 1]; - public ControlGenerator(boolean useDirectByteBuffers) + public ControlGenerator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers) { - generators[FrameType.CANCEL_PUSH.type()] = new CancelPushGenerator(); - generators[FrameType.SETTINGS.type()] = new SettingsGenerator(useDirectByteBuffers); - generators[FrameType.GOAWAY.type()] = new GoAwayGenerator(useDirectByteBuffers); - generators[FrameType.MAX_PUSH_ID.type()] = new MaxPushIdGenerator(); + generators[FrameType.CANCEL_PUSH.type()] = new CancelPushGenerator(bufferPool); + generators[FrameType.SETTINGS.type()] = new SettingsGenerator(bufferPool, useDirectByteBuffers); + generators[FrameType.GOAWAY.type()] = new GoAwayGenerator(bufferPool, useDirectByteBuffers); + generators[FrameType.MAX_PUSH_ID.type()] = new MaxPushIdGenerator(bufferPool); } - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { - return generators[frame.getFrameType().type()].generate(lease, streamId, frame, fail); + return generators[frame.getFrameType().type()].generate(accumulator, streamId, frame, fail); } } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/DataGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/DataGenerator.java index 46e07ca2a7e..ec1909c30fc 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/DataGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/DataGenerator.java @@ -20,35 +20,40 @@ import org.eclipse.jetty.http3.frames.DataFrame; import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.FrameType; import org.eclipse.jetty.http3.internal.VarLenInt; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; public class DataGenerator extends FrameGenerator { private final boolean useDirectByteBuffers; - public DataGenerator(boolean useDirectByteBuffers) + public DataGenerator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers) { + super(bufferPool); this.useDirectByteBuffers = useDirectByteBuffers; } @Override - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { DataFrame dataFrame = (DataFrame)frame; - return generateDataFrame(lease, dataFrame); + return generateDataFrame(accumulator, dataFrame); } - private int generateDataFrame(ByteBufferPool.Lease lease, DataFrame frame) + private int generateDataFrame(RetainableByteBufferPool.Accumulator accumulator, DataFrame frame) { ByteBuffer data = frame.getByteBuffer(); int dataLength = data.remaining(); int headerLength = VarLenInt.length(FrameType.DATA.type()) + VarLenInt.length(dataLength); - ByteBuffer header = lease.acquire(headerLength, useDirectByteBuffers); - VarLenInt.encode(header, FrameType.DATA.type()); - VarLenInt.encode(header, dataLength); - header.flip(); - lease.append(header, true); - lease.append(data, false); + RetainableByteBuffer header = getRetainableByteBufferPool().acquire(headerLength, useDirectByteBuffers); + ByteBuffer byteBuffer = header.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + VarLenInt.encode(byteBuffer, FrameType.DATA.type()); + VarLenInt.encode(byteBuffer, dataLength); + byteBuffer.flip(); + accumulator.append(header); + accumulator.append(RetainableByteBuffer.wrap(data)); return headerLength + dataLength; } } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/FrameGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/FrameGenerator.java index 1dbf3c9eeef..c8621209c0c 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/FrameGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/FrameGenerator.java @@ -16,9 +16,21 @@ package org.eclipse.jetty.http3.internal.generator; import java.util.function.Consumer; import org.eclipse.jetty.http3.frames.Frame; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public abstract class FrameGenerator { - public abstract int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail); + private final RetainableByteBufferPool bufferPool; + + public FrameGenerator(RetainableByteBufferPool bufferPool) + { + this.bufferPool = bufferPool; + } + + public RetainableByteBufferPool getRetainableByteBufferPool() + { + return bufferPool; + } + + public abstract int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail); } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/GoAwayGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/GoAwayGenerator.java index 2d67c63c0c3..97715c2249f 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/GoAwayGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/GoAwayGenerator.java @@ -20,35 +20,40 @@ import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.FrameType; import org.eclipse.jetty.http3.frames.GoAwayFrame; import org.eclipse.jetty.http3.internal.VarLenInt; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; public class GoAwayGenerator extends FrameGenerator { private final boolean useDirectByteBuffers; - public GoAwayGenerator(boolean useDirectByteBuffers) + public GoAwayGenerator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers) { + super(bufferPool); this.useDirectByteBuffers = useDirectByteBuffers; } @Override - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { GoAwayFrame goAwayFrame = (GoAwayFrame)frame; - return generateGoAwayFrame(lease, goAwayFrame); + return generateGoAwayFrame(accumulator, goAwayFrame); } - private int generateGoAwayFrame(ByteBufferPool.Lease lease, GoAwayFrame frame) + private int generateGoAwayFrame(RetainableByteBufferPool.Accumulator accumulator, GoAwayFrame frame) { long lastId = frame.getLastId(); int lastIdLength = VarLenInt.length(lastId); int length = VarLenInt.length(FrameType.GOAWAY.type()) + VarLenInt.length(lastIdLength) + lastIdLength; - ByteBuffer buffer = lease.acquire(length, useDirectByteBuffers); - VarLenInt.encode(buffer, FrameType.GOAWAY.type()); - VarLenInt.encode(buffer, lastIdLength); - VarLenInt.encode(buffer, lastId); - buffer.flip(); - lease.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(length, useDirectByteBuffers); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + VarLenInt.encode(byteBuffer, FrameType.GOAWAY.type()); + VarLenInt.encode(byteBuffer, lastIdLength); + VarLenInt.encode(byteBuffer, lastId); + byteBuffer.flip(); + accumulator.append(buffer); return length; } } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/HeadersGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/HeadersGenerator.java index fcfe269811f..b27784d89da 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/HeadersGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/HeadersGenerator.java @@ -22,7 +22,9 @@ import org.eclipse.jetty.http3.frames.HeadersFrame; import org.eclipse.jetty.http3.internal.VarLenInt; import org.eclipse.jetty.http3.qpack.QpackEncoder; import org.eclipse.jetty.http3.qpack.QpackException; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; public class HeadersGenerator extends FrameGenerator { @@ -30,21 +32,22 @@ public class HeadersGenerator extends FrameGenerator private final int maxLength; private final boolean useDirectByteBuffers; - public HeadersGenerator(QpackEncoder encoder, int maxLength, boolean useDirectByteBuffers) + public HeadersGenerator(RetainableByteBufferPool bufferPool, QpackEncoder encoder, int maxLength, boolean useDirectByteBuffers) { + super(bufferPool); this.encoder = encoder; this.maxLength = maxLength; this.useDirectByteBuffers = useDirectByteBuffers; } @Override - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { HeadersFrame headersFrame = (HeadersFrame)frame; - return generateHeadersFrame(lease, streamId, headersFrame, fail); + return generateHeadersFrame(accumulator, streamId, headersFrame, fail); } - private int generateHeadersFrame(ByteBufferPool.Lease lease, long streamId, HeadersFrame frame, Consumer fail) + private int generateHeadersFrame(RetainableByteBufferPool.Accumulator accumulator, long streamId, HeadersFrame frame, Consumer fail) { try { @@ -52,21 +55,23 @@ public class HeadersGenerator extends FrameGenerator int frameTypeLength = VarLenInt.length(FrameType.HEADERS.type()); int maxHeaderLength = frameTypeLength + VarLenInt.MAX_LENGTH; // The capacity of the buffer is larger than maxLength, but we need to enforce at most maxLength. - ByteBuffer buffer = lease.acquire(maxHeaderLength + maxLength, useDirectByteBuffers); - buffer.position(maxHeaderLength); - buffer.limit(buffer.position() + maxLength); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(maxHeaderLength + maxLength, useDirectByteBuffers); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + byteBuffer.position(maxHeaderLength); + byteBuffer.limit(byteBuffer.position() + maxLength); // Encode after the maxHeaderLength. - encoder.encode(buffer, streamId, frame.getMetaData()); - buffer.flip(); - buffer.position(maxHeaderLength); + encoder.encode(byteBuffer, streamId, frame.getMetaData()); + byteBuffer.flip(); + byteBuffer.position(maxHeaderLength); int dataLength = buffer.remaining(); int headerLength = frameTypeLength + VarLenInt.length(dataLength); - int position = buffer.position() - headerLength; - buffer.position(position); - VarLenInt.encode(buffer, FrameType.HEADERS.type()); - VarLenInt.encode(buffer, dataLength); - buffer.position(position); - lease.append(buffer, true); + int position = byteBuffer.position() - headerLength; + byteBuffer.position(position); + VarLenInt.encode(byteBuffer, FrameType.HEADERS.type()); + VarLenInt.encode(byteBuffer, dataLength); + byteBuffer.position(position); + accumulator.append(buffer); return headerLength + dataLength; } catch (QpackException x) diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MaxPushIdGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MaxPushIdGenerator.java index ee132a0d9ea..323bbdf29c9 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MaxPushIdGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MaxPushIdGenerator.java @@ -16,12 +16,17 @@ package org.eclipse.jetty.http3.internal.generator; import java.util.function.Consumer; import org.eclipse.jetty.http3.frames.Frame; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class MaxPushIdGenerator extends FrameGenerator { + public MaxPushIdGenerator(RetainableByteBufferPool bufferPool) + { + super(bufferPool); + } + @Override - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { return 0; } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MessageGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MessageGenerator.java index 8b6330cc971..a0339f7d344 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MessageGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/MessageGenerator.java @@ -18,21 +18,21 @@ import java.util.function.Consumer; import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.FrameType; import org.eclipse.jetty.http3.qpack.QpackEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class MessageGenerator { private final FrameGenerator[] generators = new FrameGenerator[FrameType.maxType() + 1]; - public MessageGenerator(QpackEncoder encoder, int maxHeadersLength, boolean useDirectByteBuffers) + public MessageGenerator(RetainableByteBufferPool bufferPool, QpackEncoder encoder, int maxHeadersLength, boolean useDirectByteBuffers) { - generators[FrameType.DATA.type()] = new DataGenerator(useDirectByteBuffers); - generators[FrameType.HEADERS.type()] = new HeadersGenerator(encoder, maxHeadersLength, useDirectByteBuffers); - generators[FrameType.PUSH_PROMISE.type()] = new PushPromiseGenerator(); + generators[FrameType.DATA.type()] = new DataGenerator(bufferPool, useDirectByteBuffers); + generators[FrameType.HEADERS.type()] = new HeadersGenerator(bufferPool, encoder, maxHeadersLength, useDirectByteBuffers); + generators[FrameType.PUSH_PROMISE.type()] = new PushPromiseGenerator(bufferPool); } - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { - return generators[frame.getFrameType().type()].generate(lease, streamId, frame, fail); + return generators[frame.getFrameType().type()].generate(accumulator, streamId, frame, fail); } } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/PushPromiseGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/PushPromiseGenerator.java index 0acfb70bec8..2a849106681 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/PushPromiseGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/PushPromiseGenerator.java @@ -16,12 +16,17 @@ package org.eclipse.jetty.http3.internal.generator; import java.util.function.Consumer; import org.eclipse.jetty.http3.frames.Frame; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class PushPromiseGenerator extends FrameGenerator { + public PushPromiseGenerator(RetainableByteBufferPool bufferPool) + { + super(bufferPool); + } + @Override - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { return 0; } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/SettingsGenerator.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/SettingsGenerator.java index 12a5f18b814..6e200195e75 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/SettingsGenerator.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/internal/generator/SettingsGenerator.java @@ -20,26 +20,28 @@ import java.util.function.Consumer; import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.SettingsFrame; import org.eclipse.jetty.http3.internal.VarLenInt; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; public class SettingsGenerator extends FrameGenerator { private final boolean useDirectByteBuffers; - public SettingsGenerator(boolean useDirectByteBuffers) + public SettingsGenerator(RetainableByteBufferPool bufferPool, boolean useDirectByteBuffers) { + super(bufferPool); this.useDirectByteBuffers = useDirectByteBuffers; } @Override - public int generate(ByteBufferPool.Lease lease, long streamId, Frame frame, Consumer fail) + public int generate(RetainableByteBufferPool.Accumulator accumulator, long streamId, Frame frame, Consumer fail) { SettingsFrame settingsFrame = (SettingsFrame)frame; - return generateSettings(lease, settingsFrame); + return generateSettings(accumulator, settingsFrame); } - private int generateSettings(ByteBufferPool.Lease lease, SettingsFrame frame) + private int generateSettings(RetainableByteBufferPool.Accumulator accumulator, SettingsFrame frame) { int length = 0; Map settings = frame.getSettings(); @@ -48,16 +50,18 @@ public class SettingsGenerator extends FrameGenerator length += VarLenInt.length(e.getKey()) + VarLenInt.length(e.getValue()); } int capacity = VarLenInt.length(frame.getFrameType().type()) + VarLenInt.length(length) + length; - ByteBuffer buffer = lease.acquire(capacity, useDirectByteBuffers); - VarLenInt.encode(buffer, frame.getFrameType().type()); - VarLenInt.encode(buffer, length); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(capacity, useDirectByteBuffers); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + VarLenInt.encode(byteBuffer, frame.getFrameType().type()); + VarLenInt.encode(byteBuffer, length); for (Map.Entry e : settings.entrySet()) { - VarLenInt.encode(buffer, e.getKey()); - VarLenInt.encode(buffer, e.getValue()); + VarLenInt.encode(byteBuffer, e.getKey()); + VarLenInt.encode(byteBuffer, e.getValue()); } - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); return capacity; } } diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java index 9e2f65bdd36..3adbb3c5454 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java @@ -23,7 +23,7 @@ import org.eclipse.jetty.http3.frames.DataFrame; import org.eclipse.jetty.http3.internal.generator.MessageGenerator; import org.eclipse.jetty.http3.internal.parser.MessageParser; import org.eclipse.jetty.http3.internal.parser.ParserListener; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.junit.jupiter.api.Test; @@ -53,8 +53,9 @@ public class DataGenerateParseTest byteBuffer.get(inputBytes); DataFrame input = new DataFrame(ByteBuffer.wrap(inputBytes), true); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(ByteBufferPool.NOOP); - new MessageGenerator(null, 8192, true).generate(lease, 0, input, null); + RetainableByteBufferPool.NonPooling bufferPool = new RetainableByteBufferPool.NonPooling(); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + new MessageGenerator(bufferPool, null, 8192, true).generate(accumulator, 0, input, null); List frames = new ArrayList<>(); MessageParser parser = new MessageParser(new ParserListener() @@ -66,7 +67,7 @@ public class DataGenerateParseTest } }, null, 13, () -> true); parser.init(UnaryOperator.identity()); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/GoAwayGenerateParseTest.java b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/GoAwayGenerateParseTest.java index 315d2051078..d0eae5c137c 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/GoAwayGenerateParseTest.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/GoAwayGenerateParseTest.java @@ -21,7 +21,7 @@ import org.eclipse.jetty.http3.frames.GoAwayFrame; import org.eclipse.jetty.http3.internal.generator.ControlGenerator; import org.eclipse.jetty.http3.internal.parser.ControlParser; import org.eclipse.jetty.http3.internal.parser.ParserListener; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -34,8 +34,9 @@ public class GoAwayGenerateParseTest { GoAwayFrame input = GoAwayFrame.CLIENT_GRACEFUL; - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(ByteBufferPool.NOOP); - new ControlGenerator(true).generate(lease, 0, input, null); + RetainableByteBufferPool.NonPooling bufferPool = new RetainableByteBufferPool.NonPooling(); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + new ControlGenerator(bufferPool, true).generate(accumulator, 0, input, null); List frames = new ArrayList<>(); ControlParser parser = new ControlParser(new ParserListener() @@ -46,7 +47,7 @@ public class GoAwayGenerateParseTest frames.add(frame); } }); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java index 6230acfa41b..436c8799899 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java @@ -29,7 +29,7 @@ import org.eclipse.jetty.http3.internal.parser.MessageParser; import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.eclipse.jetty.http3.qpack.QpackEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -46,11 +46,12 @@ public class HeadersGenerateParseTest .put("Cookie", "c=d"); HeadersFrame input = new HeadersFrame(new MetaData.Request(HttpMethod.GET.asString(), uri, HttpVersion.HTTP_3, fields), true); - QpackEncoder encoder = new QpackEncoder(instructions -> {}, 100); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(ByteBufferPool.NOOP); - new MessageGenerator(encoder, 8192, true).generate(lease, 0, input, null); + RetainableByteBufferPool.NonPooling bufferPool = new RetainableByteBufferPool.NonPooling(); + QpackEncoder encoder = new QpackEncoder(bufferPool, instructions -> {}, 100); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + new MessageGenerator(bufferPool, encoder, 8192, true).generate(accumulator, 0, input, null); - QpackDecoder decoder = new QpackDecoder(instructions -> {}, 8192); + QpackDecoder decoder = new QpackDecoder(bufferPool, instructions -> {}, 8192); List frames = new ArrayList<>(); MessageParser parser = new MessageParser(new ParserListener() { @@ -61,7 +62,7 @@ public class HeadersGenerateParseTest } }, decoder, 13, () -> true); parser.init(UnaryOperator.identity()); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java index 53e8c70402a..7af1c72f48a 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java +++ b/jetty-core/jetty-http3/jetty-http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java @@ -22,7 +22,7 @@ import org.eclipse.jetty.http3.frames.SettingsFrame; import org.eclipse.jetty.http3.internal.generator.ControlGenerator; import org.eclipse.jetty.http3.internal.parser.ControlParser; import org.eclipse.jetty.http3.internal.parser.ParserListener; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -46,8 +46,9 @@ public class SettingsGenerateParseTest { SettingsFrame input = new SettingsFrame(settings); - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(ByteBufferPool.NOOP); - new ControlGenerator(true).generate(lease, 0, input, null); + RetainableByteBufferPool.NonPooling bufferPool = new RetainableByteBufferPool.NonPooling(); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + new ControlGenerator(bufferPool, true).generate(accumulator, 0, input, null); List frames = new ArrayList<>(); ControlParser parser = new ControlParser(new ParserListener() @@ -58,7 +59,7 @@ public class SettingsGenerateParseTest frames.add(frame); } }); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { parser.parse(buffer); assertFalse(buffer.hasRemaining()); diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/Instruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/Instruction.java index b3f472184c6..66824705efa 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/Instruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/Instruction.java @@ -15,11 +15,11 @@ package org.eclipse.jetty.http3.qpack; import java.util.List; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; public interface Instruction { - void encode(ByteBufferPool.Lease lease); + void encode(RetainableByteBufferPool.Accumulator accumulator); /** *

A handler for instructions issued by an {@link QpackEncoder} or {@link QpackDecoder}.

diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java index ca2cecb584f..9c667a1a7fb 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.http3.qpack.internal.table.DynamicTable; import org.eclipse.jetty.http3.qpack.internal.table.Entry; import org.eclipse.jetty.http3.qpack.internal.table.StaticTable; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerParser; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.component.Dumpable; import org.slf4j.Logger; @@ -55,6 +56,7 @@ public class QpackDecoder implements Dumpable private final NBitIntegerParser _integerDecoder = new NBitIntegerParser(); private final InstructionHandler _instructionHandler = new InstructionHandler(); private final Map _blockedStreams = new HashMap<>(); + private final RetainableByteBufferPool _bufferPool; private int _maxHeaderSize; private int _maxBlockedStreams; @@ -80,14 +82,20 @@ public class QpackDecoder implements Dumpable /** * @param maxHeaderSize The maximum allowed size of a headers block, expressed as total of all name and value characters, plus 32 per field */ - public QpackDecoder(Instruction.Handler handler, int maxHeaderSize) + public QpackDecoder(RetainableByteBufferPool bufferPool, Instruction.Handler handler, int maxHeaderSize) { + _bufferPool = bufferPool; _context = new QpackContext(); _handler = handler; _parser = new DecoderInstructionParser(_instructionHandler); _maxHeaderSize = maxHeaderSize; } + public RetainableByteBufferPool getRetainableByteBufferPool() + { + return _bufferPool; + } + QpackContext getQpackContext() { return _context; @@ -171,7 +179,7 @@ public class QpackDecoder implements Dumpable LOG.debug("Decoded: streamId={}, metadata={}", streamId, metaData); _metaDataNotifications.add(new MetaDataNotification(streamId, metaData, handler)); if (requiredInsertCount > 0) - _instructions.add(new SectionAcknowledgmentInstruction(streamId)); + _instructions.add(new SectionAcknowledgmentInstruction(_bufferPool, streamId)); } else { @@ -237,7 +245,7 @@ public class QpackDecoder implements Dumpable _encodedFieldSections.removeIf(encodedFieldSection -> encodedFieldSection.getStreamId() == streamId); _blockedStreams.remove(streamId); _metaDataNotifications.removeIf(notification -> notification._streamId == streamId); - _instructions.add(new StreamCancellationInstruction(streamId)); + _instructions.add(new StreamCancellationInstruction(_bufferPool, streamId)); notifyInstructionHandler(); } @@ -261,7 +269,7 @@ public class QpackDecoder implements Dumpable _metaDataNotifications.add(new MetaDataNotification(streamId, metaData, encodedFieldSection.getHandler())); if (requiredInsertCount > 0) - _instructions.add(new SectionAcknowledgmentInstruction(streamId)); + _instructions.add(new SectionAcknowledgmentInstruction(_bufferPool, streamId)); } } } @@ -352,7 +360,7 @@ public class QpackDecoder implements Dumpable // Add the new Entry to the DynamicTable. Entry entry = new Entry(referencedEntry.getHttpField()); dynamicTable.add(entry); - _instructions.add(new InsertCountIncrementInstruction(1)); + _instructions.add(new InsertCountIncrementInstruction(_bufferPool, 1)); checkEncodedFieldSections(); } @@ -369,7 +377,7 @@ public class QpackDecoder implements Dumpable // Add the new Entry to the DynamicTable. Entry entry = new Entry(new HttpField(referencedEntry.getHttpField().getHeader(), referencedEntry.getHttpField().getName(), value)); dynamicTable.add(entry); - _instructions.add(new InsertCountIncrementInstruction(1)); + _instructions.add(new InsertCountIncrementInstruction(_bufferPool, 1)); checkEncodedFieldSections(); } @@ -384,7 +392,7 @@ public class QpackDecoder implements Dumpable // Add the new Entry to the DynamicTable. DynamicTable dynamicTable = _context.getDynamicTable(); dynamicTable.add(entry); - _instructions.add(new InsertCountIncrementInstruction(1)); + _instructions.add(new InsertCountIncrementInstruction(_bufferPool, 1)); checkEncodedFieldSections(); } } diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java index d8427466f9c..190a6706320 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java @@ -38,6 +38,7 @@ import org.eclipse.jetty.http3.qpack.internal.parser.EncoderInstructionParser; import org.eclipse.jetty.http3.qpack.internal.table.DynamicTable; import org.eclipse.jetty.http3.qpack.internal.table.Entry; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.thread.AutoLock; @@ -89,6 +90,7 @@ public class QpackEncoder implements Dumpable private final AutoLock lock = new AutoLock(); private final List _instructions = new ArrayList<>(); + private final RetainableByteBufferPool _bufferPool; private final Instruction.Handler _handler; private final QpackContext _context; private int _maxBlockedStreams; @@ -98,14 +100,20 @@ public class QpackEncoder implements Dumpable private int _knownInsertCount = 0; private int _blockedStreams = 0; - public QpackEncoder(Instruction.Handler handler, int maxBlockedStreams) + public QpackEncoder(RetainableByteBufferPool bufferPool, Instruction.Handler handler, int maxBlockedStreams) { + _bufferPool = bufferPool; _handler = handler; _context = new QpackContext(); _maxBlockedStreams = maxBlockedStreams; _parser = new EncoderInstructionParser(_instructionHandler); } + public RetainableByteBufferPool getRetainableByteBufferPool() + { + return _bufferPool; + } + Map getStreamInfoMap() { return _streamInfoMap; @@ -134,7 +142,7 @@ public class QpackEncoder implements Dumpable public void setCapacity(int capacity) { _context.getDynamicTable().setCapacity(capacity); - _handler.onInstructions(List.of(new SetCapacityInstruction(capacity))); + _handler.onInstructions(List.of(new SetCapacityInstruction(_bufferPool, capacity))); notifyInstructionHandler(); } @@ -294,7 +302,7 @@ public class QpackEncoder implements Dumpable { int index = _context.indexOf(entry); dynamicTable.add(new Entry(field)); - _instructions.add(new DuplicateInstruction(index)); + _instructions.add(new DuplicateInstruction(_bufferPool, index)); notifyInstructionHandler(); return true; } @@ -306,14 +314,14 @@ public class QpackEncoder implements Dumpable { int index = _context.indexOf(nameEntry); dynamicTable.add(new Entry(field)); - _instructions.add(new IndexedNameEntryInstruction(!nameEntry.isStatic(), index, huffman, field.getValue())); + _instructions.add(new IndexedNameEntryInstruction(_bufferPool, !nameEntry.isStatic(), index, huffman, field.getValue())); notifyInstructionHandler(); return true; } // Add the entry without referencing an existing entry. dynamicTable.add(new Entry(field)); - _instructions.add(new LiteralNameEntryInstruction(field, huffman)); + _instructions.add(new LiteralNameEntryInstruction(_bufferPool, field, huffman)); notifyInstructionHandler(); return true; } @@ -366,7 +374,7 @@ public class QpackEncoder implements Dumpable int index = _context.indexOf(entry); Entry newEntry = new Entry(field); dynamicTable.add(newEntry); - _instructions.add(new DuplicateInstruction(index)); + _instructions.add(new DuplicateInstruction(_bufferPool, index)); // Should we reference this entry and risk blocking. if (referenceEntry(newEntry, streamInfo)) @@ -384,7 +392,7 @@ public class QpackEncoder implements Dumpable int index = _context.indexOf(nameEntry); Entry newEntry = new Entry(field); dynamicTable.add(newEntry); - _instructions.add(new IndexedNameEntryInstruction(!nameEntry.isStatic(), index, huffman, field.getValue())); + _instructions.add(new IndexedNameEntryInstruction(_bufferPool, !nameEntry.isStatic(), index, huffman, field.getValue())); // Should we reference this entry and risk blocking. if (referenceEntry(newEntry, streamInfo)) @@ -399,7 +407,7 @@ public class QpackEncoder implements Dumpable { Entry newEntry = new Entry(field); dynamicTable.add(newEntry); - _instructions.add(new LiteralNameEntryInstruction(field, huffman)); + _instructions.add(new LiteralNameEntryInstruction(_bufferPool, field, huffman)); // Should we reference this entry and risk blocking. if (referenceEntry(newEntry, streamInfo)) diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/AbstractInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/AbstractInstruction.java new file mode 100644 index 00000000000..277d66e87d1 --- /dev/null +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/AbstractInstruction.java @@ -0,0 +1,32 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.http3.qpack.internal.instruction; + +import org.eclipse.jetty.http3.qpack.Instruction; +import org.eclipse.jetty.io.RetainableByteBufferPool; + +public abstract class AbstractInstruction implements Instruction +{ + private final RetainableByteBufferPool bufferPool; + + protected AbstractInstruction(RetainableByteBufferPool bufferPool) + { + this.bufferPool = bufferPool; + } + + public RetainableByteBufferPool getRetainableByteBufferPool() + { + return bufferPool; + } +} diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/DuplicateInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/DuplicateInstruction.java index 8d88d81c2bc..f9c6e362229 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/DuplicateInstruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/DuplicateInstruction.java @@ -15,17 +15,18 @@ package org.eclipse.jetty.http3.qpack.internal.instruction; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.qpack.Instruction; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -public class DuplicateInstruction implements Instruction +public class DuplicateInstruction extends AbstractInstruction { private final int _index; - public DuplicateInstruction(int index) + public DuplicateInstruction(RetainableByteBufferPool bufferPool, int index) { + super(bufferPool); _index = index; } @@ -35,14 +36,16 @@ public class DuplicateInstruction implements Instruction } @Override - public void encode(ByteBufferPool.Lease lease) + public void encode(RetainableByteBufferPool.Accumulator accumulator) { int size = NBitIntegerEncoder.octetsNeeded(5, _index) + 1; - ByteBuffer buffer = lease.acquire(size, false); - buffer.put((byte)0x00); - NBitIntegerEncoder.encode(buffer, 5, _index); - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(size, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + byteBuffer.put((byte)0x00); + NBitIntegerEncoder.encode(byteBuffer, 5, _index); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); } @Override diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/IndexedNameEntryInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/IndexedNameEntryInstruction.java index 68da23fa120..0e66dd59652 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/IndexedNameEntryInstruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/IndexedNameEntryInstruction.java @@ -15,21 +15,22 @@ package org.eclipse.jetty.http3.qpack.internal.instruction; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.qpack.Instruction; import org.eclipse.jetty.http3.qpack.internal.util.HuffmanEncoder; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -public class IndexedNameEntryInstruction implements Instruction +public class IndexedNameEntryInstruction extends AbstractInstruction { private final boolean _dynamic; private final int _index; private final boolean _huffman; private final String _value; - public IndexedNameEntryInstruction(boolean dynamic, int index, boolean huffman, String value) + public IndexedNameEntryInstruction(RetainableByteBufferPool bufferPool, boolean dynamic, int index, boolean huffman, String value) { + super(bufferPool); _dynamic = dynamic; _index = index; _huffman = huffman; @@ -52,31 +53,33 @@ public class IndexedNameEntryInstruction implements Instruction } @Override - public void encode(ByteBufferPool.Lease lease) + public void encode(RetainableByteBufferPool.Accumulator accumulator) { int size = NBitIntegerEncoder.octetsNeeded(6, _index) + (_huffman ? HuffmanEncoder.octetsNeeded(_value) : _value.length()) + 2; - ByteBuffer buffer = lease.acquire(size, false); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(size, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); // First bit indicates the instruction, second bit is whether it is a dynamic table reference or not. - buffer.put((byte)(0x80 | (_dynamic ? 0x00 : 0x40))); - NBitIntegerEncoder.encode(buffer, 6, _index); + byteBuffer.put((byte)(0x80 | (_dynamic ? 0x00 : 0x40))); + NBitIntegerEncoder.encode(byteBuffer, 6, _index); // We will not huffman encode the string. if (_huffman) { - buffer.put((byte)(0x80)); - NBitIntegerEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(_value)); - HuffmanEncoder.encode(buffer, _value); + byteBuffer.put((byte)(0x80)); + NBitIntegerEncoder.encode(byteBuffer, 7, HuffmanEncoder.octetsNeeded(_value)); + HuffmanEncoder.encode(byteBuffer, _value); } else { - buffer.put((byte)(0x00)); - NBitIntegerEncoder.encode(buffer, 7, _value.length()); - buffer.put(_value.getBytes()); + byteBuffer.put((byte)(0x00)); + NBitIntegerEncoder.encode(byteBuffer, 7, _value.length()); + byteBuffer.put(_value.getBytes()); } - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); } @Override diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/InsertCountIncrementInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/InsertCountIncrementInstruction.java index d9b02db55f4..5bff85dc91e 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/InsertCountIncrementInstruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/InsertCountIncrementInstruction.java @@ -15,17 +15,18 @@ package org.eclipse.jetty.http3.qpack.internal.instruction; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.qpack.Instruction; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -public class InsertCountIncrementInstruction implements Instruction +public class InsertCountIncrementInstruction extends AbstractInstruction { private final int _increment; - public InsertCountIncrementInstruction(int increment) + public InsertCountIncrementInstruction(RetainableByteBufferPool bufferPool, int increment) { + super(bufferPool); _increment = increment; } @@ -35,14 +36,16 @@ public class InsertCountIncrementInstruction implements Instruction } @Override - public void encode(ByteBufferPool.Lease lease) + public void encode(RetainableByteBufferPool.Accumulator accumulator) { int size = NBitIntegerEncoder.octetsNeeded(6, _increment) + 1; - ByteBuffer buffer = lease.acquire(size, false); - buffer.put((byte)0x00); - NBitIntegerEncoder.encode(buffer, 6, _increment); - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(size, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + byteBuffer.put((byte)0x00); + NBitIntegerEncoder.encode(byteBuffer, 6, _increment); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); } @Override diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/LiteralNameEntryInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/LiteralNameEntryInstruction.java index 623951545f4..a2c2c5c26dd 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/LiteralNameEntryInstruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/LiteralNameEntryInstruction.java @@ -16,26 +16,27 @@ package org.eclipse.jetty.http3.qpack.internal.instruction; import java.nio.ByteBuffer; import org.eclipse.jetty.http.HttpField; -import org.eclipse.jetty.http3.qpack.Instruction; import org.eclipse.jetty.http3.qpack.internal.util.HuffmanEncoder; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -public class LiteralNameEntryInstruction implements Instruction +public class LiteralNameEntryInstruction extends AbstractInstruction { private final boolean _huffmanName; private final boolean _huffmanValue; private final String _name; private final String _value; - public LiteralNameEntryInstruction(HttpField httpField, boolean huffman) + public LiteralNameEntryInstruction(RetainableByteBufferPool bufferPool, HttpField httpField, boolean huffman) { - this(httpField, huffman, huffman); + this(bufferPool, httpField, huffman, huffman); } - public LiteralNameEntryInstruction(HttpField httpField, boolean huffmanName, boolean huffmanValue) + public LiteralNameEntryInstruction(RetainableByteBufferPool bufferPool, HttpField httpField, boolean huffmanName, boolean huffmanValue) { + super(bufferPool); _huffmanName = huffmanName; _huffmanValue = huffmanValue; _name = httpField.getName(); @@ -53,40 +54,42 @@ public class LiteralNameEntryInstruction implements Instruction } @Override - public void encode(ByteBufferPool.Lease lease) + public void encode(RetainableByteBufferPool.Accumulator accumulator) { int size = (_huffmanName ? HuffmanEncoder.octetsNeeded(_name) : _name.length()) + (_huffmanValue ? HuffmanEncoder.octetsNeeded(_value) : _value.length()) + 2; - ByteBuffer buffer = lease.acquire(size, false); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(size, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); if (_huffmanName) { - buffer.put((byte)(0x40 | 0x20)); - NBitIntegerEncoder.encode(buffer, 5, HuffmanEncoder.octetsNeeded(_name)); - HuffmanEncoder.encode(buffer, _name); + byteBuffer.put((byte)(0x40 | 0x20)); + NBitIntegerEncoder.encode(byteBuffer, 5, HuffmanEncoder.octetsNeeded(_name)); + HuffmanEncoder.encode(byteBuffer, _name); } else { - buffer.put((byte)(0x40)); - NBitIntegerEncoder.encode(buffer, 5, _name.length()); - buffer.put(_name.getBytes()); + byteBuffer.put((byte)(0x40)); + NBitIntegerEncoder.encode(byteBuffer, 5, _name.length()); + byteBuffer.put(_name.getBytes()); } if (_huffmanValue) { - buffer.put((byte)(0x80)); - NBitIntegerEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(_value)); - HuffmanEncoder.encode(buffer, _value); + byteBuffer.put((byte)(0x80)); + NBitIntegerEncoder.encode(byteBuffer, 7, HuffmanEncoder.octetsNeeded(_value)); + HuffmanEncoder.encode(byteBuffer, _value); } else { - buffer.put((byte)(0x00)); - NBitIntegerEncoder.encode(buffer, 5, _value.length()); - buffer.put(_value.getBytes()); + byteBuffer.put((byte)(0x00)); + NBitIntegerEncoder.encode(byteBuffer, 5, _value.length()); + byteBuffer.put(_value.getBytes()); } - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); } @Override diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SectionAcknowledgmentInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SectionAcknowledgmentInstruction.java index 9cd0dba17f1..85cd979072d 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SectionAcknowledgmentInstruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SectionAcknowledgmentInstruction.java @@ -15,17 +15,18 @@ package org.eclipse.jetty.http3.qpack.internal.instruction; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.qpack.Instruction; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -public class SectionAcknowledgmentInstruction implements Instruction +public class SectionAcknowledgmentInstruction extends AbstractInstruction { private final long _streamId; - public SectionAcknowledgmentInstruction(long streamId) + public SectionAcknowledgmentInstruction(RetainableByteBufferPool bufferPool, long streamId) { + super(bufferPool); _streamId = streamId; } @@ -35,14 +36,16 @@ public class SectionAcknowledgmentInstruction implements Instruction } @Override - public void encode(ByteBufferPool.Lease lease) + public void encode(RetainableByteBufferPool.Accumulator accumulator) { int size = NBitIntegerEncoder.octetsNeeded(7, _streamId) + 1; - ByteBuffer buffer = lease.acquire(size, false); - buffer.put((byte)0x80); - NBitIntegerEncoder.encode(buffer, 7, _streamId); - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(size, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + byteBuffer.put((byte)0x80); + NBitIntegerEncoder.encode(byteBuffer, 7, _streamId); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); } @Override diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SetCapacityInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SetCapacityInstruction.java index 68bc69d0f49..9582db62b87 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SetCapacityInstruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/SetCapacityInstruction.java @@ -15,17 +15,18 @@ package org.eclipse.jetty.http3.qpack.internal.instruction; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.qpack.Instruction; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -public class SetCapacityInstruction implements Instruction +public class SetCapacityInstruction extends AbstractInstruction { private final int _capacity; - public SetCapacityInstruction(int capacity) + public SetCapacityInstruction(RetainableByteBufferPool bufferPool, int capacity) { + super(bufferPool); _capacity = capacity; } @@ -35,14 +36,16 @@ public class SetCapacityInstruction implements Instruction } @Override - public void encode(ByteBufferPool.Lease lease) + public void encode(RetainableByteBufferPool.Accumulator accumulator) { int size = NBitIntegerEncoder.octetsNeeded(5, _capacity) + 1; - ByteBuffer buffer = lease.acquire(size, false); - buffer.put((byte)0x20); - NBitIntegerEncoder.encode(buffer, 5, _capacity); - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(size, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + byteBuffer.put((byte)0x20); + NBitIntegerEncoder.encode(byteBuffer, 5, _capacity); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); } @Override diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/StreamCancellationInstruction.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/StreamCancellationInstruction.java index fc8ce46e0cd..ae3ad81b59e 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/StreamCancellationInstruction.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/instruction/StreamCancellationInstruction.java @@ -15,29 +15,32 @@ package org.eclipse.jetty.http3.qpack.internal.instruction; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.qpack.Instruction; import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -public class StreamCancellationInstruction implements Instruction +public class StreamCancellationInstruction extends AbstractInstruction { private final long _streamId; - public StreamCancellationInstruction(long streamId) + public StreamCancellationInstruction(RetainableByteBufferPool bufferPool, long streamId) { + super(bufferPool); _streamId = streamId; } @Override - public void encode(ByteBufferPool.Lease lease) + public void encode(RetainableByteBufferPool.Accumulator accumulator) { int size = NBitIntegerEncoder.octetsNeeded(6, _streamId) + 1; - ByteBuffer buffer = lease.acquire(size, false); - buffer.put((byte)0x40); - NBitIntegerEncoder.encode(buffer, 6, _streamId); - BufferUtil.flipToFlush(buffer, 0); - lease.append(buffer, true); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(size, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + byteBuffer.put((byte)0x40); + NBitIntegerEncoder.encode(byteBuffer, 6, _streamId); + BufferUtil.flipToFlush(byteBuffer, 0); + accumulator.append(buffer); } @Override diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java index 2584c43d52a..c72e0a9621c 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java @@ -23,6 +23,8 @@ import org.eclipse.jetty.http3.qpack.internal.instruction.InsertCountIncrementIn import org.eclipse.jetty.http3.qpack.internal.instruction.LiteralNameEntryInstruction; import org.eclipse.jetty.http3.qpack.internal.instruction.SectionAcknowledgmentInstruction; import org.eclipse.jetty.http3.qpack.internal.instruction.SetCapacityInstruction; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -54,8 +56,9 @@ public class BlockedStreamsTest { _encoderHandler = new TestEncoderHandler(); _decoderHandler = new TestDecoderHandler(); - _encoder = new QpackEncoder(_encoderHandler, MAX_BLOCKED_STREAMS); - _decoder = new QpackDecoder(_decoderHandler, MAX_HEADER_SIZE); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + _encoder = new QpackEncoder(bufferPool, _encoderHandler, MAX_BLOCKED_STREAMS); + _decoder = new QpackDecoder(bufferPool, _decoderHandler, MAX_HEADER_SIZE); } @Test diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java index 6e9de70b601..f2b8b6baf60 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java @@ -27,6 +27,8 @@ import org.eclipse.jetty.http3.qpack.internal.instruction.SectionAcknowledgmentI import org.eclipse.jetty.http3.qpack.internal.instruction.SetCapacityInstruction; import org.eclipse.jetty.http3.qpack.internal.parser.DecoderInstructionParser; import org.eclipse.jetty.http3.qpack.internal.parser.EncoderInstructionParser; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -56,7 +58,8 @@ public class EncodeDecodeTest { _encoderHandler = new TestEncoderHandler(); _decoderHandler = new TestDecoderHandler(); - _encoder = new QpackEncoder(_encoderHandler, MAX_BLOCKED_STREAMS) + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + _encoder = new QpackEncoder(bufferPool, _encoderHandler, MAX_BLOCKED_STREAMS) { @Override protected boolean shouldHuffmanEncode(HttpField httpField) @@ -64,7 +67,7 @@ public class EncodeDecodeTest return false; } }; - _decoder = new QpackDecoder(_decoderHandler, MAX_HEADER_SIZE); + _decoder = new QpackDecoder(bufferPool, _decoderHandler, MAX_HEADER_SIZE); _encoderInstructionParser = new EncoderInstructionParser(new EncoderParserDebugHandler(_encoder)); _decoderInstructionParser = new DecoderInstructionParser(new DecoderParserDebugHandler(_decoder)); @@ -91,7 +94,7 @@ public class EncodeDecodeTest assertThat(_decoderHandler.getInstruction(), instanceOf(SectionAcknowledgmentInstruction.class)); assertTrue(_decoderHandler.isEmpty()); - _encoderInstructionParser.parse(QpackTestUtil.toBuffer(List.of(new SectionAcknowledgmentInstruction(streamId)))); + _encoderInstructionParser.parse(QpackTestUtil.toBuffer(List.of(new SectionAcknowledgmentInstruction(_encoder.getRetainableByteBufferPool(), streamId)))); // B.2. Dynamic Table. @@ -143,8 +146,8 @@ public class EncodeDecodeTest assertTrue(_decoderHandler.isEmpty()); // Parse the decoder instructions on the encoder. - _encoderInstructionParser.parse(QpackTestUtil.toBuffer(List.of(new InsertCountIncrementInstruction(2)))); - _encoderInstructionParser.parse(QpackTestUtil.toBuffer(List.of(new SectionAcknowledgmentInstruction(streamId)))); + _encoderInstructionParser.parse(QpackTestUtil.toBuffer(List.of(new InsertCountIncrementInstruction(_encoder.getRetainableByteBufferPool(), 2)))); + _encoderInstructionParser.parse(QpackTestUtil.toBuffer(List.of(new SectionAcknowledgmentInstruction(_encoder.getRetainableByteBufferPool(), streamId)))); // B.3. Speculative Insert _encoder.insert(new HttpField("custom-key", "custom-value")); diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java index d0e694bf1b6..a24b3664cc8 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java @@ -20,6 +20,8 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,8 +42,9 @@ public class EvictionTest @BeforeEach public void before() { - _decoder = new QpackDecoder(_decoderHandler, MAX_HEADER_SIZE); - _encoder = new QpackEncoder(_encoderHandler, MAX_BLOCKED_STREAMS) + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + _decoder = new QpackDecoder(bufferPool, _decoderHandler, MAX_HEADER_SIZE); + _encoder = new QpackEncoder(bufferPool, _encoderHandler, MAX_BLOCKED_STREAMS) { @Override protected boolean shouldHuffmanEncode(HttpField httpField) diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/InstructionGeneratorTest.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/InstructionGeneratorTest.java index 03352c447b4..42eb19789ee 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/InstructionGeneratorTest.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/InstructionGeneratorTest.java @@ -15,8 +15,8 @@ package org.eclipse.jetty.http3.qpack; import org.eclipse.jetty.http3.qpack.internal.instruction.IndexedNameEntryInstruction; import org.eclipse.jetty.http3.qpack.internal.instruction.SectionAcknowledgmentInstruction; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.junit.jupiter.api.Test; @@ -26,14 +26,14 @@ import static org.hamcrest.Matchers.is; public class InstructionGeneratorTest { - private final ByteBufferPool _bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool _bufferPool = new ArrayRetainableByteBufferPool(); private String toHexString(Instruction instruction) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(_bufferPool); - instruction.encode(lease); - assertThat(lease.getSize(), is(1)); - return BufferUtil.toHexString(lease.getByteBuffers().get(0)); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + instruction.encode(accumulator); + assertThat(accumulator.getSize(), is(1)); + return BufferUtil.toHexString(accumulator.getByteBuffers().get(0)); } @Test @@ -41,10 +41,10 @@ public class InstructionGeneratorTest { Instruction instruction; - instruction = new SectionAcknowledgmentInstruction(4); + instruction = new SectionAcknowledgmentInstruction(_bufferPool, 4); assertThat(toHexString(instruction), equalToIgnoringCase("84")); - instruction = new SectionAcknowledgmentInstruction(1337); + instruction = new SectionAcknowledgmentInstruction(_bufferPool, 1337); assertThat(toHexString(instruction), equalToIgnoringCase("FFBA09")); } @@ -53,10 +53,10 @@ public class InstructionGeneratorTest { Instruction instruction; - instruction = new IndexedNameEntryInstruction(false, 0, false, "www.example.com"); + instruction = new IndexedNameEntryInstruction(_bufferPool, false, 0, false, "www.example.com"); assertThat(toHexString(instruction), equalToIgnoringCase("c00f7777772e6578616d706c652e636f6d")); - instruction = new IndexedNameEntryInstruction(false, 1, false, "/sample/path"); + instruction = new IndexedNameEntryInstruction(_bufferPool, false, 1, false, "/sample/path"); assertThat(toHexString(instruction), equalToIgnoringCase("c10c2f73616d706c652f70617468")); } } diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/QpackTestUtil.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/QpackTestUtil.java index 5cacd32c972..4040d85cc62 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/QpackTestUtil.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/QpackTestUtil.java @@ -20,9 +20,9 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.StringUtil; import org.hamcrest.Matcher; import static org.hamcrest.MatcherAssert.assertThat; @@ -32,14 +32,14 @@ public class QpackTestUtil { public static ByteBuffer toBuffer(Instruction... instructions) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(ByteBufferPool.NOOP); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); for (Instruction instruction : instructions) { - instruction.encode(lease); + instruction.encode(accumulator); } - ByteBuffer combinedBuffer = BufferUtil.allocate(Math.toIntExact(lease.getTotalLength())); + ByteBuffer combinedBuffer = BufferUtil.allocate(Math.toIntExact(accumulator.getTotalLength())); BufferUtil.clearToFill(combinedBuffer); - for (ByteBuffer buffer : lease.getByteBuffers()) + for (ByteBuffer buffer : accumulator.getByteBuffers()) { combinedBuffer.put(buffer); } @@ -55,12 +55,12 @@ public class QpackTestUtil public static ByteBuffer toBuffer(List instructions) { - ByteBufferPool.Lease lease = new ByteBufferPool.Lease(ByteBufferPool.NOOP); - instructions.forEach(i -> i.encode(lease)); - assertThat(lease.getSize(), is(instructions.size())); - ByteBuffer combinedBuffer = BufferUtil.allocate(Math.toIntExact(lease.getTotalLength()), false); + RetainableByteBufferPool.Accumulator accumulator = new RetainableByteBufferPool.Accumulator(); + instructions.forEach(i -> i.encode(accumulator)); + assertThat(accumulator.getSize(), is(instructions.size())); + ByteBuffer combinedBuffer = BufferUtil.allocate(Math.toIntExact(accumulator.getTotalLength()), false); BufferUtil.clearToFill(combinedBuffer); - lease.getByteBuffers().forEach(combinedBuffer::put); + accumulator.getByteBuffers().forEach(combinedBuffer::put); BufferUtil.flipToFlush(combinedBuffer, 0); return combinedBuffer; } @@ -68,7 +68,7 @@ public class QpackTestUtil public static ByteBuffer hexToBuffer(String hexString) { hexString = hexString.replaceAll("\\s+", ""); - return ByteBuffer.wrap(TypeUtil.fromHexString(hexString)); + return ByteBuffer.wrap(StringUtil.fromHexString(hexString)); } public static String toHexString(Instruction instruction) diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java index bc2eda81ce6..f58d30a74e7 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java +++ b/jetty-core/jetty-http3/jetty-http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java @@ -17,6 +17,8 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.http3.qpack.QpackException.SessionException; import org.eclipse.jetty.http3.qpack.internal.instruction.SectionAcknowledgmentInstruction; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -46,8 +48,9 @@ public class SectionAcknowledgmentTest { _encoderHandler = new TestEncoderHandler(); _decoderHandler = new TestDecoderHandler(); - _encoder = new QpackEncoder(_encoderHandler, MAX_BLOCKED_STREAMS); - _decoder = new QpackDecoder(_decoderHandler, MAX_HEADER_SIZE); + RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); + _encoder = new QpackEncoder(bufferPool, _encoderHandler, MAX_BLOCKED_STREAMS); + _decoder = new QpackDecoder(bufferPool, _decoderHandler, MAX_HEADER_SIZE); } @Test @@ -74,7 +77,7 @@ public class SectionAcknowledgmentTest assertThat(BufferUtil.remaining(buffer), greaterThan(0L)); // Parsing a section ack instruction on the encoder when we are not expecting it should result in QPACK_DECODER_STREAM_ERROR. - SectionAcknowledgmentInstruction instruction = new SectionAcknowledgmentInstruction(0); + SectionAcknowledgmentInstruction instruction = new SectionAcknowledgmentInstruction(_encoder.getRetainableByteBufferPool(), 0); ByteBuffer instructionBuffer = toBuffer(instruction); SessionException error = assertThrows(SessionException.class, () -> _encoder.parseInstructions(instructionBuffer)); assertThat(error.getErrorCode(), equalTo(QpackException.QPACK_ENCODER_STREAM_ERROR)); diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnector.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnector.java index b8aa6230be2..dc83937ae02 100644 --- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnector.java +++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnector.java @@ -18,7 +18,7 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.PreEncodedHttpField; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.server.QuicServerConnector; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Server; @@ -44,7 +44,7 @@ public class HTTP3ServerConnector extends QuicServerConnector this(server, null, null, null, sslContextFactory, factories); } - public HTTP3ServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, SslContextFactory.Server sslContextFactory, ConnectionFactory... factories) + public HTTP3ServerConnector(Server server, Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, SslContextFactory.Server sslContextFactory, ConnectionFactory... factories) { super(server, executor, scheduler, bufferPool, sslContextFactory, factories); // Max concurrent streams that a client can open. diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java index adcf58e44d4..8003b737acd 100644 --- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java +++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.http3.internal.MessageFlusher; import org.eclipse.jetty.http3.internal.UnidirectionalStreamConnection; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.eclipse.jetty.http3.qpack.QpackEncoder; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; import org.eclipse.jetty.quic.common.StreamType; import org.eclipse.jetty.quic.server.ServerProtocolSession; @@ -62,7 +63,8 @@ public class ServerHTTP3Session extends ServerProtocolSession long encoderStreamId = getQuicSession().newStreamId(StreamType.SERVER_UNIDIRECTIONAL); QuicStreamEndPoint encoderEndPoint = openInstructionEndPoint(encoderStreamId); InstructionFlusher encoderInstructionFlusher = new InstructionFlusher(quicSession, encoderEndPoint, EncoderStreamConnection.STREAM_TYPE); - this.encoder = new QpackEncoder(new InstructionHandler(encoderInstructionFlusher), configuration.getMaxBlockedStreams()); + RetainableByteBufferPool bufferPool = quicSession.getRetainableByteBufferPool(); + this.encoder = new QpackEncoder(bufferPool, new InstructionHandler(encoderInstructionFlusher), configuration.getMaxBlockedStreams()); addBean(encoder); if (LOG.isDebugEnabled()) LOG.debug("created encoder stream #{} on {}", encoderStreamId, encoderEndPoint); @@ -70,7 +72,7 @@ public class ServerHTTP3Session extends ServerProtocolSession long decoderStreamId = getQuicSession().newStreamId(StreamType.SERVER_UNIDIRECTIONAL); QuicStreamEndPoint decoderEndPoint = openInstructionEndPoint(decoderStreamId); InstructionFlusher decoderInstructionFlusher = new InstructionFlusher(quicSession, decoderEndPoint, DecoderStreamConnection.STREAM_TYPE); - this.decoder = new QpackDecoder(new InstructionHandler(decoderInstructionFlusher), configuration.getMaxRequestHeadersSize()); + this.decoder = new QpackDecoder(bufferPool, new InstructionHandler(decoderInstructionFlusher), configuration.getMaxRequestHeadersSize()); addBean(decoder); if (LOG.isDebugEnabled()) LOG.debug("created decoder stream #{} on {}", decoderStreamId, decoderEndPoint); @@ -82,7 +84,7 @@ public class ServerHTTP3Session extends ServerProtocolSession if (LOG.isDebugEnabled()) LOG.debug("created control stream #{} on {}", controlStreamId, controlEndPoint); - this.messageFlusher = new MessageFlusher(quicSession.getByteBufferPool(), encoder, configuration.getMaxResponseHeadersSize(), configuration.isUseOutputDirectByteBuffers()); + this.messageFlusher = new MessageFlusher(quicSession.getRetainableByteBufferPool(), encoder, configuration.getMaxResponseHeadersSize(), configuration.isUseOutputDirectByteBuffers()); addBean(messageFlusher); } @@ -189,7 +191,7 @@ public class ServerHTTP3Session extends ServerProtocolSession private void openUnidirectionalStreamEndPoint(QuicStreamEndPoint endPoint) { - UnidirectionalStreamConnection connection = new UnidirectionalStreamConnection(endPoint, getQuicSession().getExecutor(), getQuicSession().getByteBufferPool(), encoder, decoder, session); + UnidirectionalStreamConnection connection = new UnidirectionalStreamConnection(endPoint, getQuicSession().getExecutor(), getQuicSession().getRetainableByteBufferPool(), encoder, decoder, session); endPoint.setConnection(connection); endPoint.opened(); } diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java index 01d08089491..0539977f962 100644 --- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java +++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java @@ -42,7 +42,7 @@ public class ServerHTTP3StreamConnection extends HTTP3StreamConnection implement public ServerHTTP3StreamConnection(Connector connector, HttpConfiguration httpConfiguration, QuicStreamEndPoint endPoint, ServerHTTP3Session session, MessageParser parser) { - super(endPoint, connector.getExecutor(), connector.getByteBufferPool(), parser); + super(endPoint, connector.getExecutor(), connector.getRetainableByteBufferPool(), parser); this.connector = connector; this.httpConfiguration = httpConfiguration; this.session = session; diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java deleted file mode 100644 index 748ec94a3a3..00000000000 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java +++ /dev/null @@ -1,276 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.nio.ByteBuffer; -import java.util.Objects; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; -import java.util.function.IntConsumer; - -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.NanoTime; -import org.eclipse.jetty.util.annotation.ManagedAttribute; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.annotation.ManagedOperation; - -/** - * The {@code maxHeapMemory} and {@code maxDirectMemory} default heuristic is to use {@link Runtime#maxMemory()} - * divided by 4. - */ -@ManagedObject -abstract class AbstractByteBufferPool implements ByteBufferPool -{ - public static final int DEFAULT_FACTOR = 4096; - public static final int DEFAULT_MAX_CAPACITY_BY_FACTOR = 16; - private final int _factor; - private final int _maxCapacity; - private final int _maxBucketSize; - private final long _maxHeapMemory; - private final long _maxDirectMemory; - private final AtomicLong _heapMemory = new AtomicLong(); - private final AtomicLong _directMemory = new AtomicLong(); - private final RetainableByteBufferPool _retainableByteBufferPool; - - /** - * Creates a new ByteBufferPool with the given configuration. - * - * @param factor the capacity factor - * @param maxBucketSize the maximum ByteBuffer queue length - * @param maxHeapMemory the max heap memory in bytes, -1 for unlimited memory or 0 to use default heuristic - * @param maxDirectMemory the max direct memory in bytes, -1 for unlimited memory or 0 to use default heuristic - * @param retainedHeapMemory the max heap memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - * @param retainedDirectMemory the max direct memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - */ - protected AbstractByteBufferPool(int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory, long retainedHeapMemory, long retainedDirectMemory) - { - _factor = factor <= 0 ? DEFAULT_FACTOR : factor; - _maxCapacity = maxCapacity > 0 ? maxCapacity : DEFAULT_MAX_CAPACITY_BY_FACTOR * _factor; - _maxBucketSize = maxBucketSize; - _maxHeapMemory = memorySize(maxHeapMemory); - _maxDirectMemory = memorySize(maxDirectMemory); - _retainableByteBufferPool = (retainedHeapMemory == -2 && retainedDirectMemory == -2) - ? RetainableByteBufferPool.from(this) - : newRetainableByteBufferPool(factor, maxCapacity, maxBucketSize, retainedSize(retainedHeapMemory), retainedSize(retainedDirectMemory)); - } - - static long retainedSize(long size) - { - if (size == -2) - return 0; - return memorySize(size); - } - - static long memorySize(long size) - { - if (size < 0) - return -1; - if (size == 0) - return Runtime.getRuntime().maxMemory() / 4; - return size; - } - - protected RetainableByteBufferPool newRetainableByteBufferPool(int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - return RetainableByteBufferPool.from(this); - } - - @Override - public RetainableByteBufferPool asRetainableByteBufferPool() - { - return _retainableByteBufferPool; - } - - protected int getCapacityFactor() - { - return _factor; - } - - protected int getMaxCapacity() - { - return _maxCapacity; - } - - protected int getMaxBucketSize() - { - return _maxBucketSize; - } - - @Deprecated - protected void decrementMemory(ByteBuffer buffer) - { - updateMemory(buffer, false); - } - - @Deprecated - protected void incrementMemory(ByteBuffer buffer) - { - updateMemory(buffer, true); - } - - private void updateMemory(ByteBuffer buffer, boolean addOrSub) - { - AtomicLong memory = buffer.isDirect() ? _directMemory : _heapMemory; - int capacity = buffer.capacity(); - memory.addAndGet(addOrSub ? capacity : -capacity); - } - - protected void releaseExcessMemory(boolean direct, Consumer clearFn) - { - long maxMemory = direct ? _maxDirectMemory : _maxHeapMemory; - if (maxMemory > 0) - { - while (getMemory(direct) > maxMemory) - { - clearFn.accept(direct); - } - } - } - - @ManagedAttribute("The bytes retained by direct ByteBuffers") - public long getDirectMemory() - { - return getMemory(true); - } - - @ManagedAttribute("The bytes retained by heap ByteBuffers") - public long getHeapMemory() - { - return getMemory(false); - } - - @ManagedAttribute("The max num of bytes that can be retained from direct ByteBuffers") - public long getMaxDirectMemory() - { - return _maxDirectMemory; - } - - @ManagedAttribute("The max num of bytes that can be retained from heap ByteBuffers") - public long getMaxHeapMemory() - { - return _maxHeapMemory; - } - - public long getMemory(boolean direct) - { - AtomicLong memory = direct ? _directMemory : _heapMemory; - return memory.get(); - } - - protected static class Bucket - { - private final Queue _queue = new ConcurrentLinkedQueue<>(); - private final int _capacity; - private final int _maxSize; - private final AtomicInteger _size; - private final AtomicLong _lastUpdate = new AtomicLong(NanoTime.now()); - private final IntConsumer _memoryFunction; - - @Deprecated - public Bucket(int capacity, int maxSize) - { - this(capacity, maxSize, i -> {}); - } - - public Bucket(int capacity, int maxSize, IntConsumer memoryFunction) - { - _capacity = capacity; - _maxSize = maxSize; - _size = maxSize > 0 ? new AtomicInteger() : null; - _memoryFunction = Objects.requireNonNull(memoryFunction); - } - - public ByteBuffer acquire() - { - ByteBuffer buffer = _queue.poll(); - if (buffer != null) - { - if (_size != null) - _size.decrementAndGet(); - _memoryFunction.accept(-buffer.capacity()); - } - - return buffer; - } - - public void release(ByteBuffer buffer) - { - resetUpdateTime(); - BufferUtil.reset(buffer); - if (_size == null || _size.incrementAndGet() <= _maxSize) - { - _queue.offer(buffer); - _memoryFunction.accept(buffer.capacity()); - } - else - { - _size.decrementAndGet(); - } - } - - void resetUpdateTime() - { - _lastUpdate.lazySet(NanoTime.now()); - } - - public void clear() - { - int size = _size == null ? 0 : _size.get() - 1; - while (size >= 0) - { - ByteBuffer buffer = acquire(); - if (buffer == null) - break; - if (_size != null) - --size; - } - } - - boolean isEmpty() - { - return _queue.isEmpty(); - } - - int size() - { - return _queue.size(); - } - - long getLastUpdate() - { - return _lastUpdate.getOpaque(); - } - - @Override - public String toString() - { - return String.format("%s@%x{capacity=%d, size=%d, maxSize=%d}", getClass().getSimpleName(), hashCode(), _capacity, size(), _maxSize); - } - } - - IntConsumer updateMemory(boolean direct) - { - return (direct) ? _directMemory::addAndGet : _heapMemory::addAndGet; - } - - @ManagedOperation(value = "Clears this ByteBufferPool", impact = "ACTION") - public void clear() - { - _heapMemory.set(0); - _directMemory.set(0); - } -} diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractRetainableByteBuffer.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractRetainableByteBuffer.java new file mode 100644 index 00000000000..1c1a859a3c2 --- /dev/null +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractRetainableByteBuffer.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import java.nio.ByteBuffer; +import java.util.Objects; + +import org.eclipse.jetty.util.BufferUtil; + +/** + *

Abstract implementation of {@link RetainableByteBuffer} with + * reference counting.

+ */ +public abstract class AbstractRetainableByteBuffer implements RetainableByteBuffer +{ + private final ReferenceCounter refCount = new ReferenceCounter(0); + private final ByteBuffer byteBuffer; + + public AbstractRetainableByteBuffer(ByteBuffer byteBuffer) + { + this.byteBuffer = Objects.requireNonNull(byteBuffer); + } + + /** + * @see ReferenceCounter#acquire() + */ + protected void acquire() + { + refCount.acquire(); + } + + @Override + public boolean canRetain() + { + return refCount.canRetain(); + } + + @Override + public void retain() + { + refCount.retain(); + } + + @Override + public boolean release() + { + return refCount.release(); + } + + @Override + public boolean isRetained() + { + return refCount.isRetained(); + } + + @Override + public ByteBuffer getByteBuffer() + { + return byteBuffer; + } + + @Override + public String toString() + { + return "%s@%x[r=%d,%s]".formatted(getClass().getSimpleName(), hashCode(), refCount.get(), BufferUtil.toDetailString(byteBuffer)); + } +} diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java deleted file mode 100644 index 57600ea4514..00000000000 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java +++ /dev/null @@ -1,342 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.NanoTime; -import org.eclipse.jetty.util.annotation.ManagedAttribute; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.component.Dumpable; -import org.eclipse.jetty.util.component.DumpableCollection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - *

A ByteBuffer pool where ByteBuffers are held in queues that are held in array elements.

- *

Given a capacity {@code factor} of 4096, the first array element holds a bucket of ByteBuffers - * each of capacity 4096, the second array element holds a bucket of ByteBuffers each of capacity - * 8192, and so on.

- *

The {@code maxHeapMemory} and {@code maxDirectMemory} default heuristic is to use {@link Runtime#maxMemory()} - * divided by 4.

- */ -@ManagedObject -public class ArrayByteBufferPool extends AbstractByteBufferPool implements Dumpable -{ - private static final Logger LOG = LoggerFactory.getLogger(ArrayByteBufferPool.class); - - private final int _maxCapacity; - private final int _minCapacity; - private final Bucket[] _direct; - private final Bucket[] _indirect; - private boolean _detailedDump = false; - - /** - * Creates a new ArrayByteBufferPool with a default configuration. - * Both {@code maxHeapMemory} and {@code maxDirectMemory} default to 0 to use default heuristic. - */ - public ArrayByteBufferPool() - { - this(-1, -1, -1); - } - - /** - * Creates a new ArrayByteBufferPool with the given configuration. - * Both {@code maxHeapMemory} and {@code maxDirectMemory} default to 0 to use default heuristic. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param factor the capacity factor - * @param maxCapacity the maximum ByteBuffer capacity - */ - public ArrayByteBufferPool(int minCapacity, int factor, int maxCapacity) - { - this(minCapacity, factor, maxCapacity, -1, 0, 0); - } - - /** - * Creates a new ArrayByteBufferPool with the given configuration. - * Both {@code maxHeapMemory} and {@code maxDirectMemory} default to 0 to use default heuristic. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param factor the capacity factor - * @param maxCapacity the maximum ByteBuffer capacity - * @param maxQueueLength the maximum ByteBuffer queue length - */ - public ArrayByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxQueueLength) - { - this(minCapacity, factor, maxCapacity, maxQueueLength, 0, 0); - } - - /** - * Creates a new ArrayByteBufferPool with the given configuration. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param factor the capacity factor - * @param maxCapacity the maximum ByteBuffer capacity - * @param maxBucketSize the maximum ByteBuffer queue length in a {@link Bucket} - * @param maxHeapMemory the max heap memory in bytes, -1 for unlimited memory or 0 to use default heuristic - * @param maxDirectMemory the max direct memory in bytes, -1 for unlimited memory or 0 to use default heuristic - */ - public ArrayByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) - { - this(minCapacity, factor, maxCapacity, maxBucketSize, maxHeapMemory, maxDirectMemory, maxHeapMemory, maxDirectMemory); - } - - /** - * Creates a new ArrayByteBufferPool with the given configuration. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param factor the capacity factor - * @param maxCapacity the maximum ByteBuffer capacity - * @param maxBucketSize the maximum ByteBuffer queue length in a {@link Bucket} - * @param maxHeapMemory the max heap memory in bytes, -1 for unlimited memory or 0 to use default heuristic - * @param maxDirectMemory the max direct memory in bytes, -1 for unlimited memory or 0 to use default heuristic - * @param retainedHeapMemory the max heap memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - * @param retainedDirectMemory the max direct memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - */ - public ArrayByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory, long retainedHeapMemory, long retainedDirectMemory) - { - super(factor, maxCapacity, maxBucketSize, maxHeapMemory, maxDirectMemory, retainedHeapMemory, retainedDirectMemory); - maxCapacity = getMaxCapacity(); - - factor = getCapacityFactor(); - if (minCapacity <= 0) - minCapacity = 0; - if ((maxCapacity % factor) != 0 || factor >= maxCapacity) - throw new IllegalArgumentException("The capacity factor must be a divisor of maxCapacity"); - _maxCapacity = maxCapacity; - _minCapacity = minCapacity; - - // Initialize all buckets in constructor and never modify the array again. - int length = bucketFor(maxCapacity) + 1; - _direct = new Bucket[length]; - _indirect = new Bucket[length]; - for (int i = 0; i < length; i++) - { - _direct[i] = newBucket(i, true); - _indirect[i] = newBucket(i, false); - } - } - - @Override - protected RetainableByteBufferPool newRetainableByteBufferPool(int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - return new Retained(factor, maxCapacity, maxBucketSize, retainedHeapMemory, retainedDirectMemory); - } - - @Override - public ByteBuffer acquire(int size, boolean direct) - { - int capacity = size < _minCapacity ? size : capacityFor(bucketFor(size)); - Bucket bucket = bucketFor(size, direct); - if (bucket == null) - return newByteBuffer(capacity, direct); - ByteBuffer buffer = bucket.acquire(); - if (buffer == null) - return newByteBuffer(capacity, direct); - return buffer; - } - - @Override - public void release(ByteBuffer buffer) - { - if (buffer == null) - return; - - int capacity = buffer.capacity(); - // Validate that this buffer is from this pool. - if (capacity != capacityFor(bucketFor(capacity))) - { - if (LOG.isDebugEnabled()) - LOG.debug("ByteBuffer {} does not belong to this pool, discarding it", BufferUtil.toDetailString(buffer)); - return; - } - - // Don't release into the pool if greater than the maximum ByteBuffer capacity. - if (capacity > _maxCapacity) - return; - - boolean direct = buffer.isDirect(); - Bucket bucket = bucketFor(capacity, direct); - if (bucket != null) - { - bucket.release(buffer); - releaseExcessMemory(direct, this::releaseMemory); - } - } - - private Bucket newBucket(int key, boolean direct) - { - return new Bucket(capacityFor(key), getMaxBucketSize(), updateMemory(direct)); - } - - @Override - public void clear() - { - super.clear(); - for (int i = 0; i < _direct.length; ++i) - { - _direct[i].clear(); - _indirect[i].clear(); - } - } - - protected void releaseMemory(boolean direct) - { - long oldest = Long.MAX_VALUE; - int index = -1; - Bucket[] buckets = bucketsFor(direct); - for (int i = 0; i < buckets.length; ++i) - { - Bucket bucket = buckets[i]; - if (bucket.isEmpty()) - continue; - long lastUpdateNanoTime = bucket.getLastUpdate(); - if (oldest == Long.MAX_VALUE || NanoTime.isBefore(lastUpdateNanoTime, oldest)) - { - oldest = lastUpdateNanoTime; - index = i; - } - } - if (index >= 0) - { - Bucket bucket = buckets[index]; - bucket.clear(); - } - } - - protected int bucketFor(int capacity) - { - return (int)Math.ceil((double)capacity / getCapacityFactor()); - } - - protected int capacityFor(int bucket) - { - return bucket * getCapacityFactor(); - } - - protected Bucket bucketFor(int capacity, boolean direct) - { - if (capacity < _minCapacity) - return null; - int bucket = bucketFor(capacity); - if (bucket >= _direct.length) - return null; - Bucket[] buckets = bucketsFor(direct); - return buckets[bucket]; - } - - @ManagedAttribute("The number of pooled direct ByteBuffers") - public long getDirectByteBufferCount() - { - return getByteBufferCount(true); - } - - @ManagedAttribute("The number of pooled heap ByteBuffers") - public long getHeapByteBufferCount() - { - return getByteBufferCount(false); - } - - private long getByteBufferCount(boolean direct) - { - return Arrays.stream(bucketsFor(direct)) - .filter(Objects::nonNull) - .mapToLong(Bucket::size) - .sum(); - } - - // Package local for testing - Bucket[] bucketsFor(boolean direct) - { - return direct ? _direct : _indirect; - } - - public boolean isDetailedDump() - { - return _detailedDump; - } - - public void setDetailedDump(boolean detailedDump) - { - _detailedDump = detailedDump; - } - - @Override - public void dump(Appendable out, String indent) throws IOException - { - List dump = new ArrayList<>(); - dump.add(String.format("HeapMemory: %d/%d", getHeapMemory(), getMaxHeapMemory())); - dump.add(String.format("DirectMemory: %d/%d", getDirectMemory(), getMaxDirectMemory())); - - List indirect = Arrays.stream(_indirect).filter(b -> !b.isEmpty()).collect(Collectors.toList()); - List direct = Arrays.stream(_direct).filter(b -> !b.isEmpty()).collect(Collectors.toList()); - if (isDetailedDump()) - { - dump.add(new DumpableCollection("Indirect Buckets", indirect)); - dump.add(new DumpableCollection("Direct Buckets", direct)); - } - else - { - dump.add("Indirect Buckets size=" + indirect.size()); - dump.add("Direct Buckets size=" + direct.size()); - } - dump.add(asRetainableByteBufferPool()); - Dumpable.dumpObjects(out, indent, this, dump); - } - - @Override - public String toString() - { - return String.format("%s@%x{minBufferCapacity=%s, maxBufferCapacity=%s, maxQueueLength=%s, factor=%s}", - this.getClass().getSimpleName(), hashCode(), - _minCapacity, - _maxCapacity, - getMaxBucketSize(), - getCapacityFactor()); - } - - protected class Retained extends ArrayRetainableByteBufferPool - { - public Retained(int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - super(0, factor, maxCapacity, maxBucketSize, retainedHeapMemory, retainedDirectMemory); - } - - @Override - protected ByteBuffer allocate(int capacity) - { - return ArrayByteBufferPool.this.acquire(capacity, false); - } - - @Override - protected ByteBuffer allocateDirect(int capacity) - { - return ArrayByteBufferPool.this.acquire(capacity, true); - } - - @Override - protected void removed(RetainableByteBuffer retainedBuffer) - { - ArrayByteBufferPool.this.release(retainedBuffer.getBuffer()); - } - } -} diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index 6f0e74af126..ac6972daf8d 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -36,15 +36,16 @@ import org.slf4j.LoggerFactory; * held in array elements.

*

Given a capacity {@code factor} of 1024, the first array element holds a Pool of RetainableByteBuffers * each of capacity 1024, the second array element holds a Pool of RetainableByteBuffers each of capacity - * 2048, and so on.

+ * 2048, and so on with capacities 3072, 4096, 5120, etc.

*

The {@code maxHeapMemory} and {@code maxDirectMemory} default heuristic is to use {@link Runtime#maxMemory()} - * divided by 4.

+ * divided by 8.

*/ -@SuppressWarnings("resource") @ManagedObject public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, Dumpable { private static final Logger LOG = LoggerFactory.getLogger(ArrayRetainableByteBufferPool.class); + static final int DEFAULT_FACTOR = 4096; + static final int DEFAULT_MAX_CAPACITY_BY_FACTOR = 16; private final RetainedBucket[] _direct; private final RetainedBucket[] _indirect; @@ -62,7 +63,20 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, */ public ArrayRetainableByteBufferPool() { - this(0, -1, -1, Integer.MAX_VALUE); + this(0, -1, -1); + } + + /** + * Creates a new ArrayRetainableByteBufferPool with the given configuration. + * Both {@code maxHeapMemory} and {@code maxDirectMemory} default to 0 to use default heuristic. + * + * @param minCapacity the minimum ByteBuffer capacity + * @param factor the capacity factor + * @param maxCapacity the maximum ByteBuffer capacity + */ + public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity) + { + this(minCapacity, factor, maxCapacity, Integer.MAX_VALUE); } /** @@ -110,9 +124,9 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, { if (minCapacity <= 0) minCapacity = 0; - factor = factor <= 0 ? AbstractByteBufferPool.DEFAULT_FACTOR : factor; + factor = factor <= 0 ? DEFAULT_FACTOR : factor; if (maxCapacity <= 0) - maxCapacity = AbstractByteBufferPool.DEFAULT_MAX_CAPACITY_BY_FACTOR * factor; + maxCapacity = DEFAULT_MAX_CAPACITY_BY_FACTOR * factor; if ((maxCapacity % factor) != 0 || factor >= maxCapacity) throw new IllegalArgumentException(String.format("The capacity factor(%d) must be a divisor of maxCapacity(%d)", factor, maxCapacity)); @@ -136,11 +150,20 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, _maxCapacity = maxCapacity; _direct = directArray; _indirect = indirectArray; - _maxHeapMemory = AbstractByteBufferPool.retainedSize(maxHeapMemory); - _maxDirectMemory = AbstractByteBufferPool.retainedSize(maxDirectMemory); + _maxHeapMemory = maxMemory(maxHeapMemory); + _maxDirectMemory = maxMemory(maxDirectMemory); _bucketIndexFor = bucketIndexFor; } + private long maxMemory(long maxMemory) + { + if (maxMemory < 0) + return -1; + if (maxMemory == 0) + return Runtime.getRuntime().maxMemory() / 8; + return maxMemory; + } + @ManagedAttribute("The minimum pooled buffer capacity") public int getMinCapacity() { @@ -159,17 +182,17 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, RetainedBucket bucket = bucketFor(size, direct); if (bucket == null) return newRetainableByteBuffer(size, direct, this::removed); - RetainedBucket.Entry entry = bucket.acquire(); + Pool.Entry entry = bucket.getPool().acquire(); RetainableByteBuffer buffer; if (entry == null) { - RetainedBucket.Entry reservedEntry = bucket.reserve(); + Pool.Entry reservedEntry = bucket.getPool().reserve(); if (reservedEntry != null) { buffer = newRetainableByteBuffer(bucket._capacity, direct, retainedBuffer -> { - BufferUtil.reset(retainedBuffer.getBuffer()); + BufferUtil.reset(retainedBuffer.getByteBuffer()); reservedEntry.release(); }); reservedEntry.enable(buffer, true); @@ -187,7 +210,7 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, else { buffer = entry.getPooled(); - buffer.acquire(); + ((Buffer)buffer).acquire(); } return buffer; } @@ -208,16 +231,16 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direct, Consumer releaser) { - ByteBuffer buffer = direct ? allocateDirect(capacity) : allocate(capacity); - BufferUtil.clear(buffer); - RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(buffer, releaser); + ByteBuffer buffer = BufferUtil.allocate(capacity, direct); + Buffer retainableByteBuffer = new Buffer(buffer, releaser); retainableByteBuffer.acquire(); return retainableByteBuffer; } - protected Pool poolFor(int capacity, boolean direct) + public Pool poolFor(int capacity, boolean direct) { - return bucketFor(capacity, direct); + RetainedBucket bucket = bucketFor(capacity, direct); + return bucket == null ? null : bucket.getPool(); } private RetainedBucket bucketFor(int capacity, boolean direct) @@ -246,7 +269,7 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, private long getByteBufferCount(boolean direct) { RetainedBucket[] buckets = direct ? _direct : _indirect; - return Arrays.stream(buckets).mapToLong(RetainedBucket::size).sum(); + return Arrays.stream(buckets).mapToLong(bucket -> bucket.getPool().size()).sum(); } @ManagedAttribute("The number of pooled direct ByteBuffers that are available") @@ -264,7 +287,7 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, private long getAvailableByteBufferCount(boolean direct) { RetainedBucket[] buckets = direct ? _direct : _indirect; - return Arrays.stream(buckets).mapToLong(bucket -> bucket.values().stream().filter(Pool.Entry::isIdle).count()).sum(); + return Arrays.stream(buckets).mapToLong(bucket -> bucket.getPool().values().stream().filter(Pool.Entry::isIdle).count()).sum(); } @ManagedAttribute("The bytes retained by direct ByteBuffers") @@ -306,7 +329,7 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, for (RetainedBucket bucket : buckets) { int capacity = bucket._capacity; - total += bucket.values().stream().filter(Pool.Entry::isIdle).count() * capacity; + total += bucket.getPool().values().stream().filter(Pool.Entry::isIdle).count() * capacity; } return total; } @@ -322,7 +345,7 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, { for (RetainedBucket pool : poolArray) { - for (RetainedBucket.Entry entry : pool.values()) + for (Pool.Entry entry : pool.getPool().values()) { if (entry.remove()) { @@ -362,7 +385,7 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, { for (RetainedBucket bucket : buckets) { - RetainedBucket.Entry oldestEntry = findOldestEntry(now, bucket); + Pool.Entry oldestEntry = findOldestEntry(now, bucket.getPool()); if (oldestEntry == null) continue; @@ -408,13 +431,15 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, private Pool.Entry findOldestEntry(long now, Pool bucket) { - RetainedBucket.Entry oldestEntry = null; - for (RetainedBucket.Entry entry : bucket.values()) + Pool.Entry oldestEntry = null; + for (Pool.Entry entry : bucket.values()) { if (oldestEntry != null) { - long entryAge = NanoTime.elapsed(entry.getPooled().getLastUpdate(), now); - if (entryAge > NanoTime.elapsed(oldestEntry.getPooled().getLastUpdate(), now)) + Buffer entryBuffer = (Buffer)entry.getPooled(); + long entryAge = NanoTime.elapsed(entryBuffer.getLastNanoTime(), now); + Buffer oldestEntryBuffer = (Buffer)oldestEntry.getPooled(); + if (entryAge > NanoTime.elapsed(oldestEntryBuffer.getLastNanoTime(), now)) oldestEntry = entry; } else @@ -425,22 +450,28 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, return oldestEntry; } - private static class RetainedBucket extends Pool + private static class RetainedBucket { + private final Pool _pool; private final int _capacity; - RetainedBucket(int capacity, int size) + private RetainedBucket(int capacity, int size) { - super(Pool.StrategyType.THREAD_ID, size, true); + _pool = new Pool<>(Pool.StrategyType.THREAD_ID, size, true); _capacity = capacity; } + public Pool getPool() + { + return _pool; + } + @Override public String toString() { int entries = 0; int inUse = 0; - for (Entry entry : values()) + for (Pool.Entry entry : _pool.values()) { entries++; if (entry.isInUse()) @@ -455,24 +486,53 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, } } + private static class Buffer extends AbstractRetainableByteBuffer + { + private final Consumer releaser; + private final AtomicLong lastNanoTime = new AtomicLong(NanoTime.now()); + + private Buffer(ByteBuffer buffer, Consumer releaser) + { + super(buffer); + this.releaser = releaser; + } + + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + { + lastNanoTime.setOpaque(NanoTime.now()); + releaser.accept(this); + } + return released; + } + + public long getLastNanoTime() + { + return lastNanoTime.getOpaque(); + } + } + /** * A variant of the {@link ArrayRetainableByteBufferPool} that * uses buckets of buffers that increase in size by a power of * 2 (eg 1k, 2k, 4k, 8k, etc.). */ - public static class ExponentialPool extends ArrayRetainableByteBufferPool + public static class Quadratic extends ArrayRetainableByteBufferPool { - public ExponentialPool() + public Quadratic() { this(0, -1, Integer.MAX_VALUE); } - public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize) + public Quadratic(int minCapacity, int maxCapacity, int maxBucketSize) { this(minCapacity, maxCapacity, maxBucketSize, -1L, -1L); } - public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) + public Quadratic(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) { super(minCapacity, -1, @@ -481,7 +541,8 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, maxHeapMemory, maxDirectMemory, c -> 32 - Integer.numberOfLeadingZeros(c - 1), - i -> 1 << i); + i -> 1 << i + ); } } } diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferAccumulator.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferAccumulator.java index 91b7ab62387..90f05e773ef 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferAccumulator.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferAccumulator.java @@ -24,16 +24,17 @@ import org.eclipse.jetty.util.BufferUtil; /** * Accumulates data into a list of ByteBuffers which can then be combined into a single buffer or written to an OutputStream. * The buffer list automatically grows as data is written to it, the buffers are taken from the - * supplied {@link ByteBufferPool} or freshly allocated if one is not supplied. + * supplied {@link RetainableByteBufferPool} or freshly allocated if one is not supplied. * * The method {@link #ensureBuffer(int, int)} is used to write directly to the last buffer stored in the buffer list, * if there is less than a certain amount of space available in that buffer then a new one will be allocated and returned instead. * @see #ensureBuffer(int, int) */ +// TODO: rename to *Aggregator to avoid confusion with RBBP.Accumulator? public class ByteBufferAccumulator implements AutoCloseable { - private final List _buffers = new ArrayList<>(); - private final ByteBufferPool _bufferPool; + private final List _buffers = new ArrayList<>(); + private final RetainableByteBufferPool _bufferPool; private final boolean _direct; public ByteBufferAccumulator() @@ -41,9 +42,9 @@ public class ByteBufferAccumulator implements AutoCloseable this(null, false); } - public ByteBufferAccumulator(ByteBufferPool bufferPool, boolean direct) + public ByteBufferAccumulator(RetainableByteBufferPool bufferPool, boolean direct) { - _bufferPool = (bufferPool == null) ? ByteBufferPool.NOOP : bufferPool; + _bufferPool = (bufferPool == null) ? new RetainableByteBufferPool.NonPooling() : bufferPool; _direct = direct; } @@ -55,22 +56,17 @@ public class ByteBufferAccumulator implements AutoCloseable public int getLength() { int length = 0; - for (ByteBuffer buffer : _buffers) + for (RetainableByteBuffer buffer : _buffers) length = Math.addExact(length, buffer.remaining()); return length; } - public ByteBufferPool getByteBufferPool() - { - return _bufferPool; - } - /** * Get the last buffer of the accumulator, this can be written to directly to avoid copying into the accumulator. * @param minAllocationSize new buffers will be allocated to have at least this size. * @return a buffer with at least {@code minSize} space to write into. */ - public ByteBuffer ensureBuffer(int minAllocationSize) + public RetainableByteBuffer ensureBuffer(int minAllocationSize) { return ensureBuffer(1, minAllocationSize); } @@ -81,15 +77,14 @@ public class ByteBufferAccumulator implements AutoCloseable * @param minAllocationSize new buffers will be allocated to have at least this size. * @return a buffer with at least {@code minSize} space to write into. */ - public ByteBuffer ensureBuffer(int minSize, int minAllocationSize) + public RetainableByteBuffer ensureBuffer(int minSize, int minAllocationSize) { - ByteBuffer buffer = _buffers.isEmpty() ? BufferUtil.EMPTY_BUFFER : _buffers.get(_buffers.size() - 1); - if (BufferUtil.space(buffer) < minSize) + RetainableByteBuffer buffer = _buffers.isEmpty() ? null : _buffers.get(_buffers.size() - 1); + if (buffer == null || BufferUtil.space(buffer.getByteBuffer()) < minSize) { buffer = _bufferPool.acquire(minAllocationSize, _direct); _buffers.add(buffer); } - return buffer; } @@ -98,26 +93,27 @@ public class ByteBufferAccumulator implements AutoCloseable copyBuffer(BufferUtil.toBuffer(buf, offset, length)); } - public void copyBuffer(ByteBuffer buffer) + public void copyBuffer(ByteBuffer source) { - while (buffer.hasRemaining()) + while (source.hasRemaining()) { - ByteBuffer b = ensureBuffer(buffer.remaining()); - int pos = BufferUtil.flipToFill(b); - BufferUtil.put(buffer, b); - BufferUtil.flipToFlush(b, pos); + RetainableByteBuffer buffer = ensureBuffer(source.remaining()); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + int pos = BufferUtil.flipToFill(byteBuffer); + BufferUtil.put(source, byteBuffer); + BufferUtil.flipToFlush(byteBuffer, pos); } } /** * Take the combined buffer containing all content written to the accumulator. - * The caller is responsible for releasing this {@link ByteBuffer} back into the {@link ByteBufferPool}. + * The caller is responsible for releasing this {@link RetainableByteBuffer}. * @return a buffer containing all content written to the accumulator. - * @see #toByteBuffer() + * @see #toRetainableByteBuffer() */ - public ByteBuffer takeByteBuffer() + public RetainableByteBuffer takeRetainableByteBuffer() { - ByteBuffer combinedBuffer; + RetainableByteBuffer combinedBuffer; if (_buffers.size() == 1) { combinedBuffer = _buffers.get(0); @@ -127,28 +123,36 @@ public class ByteBufferAccumulator implements AutoCloseable int length = getLength(); combinedBuffer = _bufferPool.acquire(length, _direct); - BufferUtil.clearToFill(combinedBuffer); - for (ByteBuffer buffer : _buffers) + ByteBuffer byteBuffer = combinedBuffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + for (RetainableByteBuffer buffer : _buffers) { - combinedBuffer.put(buffer); - _bufferPool.release(buffer); + byteBuffer.put(buffer.getByteBuffer()); + buffer.release(); } - BufferUtil.flipToFlush(combinedBuffer, 0); + BufferUtil.flipToFlush(byteBuffer, 0); _buffers.clear(); return combinedBuffer; } + public ByteBuffer takeByteBuffer() + { + byte[] bytes = toByteArray(); + close(); + return ByteBuffer.wrap(bytes); + } + /** * Take the combined buffer containing all content written to the accumulator. - * The returned buffer is still contained within the accumulator and will be released back to the {@link ByteBufferPool} + * The returned buffer is still contained within the accumulator and will be released * when the accumulator is closed. * @return a buffer containing all content written to the accumulator. - * @see #takeByteBuffer() + * @see #takeRetainableByteBuffer() * @see #close() */ - public ByteBuffer toByteBuffer() + public RetainableByteBuffer toRetainableByteBuffer() { - ByteBuffer combinedBuffer = takeByteBuffer(); + RetainableByteBuffer combinedBuffer = takeRetainableByteBuffer(); _buffers.add(combinedBuffer); return combinedBuffer; } @@ -169,28 +173,28 @@ public class ByteBufferAccumulator implements AutoCloseable return bytes; } - public void writeTo(ByteBuffer buffer) + public void writeTo(ByteBuffer byteBuffer) { - int pos = BufferUtil.flipToFill(buffer); - for (ByteBuffer bb : _buffers) + int pos = BufferUtil.flipToFill(byteBuffer); + for (RetainableByteBuffer buffer : _buffers) { - buffer.put(bb.slice()); + byteBuffer.put(buffer.getByteBuffer().slice()); } - BufferUtil.flipToFlush(buffer, pos); + BufferUtil.flipToFlush(byteBuffer, pos); } public void writeTo(OutputStream out) throws IOException { - for (ByteBuffer bb : _buffers) + for (RetainableByteBuffer buffer : _buffers) { - BufferUtil.writeTo(bb.slice(), out); + BufferUtil.writeTo(buffer.getByteBuffer().slice(), out); } } @Override public void close() { - _buffers.forEach(_bufferPool::release); + _buffers.forEach(RetainableByteBuffer::release); _buffers.clear(); } } diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferOutputStream2.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferOutputStream2.java index 28f0fb2844d..fd8fda345e8 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferOutputStream2.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferOutputStream2.java @@ -20,7 +20,7 @@ import java.nio.ByteBuffer; /** * This class implements an output stream in which the data is written into a list of ByteBuffer, * the buffer list automatically grows as data is written to it, the buffers are taken from the - * supplied {@link ByteBufferPool} or freshly allocated if one is not supplied. + * supplied {@link RetainableByteBufferPool} or freshly allocated if one is not supplied. * * Designed to mimic {@link java.io.ByteArrayOutputStream} but with better memory usage, and less copying. */ @@ -34,35 +34,30 @@ public class ByteBufferOutputStream2 extends OutputStream this(null, false); } - public ByteBufferOutputStream2(ByteBufferPool bufferPool, boolean direct) + public ByteBufferOutputStream2(RetainableByteBufferPool bufferPool, boolean direct) { - _accumulator = new ByteBufferAccumulator((bufferPool == null) ? ByteBufferPool.NOOP : bufferPool, direct); - } - - public ByteBufferPool getByteBufferPool() - { - return _accumulator.getByteBufferPool(); + _accumulator = new ByteBufferAccumulator(bufferPool == null ? new RetainableByteBufferPool.NonPooling() : bufferPool, direct); } /** * Take the combined buffer containing all content written to the OutputStream. - * The caller is responsible for releasing this {@link ByteBuffer} back into the {@link ByteBufferPool}. + * The caller is responsible for releasing this {@link RetainableByteBuffer}. * @return a buffer containing all content written to the OutputStream. */ - public ByteBuffer takeByteBuffer() + public RetainableByteBuffer takeByteBuffer() { - return _accumulator.takeByteBuffer(); + return _accumulator.takeRetainableByteBuffer(); } /** * Take the combined buffer containing all content written to the OutputStream. - * The returned buffer is still contained within the OutputStream and will be released back to the {@link ByteBufferPool} + * The returned buffer is still contained within the OutputStream and will be released * when the OutputStream is closed. * @return a buffer containing all content written to the OutputStream. */ - public ByteBuffer toByteBuffer() + public RetainableByteBuffer toByteBuffer() { - return _accumulator.toByteBuffer(); + return _accumulator.toRetainableByteBuffer(); } /** diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java deleted file mode 100644 index 852f5512c56..00000000000 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java +++ /dev/null @@ -1,152 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.jetty.util.BufferUtil; - -/** - *

A {@link ByteBuffer} pool.

- *

Acquired buffers may be {@link #release(ByteBuffer) released} but they do not need to; - * if they are released, they may be recycled and reused, otherwise they will be garbage - * collected as usual.

- */ -public interface ByteBufferPool -{ - ByteBufferPool NOOP = new NoopByteBufferPool(); - - /** - *

Requests a {@link ByteBuffer} of the given size.

- *

The returned buffer may have a bigger capacity than the size being requested.

- * - * @param size the size of the buffer - * @param direct whether the buffer must be direct or not - * @return a buffer with at least the requested capacity, with position and limit set to 0. - * @see #release(ByteBuffer) - */ - ByteBuffer acquire(int size, boolean direct); - - /** - *

Returns a {@link ByteBuffer}, usually obtained with {@link #acquire(int, boolean)} - * (but not necessarily), making it available for recycling and reuse.

- * - * @param buffer the buffer to return - * @see #acquire(int, boolean) - */ - void release(ByteBuffer buffer); - - /** - *

Removes a {@link ByteBuffer} that was previously obtained with {@link #acquire(int, boolean)}.

- *

The buffer will not be available for further reuse.

- * - * @param buffer the buffer to remove - * @see #acquire(int, boolean) - * @see #release(ByteBuffer) - */ - default void remove(ByteBuffer buffer) - { - } - - /** - *

Creates a new ByteBuffer of the given capacity and the given directness.

- * - * @param capacity the ByteBuffer capacity - * @param direct the ByteBuffer directness - * @return a newly allocated ByteBuffer - */ - default ByteBuffer newByteBuffer(int capacity, boolean direct) - { - return direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity); - } - - /** - * Get this pool as a {@link RetainableByteBufferPool}, which supports reference counting of the - * buffers and possibly a more efficient lookup mechanism based on the {@link org.eclipse.jetty.util.Pool} class. - * @return This pool as a RetainableByteBufferPool. The same instance is always returned by multiple calls to this method. - */ - RetainableByteBufferPool asRetainableByteBufferPool(); - - class Lease - { - private final ByteBufferPool byteBufferPool; - private final List buffers; - private final List recycles; - - public Lease(ByteBufferPool byteBufferPool) - { - this.byteBufferPool = byteBufferPool; - this.buffers = new ArrayList<>(); - this.recycles = new ArrayList<>(); - } - - public ByteBuffer acquire(int capacity, boolean direct) - { - ByteBuffer buffer = byteBufferPool.acquire(capacity, direct); - BufferUtil.clearToFill(buffer); - return buffer; - } - - public void append(ByteBuffer buffer, boolean recycle) - { - buffers.add(buffer); - recycles.add(recycle); - } - - public void insert(int index, ByteBuffer buffer, boolean recycle) - { - buffers.add(index, buffer); - recycles.add(index, recycle); - } - - public List getByteBuffers() - { - return buffers; - } - - public long getTotalLength() - { - long length = 0; - for (ByteBuffer buffer : buffers) - { - length += buffer.remaining(); - } - return length; - } - - public int getSize() - { - return buffers.size(); - } - - public void recycle() - { - for (int i = 0; i < buffers.size(); ++i) - { - ByteBuffer buffer = buffers.get(i); - if (recycles.get(i)) - release(buffer); - } - buffers.clear(); - recycles.clear(); - } - - public void release(ByteBuffer buffer) - { - byteBufferPool.release(buffer); - } - } -} diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java index 1129e7bc681..df900ad99b8 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java @@ -94,7 +94,7 @@ public class ClientConnector extends ContainerLifeCycle private final Configurator configurator; private Executor executor; private Scheduler scheduler; - private ByteBufferPool byteBufferPool; + private RetainableByteBufferPool retainableByteBufferPool; private SslContextFactory.Client sslContextFactory; private SelectorManager selectorManager; private int selectors = 1; @@ -155,17 +155,17 @@ public class ClientConnector extends ContainerLifeCycle this.scheduler = scheduler; } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return byteBufferPool; + return retainableByteBufferPool; } - public void setByteBufferPool(ByteBufferPool byteBufferPool) + public void setRetainableByteBufferPool(RetainableByteBufferPool retainableByteBufferPool) { if (isStarted()) throw new IllegalStateException(); - updateBean(this.byteBufferPool, byteBufferPool); - this.byteBufferPool = byteBufferPool; + updateBean(this.retainableByteBufferPool, retainableByteBufferPool); + this.retainableByteBufferPool = retainableByteBufferPool; } public SslContextFactory.Client getSslContextFactory() @@ -366,8 +366,8 @@ public class ClientConnector extends ContainerLifeCycle } if (scheduler == null) setScheduler(new ScheduledExecutorScheduler(String.format("client-scheduler@%x", hashCode()), false)); - if (byteBufferPool == null) - setByteBufferPool(new MappedByteBufferPool()); + if (retainableByteBufferPool == null) + setRetainableByteBufferPool(new ArrayRetainableByteBufferPool()); if (sslContextFactory == null) setSslContextFactory(newSslContextFactory()); selectorManager = newSelectorManager(); diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java deleted file mode 100644 index 07a430e22da..00000000000 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java +++ /dev/null @@ -1,163 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.nio.ByteBuffer; -import java.util.concurrent.atomic.AtomicLong; - -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.LeakDetector; -import org.eclipse.jetty.util.annotation.ManagedAttribute; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.component.ContainerLifeCycle; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@ManagedObject -public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements ByteBufferPool -{ - private static final Logger LOG = LoggerFactory.getLogger(LeakTrackingByteBufferPool.class); - - private final LeakDetector leakDetector = new LeakDetector() - { - @Override - public String id(ByteBuffer resource) - { - return BufferUtil.toIDString(resource); - } - - @Override - protected void leaked(LeakInfo leakInfo) - { - leaked.incrementAndGet(); - LeakTrackingByteBufferPool.this.leaked(leakInfo); - } - }; - - private final AtomicLong leakedAcquires = new AtomicLong(0); - private final AtomicLong leakedReleases = new AtomicLong(0); - private final AtomicLong leakedRemoves = new AtomicLong(0); - private final AtomicLong leaked = new AtomicLong(0); - private final ByteBufferPool delegate; - - public LeakTrackingByteBufferPool(ByteBufferPool delegate) - { - this.delegate = delegate; - addBean(leakDetector); - addBean(delegate); - } - - @Override - public RetainableByteBufferPool asRetainableByteBufferPool() - { - // the retainable pool is just a client of the normal pool, so no special handling required. - return delegate.asRetainableByteBufferPool(); - } - - @Override - public ByteBuffer acquire(int size, boolean direct) - { - ByteBuffer buffer = delegate.acquire(size, direct); - boolean acquired = leakDetector.acquired(buffer); - if (!acquired) - { - leakedAcquires.incrementAndGet(); - if (LOG.isDebugEnabled()) - LOG.debug("ByteBuffer leaked acquire for id {}", leakDetector.id(buffer), new Throwable("acquire")); - } - return buffer; - } - - @Override - public void release(ByteBuffer buffer) - { - if (buffer == null) - return; - boolean released = leakDetector.released(buffer); - if (!released) - { - leakedReleases.incrementAndGet(); - if (LOG.isDebugEnabled()) - LOG.debug("ByteBuffer leaked release for id {}", leakDetector.id(buffer), new Throwable("release")); - } - delegate.release(buffer); - } - - @Override - public void remove(ByteBuffer buffer) - { - if (buffer == null) - return; - boolean released = leakDetector.released(buffer); - if (!released) - { - leakedRemoves.incrementAndGet(); - if (LOG.isDebugEnabled()) - LOG.debug("ByteBuffer leaked remove for id {}", leakDetector.id(buffer), new Throwable("remove")); - } - delegate.remove(buffer); - } - - /** - * Clears the tracking data returned by {@link #getLeakedAcquires()}, - * {@link #getLeakedReleases()}, {@link #getLeakedResources()}. - */ - @ManagedAttribute("Clears the tracking data") - public void clearTracking() - { - leakedAcquires.set(0); - leakedReleases.set(0); - } - - /** - * @return count of ByteBufferPool.acquire() calls that detected a leak - */ - @ManagedAttribute("The number of acquires that produced a leak") - public long getLeakedAcquires() - { - return leakedAcquires.get(); - } - - /** - * @return count of ByteBufferPool.release() calls that detected a leak - */ - @ManagedAttribute("The number of releases that produced a leak") - public long getLeakedReleases() - { - return leakedReleases.get(); - } - - /** - * @return count of ByteBufferPool.remove() calls that detected a leak - */ - @ManagedAttribute("The number of removes that produced a leak") - public long getLeakedRemoves() - { - return leakedRemoves.get(); - } - - /** - * @return count of resources that were acquired but not released - */ - @ManagedAttribute("The number of resources that were leaked") - public long getLeakedResources() - { - return leaked.get(); - } - - protected void leaked(LeakDetector.LeakInfo leakInfo) - { - LOG.warn("ByteBuffer {} leaked at: {}", leakInfo.getResourceDescription(), leakInfo.getStackFrames()); - } -} diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/LogarithmicArrayByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/LogarithmicArrayByteBufferPool.java deleted file mode 100644 index 72dd85d21e9..00000000000 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/LogarithmicArrayByteBufferPool.java +++ /dev/null @@ -1,164 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import org.eclipse.jetty.util.NanoTime; - -/** - * Extension of the {@link ArrayByteBufferPool} whose bucket sizes increase exponentially instead of linearly. - * Each bucket will be double the size of the previous bucket, this decreases the amounts of buckets required - * which can lower total memory usage if buffers are often being acquired of different sizes. However as there are - * fewer buckets this will also increase the contention on each bucket. - */ -public class LogarithmicArrayByteBufferPool extends ArrayByteBufferPool -{ - // TODO test this class and use it! - - /** - * Creates a new ByteBufferPool with a default configuration. - */ - public LogarithmicArrayByteBufferPool() - { - this(-1, -1, -1); - } - - /** - * Creates a new ByteBufferPool with the given configuration. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param maxCapacity the maximum ByteBuffer capacity - */ - public LogarithmicArrayByteBufferPool(int minCapacity, int maxCapacity) - { - this(minCapacity, maxCapacity, -1, -1, -1); - } - - /** - * Creates a new ByteBufferPool with the given configuration. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param maxCapacity the maximum ByteBuffer capacity - * @param maxQueueLength the maximum ByteBuffer queue length - */ - public LogarithmicArrayByteBufferPool(int minCapacity, int maxCapacity, int maxQueueLength) - { - this(minCapacity, maxCapacity, maxQueueLength, -1, -1); - } - - /** - * Creates a new ByteBufferPool with the given configuration. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param maxCapacity the maximum ByteBuffer capacity - * @param maxQueueLength the maximum ByteBuffer queue length - * @param maxHeapMemory the max heap memory in bytes - * @param maxDirectMemory the max direct memory in bytes - */ - public LogarithmicArrayByteBufferPool(int minCapacity, int maxCapacity, int maxQueueLength, long maxHeapMemory, long maxDirectMemory) - { - this(minCapacity, maxCapacity, maxQueueLength, maxHeapMemory, maxDirectMemory, maxHeapMemory, maxDirectMemory); - } - - /** - * Creates a new ByteBufferPool with the given configuration. - * - * @param minCapacity the minimum ByteBuffer capacity - * @param maxCapacity the maximum ByteBuffer capacity - * @param maxQueueLength the maximum ByteBuffer queue length - * @param maxHeapMemory the max heap memory in bytes - * @param maxDirectMemory the max direct memory in bytes - * @param retainedHeapMemory the max heap memory in bytes, -1 for unlimited retained memory or 0 to use default heuristic - * @param retainedDirectMemory the max direct memory in bytes, -1 for unlimited retained memory or 0 to use default heuristic - */ - public LogarithmicArrayByteBufferPool(int minCapacity, int maxCapacity, int maxQueueLength, long maxHeapMemory, long maxDirectMemory, long retainedHeapMemory, long retainedDirectMemory) - { - super(minCapacity, -1, maxCapacity, maxQueueLength, maxHeapMemory, maxDirectMemory, retainedHeapMemory, retainedDirectMemory); - } - - @Override - protected RetainableByteBufferPool newRetainableByteBufferPool(int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - return new LogarithmicRetainablePool(0, maxCapacity, maxBucketSize, retainedHeapMemory, retainedDirectMemory); - } - - @Override - protected int bucketFor(int capacity) - { - return 32 - Integer.numberOfLeadingZeros(capacity - 1); - } - - @Override - protected int capacityFor(int bucket) - { - return 1 << bucket; - } - - @Override - protected void releaseMemory(boolean direct) - { - long oldest = Long.MAX_VALUE; - int index = -1; - Bucket[] buckets = bucketsFor(direct); - for (int i = 0; i < buckets.length; ++i) - { - Bucket bucket = buckets[i]; - if (bucket.isEmpty()) - continue; - long lastUpdateNanoTime = bucket.getLastUpdate(); - if (oldest == Long.MAX_VALUE || NanoTime.isBefore(lastUpdateNanoTime, oldest)) - { - oldest = lastUpdateNanoTime; - index = i; - } - } - if (index >= 0) - { - Bucket bucket = buckets[index]; - // Acquire a buffer but never return it to the pool. - bucket.acquire(); - bucket.resetUpdateTime(); - } - } - - /** - * A variant of the {@link ArrayRetainableByteBufferPool} that - * uses buckets of buffers that increase in size by a power of - * 2 (eg 1k, 2k, 4k, 8k, etc.). - */ - public static class LogarithmicRetainablePool extends ArrayRetainableByteBufferPool - { - public LogarithmicRetainablePool() - { - this(0, -1, Integer.MAX_VALUE); - } - - public LogarithmicRetainablePool(int minCapacity, int maxCapacity, int maxBucketSize) - { - this(minCapacity, maxCapacity, maxBucketSize, -1L, -1L); - } - - public LogarithmicRetainablePool(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) - { - super(minCapacity, - -1, - maxCapacity, - maxBucketSize, - maxHeapMemory, - maxDirectMemory, - c -> 32 - Integer.numberOfLeadingZeros(c - 1), - i -> 1 << i - ); - } - } -} diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/MappedByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/MappedByteBufferPool.java deleted file mode 100644 index 6d484e120a5..00000000000 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/MappedByteBufferPool.java +++ /dev/null @@ -1,340 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; - -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.NanoTime; -import org.eclipse.jetty.util.annotation.ManagedAttribute; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.component.Dumpable; -import org.eclipse.jetty.util.component.DumpableCollection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - *

A ByteBuffer pool where ByteBuffers are held in queues that are held in a Map.

- *

Given a capacity {@code factor} of 1024, the Map entry with key {@code 1} holds a - * queue of ByteBuffers each of capacity 1024, the Map entry with key {@code 2} holds a - * queue of ByteBuffers each of capacity 2048, and so on.

- */ -@ManagedObject -public class MappedByteBufferPool extends AbstractByteBufferPool implements Dumpable -{ - // TODO Logarithmic buffer sizes - - private static final Logger LOG = LoggerFactory.getLogger(MappedByteBufferPool.class); - - private final ConcurrentMap _directBuffers = new ConcurrentHashMap<>(); - private final ConcurrentMap _heapBuffers = new ConcurrentHashMap<>(); - private final Function _newBucket; - private boolean _detailedDump = false; - - /** - * Creates a new MappedByteBufferPool with a default configuration. - */ - public MappedByteBufferPool() - { - this(-1); - } - - /** - * Creates a new MappedByteBufferPool with the given capacity factor. - * - * @param factor the capacity factor - */ - public MappedByteBufferPool(int factor) - { - this(factor, -1); - } - - /** - * Creates a new MappedByteBufferPool with the given configuration. - * - * @param factor the capacity factor - * @param maxBucketSize the maximum ByteBuffer bucket size - */ - public MappedByteBufferPool(int factor, int maxBucketSize) - { - this(factor, maxBucketSize, null); - } - - /** - * Creates a new MappedByteBufferPool with the given configuration. - * - * @param factor the capacity factor - * @param maxBucketSize the maximum ByteBuffer bucket size - * @param newBucket the function that creates a Bucket - */ - private MappedByteBufferPool(int factor, int maxBucketSize, Function newBucket) - { - this(factor, maxBucketSize, newBucket, 0, 0, 0, 0); - } - - /** - * Creates a new MappedByteBufferPool with the given configuration. - * - * @param factor the capacity factor - * @param maxBucketSize the maximum ByteBuffer bucket size - * @param maxHeapMemory the max heap memory in bytes, -1 for unlimited memory or 0 to use default heuristic. - * @param maxDirectMemory the max direct memory in bytes, -1 for unlimited memory or 0 to use default heuristic. - */ - public MappedByteBufferPool(int factor, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) - { - this(factor, maxBucketSize, null, maxHeapMemory, maxDirectMemory, maxHeapMemory, maxDirectMemory); - } - - /** - * Creates a new MappedByteBufferPool with the given configuration. - * - * @param factor the capacity factor - * @param maxBucketSize the maximum ByteBuffer bucket size - * @param maxHeapMemory the max heap memory in bytes, -1 for unlimited memory or 0 to use default heuristic. - * @param maxDirectMemory the max direct memory in bytes, -1 for unlimited memory or 0 to use default heuristic. - * @param retainedHeapMemory the max heap memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - * @param retainedDirectMemory the max direct memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - */ - public MappedByteBufferPool(int factor, int maxBucketSize, long maxHeapMemory, long maxDirectMemory, long retainedHeapMemory, long retainedDirectMemory) - { - this(factor, maxBucketSize, null, maxHeapMemory, maxDirectMemory, retainedHeapMemory, retainedDirectMemory); - } - - /** - * Creates a new MappedByteBufferPool with the given configuration. - * - * @param factor the capacity factor - * @param maxBucketSize the maximum ByteBuffer bucket size - * @param newBucket the function that creates a Bucket - * @param maxHeapMemory the max heap memory in bytes, -1 for unlimited memory or 0 to use default heuristic. - * @param maxDirectMemory the max direct memory in bytes, -1 for unlimited memory or 0 to use default heuristic. - * @param retainedHeapMemory the max heap memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - * @param retainedDirectMemory the max direct memory in bytes, -2 for no retained memory, -1 for unlimited retained memory or 0 to use default heuristic - */ - private MappedByteBufferPool(int factor, int maxBucketSize, Function newBucket, long maxHeapMemory, long maxDirectMemory, long retainedHeapMemory, long retainedDirectMemory) - { - super(factor, 0, maxBucketSize, maxHeapMemory, maxDirectMemory, retainedHeapMemory, retainedDirectMemory); - _newBucket = newBucket; - } - - @Override - protected RetainableByteBufferPool newRetainableByteBufferPool(int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - return new Retained(factor, maxCapacity, maxBucketSize, retainedHeapMemory, retainedDirectMemory); - } - - private Bucket newBucket(int key, boolean direct) - { - return (_newBucket != null) ? _newBucket.apply(key) : new Bucket(capacityFor(key), getMaxBucketSize(), updateMemory(direct)); - } - - @Override - public ByteBuffer acquire(int size, boolean direct) - { - int b = bucketFor(size); - int capacity = capacityFor(b); - ConcurrentMap buffers = bucketsFor(direct); - Bucket bucket = buffers.get(b); - if (bucket == null) - return newByteBuffer(capacity, direct); - ByteBuffer buffer = bucket.acquire(); - if (buffer == null) - return newByteBuffer(capacity, direct); - return buffer; - } - - @Override - public void release(ByteBuffer buffer) - { - if (buffer == null) - return; // nothing to do - - int capacity = buffer.capacity(); - int b = bucketFor(capacity); - // Validate that this buffer is from this pool. - if (capacity != capacityFor(b)) - { - if (LOG.isDebugEnabled()) - LOG.debug("ByteBuffer {} does not belong to this pool, discarding it", BufferUtil.toDetailString(buffer)); - return; - } - - boolean direct = buffer.isDirect(); - ConcurrentMap buckets = bucketsFor(direct); - Bucket bucket = buckets.computeIfAbsent(b, i -> newBucket(i, direct)); - bucket.release(buffer); - releaseExcessMemory(direct, this::releaseMemory); - } - - @Override - public void clear() - { - super.clear(); - _directBuffers.values().forEach(Bucket::clear); - _directBuffers.clear(); - _heapBuffers.values().forEach(Bucket::clear); - _heapBuffers.clear(); - } - - protected void releaseMemory(boolean direct) - { - long oldest = Long.MAX_VALUE; - int index = -1; - ConcurrentMap buckets = bucketsFor(direct); - for (Map.Entry entry : buckets.entrySet()) - { - Bucket bucket = entry.getValue(); - if (bucket.isEmpty()) - continue; - - long lastUpdateNanoTime = bucket.getLastUpdate(); - if (oldest == Long.MAX_VALUE || NanoTime.isBefore(lastUpdateNanoTime, oldest)) - { - oldest = lastUpdateNanoTime; - index = entry.getKey(); - } - } - if (index >= 0) - { - Bucket bucket = buckets.remove(index); - // Null guard in case this.clear() is called concurrently. - if (bucket != null) - bucket.clear(); - } - } - - protected int bucketFor(int capacity) - { - return (int)Math.ceil((double)capacity / getCapacityFactor()); - } - - protected int capacityFor(int bucket) - { - return bucket * getCapacityFactor(); - } - - @ManagedAttribute("The number of pooled direct ByteBuffers") - public long getDirectByteBufferCount() - { - return getByteBufferCount(true); - } - - @ManagedAttribute("The number of pooled heap ByteBuffers") - public long getHeapByteBufferCount() - { - return getByteBufferCount(false); - } - - private long getByteBufferCount(boolean direct) - { - return bucketsFor(direct).values().stream() - .mapToLong(Bucket::size) - .sum(); - } - - // Package local for testing - ConcurrentMap bucketsFor(boolean direct) - { - return direct ? _directBuffers : _heapBuffers; - } - - public static class Tagged extends MappedByteBufferPool - { - private final AtomicInteger tag = new AtomicInteger(); - - @Override - public ByteBuffer newByteBuffer(int capacity, boolean direct) - { - ByteBuffer buffer = super.newByteBuffer(capacity + 4, direct); - buffer.limit(buffer.capacity()); - buffer.putInt(tag.incrementAndGet()); - ByteBuffer slice = buffer.slice(); - BufferUtil.clear(slice); - return slice; - } - } - - public boolean isDetailedDump() - { - return _detailedDump; - } - - public void setDetailedDump(boolean detailedDump) - { - _detailedDump = detailedDump; - } - - @Override - public void dump(Appendable out, String indent) throws IOException - { - List dump = new ArrayList<>(); - dump.add(String.format("HeapMemory: %d/%d", getHeapMemory(), getMaxHeapMemory())); - dump.add(String.format("DirectMemory: %d/%d", getDirectMemory(), getMaxDirectMemory())); - - if (isDetailedDump()) - { - dump.add(new DumpableCollection("Indirect Buckets", _heapBuffers.values())); - dump.add(new DumpableCollection("Direct Buckets", _directBuffers.values())); - } - else - { - dump.add("Indirect Buckets size=" + _heapBuffers.size()); - dump.add("Direct Buckets size=" + _directBuffers.size()); - } - Dumpable.dumpObjects(out, indent, this, dump); - } - - @Override - public String toString() - { - return String.format("%s@%x{maxQueueLength=%s, factor=%s}", - this.getClass().getSimpleName(), hashCode(), - getMaxBucketSize(), - getCapacityFactor()); - } - - protected class Retained extends ArrayRetainableByteBufferPool - { - public Retained(int factor, int maxCapacity, int maxBucketSize, long retainedHeapMemory, long retainedDirectMemory) - { - super(0, factor, maxCapacity, maxBucketSize, retainedHeapMemory, retainedDirectMemory); - } - - @Override - protected ByteBuffer allocate(int capacity) - { - return MappedByteBufferPool.this.acquire(capacity, false); - } - - @Override - protected ByteBuffer allocateDirect(int capacity) - { - return MappedByteBufferPool.this.acquire(capacity, true); - } - - @Override - protected void removed(RetainableByteBuffer retainedBuffer) - { - MappedByteBufferPool.this.release(retainedBuffer.getBuffer()); - } - } -} diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/Retainable.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/Retainable.java index d8e7dcabff5..ada4cff9410 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/Retainable.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/Retainable.java @@ -23,6 +23,28 @@ import java.util.concurrent.atomic.AtomicInteger; *

The resource is typically implicitly retained when it is first created. * It may be retained more times (thus incrementing its reference count) and released * (thus decrementing its reference count), until the reference count goes to zero.

+ *

Idiomatic usage

+ *

The general rules to use {@code Retainable} objects are the following:

+ *
    + *
  1. If the {@code Retainable} has been obtained by calling a method, and the + * caller code consumes it, then the caller code must call {@link #release()}.
  2. + *
  3. If the {@code Retainable} has been obtained by {@code caller2} by calling a + * method, and {@code caller2} returns it without consuming it to {@code caller1}, + * then {@code caller2} must not call {@link #release()}, since {@code caller1} will.
  4. + *
  5. If the {@code Retainable} has been obtained as a method argument, the + * receiver code must either: + *
      + *
    1. Consume the {@code Retainable} synchronously within the method, in which case + * {@link #release()} must not be called.
    2. + *
    3. Pass the {@code Retainable} to some other method, in which case {@link #release()} + * must not be called.
    4. + *
    5. Store away the {@code Retainable} for later or asynchronous processing, for + * example storing it in containers such as {@link java.util.Collection}s, or capturing + * it in a lambda that is passed to another thread, etc., in which case {@link #retain()} + * must be called and a mechanism to call {@link #release()} later or asynchronously + * for this additional {@link #retain()} must be arranged.
    6. + *
    + *
*/ public interface Retainable { @@ -124,12 +146,20 @@ public interface Retainable references = new AtomicInteger(initialCount); } + /** + * @return the current reference count + */ + public int get() + { + return references.get(); + } + /** *

Updates the reference count from {@code 0} to {@code 1}.

*

This method should only be used when this resource is acquired * from a pool.

*/ - protected void acquire() + public void acquire() { if (references.getAndUpdate(c -> c == 0 ? 1 : c) != 0) throw new IllegalStateException("acquired while in use " + this); @@ -178,7 +208,7 @@ public interface Retainable @Override public String toString() { - return String.format("%s@%x[r=%d]", getClass().getSimpleName(), hashCode(), references.get()); + return String.format("%s@%x[r=%d]", getClass().getSimpleName(), hashCode(), get()); } } } diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index bba96a55777..38e2b5f4cc2 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -14,97 +14,155 @@ package org.eclipse.jetty.io; import java.nio.ByteBuffer; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; +import org.eclipse.jetty.io.internal.NonRetainableByteBuffer; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.NanoTime; /** - *

A pooled ByteBuffer which maintains a reference count that is - * incremented with {@link #retain()} and decremented with {@link #release()}. The buffer - * is released to the pool when {@link #release()} is called one more time than {@link #retain()}.

- *

A {@code RetainableByteBuffer} can either be: + *

A pooled {@link ByteBuffer} which maintains a reference count that is + * incremented with {@link #retain()} and decremented with {@link #release()}.

+ *

The {@code ByteBuffer} is released to a {@link RetainableByteBufferPool} + * when {@link #release()} is called one more time than {@link #retain()}; + * in such case, the call to {@link #release()} returns {@code true}.

+ *

A {@code RetainableByteBuffer} can either be:

*
    - *
  • in pool; in this case {@link #isRetained()} returns {@code false} and calling {@link #release()} throws {@link IllegalStateException}
  • - *
  • out of pool but not retained; in this case {@link #isRetained()} returns {@code false} and calling {@link #release()} returns {@code true}
  • - *
  • out of pool and retained; in this case {@link #isRetained()} returns {@code true} and calling {@link #release()} returns {@code false}
  • + *
  • in pool; in this case {@link #isRetained()} returns {@code false} + * and calling {@link #release()} throws {@link IllegalStateException}
  • + *
  • out of pool but not retained; in this case {@link #isRetained()} + * returns {@code false} and calling {@link #release()} returns {@code true}
  • + *
  • out of pool and retained; in this case {@link #isRetained()} + * returns {@code true} and calling {@link #release()} returns {@code false}
  • *
- *

Calling {@link #release()} on a out of pool and retained instance does not re-pool it while that re-pools it on a out of pool but not retained instance.

*/ -public class RetainableByteBuffer extends Retainable.ReferenceCounter +public interface RetainableByteBuffer extends Retainable { - private final ByteBuffer buffer; - private final Consumer releaser; - private final AtomicLong lastUpdate = new AtomicLong(NanoTime.now()); - - RetainableByteBuffer(ByteBuffer buffer, Consumer releaser) + /** + *

Returns a non-retainable {@code RetainableByteBuffer} that wraps + * the given {@code ByteBuffer}.

+ *

Use this method to wrap user-provided {@code ByteBuffer}s, or + * {@code ByteBuffer}s that hold constant bytes, to make them look + * like {@code RetainableByteBuffer}s.

+ *

The returned {@code RetainableByteBuffer} {@link #canRetain()} + * method always returns {@code false}.

+ *

{@code RetainableByteBuffer}s returned by this method are not + * suitable to be wrapped in other {@link Retainable} implementations + * that may delegate calls to {@link #retain()}.

+ * + * @param byteBuffer the {@code ByteBuffer} to wrap + * @return a non-retainable {@code RetainableByteBuffer} + * @see RetainableByteBufferPool.NonPooling + */ + public static RetainableByteBuffer wrap(ByteBuffer byteBuffer) { - super(0); - this.releaser = releaser; - this.buffer = buffer; + return new NonRetainableByteBuffer(byteBuffer); } - public int capacity() + /** + * @return whether this instance is retained + * @see ReferenceCounter#isRetained() + */ + public boolean isRetained(); + + /** + * @return the wrapped, not {@code null}, {@code ByteBuffer} + */ + public ByteBuffer getByteBuffer(); + + /** + * @return whether the {@code ByteBuffer} is direct + */ + public default boolean isDirect() { - return buffer.capacity(); + return getByteBuffer().isDirect(); } - public ByteBuffer getBuffer() + /** + * @return the number of remaining bytes in the {@code ByteBuffer} + */ + public default int remaining() { - return buffer; + return getByteBuffer().remaining(); } - public long getLastUpdate() + /** + * @return whether the {@code ByteBuffer} has remaining bytes + */ + public default boolean hasRemaining() { - return lastUpdate.getOpaque(); + return getByteBuffer().hasRemaining(); } - public boolean isDirect() + /** + * @return the {@code ByteBuffer} capacity + */ + public default int capacity() { - return buffer.isDirect(); + return getByteBuffer().capacity(); } - protected void acquire() + /** + * @see BufferUtil#clear(ByteBuffer) + */ + public default void clear() { - // Overridden for visibility. - super.acquire(); + BufferUtil.clear(getByteBuffer()); } - public boolean release() + /** + * A wrapper for {@link RetainableByteBuffer} instances + */ + public class Wrapper extends Retainable.Wrapper implements RetainableByteBuffer { - boolean released = super.release(); - if (released) + public Wrapper(RetainableByteBuffer wrapped) { - lastUpdate.setOpaque(NanoTime.now()); - releaser.accept(this); + super(wrapped); } - return released; - } - public int remaining() - { - return buffer.remaining(); - } + public RetainableByteBuffer getWrapped() + { + return (RetainableByteBuffer)super.getWrapped(); + } - public boolean hasRemaining() - { - return remaining() > 0; - } + @Override + public boolean isRetained() + { + return getWrapped().isRetained(); + } - public boolean isEmpty() - { - return !hasRemaining(); - } + @Override + public ByteBuffer getByteBuffer() + { + return getWrapped().getByteBuffer(); + } - public void clear() - { - BufferUtil.clear(buffer); - } + @Override + public boolean isDirect() + { + return getWrapped().isDirect(); + } - @Override - public String toString() - { - return "%s[%s]".formatted(super.toString(), BufferUtil.toDetailString(buffer)); + @Override + public int remaining() + { + return getWrapped().remaining(); + } + + @Override + public boolean hasRemaining() + { + return getWrapped().hasRemaining(); + } + + @Override + public int capacity() + { + return getWrapped().capacity(); + } + + @Override + public void clear() + { + getWrapped().clear(); + } } } diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index eeaa8d8c16f..d031144c622 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -14,50 +14,100 @@ package org.eclipse.jetty.io; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.util.BufferUtil; /** - *

A {@link RetainableByteBuffer} pool.

- *

Acquired buffers must be released by calling {@link RetainableByteBuffer#release()} otherwise the memory they hold will - * be leaked.

+ *

A pool for {@link RetainableByteBuffer} instances.

+ *

{@link RetainableByteBuffer} that are {@link #acquire(int, boolean) acquired} + * must be released by calling {@link RetainableByteBuffer#release()} + * otherwise the memory they hold will be leaked.

+ * + *

API NOTE

+ *

This interface does not have a symmetric {@code release(RetainableByteBuffer)} + * method, because it will be confusing to use due to the fact that the acquired instance + * is-a {@link Retainable}.

+ *

Imagine this (hypothetical) code sequence:

+ *
{@code
+ * RetainableByteBuffer buffer = pool.acquire(size, direct);
+ * buffer.retain();
+ * pool.release(buffer);
+ * }
+ *

The hypothetical call to {@code release(RetainableByteBuffer)} would appear to + * release the buffer to the pool, but in fact the buffer is retained one more time + * (and therefore still in use) and not really released to the pool. + * For this reason there is no {@code release(RetainableByteBuffer)} method.

+ *

Therefore, in order to track acquire/release counts both the pool and the + * buffer returned by {@link #acquire(int, boolean)} must be wrapped, see + * {@link RetainableByteBuffer.Wrapper}

*/ public interface RetainableByteBufferPool { /** - * Acquires a memory buffer from the pool. + *

Acquires a {@link RetainableByteBuffer} from this pool.

+ * * @param size The size of the buffer. The returned buffer will have at least this capacity. * @param direct true if a direct memory buffer is needed, false otherwise. - * @return a memory buffer with position and size set to 0. + * @return a {@link RetainableByteBuffer} with position and limit set to 0. */ RetainableByteBuffer acquire(int size, boolean direct); + /** + *

Removes all {@link RetainableByteBuffer#isRetained() non-retained} + * pooled instances from this pool.

+ */ void clear(); - static RetainableByteBufferPool from(ByteBufferPool byteBufferPool) + /** + *

A wrapper for {@link RetainableByteBufferPool} instances.

+ */ + class Wrapper implements RetainableByteBufferPool { - return new NotRetainedByteBufferPool(byteBufferPool); - } + private final RetainableByteBufferPool wrapped; - class NotRetainedByteBufferPool implements RetainableByteBufferPool - { - private final ByteBufferPool _byteBufferPool; - - public NotRetainedByteBufferPool(ByteBufferPool byteBufferPool) + public Wrapper(RetainableByteBufferPool wrapped) { - _byteBufferPool = byteBufferPool; + this.wrapped = wrapped; + } + + public RetainableByteBufferPool getWrapped() + { + return wrapped; } @Override public RetainableByteBuffer acquire(int size, boolean direct) { - ByteBuffer byteBuffer = _byteBufferPool.acquire(size, direct); - RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(byteBuffer, this::release); - retainableByteBuffer.acquire(); - return retainableByteBuffer; + return getWrapped().acquire(size, direct); } - private void release(RetainableByteBuffer retainedBuffer) + @Override + public void clear() { - _byteBufferPool.release(retainedBuffer.getBuffer()); + getWrapped().clear(); + } + } + + /** + *

A {@link RetainableByteBufferPool} that does not pool its + * {@link RetainableByteBuffer}s.

+ *

The returned {@code RetainableByteBuffer}s are reference + * counted.

+ *

{@code RetainableByteBuffer}s returned by this class + * are suitable to be wrapped in other {@link Retainable} + * implementations that may delegate calls to + * {@link Retainable#retain()}.

+ * + * @see RetainableByteBuffer#wrap(ByteBuffer) + */ + class NonPooling implements RetainableByteBufferPool + { + @Override + public RetainableByteBuffer acquire(int size, boolean direct) + { + return new Buffer(BufferUtil.allocate(size, direct)); } @Override @@ -65,10 +115,67 @@ public interface RetainableByteBufferPool { } - @Override - public String toString() + private static class Buffer extends AbstractRetainableByteBuffer { - return String.format("NonRetainableByteBufferPool@%x{%s}", hashCode(), _byteBufferPool.toString()); + private Buffer(ByteBuffer byteBuffer) + { + super(byteBuffer); + acquire(); + } + } + } + + /** + *

Accumulates a sequence of {@link RetainableByteBuffer} that + * are typically created during the generation of protocol bytes.

+ *

{@code RetainableByteBuffer}s can be either + * {@link #append(RetainableByteBuffer) appended} to the sequence, + * or {@link #insert(int, RetainableByteBuffer) inserted} at a + * specific position in the sequence, and then + * {@link #release() released} when they are consumed.

+ */ + class Accumulator + { + private final List buffers = new ArrayList<>(); + private final List byteBuffers = new ArrayList<>(); + + public void append(RetainableByteBuffer buffer) + { + buffers.add(buffer); + byteBuffers.add(buffer.getByteBuffer()); + } + + public void insert(int index, RetainableByteBuffer buffer) + { + buffers.add(index, buffer); + byteBuffers.add(index, buffer.getByteBuffer()); + } + + public List getByteBuffers() + { + return byteBuffers; + } + + public long getTotalLength() + { + long length = 0; + for (ByteBuffer buffer : byteBuffers) + { + length += buffer.remaining(); + } + return length; + } + + public int getSize() + { + return byteBuffers.size(); + } + + public void release() + { + buffers.forEach(RetainableByteBuffer::release); + buffers.clear(); + byteBuffers.clear(); } } } diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/InputStreamContentSource.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/InputStreamContentSource.java index 167317f425a..3f50132771d 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/InputStreamContentSource.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/InputStreamContentSource.java @@ -16,9 +16,7 @@ package org.eclipse.jetty.io.content; import java.io.InputStream; import java.nio.ByteBuffer; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.NoopByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.IO; @@ -29,9 +27,9 @@ import org.eclipse.jetty.util.thread.SerializedInvoker; *

* A {@link Content.Source} that is backed by an {@link InputStream}. * Data is read from the {@link InputStream} into a buffer that is optionally acquired - * from a {@link ByteBufferPool}, and converted to a {@link Content.Chunk} that is - * returned from {@link #read()}. If no {@link ByteBufferPool} is provided, then - * a {@link NoopByteBufferPool} is used. + * from a {@link RetainableByteBufferPool}, and converted to a {@link Content.Chunk} that is + * returned from {@link #read()}. If no {@link RetainableByteBufferPool} is provided, then + * a {@link RetainableByteBufferPool.NonPooling} is used. *

*/ public class InputStreamContentSource implements Content.Source @@ -47,18 +45,13 @@ public class InputStreamContentSource implements Content.Source public InputStreamContentSource(InputStream inputStream) { - this(inputStream, (ByteBufferPool)null); - } - - public InputStreamContentSource(InputStream inputStream, ByteBufferPool bufferPool) - { - this(inputStream, (bufferPool == null ? ByteBufferPool.NOOP : bufferPool).asRetainableByteBufferPool()); + this(inputStream, null); } public InputStreamContentSource(InputStream inputStream, RetainableByteBufferPool bufferPool) { this.inputStream = inputStream; - this.bufferPool = bufferPool == null ? ByteBufferPool.NOOP.asRetainableByteBufferPool() : bufferPool; + this.bufferPool = bufferPool != null ? bufferPool : new RetainableByteBufferPool.NonPooling(); } public int getBufferSize() @@ -85,7 +78,7 @@ public class InputStreamContentSource implements Content.Source RetainableByteBuffer streamBuffer = bufferPool.acquire(getBufferSize(), false); try { - ByteBuffer buffer = streamBuffer.getBuffer(); + ByteBuffer buffer = streamBuffer.getByteBuffer(); int read = inputStream.read(buffer.array(), buffer.arrayOffset(), buffer.capacity()); if (read < 0) { diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/PathContentSource.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/PathContentSource.java index 5c3491c649b..04e05a6eb70 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/PathContentSource.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/content/PathContentSource.java @@ -23,7 +23,6 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; @@ -51,12 +50,7 @@ public class PathContentSource implements Content.Source public PathContentSource(Path path) { - this(path, (ByteBufferPool)null); - } - - public PathContentSource(Path path, ByteBufferPool byteBufferPool) - { - this(path, (byteBufferPool == null ? ByteBufferPool.NOOP : byteBufferPool).asRetainableByteBufferPool()); + this(path, null); } public PathContentSource(Path path, RetainableByteBufferPool byteBufferPool) @@ -69,7 +63,7 @@ public class PathContentSource implements Content.Source throw new AccessDeniedException(path.toString()); this.path = path; this.length = Files.size(path); - this.byteBufferPool = byteBufferPool == null ? ByteBufferPool.NOOP.asRetainableByteBufferPool() : byteBufferPool; + this.byteBufferPool = byteBufferPool != null ? byteBufferPool : new RetainableByteBufferPool.NonPooling(); } catch (IOException x) { @@ -135,7 +129,7 @@ public class PathContentSource implements Content.Source return Content.Chunk.EOF; RetainableByteBuffer retainableByteBuffer = byteBufferPool.acquire(getBufferSize(), isUseDirectByteBuffers()); - ByteBuffer byteBuffer = retainableByteBuffer.getBuffer(); + ByteBuffer byteBuffer = retainableByteBuffer.getByteBuffer(); int read; try diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/NoopByteBufferPool.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/internal/NonRetainableByteBuffer.java similarity index 52% rename from jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/NoopByteBufferPool.java rename to jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/internal/NonRetainableByteBuffer.java index 9c3ba95f26d..3c54bb4bfe2 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/NoopByteBufferPool.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/internal/NonRetainableByteBuffer.java @@ -11,34 +11,30 @@ // ======================================================================== // -package org.eclipse.jetty.io; +package org.eclipse.jetty.io.internal; import java.nio.ByteBuffer; -import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.io.RetainableByteBuffer; -public class NoopByteBufferPool implements ByteBufferPool +public class NonRetainableByteBuffer implements RetainableByteBuffer { - private final RetainableByteBufferPool _retainableByteBufferPool = RetainableByteBufferPool.from(this); + private final ByteBuffer byteBuffer; - @Override - public ByteBuffer acquire(int size, boolean direct) + public NonRetainableByteBuffer(ByteBuffer byteBuffer) { - if (direct) - return BufferUtil.allocateDirect(size); - else - return BufferUtil.allocate(size); + this.byteBuffer = byteBuffer; } @Override - public void release(ByteBuffer buffer) + public boolean isRetained() { - BufferUtil.clear(buffer); + return false; } @Override - public RetainableByteBufferPool asRetainableByteBufferPool() + public ByteBuffer getByteBuffer() { - return _retainableByteBufferPool; + return byteBuffer; } } diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java index 9e166e49550..826cab32813 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java @@ -24,11 +24,11 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -40,17 +40,17 @@ public class SslClientConnectionFactory implements ClientConnectionFactory public static final String SSL_ENGINE_CONTEXT_KEY = "org.eclipse.jetty.client.ssl.engine"; private final SslContextFactory sslContextFactory; - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final Executor executor; private final ClientConnectionFactory connectionFactory; private boolean _directBuffersForEncryption = true; private boolean _directBuffersForDecryption = true; private boolean _requireCloseMessage; - public SslClientConnectionFactory(SslContextFactory sslContextFactory, ByteBufferPool byteBufferPool, Executor executor, ClientConnectionFactory connectionFactory) + public SslClientConnectionFactory(SslContextFactory sslContextFactory, RetainableByteBufferPool retainableByteBufferPool, Executor executor, ClientConnectionFactory connectionFactory) { this.sslContextFactory = Objects.requireNonNull(sslContextFactory, "Missing SslContextFactory"); - this.byteBufferPool = byteBufferPool; + this.retainableByteBufferPool = retainableByteBufferPool; this.executor = executor; this.connectionFactory = connectionFactory; } @@ -119,7 +119,7 @@ public class SslClientConnectionFactory implements ClientConnectionFactory engine.setUseClientMode(true); context.put(SSL_ENGINE_CONTEXT_KEY, engine); - SslConnection sslConnection = newSslConnection(byteBufferPool, executor, endPoint, engine); + SslConnection sslConnection = newSslConnection(retainableByteBufferPool, executor, endPoint, engine); EndPoint appEndPoint = sslConnection.getDecryptedEndPoint(); appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context)); @@ -130,9 +130,9 @@ public class SslClientConnectionFactory implements ClientConnectionFactory return sslConnection; } - protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) + protected SslConnection newSslConnection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(byteBufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); + return new SslConnection(retainableByteBufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } @Override diff --git a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 2d23fb9e7e6..62de21b0291 100644 --- a/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -32,7 +32,6 @@ import javax.net.ssl.SSLSession; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractEndPoint; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; @@ -109,15 +108,14 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr private final List handshakeListeners = new ArrayList<>(); private final AtomicLong _bytesIn = new AtomicLong(); private final AtomicLong _bytesOut = new AtomicLong(); - private final ByteBufferPool _bufferPool; private final RetainableByteBufferPool _retainableByteBufferPool; private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; - private ByteBuffer _decryptedInput; - private RetainableByteBuffer _encryptedInput; - private ByteBuffer _encryptedOutput; private final boolean _encryptedDirectBuffers; private final boolean _decryptedDirectBuffers; + private RetainableByteBuffer _decryptedInput; + private RetainableByteBuffer _encryptedInput; + private RetainableByteBuffer _encryptedOutput; private boolean _renegotiationAllowed; private int _renegotiationLimit = -1; private boolean _closedOutbound; @@ -166,24 +164,17 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr } }; - public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine) + public SslConnection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine) { - this(byteBufferPool, executor, endPoint, sslEngine, false, false); + this(retainableByteBufferPool, executor, endPoint, sslEngine, false, false); } - public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, - boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) - { - this(byteBufferPool.asRetainableByteBufferPool(), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); - } - - public SslConnection(RetainableByteBufferPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, + public SslConnection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { // This connection does not execute calls to onFillable(), so they will be called by the selector thread. // onFillable() does not block and will only wakeup another thread to do the actual reading and handling. super(endPoint, executor); - this._bufferPool = byteBufferPool; this._retainableByteBufferPool = retainableByteBufferPool; this._sslEngine = sslEngine; this._decryptedEndPoint = newDecryptedEndPoint(); @@ -324,15 +315,16 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr private void acquireEncryptedOutput() { + // TODO: before the output was done with the BBP only. if (_encryptedOutput == null) - _encryptedOutput = _bufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); + _encryptedOutput = _retainableByteBufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); } @Override public void onUpgradeTo(ByteBuffer buffer) { acquireEncryptedInput(); - BufferUtil.append(_encryptedInput.getBuffer(), buffer); + BufferUtil.append(_encryptedInput.getByteBuffer(), buffer); } @Override @@ -402,11 +394,11 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr @Override public String toConnectionString() { - ByteBuffer b = _encryptedInput == null ? null : _encryptedInput.getBuffer(); + ByteBuffer b = _encryptedInput == null ? null : _encryptedInput.getByteBuffer(); int ei = b == null ? -1 : b.remaining(); - b = _encryptedOutput; + b = _encryptedOutput == null ? null : _encryptedOutput.getByteBuffer(); int eo = b == null ? -1 : b.remaining(); - b = _decryptedInput; + b = _decryptedInput == null ? null : _decryptedInput.getByteBuffer(); int di = b == null ? -1 : b.remaining(); Connection connection = _decryptedEndPoint.getConnection(); @@ -437,7 +429,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr throw new IllegalStateException(); if (_decryptedInput != null && !_decryptedInput.hasRemaining()) { - _bufferPool.release(_decryptedInput); + _decryptedInput.release(); _decryptedInput = null; } } @@ -448,7 +440,8 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr throw new IllegalStateException(); if (_encryptedInput != null) _encryptedInput.clear(); - BufferUtil.clear(_decryptedInput); + if (_decryptedInput != null) + _decryptedInput.clear(); releaseEmptyInputBuffers(); } @@ -462,7 +455,8 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr { if (!_lock.isHeldByCurrentThread()) throw new IllegalStateException(); - BufferUtil.clear(_encryptedOutput); + if (_encryptedOutput != null) + _encryptedOutput.clear(); releaseEmptyEncryptedOutputBuffer(); } @@ -472,7 +466,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr throw new IllegalStateException(); if (_encryptedOutput != null && !_encryptedOutput.hasRemaining()) { - _bufferPool.release(_encryptedOutput); + _encryptedOutput.release(); _encryptedOutput = null; } } @@ -639,8 +633,8 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr return filled = 0; // Do we already have some decrypted data? - if (BufferUtil.hasContent(_decryptedInput)) - return filled = BufferUtil.append(buffer, _decryptedInput); + if (_decryptedInput != null && _decryptedInput.hasRemaining()) + return filled = BufferUtil.append(buffer, _decryptedInput.getByteBuffer()); // loop filling and unwrapping until we have something while (true) @@ -683,24 +677,29 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr if (_decryptedInput == null) { if (BufferUtil.space(buffer) > appBufferSize) + { appIn = buffer; + } else - appIn = _decryptedInput = _bufferPool.acquire(appBufferSize, _decryptedDirectBuffers); + { + _decryptedInput = _retainableByteBufferPool.acquire(appBufferSize, _decryptedDirectBuffers); + appIn = _decryptedInput.getByteBuffer(); + } } else { - appIn = _decryptedInput; + appIn = _decryptedInput.getByteBuffer(); } // Let's try reading some encrypted data... even if we have some already. - int netFilled = networkFill(_encryptedInput.getBuffer()); + int netFilled = networkFill(_encryptedInput.getByteBuffer()); if (netFilled > 0) _bytesIn.addAndGet(netFilled); if (LOG.isDebugEnabled()) LOG.debug("net filled={}", netFilled); // Workaround for Java 11 behavior. - if (netFilled < 0 && isHandshakeInitial() && (_encryptedInput == null || _encryptedInput.isEmpty())) + if (netFilled < 0 && isHandshakeInitial() && (_encryptedInput == null || !_encryptedInput.hasRemaining())) closeInbound(); if (netFilled > 0 && !isHandshakeComplete() && isOutboundDone()) @@ -719,7 +718,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr try { _underflown = false; - unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput.getBuffer(), appIn); + unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput.getByteBuffer(), appIn); } finally { @@ -750,13 +749,13 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr case BUFFER_UNDERFLOW: // Continue if we can compact? - if (BufferUtil.compact(_encryptedInput.getBuffer())) + if (BufferUtil.compact(_encryptedInput.getByteBuffer())) continue; // Are we out of space? - if (BufferUtil.space(_encryptedInput.getBuffer()) == 0) + if (BufferUtil.space(_encryptedInput.getByteBuffer()) == 0) { - BufferUtil.clear(_encryptedInput.getBuffer()); + BufferUtil.clear(_encryptedInput.getByteBuffer()); throw new SSLHandshakeException("Encrypted buffer max length exceeded"); } @@ -786,7 +785,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr // too small. Release the decrypted input buffer so it will be re-acquired // with the larger capacity. // See also system property "jsse.SSLEngine.acceptLargeFragments". - if (BufferUtil.isEmpty(_decryptedInput) && appBufferSize < getApplicationBufferSize()) + if ((_decryptedInput == null || !_decryptedInput.hasRemaining()) && appBufferSize < getApplicationBufferSize()) { releaseEmptyDecryptedInputBuffer(); continue; @@ -807,7 +806,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr { if (appIn == buffer) return filled = unwrapResult.bytesProduced(); - return filled = BufferUtil.append(buffer, _decryptedInput); + return filled = BufferUtil.append(buffer, _decryptedInput.getByteBuffer()); } break; @@ -867,14 +866,14 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr _fillState, _underflown, _encryptedInput, - BufferUtil.toDetailString(_decryptedInput), + _decryptedInput, SslConnection.this); if (_fillState != FillState.IDLE) return; // Fillable if we have decrypted input OR enough encrypted input. - fillable = BufferUtil.hasContent(_decryptedInput) || (_encryptedInput != null && _encryptedInput.hasRemaining() && !_underflown); + fillable = (_decryptedInput != null && _decryptedInput.hasRemaining()) || (_encryptedInput != null && _encryptedInput.hasRemaining() && !_underflown); HandshakeStatus status = _sslEngine.getHandshakeStatus(); switch (status) @@ -890,10 +889,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr { interest = true; _fillState = FillState.INTERESTED; - if (_flushState == FlushState.IDLE && BufferUtil.hasContent(_encryptedOutput)) + if (_flushState == FlushState.IDLE && (_encryptedOutput != null && _encryptedOutput.hasRemaining())) { _flushState = FlushState.WRITING; - write = _encryptedOutput; + write = _encryptedOutput.getByteBuffer(); } } break; @@ -905,7 +904,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr if (_flushState == FlushState.IDLE) { _flushState = FlushState.WRITING; - write = BufferUtil.hasContent(_encryptedOutput) ? _encryptedOutput : BufferUtil.EMPTY_BUFFER; + write = (_encryptedOutput != null && _encryptedOutput.hasRemaining()) ? _encryptedOutput.getByteBuffer() : BufferUtil.EMPTY_BUFFER; } } break; @@ -1024,7 +1023,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr int remaining = _encryptedOutput.remaining(); if (remaining > 0) { - boolean flushed = networkFlush(_encryptedOutput); + boolean flushed = networkFlush(_encryptedOutput.getByteBuffer()); int written = remaining - _encryptedOutput.remaining(); if (written > 0) _bytesOut.addAndGet(written); @@ -1083,21 +1082,22 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr // We call sslEngine.wrap to try to take bytes from appOuts // buffers and encrypt them into the _encryptedOutput buffer. - BufferUtil.compact(_encryptedOutput); - int pos = BufferUtil.flipToFill(_encryptedOutput); + ByteBuffer encryptedOutputBuffer = _encryptedOutput.getByteBuffer(); + BufferUtil.compact(encryptedOutputBuffer); + int pos = BufferUtil.flipToFill(encryptedOutputBuffer); SSLEngineResult wrapResult; try { - wrapResult = wrap(_sslEngine, appOuts, _encryptedOutput); + wrapResult = wrap(_sslEngine, appOuts, encryptedOutputBuffer); } finally { - BufferUtil.flipToFlush(_encryptedOutput, pos); + BufferUtil.flipToFlush(encryptedOutputBuffer, pos); } if (LOG.isDebugEnabled()) LOG.debug("wrap {} {} ioDone={}/{}", StringUtil.replace(wrapResult.toString(), '\n', ' '), - BufferUtil.toSummaryString(_encryptedOutput), + _encryptedOutput, _sslEngine.isInboundDone(), _sslEngine.isOutboundDone()); @@ -1106,16 +1106,13 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr // if we have net bytes, let's try to flush them boolean flushed = true; - if (_encryptedOutput != null) + int remaining = encryptedOutputBuffer.remaining(); + if (remaining > 0) { - int remaining = _encryptedOutput.remaining(); - if (remaining > 0) - { - flushed = networkFlush(_encryptedOutput); - int written = remaining - _encryptedOutput.remaining(); - if (written > 0) - _bytesOut.addAndGet(written); - } + flushed = networkFlush(encryptedOutputBuffer); + int written = remaining - encryptedOutputBuffer.remaining(); + if (written > 0) + _bytesOut.addAndGet(written); } if (LOG.isDebugEnabled()) @@ -1160,7 +1157,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr if (isRenegotiating() && !allowRenegotiate()) { getEndPoint().shutdownOutput(); - if (isEmpty && BufferUtil.isEmpty(_encryptedOutput)) + if (isEmpty && (_encryptedOutput == null || !_encryptedOutput.hasRemaining())) return result = true; throw new IOException("Broken pipe"); } @@ -1215,7 +1212,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr try (AutoLock l = _lock.lock()) { if (LOG.isDebugEnabled()) - LOG.debug(">onIncompleteFlush {} {}", SslConnection.this, BufferUtil.toDetailString(_encryptedOutput)); + LOG.debug(">onIncompleteFlush {} {}", SslConnection.this, _encryptedOutput); if (_flushState != FlushState.IDLE) return; @@ -1229,15 +1226,15 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr case NEED_WRAP: case NOT_HANDSHAKING: // write what we have or an empty buffer to reschedule a call to flush - write = BufferUtil.hasContent(_encryptedOutput) ? _encryptedOutput : BufferUtil.EMPTY_BUFFER; + write = (_encryptedOutput != null && _encryptedOutput.hasRemaining()) ? _encryptedOutput.getByteBuffer() : BufferUtil.EMPTY_BUFFER; _flushState = FlushState.WRITING; break; case NEED_UNWRAP: // If we have something to write, then write it and ignore the needed unwrap for now. - if (BufferUtil.hasContent(_encryptedOutput)) + if (_encryptedOutput != null && _encryptedOutput.hasRemaining()) { - write = _encryptedOutput; + write = _encryptedOutput.getByteBuffer(); _flushState = FlushState.WRITING; break; } @@ -1339,9 +1336,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr ByteBuffer write = null; try (AutoLock l = _lock.lock()) { - if (BufferUtil.hasContent(_encryptedOutput)) + if (_encryptedOutput != null && _encryptedOutput.hasRemaining()) { - write = _encryptedOutput; + write = _encryptedOutput.getByteBuffer(); _flushState = FlushState.WRITING; } } @@ -1441,7 +1438,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr @Override public boolean isInputShutdown() { - return BufferUtil.isEmpty(_decryptedInput) && (getEndPoint().isInputShutdown() || isInboundDone()); + return (_decryptedInput == null || !_decryptedInput.hasRemaining()) && (getEndPoint().isInputShutdown() || isInboundDone()); } private boolean isInboundDone() diff --git a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java deleted file mode 100644 index 528e28cccfb..00000000000 --- a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java +++ /dev/null @@ -1,255 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.Objects; - -import org.eclipse.jetty.io.AbstractByteBufferPool.Bucket; -import org.eclipse.jetty.util.StringUtil; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ArrayByteBufferPoolTest -{ - @Test - public void testMinimumRelease() - { - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(10, 100, 1000); - Bucket[] buckets = bufferPool.bucketsFor(true); - - for (int size = 1; size <= 9; size++) - { - ByteBuffer buffer = bufferPool.acquire(size, true); - - assertTrue(buffer.isDirect()); - assertEquals(size, buffer.capacity()); - for (Bucket bucket : buckets) - { - if (bucket != null) - assertTrue(bucket.isEmpty()); - } - - bufferPool.release(buffer); - - for (Bucket bucket : buckets) - { - if (bucket != null) - assertTrue(bucket.isEmpty()); - } - } - } - - @Test - public void testMaxRelease() - { - int minCapacity = 10; - int factor = 1; - int maxCapacity = 1024; - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(minCapacity, factor, maxCapacity); - Bucket[] buckets = bufferPool.bucketsFor(true); - - for (int size = maxCapacity - 1; size <= maxCapacity + 1; size++) - { - bufferPool.clear(); - ByteBuffer buffer = bufferPool.acquire(size, true); - - assertTrue(buffer.isDirect()); - assertThat(buffer.capacity(), greaterThanOrEqualTo(size)); - for (Bucket bucket : buckets) - { - if (bucket != null) - assertTrue(bucket.isEmpty()); - } - - bufferPool.release(buffer); - - int pooled = Arrays.stream(buckets) - .filter(Objects::nonNull) - .mapToInt(AbstractByteBufferPool.Bucket::size) - .sum(); - - if (size <= maxCapacity) - assertThat(pooled, is(1)); - else - assertThat(pooled, is(0)); - } - } - - @Test - public void testAcquireRelease() - { - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(10, 100, 1000); - Bucket[] buckets = bufferPool.bucketsFor(true); - - for (int size = 390; size <= 510; size++) - { - bufferPool.clear(); - ByteBuffer buffer = bufferPool.acquire(size, true); - - assertTrue(buffer.isDirect()); - assertThat(buffer.capacity(), greaterThanOrEqualTo(size)); - for (Bucket bucket : buckets) - { - if (bucket != null) - assertTrue(bucket.isEmpty()); - } - - bufferPool.release(buffer); - - int pooled = Arrays.stream(buckets) - .filter(Objects::nonNull) - .mapToInt(AbstractByteBufferPool.Bucket::size) - .sum(); - assertEquals(1, pooled); - } - } - - @Test - public void testAcquireReleaseAcquire() - { - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(10, 100, 1000); - Bucket[] buckets = bufferPool.bucketsFor(true); - - for (int size = 390; size <= 510; size++) - { - bufferPool.clear(); - ByteBuffer buffer1 = bufferPool.acquire(size, true); - bufferPool.release(buffer1); - ByteBuffer buffer2 = bufferPool.acquire(size, true); - bufferPool.release(buffer2); - ByteBuffer buffer3 = bufferPool.acquire(size, false); - bufferPool.release(buffer3); - - int pooled = Arrays.stream(buckets) - .filter(Objects::nonNull) - .mapToInt(AbstractByteBufferPool.Bucket::size) - .sum(); - assertEquals(1, pooled); - - assertSame(buffer1, buffer2); - assertNotSame(buffer1, buffer3); - } - } - - @Test - public void testReleaseNonPooledBuffer() - { - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(); - - // Release a few small non-pool buffers - bufferPool.release(ByteBuffer.wrap(StringUtil.getUtf8Bytes("Hello"))); - - assertEquals(0, bufferPool.getHeapByteBufferCount()); - } - - @Test - public void testMaxQueue() - { - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(-1, -1, -1, 2); - - ByteBuffer buffer1 = bufferPool.acquire(512, false); - ByteBuffer buffer2 = bufferPool.acquire(512, false); - ByteBuffer buffer3 = bufferPool.acquire(512, false); - - Bucket[] buckets = bufferPool.bucketsFor(false); - Arrays.stream(buckets) - .filter(Objects::nonNull) - .forEach(b -> assertEquals(0, b.size())); - - bufferPool.release(buffer1); - Bucket bucket = Arrays.stream(buckets) - .filter(Objects::nonNull) - .filter(b -> b.size() > 0) - .findFirst() - .orElseThrow(AssertionError::new); - assertEquals(1, bucket.size()); - - bufferPool.release(buffer2); - assertEquals(2, bucket.size()); - - bufferPool.release(buffer3); - assertEquals(2, bucket.size()); - } - - @Test - public void testMaxMemory() - { - int factor = 1024; - int maxMemory = 11 * factor; - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(-1, factor, -1, -1, -1, maxMemory); - Bucket[] buckets = bufferPool.bucketsFor(true); - - // Create the buckets - the oldest is the larger. - // 1+2+3+4=10 / maxMemory=11. - for (int i = 4; i >= 1; --i) - { - int capacity = factor * i; - ByteBuffer buffer = bufferPool.acquire(capacity, true); - assertThat(buffer.capacity(), equalTo(capacity)); - bufferPool.release(buffer); - } - - // Check state of buckets. - assertThat(bufferPool.getMemory(true), equalTo(10L * factor)); - assertThat(buckets[1].size(), equalTo(1)); - assertThat(buckets[2].size(), equalTo(1)); - assertThat(buckets[3].size(), equalTo(1)); - assertThat(buckets[4].size(), equalTo(1)); - - // Create and release a buffer to exceed the max memory. - int capacity = 2 * factor; - ByteBuffer buffer = bufferPool.newByteBuffer(capacity, true); - assertThat(buffer.capacity(), equalTo(capacity)); - bufferPool.release(buffer); - - // Now the oldest buffer should be gone and we have: 1+2x2+3=8 - assertThat(bufferPool.getMemory(true), equalTo(8L * factor)); - assertThat(buckets[1].size(), equalTo(1)); - assertThat(buckets[2].size(), equalTo(2)); - assertThat(buckets[3].size(), equalTo(1)); - - // Create and release a large buffer. - // Max memory is exceeded and buckets 3 and 1 are cleared. - // We will have 2x2+7=11. - capacity = 7 * factor; - buffer = bufferPool.newByteBuffer(capacity, true); - bufferPool.release(buffer); - - assertThat(bufferPool.getMemory(true), equalTo(11L * factor)); - assertThat(buckets[2].size(), equalTo(2)); - assertThat(buckets[7].size(), equalTo(1)); - } - - @Test - public void testEndiannessResetOnRelease() - { - ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(); - ByteBuffer buffer = bufferPool.acquire(10, true); - assertThat(buffer.order(), is(ByteOrder.BIG_ENDIAN)); - buffer.order(ByteOrder.LITTLE_ENDIAN); - bufferPool.release(buffer); - assertThat(buffer.order(), is(ByteOrder.BIG_ENDIAN)); - } -} diff --git a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index f201154b152..430dd0f1972 100644 --- a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -13,8 +13,6 @@ package org.eclipse.jetty.io; -import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; @@ -241,7 +239,7 @@ public class ArrayRetainableByteBufferPoolTest pool.acquire(10, true); assertThat(pool.getDirectByteBufferCount(), is(2L)); - assertThat(pool.getDirectMemory(), is(2L * AbstractByteBufferPool.DEFAULT_FACTOR)); + assertThat(pool.getDirectMemory(), is(2L * ArrayRetainableByteBufferPool.DEFAULT_FACTOR)); assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); assertThat(pool.getAvailableDirectMemory(), is(0L)); @@ -285,16 +283,16 @@ public class ArrayRetainableByteBufferPoolTest { RetainableByteBuffer buf1 = pool.acquire(10, true); assertThat(buf1, is(notNullValue())); - assertThat(buf1.capacity(), is(AbstractByteBufferPool.DEFAULT_FACTOR)); + assertThat(buf1.capacity(), is(ArrayRetainableByteBufferPool.DEFAULT_FACTOR)); RetainableByteBuffer buf2 = pool.acquire(10, true); assertThat(buf2, is(notNullValue())); - assertThat(buf2.capacity(), is(AbstractByteBufferPool.DEFAULT_FACTOR)); + assertThat(buf2.capacity(), is(ArrayRetainableByteBufferPool.DEFAULT_FACTOR)); buf1.release(); buf2.release(); RetainableByteBuffer buf3 = pool.acquire(16384 + 1, true); assertThat(buf3, is(notNullValue())); - assertThat(buf3.capacity(), is(16384 + AbstractByteBufferPool.DEFAULT_FACTOR)); + assertThat(buf3.capacity(), is(16384 + ArrayRetainableByteBufferPool.DEFAULT_FACTOR)); buf3.release(); RetainableByteBuffer buf4 = pool.acquire(32768, true); @@ -310,7 +308,7 @@ public class ArrayRetainableByteBufferPoolTest assertThat(pool.getDirectByteBufferCount(), is(4L)); assertThat(pool.getHeapByteBufferCount(), is(1L)); - assertThat(pool.getDirectMemory(), is(AbstractByteBufferPool.DEFAULT_FACTOR * 3L + 16384 + 32768L)); + assertThat(pool.getDirectMemory(), is(ArrayRetainableByteBufferPool.DEFAULT_FACTOR * 3L + 16384 + 32768L)); assertThat(pool.getHeapMemory(), is(32768L)); pool.clear(); @@ -322,9 +320,19 @@ public class ArrayRetainableByteBufferPoolTest } @Test - public void testExponentialPool() throws IOException + public void testQuadraticPool() { - ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool.ExponentialPool(); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool.Quadratic(); + + RetainableByteBuffer retain5 = pool.acquire(5, false); + retain5.release(); + RetainableByteBuffer retain6 = pool.acquire(6, false); + assertThat(retain6, sameInstance(retain5)); + retain6.release(); + RetainableByteBuffer retain9 = pool.acquire(9, false); + assertThat(retain9, not(sameInstance(retain5))); + retain9.release(); + assertThat(pool.acquire(1, false).capacity(), is(1)); assertThat(pool.acquire(2, false).capacity(), is(2)); RetainableByteBuffer b3 = pool.acquire(3, false); @@ -350,8 +358,8 @@ public class ArrayRetainableByteBufferPoolTest } b3.release(); - b4.getBuffer().limit(b4.getBuffer().capacity() - 2); - assertThat(pool.dump(), containsString("]{capacity=4,inuse=3(75%)")); + b4.getByteBuffer().limit(b4.getByteBuffer().capacity() - 2); + assertThat(pool.dump(), containsString("{capacity=4,inuse=3(75%)")); } @Test @@ -359,34 +367,9 @@ public class ArrayRetainableByteBufferPoolTest { ArrayRetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); RetainableByteBuffer buffer = bufferPool.acquire(10, true); - assertThat(buffer.getBuffer().order(), Matchers.is(ByteOrder.BIG_ENDIAN)); - buffer.getBuffer().order(ByteOrder.LITTLE_ENDIAN); + assertThat(buffer.getByteBuffer().order(), Matchers.is(ByteOrder.BIG_ENDIAN)); + buffer.getByteBuffer().order(ByteOrder.LITTLE_ENDIAN); assertThat(buffer.release(), is(true)); - assertThat(buffer.getBuffer().order(), Matchers.is(ByteOrder.BIG_ENDIAN)); - } - - @Test - public void testLogarithmic() - { - LogarithmicArrayByteBufferPool pool = new LogarithmicArrayByteBufferPool(); - ByteBuffer buffer5 = pool.acquire(5, false); - pool.release(buffer5); - ByteBuffer buffer6 = pool.acquire(6, false); - assertThat(buffer6, sameInstance(buffer5)); - pool.release(buffer6); - ByteBuffer buffer9 = pool.acquire(9, false); - assertThat(buffer9, not(sameInstance(buffer5))); - pool.release(buffer9); - - RetainableByteBufferPool retainablePool = pool.asRetainableByteBufferPool(); - - RetainableByteBuffer retain5 = retainablePool.acquire(5, false); - retain5.release(); - RetainableByteBuffer retain6 = retainablePool.acquire(6, false); - assertThat(retain6, sameInstance(retain5)); - retain6.release(); - RetainableByteBuffer retain9 = retainablePool.acquire(9, false); - assertThat(retain9, not(sameInstance(retain5))); - retain9.release(); + assertThat(buffer.getByteBuffer().order(), Matchers.is(ByteOrder.BIG_ENDIAN)); } } diff --git a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ByteBufferAccumulatorTest.java b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ByteBufferAccumulatorTest.java index 6d32ea12ff0..758c547762a 100644 --- a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ByteBufferAccumulatorTest.java +++ b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/ByteBufferAccumulatorTest.java @@ -30,14 +30,14 @@ import static org.junit.jupiter.api.Assertions.assertThrows; public class ByteBufferAccumulatorTest { - private CountingBufferPool byteBufferPool; + private CountingBufferPool bufferPool; private ByteBufferAccumulator accumulator; @BeforeEach public void before() { - byteBufferPool = new CountingBufferPool(); - accumulator = new ByteBufferAccumulator(byteBufferPool, false); + bufferPool = new CountingBufferPool(); + accumulator = new ByteBufferAccumulator(bufferPool, false); } @Test @@ -49,58 +49,64 @@ public class ByteBufferAccumulatorTest ByteBuffer slice = content.slice(); // We completely fill up the internal buffer with the first write. - ByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); - assertThat(BufferUtil.space(internalBuffer), greaterThanOrEqualTo(allocationSize)); - writeInFlushMode(slice, internalBuffer); - assertThat(BufferUtil.space(internalBuffer), is(0)); + RetainableByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); + ByteBuffer byteBuffer = internalBuffer.getByteBuffer(); + assertThat(BufferUtil.space(byteBuffer), greaterThanOrEqualTo(allocationSize)); + writeInFlushMode(slice, byteBuffer); + assertThat(BufferUtil.space(byteBuffer), is(0)); // If we ask for min size of 0 we get the same buffer which is full. internalBuffer = accumulator.ensureBuffer(0, allocationSize); - assertThat(BufferUtil.space(internalBuffer), is(0)); + byteBuffer = internalBuffer.getByteBuffer(); + assertThat(BufferUtil.space(byteBuffer), is(0)); // If we need at least 1 minSpace we must allocate a new buffer. internalBuffer = accumulator.ensureBuffer(1, allocationSize); - assertThat(BufferUtil.space(internalBuffer), greaterThan(0)); - assertThat(BufferUtil.space(internalBuffer), greaterThanOrEqualTo(allocationSize)); + byteBuffer = internalBuffer.getByteBuffer(); + assertThat(BufferUtil.space(byteBuffer), greaterThan(0)); + assertThat(BufferUtil.space(byteBuffer), greaterThanOrEqualTo(allocationSize)); // Write 13 bytes from the end of the internal buffer. - int bytesToWrite = BufferUtil.space(internalBuffer) - 13; + int bytesToWrite = BufferUtil.space(byteBuffer) - 13; ByteBuffer buffer = BufferUtil.toBuffer(new byte[bytesToWrite]); BufferUtil.clear(buffer); assertThat(writeInFlushMode(slice, buffer), is(bytesToWrite)); - assertThat(writeInFlushMode(buffer, internalBuffer), is(bytesToWrite)); - assertThat(BufferUtil.space(internalBuffer), is(13)); + assertThat(writeInFlushMode(buffer, byteBuffer), is(bytesToWrite)); + assertThat(BufferUtil.space(byteBuffer), is(13)); // If we request anything under the amount remaining we get back the same buffer. for (int i = 0; i <= 13; i++) { internalBuffer = accumulator.ensureBuffer(i, allocationSize); - assertThat(BufferUtil.space(internalBuffer), is(13)); + byteBuffer = internalBuffer.getByteBuffer(); + assertThat(BufferUtil.space(byteBuffer), is(13)); } // If we request over 13 then we get a new buffer. internalBuffer = accumulator.ensureBuffer(14, allocationSize); - assertThat(BufferUtil.space(internalBuffer), greaterThanOrEqualTo(1024)); + byteBuffer = internalBuffer.getByteBuffer(); + assertThat(BufferUtil.space(byteBuffer), greaterThanOrEqualTo(1024)); // Copy the rest of the content. while (slice.hasRemaining()) { internalBuffer = accumulator.ensureBuffer(1, allocationSize); - assertThat(BufferUtil.space(internalBuffer), greaterThanOrEqualTo(1)); - writeInFlushMode(slice, internalBuffer); + byteBuffer = internalBuffer.getByteBuffer(); + assertThat(BufferUtil.space(byteBuffer), greaterThanOrEqualTo(1)); + writeInFlushMode(slice, byteBuffer); } // Check we have the same content as the original buffer. assertThat(accumulator.getLength(), is(size)); - assertThat(byteBufferPool.getLeasedBuffers(), greaterThan(1L)); - ByteBuffer combinedBuffer = accumulator.toByteBuffer(); - assertThat(byteBufferPool.getLeasedBuffers(), is(1L)); + assertThat(bufferPool.getAcquireCount(), greaterThan(1L)); + RetainableByteBuffer combinedBuffer = accumulator.toRetainableByteBuffer(); + assertThat(bufferPool.getAcquireCount(), is(1L)); assertThat(accumulator.getLength(), is(size)); - assertThat(combinedBuffer, is(content)); + assertThat(combinedBuffer.getByteBuffer(), is(content)); // Close the accumulator and make sure all is returned to bufferPool. accumulator.close(); - byteBufferPool.verifyClosed(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } @Test @@ -114,24 +120,25 @@ public class ByteBufferAccumulatorTest // Copy the content. while (slice.hasRemaining()) { - ByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); - assertThat(BufferUtil.space(internalBuffer), greaterThanOrEqualTo(1)); - writeInFlushMode(slice, internalBuffer); + RetainableByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); + ByteBuffer byteBuffer = internalBuffer.getByteBuffer(); + assertThat(BufferUtil.space(byteBuffer), greaterThanOrEqualTo(1)); + writeInFlushMode(slice, byteBuffer); } // Check we have the same content as the original buffer. assertThat(accumulator.getLength(), is(size)); - assertThat(byteBufferPool.getLeasedBuffers(), greaterThan(1L)); - ByteBuffer combinedBuffer = accumulator.takeByteBuffer(); - assertThat(byteBufferPool.getLeasedBuffers(), is(1L)); + assertThat(bufferPool.getAcquireCount(), greaterThan(1L)); + RetainableByteBuffer combinedBuffer = accumulator.takeRetainableByteBuffer(); + assertThat(bufferPool.getAcquireCount(), is(1L)); assertThat(accumulator.getLength(), is(0)); accumulator.close(); - assertThat(byteBufferPool.getLeasedBuffers(), is(1L)); - assertThat(combinedBuffer, is(content)); + assertThat(bufferPool.getAcquireCount(), is(1L)); + assertThat(combinedBuffer.getByteBuffer(), is(content)); // Return the buffer and make sure all is returned to bufferPool. - byteBufferPool.release(combinedBuffer); - byteBufferPool.verifyClosed(); + combinedBuffer.release(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } @Test @@ -145,42 +152,43 @@ public class ByteBufferAccumulatorTest // Copy the content. while (slice.hasRemaining()) { - ByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); - writeInFlushMode(slice, internalBuffer); + RetainableByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); + ByteBuffer byteBuffer = internalBuffer.getByteBuffer(); + writeInFlushMode(slice, byteBuffer); } // Check we have the same content as the original buffer. assertThat(accumulator.getLength(), is(size)); - assertThat(byteBufferPool.getLeasedBuffers(), greaterThan(1L)); + assertThat(bufferPool.getAcquireCount(), greaterThan(1L)); byte[] combinedBuffer = accumulator.toByteArray(); - assertThat(byteBufferPool.getLeasedBuffers(), greaterThan(1L)); + assertThat(bufferPool.getAcquireCount(), greaterThan(1L)); assertThat(accumulator.getLength(), is(size)); assertThat(BufferUtil.toBuffer(combinedBuffer), is(content)); // Close the accumulator and make sure all is returned to bufferPool. accumulator.close(); - byteBufferPool.verifyClosed(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } @Test public void testEmptyToBuffer() { - ByteBuffer combinedBuffer = accumulator.toByteBuffer(); + RetainableByteBuffer combinedBuffer = accumulator.toRetainableByteBuffer(); assertThat(combinedBuffer.remaining(), is(0)); - assertThat(byteBufferPool.getLeasedBuffers(), is(1L)); + assertThat(bufferPool.getAcquireCount(), is(1L)); accumulator.close(); - byteBufferPool.verifyClosed(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } @Test public void testEmptyTakeBuffer() { - ByteBuffer combinedBuffer = accumulator.takeByteBuffer(); + RetainableByteBuffer combinedBuffer = accumulator.takeRetainableByteBuffer(); assertThat(combinedBuffer.remaining(), is(0)); accumulator.close(); - assertThat(byteBufferPool.getLeasedBuffers(), is(1L)); - byteBufferPool.release(combinedBuffer); - byteBufferPool.verifyClosed(); + assertThat(bufferPool.getAcquireCount(), is(1L)); + combinedBuffer.release(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } @Test @@ -194,21 +202,22 @@ public class ByteBufferAccumulatorTest // Copy the content. while (slice.hasRemaining()) { - ByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); - writeInFlushMode(slice, internalBuffer); + RetainableByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); + ByteBuffer byteBuffer = internalBuffer.getByteBuffer(); + writeInFlushMode(slice, byteBuffer); } // Check we have the same content as the original buffer. - assertThat(byteBufferPool.getLeasedBuffers(), greaterThan(1L)); - ByteBuffer combinedBuffer = byteBufferPool.acquire(accumulator.getLength(), false); - accumulator.writeTo(combinedBuffer); + assertThat(bufferPool.getAcquireCount(), greaterThan(1L)); + RetainableByteBuffer combinedBuffer = bufferPool.acquire(accumulator.getLength(), false); + accumulator.writeTo(combinedBuffer.getByteBuffer()); assertThat(accumulator.getLength(), is(size)); - assertThat(combinedBuffer, is(content)); - byteBufferPool.release(combinedBuffer); + assertThat(combinedBuffer.getByteBuffer(), is(content)); + combinedBuffer.release(); // Close the accumulator and make sure all is returned to bufferPool. accumulator.close(); - byteBufferPool.verifyClosed(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } @Test @@ -222,19 +231,20 @@ public class ByteBufferAccumulatorTest // Copy the content. while (slice.hasRemaining()) { - ByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); - writeInFlushMode(slice, internalBuffer); + RetainableByteBuffer internalBuffer = accumulator.ensureBuffer(1, allocationSize); + ByteBuffer byteBuffer = internalBuffer.getByteBuffer(); + writeInFlushMode(slice, byteBuffer); } // Writing to a buffer too small gives buffer overflow. - assertThat(byteBufferPool.getLeasedBuffers(), greaterThan(1L)); + assertThat(bufferPool.getAcquireCount(), greaterThan(1L)); ByteBuffer combinedBuffer = BufferUtil.toBuffer(new byte[accumulator.getLength() - 1]); BufferUtil.clear(combinedBuffer); assertThrows(BufferOverflowException.class, () -> accumulator.writeTo(combinedBuffer)); // Close the accumulator and make sure all is returned to bufferPool. accumulator.close(); - byteBufferPool.verifyClosed(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } @Test @@ -255,16 +265,16 @@ public class ByteBufferAccumulatorTest } // Check we have the same content as the original buffer. - assertThat(byteBufferPool.getLeasedBuffers(), greaterThan(1L)); - ByteBuffer combinedBuffer = byteBufferPool.acquire(accumulator.getLength(), false); - accumulator.writeTo(combinedBuffer); + assertThat(bufferPool.getAcquireCount(), greaterThan(1L)); + RetainableByteBuffer combinedBuffer = bufferPool.acquire(accumulator.getLength(), false); + accumulator.writeTo(combinedBuffer.getByteBuffer()); assertThat(accumulator.getLength(), is(size)); - assertThat(combinedBuffer, is(content)); - byteBufferPool.release(combinedBuffer); + assertThat(combinedBuffer.getByteBuffer(), is(content)); + combinedBuffer.release(); // Close the accumulator and make sure all is returned to bufferPool. accumulator.close(); - byteBufferPool.verifyClosed(); + assertThat(bufferPool.getAcquireCount(), is(0L)); } private ByteBuffer randomBytes(int size) @@ -282,47 +292,35 @@ public class ByteBufferAccumulatorTest return written; } - public static class CountingBufferPool extends LeakTrackingByteBufferPool + private static class CountingBufferPool extends RetainableByteBufferPool.Wrapper { - private final AtomicLong _leasedBuffers = new AtomicLong(0); + private final AtomicLong _acquires = new AtomicLong(); public CountingBufferPool() { - this(new MappedByteBufferPool()); - } - - public CountingBufferPool(ByteBufferPool delegate) - { - super(delegate); + super(new ArrayRetainableByteBufferPool()); } @Override - public ByteBuffer acquire(int size, boolean direct) + public RetainableByteBuffer acquire(int size, boolean direct) { - _leasedBuffers.incrementAndGet(); - return super.acquire(size, direct); + _acquires.incrementAndGet(); + return new RetainableByteBuffer.Wrapper(super.acquire(size, direct)) + { + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + _acquires.decrementAndGet(); + return released; + } + }; } - @Override - public void release(ByteBuffer buffer) + public long getAcquireCount() { - if (buffer != null) - _leasedBuffers.decrementAndGet(); - super.release(buffer); - } - - public long getLeasedBuffers() - { - return _leasedBuffers.get(); - } - - public void verifyClosed() - { - assertThat(_leasedBuffers.get(), is(0L)); - assertThat(getLeakedAcquires(), is(0L)); - assertThat(getLeakedReleases(), is(0L)); - assertThat(getLeakedResources(), is(0L)); - assertThat(getLeakedRemoves(), is(0L)); + return _acquires.get(); } } } diff --git a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/MappedByteBufferPoolTest.java b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/MappedByteBufferPoolTest.java deleted file mode 100644 index 8fec072dc3d..00000000000 --- a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/MappedByteBufferPoolTest.java +++ /dev/null @@ -1,185 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.nio.ByteBuffer; -import java.util.concurrent.ConcurrentMap; - -import org.eclipse.jetty.io.AbstractByteBufferPool.Bucket; -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.StringUtil; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class MappedByteBufferPoolTest -{ - @Test - public void testAcquireRelease() - { - MappedByteBufferPool bufferPool = new MappedByteBufferPool(); - ConcurrentMap buckets = bufferPool.bucketsFor(true); - - int size = 512; - ByteBuffer buffer = bufferPool.acquire(size, true); - - assertTrue(buffer.isDirect()); - assertThat(buffer.capacity(), greaterThanOrEqualTo(size)); - assertTrue(buckets.isEmpty()); - - bufferPool.release(buffer); - - assertEquals(1, buckets.size()); - assertEquals(1, buckets.values().iterator().next().size()); - } - - @Test - public void testAcquireReleaseAcquire() - { - MappedByteBufferPool bufferPool = new MappedByteBufferPool(); - ConcurrentMap buckets = bufferPool.bucketsFor(false); - - ByteBuffer buffer1 = bufferPool.acquire(512, false); - bufferPool.release(buffer1); - ByteBuffer buffer2 = bufferPool.acquire(512, false); - - assertSame(buffer1, buffer2); - assertEquals(1, buckets.size()); - assertEquals(0, buckets.values().iterator().next().size()); - - bufferPool.release(buffer2); - - assertEquals(1, buckets.size()); - assertEquals(1, buckets.values().iterator().next().size()); - } - - @Test - public void testAcquireReleaseClear() - { - MappedByteBufferPool bufferPool = new MappedByteBufferPool(); - ConcurrentMap buckets = bufferPool.bucketsFor(true); - - ByteBuffer buffer = bufferPool.acquire(512, true); - bufferPool.release(buffer); - - assertEquals(1, buckets.size()); - assertEquals(1, buckets.values().iterator().next().size()); - - bufferPool.clear(); - - assertTrue(buckets.isEmpty()); - } - - @Test - public void testReleaseNonPooledBuffer() - { - MappedByteBufferPool bufferPool = new MappedByteBufferPool(); - - // Release a few small non-pool buffers - bufferPool.release(ByteBuffer.wrap(StringUtil.getUtf8Bytes("Hello"))); - - assertEquals(0, bufferPool.getHeapByteBufferCount()); - } - - @Test - public void testTagged() - { - MappedByteBufferPool pool = new MappedByteBufferPool.Tagged(); - - ByteBuffer buffer = pool.acquire(1024, false); - - assertThat(BufferUtil.toDetailString(buffer), containsString("@T00000001")); - buffer = pool.acquire(1024, false); - assertThat(BufferUtil.toDetailString(buffer), containsString("@T00000002")); - } - - @Test - public void testMaxQueue() - { - MappedByteBufferPool bufferPool = new MappedByteBufferPool(-1, 2); - ConcurrentMap buckets = bufferPool.bucketsFor(false); - - ByteBuffer buffer1 = bufferPool.acquire(512, false); - ByteBuffer buffer2 = bufferPool.acquire(512, false); - ByteBuffer buffer3 = bufferPool.acquire(512, false); - assertEquals(0, buckets.size()); - - bufferPool.release(buffer1); - assertEquals(1, buckets.size()); - Bucket bucket = buckets.values().iterator().next(); - assertEquals(1, bucket.size()); - - bufferPool.release(buffer2); - assertEquals(2, bucket.size()); - - bufferPool.release(buffer3); - assertEquals(2, bucket.size()); - } - - @Test - public void testMaxMemory() - { - int factor = 1024; - int maxMemory = 11 * factor; - MappedByteBufferPool bufferPool = new MappedByteBufferPool(factor, -1, -1, maxMemory); - ConcurrentMap buckets = bufferPool.bucketsFor(true); - - // Create the buckets - the oldest is the larger. - // 1+2+3+4=10 / maxMemory=11. - for (int i = 4; i >= 1; --i) - { - int capacity = factor * i; - ByteBuffer buffer = bufferPool.acquire(capacity, true); - assertThat(buffer.capacity(), equalTo(capacity)); - bufferPool.release(buffer); - } - - // Check state of buckets. - assertThat(bufferPool.getMemory(true), equalTo(10L * factor)); - assertThat(buckets.get(1).size(), equalTo(1)); - assertThat(buckets.get(2).size(), equalTo(1)); - assertThat(buckets.get(3).size(), equalTo(1)); - assertThat(buckets.get(4).size(), equalTo(1)); - - // Create and release a buffer to exceed the max memory. - int capacity = 2 * factor; - ByteBuffer buffer = bufferPool.newByteBuffer(capacity, true); - assertThat(buffer.capacity(), equalTo(capacity)); - bufferPool.release(buffer); - - // Now the oldest buffer should be gone and we have: 1+2x2+3=8 - assertThat(bufferPool.getMemory(true), equalTo(8L * factor)); - assertThat(buckets.get(1).size(), equalTo(1)); - assertThat(buckets.get(2).size(), equalTo(2)); - assertThat(buckets.get(3).size(), equalTo(1)); - - // Create and release a large buffer. - // Max memory is exceeded and buckets 3 and 1 are cleared. - // We will have 2x2+7=11. - capacity = 7 * factor; - buffer = bufferPool.newByteBuffer(capacity, true); - assertThat(buffer.capacity(), equalTo(capacity)); - bufferPool.release(buffer); - - assertThat(bufferPool.getMemory(true), equalTo(11L * factor)); - assertThat(buckets.get(2).size(), equalTo(2)); - assertThat(buckets.get(7).size(), equalTo(1)); - } -} diff --git a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java index 575cf988a10..afebbc2ae0f 100644 --- a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java +++ b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java @@ -612,7 +612,7 @@ public class SocketChannelEndPointTest { private final NormalScenario _normalScenario; private final SslContextFactory _sslCtxFactory = new SslContextFactory.Server(); - private final ByteBufferPool _byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool _bufferPool = new ArrayRetainableByteBufferPool(); public SslScenario(NormalScenario normalScenario) throws Exception { @@ -636,7 +636,7 @@ public class SocketChannelEndPointTest { SSLEngine engine = _sslCtxFactory.newSSLEngine(); engine.setUseClientMode(false); - SslConnection sslConnection = new SslConnection(_byteBufferPool, executor, endpoint, engine); + SslConnection sslConnection = new SslConnection(_bufferPool, executor, endpoint, engine); sslConnection.setRenegotiationAllowed(_sslCtxFactory.isRenegotiationAllowed()); sslConnection.setRenegotiationLimit(_sslCtxFactory.getRenegotiationLimit()); Connection appConnection = _normalScenario.newConnection(channel, sslConnection.getDecryptedEndPoint(), executor, blockAt, writeCount); diff --git a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java index a0e778d948e..6adaff8ac1f 100644 --- a/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java +++ b/jetty-core/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java @@ -59,8 +59,9 @@ public class SslConnectionTest private static final Logger LOG = LoggerFactory.getLogger(SslConnectionTest.class); private static final int TIMEOUT = 1000000; - private static ByteBufferPool __byteBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + // TODO: track leaks + private final RetainableByteBufferPool _bufferPool = new ArrayRetainableByteBufferPool(); private final SslContextFactory _sslCtxFactory = new SslContextFactory.Server(); protected volatile EndPoint _lastEndp; private volatile boolean _testFill = true; @@ -86,7 +87,7 @@ public class SslConnectionTest { SSLEngine engine = _sslCtxFactory.newSSLEngine(); engine.setUseClientMode(false); - SslConnection sslConnection = new SslConnection(__byteBufferPool, getExecutor(), endpoint, engine); + SslConnection sslConnection = new SslConnection(_bufferPool, getExecutor(), endpoint, engine); sslConnection.setRenegotiationAllowed(_sslCtxFactory.isRenegotiationAllowed()); sslConnection.setRenegotiationLimit(_sslCtxFactory.getRenegotiationLimit()); Connection appConnection = new TestConnection(sslConnection.getDecryptedEndPoint()); diff --git a/jetty-core/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyWithDynamicTransportTest.java b/jetty-core/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyWithDynamicTransportTest.java index 707dc98de08..37839c67cfa 100644 --- a/jetty-core/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyWithDynamicTransportTest.java +++ b/jetty-core/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyWithDynamicTransportTest.java @@ -403,7 +403,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap context) { - return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context) + return new DownstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), context) { @Override protected void close(Throwable failure) @@ -417,7 +417,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext) { - return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), connectContext) + return new UpstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), connectContext) { @Override protected void close(Throwable failure) @@ -454,7 +454,7 @@ public class ForwardProxyWithDynamicTransportTest public void onDataAvailable(Stream stream) { Stream.Data data = stream.readData(); - String response = BufferUtil.toString(data.frame().getData(), StandardCharsets.UTF_8); + String response = BufferUtil.toString(data.frame().getByteBuffer(), StandardCharsets.UTF_8); data.release(); if (response.startsWith("HTTP/1.1 200")) responseLatch.countDown(); @@ -485,7 +485,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap context) { - return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context) + return new DownstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), context) { @Override protected void close(Throwable failure) @@ -499,7 +499,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext) { - return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), connectContext) + return new UpstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), connectContext) { @Override protected void close(Throwable failure) @@ -541,7 +541,7 @@ public class ForwardProxyWithDynamicTransportTest public void onDataAvailable(Stream stream) { Stream.Data data = stream.readData(); - String response = BufferUtil.toString(data.frame().getData(), StandardCharsets.UTF_8); + String response = BufferUtil.toString(data.frame().getByteBuffer(), StandardCharsets.UTF_8); data.release(); if (response.startsWith("HTTP/1.1 200")) responseLatch.countDown(); diff --git a/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicConnection.java b/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicConnection.java index c33be6dddc9..a63456cb9c3 100644 --- a/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicConnection.java +++ b/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicConnection.java @@ -55,7 +55,7 @@ public class ClientQuicConnection extends QuicConnection public ClientQuicConnection(ClientConnector connector, EndPoint endPoint, Map context) { - super(connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), endPoint); + super(connector.getExecutor(), connector.getScheduler(), connector.getRetainableByteBufferPool(), endPoint); this.connector = connector; this.context = context; } @@ -97,7 +97,7 @@ public class ClientQuicConnection extends QuicConnection LOG.debug("connecting to {} with protocols {}", remoteAddress, protocols); QuicheConnection quicheConnection = QuicheConnection.connect(quicheConfig, getEndPoint().getLocalAddress(), remoteAddress); - ClientQuicSession session = new ClientQuicSession(getExecutor(), getScheduler(), getByteBufferPool(), quicheConnection, this, remoteAddress, context); + ClientQuicSession session = new ClientQuicSession(getExecutor(), getScheduler(), getRetainableByteBufferPool(), quicheConnection, this, remoteAddress, context); pendingSessions.put(remoteAddress, session); if (LOG.isDebugEnabled()) LOG.debug("created {}", session); diff --git a/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicSession.java b/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicSession.java index 80ca5833cbb..102a6ef75ef 100644 --- a/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicSession.java +++ b/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientQuicSession.java @@ -19,10 +19,10 @@ import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.quic.common.ProtocolSession; import org.eclipse.jetty.quic.common.QuicConnection; @@ -43,9 +43,9 @@ public class ClientQuicSession extends QuicSession private final Map context; private final AtomicReference task = new AtomicReference<>(); - protected ClientQuicSession(Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, QuicheConnection quicheConnection, QuicConnection connection, InetSocketAddress remoteAddress, Map context) + protected ClientQuicSession(Executor executor, Scheduler scheduler, RetainableByteBufferPool retainableByteBufferPool, QuicheConnection quicheConnection, QuicConnection connection, InetSocketAddress remoteAddress, Map context) { - super(executor, scheduler, byteBufferPool, quicheConnection, connection, remoteAddress); + super(executor, scheduler, retainableByteBufferPool, quicheConnection, connection, remoteAddress); this.context = context; } diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java index 94ed2bca211..db6e418c87f 100644 --- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java +++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java @@ -27,10 +27,11 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.DatagramChannelEndPoint; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.internal.QuicErrorCode; import org.eclipse.jetty.quic.quiche.QuicheConnectionId; import org.eclipse.jetty.util.BufferUtil; @@ -59,7 +60,7 @@ public abstract class QuicConnection extends AbstractConnection private final ConcurrentMap sessions = new ConcurrentHashMap<>(); private final AtomicBoolean closed = new AtomicBoolean(); private final Scheduler scheduler; - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool bufferPool; private final AdaptiveExecutionStrategy strategy; private final Flusher flusher = new Flusher(); private final Callback fillableCallback = new FillableCallback(); @@ -67,13 +68,13 @@ public abstract class QuicConnection extends AbstractConnection private boolean useInputDirectByteBuffers = true; private boolean useOutputDirectByteBuffers = true; - protected QuicConnection(Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, EndPoint endPoint) + protected QuicConnection(Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, EndPoint endPoint) { super(endPoint, executor); if (!(endPoint instanceof DatagramChannelEndPoint)) throw new IllegalArgumentException("EndPoint must be a " + DatagramChannelEndPoint.class.getSimpleName()); this.scheduler = scheduler; - this.byteBufferPool = byteBufferPool; + this.bufferPool = bufferPool; this.strategy = new AdaptiveExecutionStrategy(new QuicProducer(), getExecutor()); } @@ -88,9 +89,9 @@ public abstract class QuicConnection extends AbstractConnection return scheduler; } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return byteBufferPool; + return bufferPool; } public int getOutputBufferSize() @@ -223,7 +224,8 @@ public abstract class QuicConnection extends AbstractConnection if (interested) return null; - ByteBuffer cipherBuffer = byteBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); + RetainableByteBuffer buffer = bufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); + ByteBuffer cipherBuffer = buffer.getByteBuffer(); try { while (true) @@ -236,13 +238,13 @@ public abstract class QuicConnection extends AbstractConnection // DatagramChannelEndPoint will only return -1 if input is shut down. if (fill < 0) { - byteBufferPool.release(cipherBuffer); + buffer.release(); getEndPoint().shutdownOutput(); return null; } if (fill == 0) { - byteBufferPool.release(cipherBuffer); + buffer.release(); fillInterested(); return null; } @@ -282,7 +284,7 @@ public abstract class QuicConnection extends AbstractConnection LOG.debug("processing creation task {} on {}", task, session); if (task != null) { - byteBufferPool.release(cipherBuffer); + buffer.release(); return task; } } @@ -297,7 +299,7 @@ public abstract class QuicConnection extends AbstractConnection Runnable task = process(session, remoteAddress, cipherBuffer); if (task != null) { - byteBufferPool.release(cipherBuffer); + buffer.release(); return task; } } @@ -306,7 +308,7 @@ public abstract class QuicConnection extends AbstractConnection { if (LOG.isDebugEnabled()) LOG.debug("receiveAndProcess() failure", x); - byteBufferPool.release(cipherBuffer); + buffer.release(); onFailure(x); return null; } @@ -327,7 +329,6 @@ public abstract class QuicConnection extends AbstractConnection { if (LOG.isDebugEnabled()) LOG.debug("process failure for {}", session, x); - byteBufferPool.release(cipherBuffer); session.onFailure(x); return null; } diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java index aacabae5c45..14bb3634c42 100644 --- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java +++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java @@ -30,10 +30,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.CyclicTimeout; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.internal.QuicErrorCode; import org.eclipse.jetty.quic.quiche.QuicheConnection; import org.eclipse.jetty.quic.quiche.QuicheConnectionId; @@ -65,7 +66,7 @@ public abstract class QuicSession extends ContainerLifeCycle private final ConcurrentMap endPoints = new ConcurrentHashMap<>(); private final Executor executor; private final Scheduler scheduler; - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final QuicheConnection quicheConnection; private final QuicConnection connection; private final Flusher flusher; @@ -74,11 +75,11 @@ public abstract class QuicSession extends ContainerLifeCycle private QuicheConnectionId quicheConnectionId; private long idleTimeout; - protected QuicSession(Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress) + protected QuicSession(Executor executor, Scheduler scheduler, RetainableByteBufferPool retainableByteBufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress) { this.executor = executor; this.scheduler = scheduler; - this.byteBufferPool = byteBufferPool; + this.retainableByteBufferPool = retainableByteBufferPool; this.quicheConnection = quicheConnection; this.connection = connection; this.flusher = new Flusher(scheduler); @@ -149,9 +150,9 @@ public abstract class QuicSession extends ContainerLifeCycle return scheduler; } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return byteBufferPool; + return retainableByteBufferPool; } public ProtocolSession getProtocolSession() @@ -435,7 +436,7 @@ public abstract class QuicSession extends ContainerLifeCycle private class Flusher extends IteratingCallback { private final CyclicTimeout timeout; - private ByteBuffer cipherBuffer; + private RetainableByteBuffer cipherBuffer; public Flusher(Scheduler scheduler) { @@ -465,9 +466,10 @@ public abstract class QuicSession extends ContainerLifeCycle @Override protected Action process() throws IOException { - cipherBuffer = byteBufferPool.acquire(connection.getOutputBufferSize(), connection.isUseOutputDirectByteBuffers()); - int pos = BufferUtil.flipToFill(cipherBuffer); - int drained = quicheConnection.drainCipherBytes(cipherBuffer); + cipherBuffer = retainableByteBufferPool.acquire(connection.getOutputBufferSize(), connection.isUseOutputDirectByteBuffers()); + ByteBuffer cipherByteBuffer = cipherBuffer.getByteBuffer(); + int pos = BufferUtil.flipToFill(cipherByteBuffer); + int drained = quicheConnection.drainCipherBytes(cipherByteBuffer); if (LOG.isDebugEnabled()) LOG.debug("drained {} byte(s) of cipher bytes from {}", drained, QuicSession.this); long nextTimeoutInMs = quicheConnection.nextTimeout(); @@ -484,13 +486,13 @@ public abstract class QuicSession extends ContainerLifeCycle if (LOG.isDebugEnabled()) LOG.debug("connection draining={} closed={}, action={} on {}", quicheConnection.isDraining(), connectionClosed, action, QuicSession.this); if (action == Action.IDLE) - byteBufferPool.release(cipherBuffer); + cipherBuffer.release(); return action; } - BufferUtil.flipToFlush(cipherBuffer, pos); + BufferUtil.flipToFlush(cipherByteBuffer, pos); if (LOG.isDebugEnabled()) LOG.debug("writing cipher bytes for {} on {}", remoteAddress, QuicSession.this); - connection.write(this, remoteAddress, cipherBuffer); + connection.write(this, remoteAddress, cipherByteBuffer); return Action.SCHEDULED; } @@ -499,7 +501,7 @@ public abstract class QuicSession extends ContainerLifeCycle { if (LOG.isDebugEnabled()) LOG.debug("written cipher bytes on {}", QuicSession.this); - byteBufferPool.release(cipherBuffer); + cipherBuffer.release(); super.succeeded(); } @@ -514,7 +516,7 @@ public abstract class QuicSession extends ContainerLifeCycle { if (LOG.isDebugEnabled()) LOG.debug("connection closed {}", QuicSession.this); - byteBufferPool.release(cipherBuffer); + cipherBuffer.release(); finishOutwardClose(new ClosedChannelException()); } @@ -523,7 +525,7 @@ public abstract class QuicSession extends ContainerLifeCycle { if (LOG.isDebugEnabled()) LOG.debug("failed to write cipher bytes, closing session on {}", QuicSession.this, failure); - byteBufferPool.release(cipherBuffer); + cipherBuffer.release(); finishOutwardClose(failure); } } diff --git a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java index 0d10efa4afa..5e73e81d46b 100644 --- a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java +++ b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java @@ -26,11 +26,11 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.DatagramChannelEndPoint; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ManagedSelector; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.quic.common.QuicConfiguration; import org.eclipse.jetty.quic.common.QuicSession; @@ -74,7 +74,7 @@ public class QuicServerConnector extends AbstractNetworkConnector this(server, null, null, null, sslContextFactory, factories); } - public QuicServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, SslContextFactory.Server sslContextFactory, ConnectionFactory... factories) + public QuicServerConnector(Server server, Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, SslContextFactory.Server sslContextFactory, ConnectionFactory... factories) { super(server, executor, scheduler, bufferPool, 0, factories); this.selectorManager = new ServerDatagramSelectorManager(getExecutor(), getScheduler(), 1); diff --git a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java index 6b2e8ac8e83..58d5713d104 100644 --- a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java +++ b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java @@ -19,9 +19,10 @@ import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.Iterator; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.CyclicTimeouts; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.quic.common.QuicConnection; import org.eclipse.jetty.quic.common.QuicSession; import org.eclipse.jetty.quic.quiche.QuicheConnection; @@ -45,7 +46,7 @@ public class ServerQuicConnection extends QuicConnection protected ServerQuicConnection(QuicServerConnector connector, EndPoint endPoint) { - super(connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), endPoint); + super(connector.getExecutor(), connector.getScheduler(), connector.getRetainableByteBufferPool(), endPoint); this.connector = connector; this.sessionTimeouts = new SessionTimeouts(connector.getScheduler()); } @@ -60,31 +61,32 @@ public class ServerQuicConnection extends QuicConnection @Override protected QuicSession createSession(SocketAddress remoteAddress, ByteBuffer cipherBuffer) throws IOException { - ByteBufferPool byteBufferPool = getByteBufferPool(); + RetainableByteBufferPool bufferPool = getRetainableByteBufferPool(); // TODO make the token validator configurable QuicheConnection quicheConnection = QuicheConnection.tryAccept(connector.newQuicheConfig(), new SimpleTokenValidator((InetSocketAddress)remoteAddress), cipherBuffer, getEndPoint().getLocalAddress(), remoteAddress); if (quicheConnection == null) { - ByteBuffer negotiationBuffer = byteBufferPool.acquire(getOutputBufferSize(), true); - int pos = BufferUtil.flipToFill(negotiationBuffer); + RetainableByteBuffer negotiationBuffer = bufferPool.acquire(getOutputBufferSize(), true); + ByteBuffer byteBuffer = negotiationBuffer.getByteBuffer(); + int pos = BufferUtil.flipToFill(byteBuffer); // TODO make the token minter configurable - if (!QuicheConnection.negotiate(new SimpleTokenMinter((InetSocketAddress)remoteAddress), cipherBuffer, negotiationBuffer)) + if (!QuicheConnection.negotiate(new SimpleTokenMinter((InetSocketAddress)remoteAddress), cipherBuffer, byteBuffer)) { if (LOG.isDebugEnabled()) LOG.debug("QUIC connection negotiation failed, dropping packet"); - byteBufferPool.release(negotiationBuffer); + negotiationBuffer.release(); return null; } - BufferUtil.flipToFlush(negotiationBuffer, pos); + BufferUtil.flipToFlush(byteBuffer, pos); - write(Callback.from(() -> byteBufferPool.release(negotiationBuffer)), remoteAddress, negotiationBuffer); + write(Callback.from(negotiationBuffer::release), remoteAddress, byteBuffer); if (LOG.isDebugEnabled()) LOG.debug("QUIC connection negotiation packet sent"); return null; } else { - QuicSession session = new ServerQuicSession(getExecutor(), getScheduler(), byteBufferPool, quicheConnection, this, remoteAddress, connector); + QuicSession session = new ServerQuicSession(getExecutor(), getScheduler(), bufferPool, quicheConnection, this, remoteAddress, connector); // Send the response packet(s) that tryAccept() generated. session.flush(); return session; diff --git a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java index 24c1a6e1dca..55cf5a1645d 100644 --- a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java +++ b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java @@ -20,9 +20,9 @@ import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.CyclicTimeouts; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.quic.common.ProtocolSession; import org.eclipse.jetty.quic.common.QuicConnection; @@ -46,9 +46,9 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp private final Connector connector; private long expireNanoTime; - protected ServerQuicSession(Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress, Connector connector) + protected ServerQuicSession(Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress, Connector connector) { - super(executor, scheduler, byteBufferPool, quicheConnection, connection, remoteAddress); + super(executor, scheduler, bufferPool, quicheConnection, connection, remoteAddress); this.connector = connector; } diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool-logarithmic.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool-quadratic.xml similarity index 62% rename from jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool-logarithmic.xml rename to jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool-quadratic.xml index 39a1e58911c..c35799230da 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool-logarithmic.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool-quadratic.xml @@ -1,13 +1,11 @@ - + - + - - diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool.xml index f1d787a6201..dd8b4cd3bce 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-bytebufferpool.xml @@ -1,14 +1,12 @@ - + - + - - diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty.xml b/jetty-core/jetty-server/src/main/config/etc/jetty.xml index c2e286ad0d0..2a3c3c66dce 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty.xml @@ -24,7 +24,7 @@ - + diff --git a/jetty-core/jetty-server/src/main/config/modules/bytebufferpool-logarithmic.mod b/jetty-core/jetty-server/src/main/config/modules/bytebufferpool-quadratic.mod similarity index 86% rename from jetty-core/jetty-server/src/main/config/modules/bytebufferpool-logarithmic.mod rename to jetty-core/jetty-server/src/main/config/modules/bytebufferpool-quadratic.mod index a54485b06f2..ee9d79476c8 100644 --- a/jetty-core/jetty-server/src/main/config/modules/bytebufferpool-logarithmic.mod +++ b/jetty-core/jetty-server/src/main/config/modules/bytebufferpool-quadratic.mod @@ -1,7 +1,8 @@ # DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html [description] -Configures the ByteBufferPool used by ServerConnectors whose bucket sizes increase exponentially instead of linearly. +Configures the RetainableByteBufferPool used by ServerConnectors. +The bucket sizes increase quadratically instead of linearly. [tags] bytebufferpool @@ -10,7 +11,7 @@ bytebufferpool bytebufferpool [xml] -etc/jetty-bytebufferpool-logarithmic.xml +etc/jetty-bytebufferpool-quadratic.xml [ini-template] ### Server ByteBufferPool Configuration diff --git a/jetty-core/jetty-server/src/main/config/modules/bytebufferpool.mod b/jetty-core/jetty-server/src/main/config/modules/bytebufferpool.mod index 35a347b7744..25db6dc6cc0 100644 --- a/jetty-core/jetty-server/src/main/config/modules/bytebufferpool.mod +++ b/jetty-core/jetty-server/src/main/config/modules/bytebufferpool.mod @@ -1,6 +1,7 @@ [description] -Configures the ByteBufferPool used by ServerConnectors. -Use module "bytebufferpool-logarithmic" for a pool may hold less granulated sized buffers. +Configures the RetainableByteBufferPool used by ServerConnectors. +The bucket sizes increase linearly. +Use module "bytebufferpool-quadratic" for a pool that holds more coarse sized buffers. [depends] logging diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 1fb5f5fb410..75e2fbe7751 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.net.Socket; -import java.nio.ByteBuffer; import java.nio.channels.ClosedByInterruptException; import java.util.ArrayList; import java.util.Collection; @@ -24,6 +23,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -31,10 +31,10 @@ import java.util.concurrent.Executor; import java.util.concurrent.locks.Condition; import java.util.stream.Collectors; -import org.eclipse.jetty.io.ArrayByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.LogarithmicArrayByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.server.internal.HttpConnection; import org.eclipse.jetty.util.ProcessorUtils; @@ -65,8 +65,9 @@ import org.slf4j.LoggerFactory; * to the connections to time such things as asynchronous request timeouts. The default is to use a new * {@link ScheduledExecutorScheduler} instance. * - *
  • The {@link ByteBufferPool} service is made available to all connections to be used to acquire and release - * {@link ByteBuffer} instances from a pool. The default is to use a new {@link ArrayByteBufferPool} instance. + *
  • The {@link RetainableByteBufferPool} service is made available to all connections to be used to acquire and release + * {@link RetainableByteBuffer} instances from a pool. The default is to use a new {@link ArrayRetainableByteBufferPool} + * instance. *
  • * * These services are managed as aggregate beans by the {@link ContainerLifeCycle} super class and @@ -144,7 +145,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co private final Server _server; private final Executor _executor; private final Scheduler _scheduler; - private final ByteBufferPool _byteBufferPool; + private final RetainableByteBufferPool _retainableByteBufferPool; private final Thread[] _acceptors; private final Set _endpoints = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set _immutableEndPoints = Collections.unmodifiableSet(_endpoints); @@ -160,43 +161,32 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co private ThreadPoolBudget.Lease _lease; /** - * @param server The server this connector will be added to. Must not be null. - * @param executor An executor for this connector or null to use the servers executor - * @param scheduler A scheduler for this connector or null to either a {@link Scheduler} set as a server bean or if none set, then a new {@link ScheduledExecutorScheduler} instance. - * @param pool A buffer pool for this connector or null to either a {@link ByteBufferPool} set as a server bean or none set, the new {@link ArrayByteBufferPool} instance. - * @param acceptors the number of acceptor threads to use, or -1 for a default value. If 0, then no acceptor threads will be launched and some other mechanism will need to be used to accept new connections. - * @param factories The Connection Factories to use. + * @param server The {@link Server} this connector will be added to, must not be null + * @param executor An {@link Executor} for this connector or null to use the Server's Executor + * @param scheduler A {@link Scheduler} for this connector or null to use the Server's Scheduler + * @param bufferPool A {@link RetainableByteBufferPool} for this connector or null to use the Server's RetainableByteBufferPool + * @param acceptors the number of acceptor threads to use, or -1 for a default value. + * If 0, then no acceptor threads will be launched and some other mechanism will need to be used to accept new connections. + * @param factories The {@link ConnectionFactory} instances to use */ public AbstractConnector( Server server, Executor executor, Scheduler scheduler, - ByteBufferPool pool, + RetainableByteBufferPool bufferPool, int acceptors, ConnectionFactory... factories) { - _server = server; - _executor = executor != null ? executor : _server.getThreadPool(); - addBean(_executor); - if (executor == null) - unmanage(_executor); // inherited from server - if (scheduler == null) - scheduler = _server.getBean(Scheduler.class); - _scheduler = scheduler != null ? scheduler : new ScheduledExecutorScheduler(String.format("Connector-Scheduler-%x", hashCode()), false); - addBean(_scheduler); + _server = Objects.requireNonNull(server); - if (pool == null) - { - // Look for (and cache) a common pool on the server - pool = server.getBean(ByteBufferPool.class); - if (pool == null) - { - pool = new LogarithmicArrayByteBufferPool(); - server.addBean(pool, true); - } - } - _byteBufferPool = pool; - addBean(pool.asRetainableByteBufferPool()); + _executor = executor != null ? executor : _server.getThreadPool(); + addBean(_executor, executor != null); + + _scheduler = scheduler != null ? scheduler : _server.getScheduler(); + addBean(_scheduler, scheduler != null); + + _retainableByteBufferPool = bufferPool != null ? bufferPool : server.getRetainableByteBufferPool(); + addBean(_retainableByteBufferPool, bufferPool != null); for (ConnectionFactory factory : factories) { @@ -224,9 +214,9 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return _byteBufferPool; + return _retainableByteBufferPool; } @Override diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java index 679262ec1d1..3435ac7e109 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java @@ -17,7 +17,7 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.thread.Scheduler; @@ -33,9 +33,9 @@ public abstract class AbstractNetworkConnector extends AbstractConnector impleme private volatile String _host; private volatile int _port = 0; - public AbstractNetworkConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories) + public AbstractNetworkConnector(Server server, Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, int acceptors, ConnectionFactory... factories) { - super(server, executor, scheduler, pool, acceptors, factories); + super(server, executor, scheduler, bufferPool, acceptors, factories); } public void setHost(String host) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Components.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Components.java index e3ef0ec52d6..b605f45c533 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Components.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Components.java @@ -13,7 +13,7 @@ package org.eclipse.jetty.server; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.ThreadPool; @@ -23,7 +23,7 @@ import org.eclipse.jetty.util.thread.ThreadPool; */ public interface Components { - ByteBufferPool getByteBufferPool(); + RetainableByteBufferPool getRetainableByteBufferPool(); Scheduler getScheduler(); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java index 8b7b68d42e5..1fe6c1c44a1 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java @@ -38,7 +38,7 @@ public interface ConnectionMetaData extends Attributes Connection getConnection(); // TODO should this be only here or only on HttpChannel, should not be on both. - // Currently mostly used to get stuff like ByteBufferPool and Scheduler - maybe provide those directly? + // Currently mostly used to get stuff like RetainableByteBufferPool and Scheduler - maybe provide those directly? Connector getConnector(); boolean isPersistent(); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java index 4f6e72ba719..59721ff09f7 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java @@ -17,8 +17,8 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -51,9 +51,9 @@ public interface Connector extends LifeCycle, Container, Graceful public Scheduler getScheduler(); /** - * @return the {@link ByteBufferPool} to acquire buffers from and release buffers to + * @return the {@link RetainableByteBufferPool} to acquire buffers from and release buffers to */ - public ByteBufferPool getByteBufferPool(); + public RetainableByteBufferPool getRetainableByteBufferPool(); /** * @param nextProtocol the next protocol diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/DetectorConnectionFactory.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/DetectorConnectionFactory.java index f2528163b25..044312eae47 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/DetectorConnectionFactory.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/DetectorConnectionFactory.java @@ -22,6 +22,7 @@ import java.util.stream.Collectors; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.BufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -138,13 +139,13 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme private class DetectorConnection extends AbstractConnection implements Connection.UpgradeFrom, Connection.UpgradeTo { private final Connector _connector; - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private DetectorConnection(EndPoint endp, Connector connector) { super(endp, connector.getExecutor()); _connector = connector; - _buffer = connector.getByteBufferPool().acquire(getInputBufferSize(), true); + _buffer = connector.getRetainableByteBufferPool().acquire(getInputBufferSize(), true); } @Override @@ -152,7 +153,7 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme { if (LOG.isDebugEnabled()) LOG.debug("Detector {} copying unconsumed buffer {}", getProtocol(), BufferUtil.toDetailString(buffer)); - BufferUtil.append(_buffer, buffer); + BufferUtil.append(_buffer.getByteBuffer(), buffer); } @Override @@ -161,9 +162,9 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme if (_buffer.hasRemaining()) { ByteBuffer unconsumed = ByteBuffer.allocateDirect(_buffer.remaining()); - unconsumed.put(_buffer); + unconsumed.put(_buffer.getByteBuffer()); unconsumed.flip(); - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); return unconsumed; } return null; @@ -182,15 +183,16 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme { try { - while (BufferUtil.space(_buffer) > 0) + ByteBuffer byteBuffer = _buffer.getByteBuffer(); + while (BufferUtil.space(byteBuffer) > 0) { // Read data - int fill = getEndPoint().fill(_buffer); + int fill = getEndPoint().fill(byteBuffer); if (LOG.isDebugEnabled()) LOG.debug("Detector {} filled buffer with {} bytes", getProtocol(), fill); if (fill < 0) { - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); getEndPoint().shutdownOutput(); return; } @@ -200,6 +202,7 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme return; } + // TODO: release the buffer before returning. if (detectAndUpgrade()) return; } @@ -220,7 +223,7 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme */ private boolean detectAndUpgrade() { - if (BufferUtil.isEmpty(_buffer)) + if (!_buffer.hasRemaining()) { if (LOG.isDebugEnabled()) LOG.debug("Detector {} skipping detection on an empty buffer", getProtocol()); @@ -232,9 +235,9 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme boolean notRecognized = true; for (Detecting detectingConnectionFactory : _detectingConnectionFactories) { - Detection detection = detectingConnectionFactory.detect(_buffer); + Detection detection = detectingConnectionFactory.detect(_buffer.getByteBuffer()); if (LOG.isDebugEnabled()) - LOG.debug("Detector {} performed detection from {} with {} which returned {}", getProtocol(), BufferUtil.toDetailString(_buffer), detectingConnectionFactory, detection); + LOG.debug("Detector {} performed detection from {} with {} which returned {}", getProtocol(), _buffer, detectingConnectionFactory, detection); if (detection == Detection.RECOGNIZED) { try @@ -275,7 +278,7 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme // No DetectingConnectionFactory recognized those bytes -> call unsuccessful detection callback. if (LOG.isDebugEnabled()) LOG.debug("Detector {} failed to detect a known protocol, falling back to nextProtocol()", getProtocol()); - nextProtocol(_connector, getEndPoint(), _buffer); + nextProtocol(_connector, getEndPoint(), _buffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("Detector {} call to nextProtocol() succeeded, assuming upgrade performed", getProtocol()); return true; @@ -288,7 +291,7 @@ public class DetectorConnectionFactory extends AbstractConnectionFactory impleme { if (LOG.isDebugEnabled()) LOG.debug("Detector {} releasing buffer and closing", getProtocol()); - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); close(); } } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java index 6b1795eebfd..61733866f4a 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java @@ -26,8 +26,8 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteArrayEndPoint; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.ByteArrayOutputStream2; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -46,9 +46,9 @@ public class LocalConnector extends AbstractConnector { private final BlockingQueue _connects = new LinkedBlockingQueue<>(); - public LocalConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories) + public LocalConnector(Server server, Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, int acceptors, ConnectionFactory... factories) { - super(server, executor, scheduler, pool, acceptors, factories); + super(server, executor, scheduler, bufferPool, acceptors, factories); setIdleTimeout(30000); } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java index 07c7ab427d7..b4db07f82ed 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java @@ -322,7 +322,7 @@ public class LowResourceMonitor extends ContainerLifeCycle @Override protected void doStart() throws Exception { - _scheduler = _server.getBean(Scheduler.class); + _scheduler = _server.getScheduler(); if (_scheduler == null) { diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java index d0fdb50c5a5..fd34d134e55 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java @@ -17,10 +17,10 @@ import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.concurrent.Executor; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ManagedSelector; import org.eclipse.jetty.io.NetworkTrafficListener; import org.eclipse.jetty.io.NetworkTrafficSocketChannelEndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.SocketChannelEndPoint; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.Scheduler; @@ -49,9 +49,9 @@ public class NetworkTrafficServerConnector extends ServerConnector super(server, connectionFactory); } - public NetworkTrafficServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories) + public NetworkTrafficServerConnector(Server server, Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, int acceptors, int selectors, ConnectionFactory... factories) { - super(server, executor, scheduler, pool, acceptors, selectors, factories); + super(server, executor, scheduler, bufferPool, acceptors, selectors, factories); } public NetworkTrafficServerConnector(Server server, SslContextFactory.Server sslContextFactory) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java index cb0a85b4cb6..20d8d8439ed 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java @@ -29,10 +29,11 @@ import java.util.Map; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; -import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -146,7 +147,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory private final Connector _connector; private final ConnectionFactory _next; - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private final StringBuilder _builder = new StringBuilder(); private final String[] _fields = new String[6]; private int _index; @@ -157,7 +158,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory super(endp, connector.getExecutor()); _connector = connector; _next = next; - _buffer = _connector.getByteBufferPool().acquire(getInputBufferSize(), true); + _buffer = _connector.getRetainableByteBufferPool().acquire(getInputBufferSize(), true); } @Override @@ -170,12 +171,12 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory while (_index < LF_INDEX) { // Read data - int fill = getEndPoint().fill(_buffer); + int fill = getEndPoint().fill(_buffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("Proxy v1 filled buffer with {} bytes", fill); if (fill < 0) { - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); getEndPoint().shutdownOutput(); return; } @@ -239,9 +240,9 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory if (_buffer.hasRemaining()) { ByteBuffer unconsumed = ByteBuffer.allocateDirect(_buffer.remaining()); - unconsumed.put(_buffer); + unconsumed.put(_buffer.getByteBuffer()); unconsumed.flip(); - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); return unconsumed; } return null; @@ -252,7 +253,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory { if (LOG.isDebugEnabled()) LOG.debug("Proxy v1 copying unconsumed buffer {}", BufferUtil.toDetailString(buffer)); - BufferUtil.append(_buffer, buffer); + BufferUtil.append(_buffer.getByteBuffer(), buffer); } /** @@ -261,13 +262,13 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory private boolean parse() throws IOException { if (LOG.isDebugEnabled()) - LOG.debug("Proxy v1 parsing {}", BufferUtil.toDetailString(_buffer)); + LOG.debug("Proxy v1 parsing {}", _buffer); _length += _buffer.remaining(); // Parse fields while (_buffer.hasRemaining()) { - byte b = _buffer.get(); + byte b = _buffer.getByteBuffer().get(); if (_index < CR_INDEX) { if (b == ' ' || b == '\r') @@ -310,7 +311,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory { if (LOG.isDebugEnabled()) LOG.debug("Proxy v1 releasing buffer and closing"); - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); close(); } @@ -439,7 +440,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory private final Connector _connector; private final ConnectionFactory _next; - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private boolean _local; private Family _family; private int _length; @@ -450,7 +451,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory super(endp, connector.getExecutor()); _connector = connector; _next = next; - _buffer = _connector.getByteBufferPool().acquire(getInputBufferSize(), true); + _buffer = _connector.getRetainableByteBufferPool().acquire(getInputBufferSize(), true); } @Override @@ -458,7 +459,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory { if (LOG.isDebugEnabled()) LOG.debug("Proxy v2 copying unconsumed buffer {}", BufferUtil.toDetailString(buffer)); - BufferUtil.append(_buffer, buffer); + BufferUtil.append(_buffer.getByteBuffer(), buffer); } @Override @@ -501,12 +502,12 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory while (!_headerParsed) { // Read data - int fill = getEndPoint().fill(_buffer); + int fill = getEndPoint().fill(_buffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("Proxy v2 filled buffer with {} bytes", fill); if (fill < 0) { - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); getEndPoint().shutdownOutput(); return; } @@ -520,17 +521,17 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory } if (LOG.isDebugEnabled()) - LOG.debug("Proxy v2 onFillable header parsed, length = {}, buffer = {}", _length, BufferUtil.toDetailString(_buffer)); + LOG.debug("Proxy v2 onFillable header parsed, length = {}, buffer = {}", _length, _buffer); while (_buffer.remaining() < _length) { // Read data - int fill = getEndPoint().fill(_buffer); + int fill = getEndPoint().fill(_buffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("Proxy v2 filled buffer with {} bytes", fill); if (fill < 0) { - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); getEndPoint().shutdownOutput(); return; } @@ -558,9 +559,9 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory if (_buffer.hasRemaining()) { ByteBuffer unconsumed = ByteBuffer.allocateDirect(_buffer.remaining()); - unconsumed.put(_buffer); + unconsumed.put(_buffer.getByteBuffer()); unconsumed.flip(); - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); return unconsumed; } return null; @@ -570,17 +571,18 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory { int nonProxyRemaining = _buffer.remaining() - _length; if (LOG.isDebugEnabled()) - LOG.debug("Proxy v2 parsing body, length = {}, buffer = {}", _length, BufferUtil.toHexSummary(_buffer)); + LOG.debug("Proxy v2 parsing body, length = {}, buffer = {}", _length, _buffer); if (LOG.isDebugEnabled()) - LOG.debug("Proxy v2 body {} from {} for {}", _next, BufferUtil.toHexSummary(_buffer), this); + LOG.debug("Proxy v2 body {} from {} for {}", _next, _buffer, this); // Do we need to wrap the endpoint? + ByteBuffer byteBuffer = _buffer.getByteBuffer(); ProxyEndPoint proxyEndPoint; EndPoint endPoint = getEndPoint(); if (_local) { - _buffer.position(_buffer.position() + _length); + byteBuffer.position(byteBuffer.position() + _length); proxyEndPoint = new ProxyEndPoint(endPoint, endPoint.getLocalSocketAddress(), endPoint.getRemoteSocketAddress()); } else @@ -592,12 +594,12 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory case INET: { byte[] addr = new byte[4]; - _buffer.get(addr); + byteBuffer.get(addr); InetAddress srcAddr = Inet4Address.getByAddress(addr); - _buffer.get(addr); + byteBuffer.get(addr); InetAddress dstAddr = Inet4Address.getByAddress(addr); - int srcPort = _buffer.getChar(); - int dstPort = _buffer.getChar(); + int srcPort = byteBuffer.getChar(); + int dstPort = byteBuffer.getChar(); local = new InetSocketAddress(dstAddr, dstPort); remote = new InetSocketAddress(srcAddr, srcPort); break; @@ -605,12 +607,12 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory case INET6: { byte[] addr = new byte[16]; - _buffer.get(addr); + byteBuffer.get(addr); InetAddress srcAddr = Inet6Address.getByAddress(addr); - _buffer.get(addr); + byteBuffer.get(addr); InetAddress dstAddr = Inet6Address.getByAddress(addr); - int srcPort = _buffer.getChar(); - int dstPort = _buffer.getChar(); + int srcPort = byteBuffer.getChar(); + int dstPort = byteBuffer.getChar(); local = new InetSocketAddress(dstAddr, dstPort); remote = new InetSocketAddress(srcAddr, srcPort); break; @@ -618,9 +620,9 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory case UNIX: { byte[] addr = new byte[108]; - _buffer.get(addr); + byteBuffer.get(addr); String src = UnixDomain.toPath(addr); - _buffer.get(addr); + byteBuffer.get(addr); String dst = UnixDomain.toPath(addr); local = UnixDomain.newSocketAddress(dst); remote = UnixDomain.newSocketAddress(src); @@ -634,15 +636,15 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory proxyEndPoint = new ProxyEndPoint(endPoint, local, remote); // Any additional info? - while (_buffer.remaining() > nonProxyRemaining) + while (byteBuffer.remaining() > nonProxyRemaining) { - int type = 0xff & _buffer.get(); - int length = _buffer.getChar(); + int type = 0xff & byteBuffer.get(); + int length = byteBuffer.getChar(); byte[] value = new byte[length]; - _buffer.get(value); + byteBuffer.get(value); if (LOG.isDebugEnabled()) - LOG.debug(String.format("Proxy v2 T=%x L=%d V=%s for %s", type, length, TypeUtil.toHexString(value), this)); + LOG.debug(String.format("Proxy v2 T=%x L=%d V=%s for %s", type, length, StringUtil.toHexString(value), this)); // PP2_TYPE_NOOP is only used for byte alignment, skip them. if (type != ProxyEndPoint.PP2_TYPE_NOOP) @@ -683,12 +685,14 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory private void parseHeader() throws IOException { if (LOG.isDebugEnabled()) - LOG.debug("Proxy v2 parsing fixed length packet part, buffer = {}", BufferUtil.toDetailString(_buffer)); + LOG.debug("Proxy v2 parsing fixed length packet part, buffer = {}", _buffer); if (_buffer.remaining() < HEADER_LENGTH) return; if (LOG.isDebugEnabled()) - LOG.debug("Proxy v2 header {} for {}", BufferUtil.toHexSummary(_buffer), this); + LOG.debug("Proxy v2 header {} for {}", _buffer, this); + + ByteBuffer byteBuffer = _buffer.getByteBuffer(); // struct proxy_hdr_v2 { // uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */ @@ -698,16 +702,16 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory // }; for (byte signatureByte : SIGNATURE) { - if (_buffer.get() != signatureByte) + if (byteBuffer.get() != signatureByte) throw new IOException("Proxy v2 bad PROXY signature"); } - int versionAndCommand = 0xFF & _buffer.get(); + int versionAndCommand = 0xFF & byteBuffer.get(); if ((versionAndCommand & 0xF0) != 0x20) throw new IOException("Proxy v2 bad PROXY version"); _local = (versionAndCommand & 0xF) == 0x00; - int transportAndFamily = 0xFF & _buffer.get(); + int transportAndFamily = 0xFF & byteBuffer.get(); switch (transportAndFamily >> 4) { case 0: @@ -742,7 +746,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory throw new IOException("Proxy v2 bad PROXY family"); } - _length = _buffer.getChar(); + _length = byteBuffer.getChar(); if (!_local && (_family == Family.UNSPEC || transport != Transport.STREAM)) throw new IOException(String.format("Proxy v2 unsupported PROXY mode 0x%x,0x%x", versionAndCommand, transportAndFamily)); @@ -757,7 +761,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory private void releaseAndClose() { - _connector.getByteBufferPool().release(_buffer); + _buffer.release(); close(); } } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java index d633b9a446c..10b6cc240f7 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java @@ -40,8 +40,9 @@ import org.eclipse.jetty.http.QuotedCSV; import org.eclipse.jetty.http.QuotedQualityCSV; import org.eclipse.jetty.http.content.HttpContent; import org.eclipse.jetty.http.content.PreCompressedHttpContent; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -855,18 +856,17 @@ public class ResourceService private final ReadableByteChannel source; private final Content.Sink sink; private final Callback callback; - private final ByteBuffer byteBuffer; - private final ByteBufferPool byteBufferPool; + private final RetainableByteBuffer buffer; public ContentWriterIteratingCallback(HttpContent content, Response target, Callback callback) throws IOException { - this.byteBufferPool = target.getRequest().getComponents().getByteBufferPool(); this.source = content.getResource().newReadableByteChannel(); this.sink = target; this.callback = callback; + RetainableByteBufferPool bufferPool = target.getRequest().getComponents().getRetainableByteBufferPool(); int outputBufferSize = target.getRequest().getConnectionMetaData().getHttpConfiguration().getOutputBufferSize(); boolean useOutputDirectByteBuffers = target.getRequest().getConnectionMetaData().getHttpConfiguration().isUseOutputDirectByteBuffers(); - this.byteBuffer = byteBufferPool.acquire(outputBufferSize, useOutputDirectByteBuffers); + this.buffer = bufferPool.acquire(outputBufferSize, useOutputDirectByteBuffers); } @Override @@ -875,6 +875,7 @@ public class ResourceService if (!source.isOpen()) return Action.SUCCEEDED; + ByteBuffer byteBuffer = buffer.getByteBuffer(); BufferUtil.clearToFill(byteBuffer); int read = source.read(byteBuffer); if (read == -1) @@ -891,14 +892,14 @@ public class ResourceService @Override protected void onCompleteSuccess() { - byteBufferPool.release(byteBuffer); + buffer.release(); callback.succeeded(); } @Override protected void onCompleteFailure(Throwable x) { - byteBufferPool.release(byteBuffer); + buffer.release(); callback.failed(x); } } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 5c17a91ddf6..89b5b0714d7 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -35,7 +35,9 @@ import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ErrorProcessor; import org.eclipse.jetty.util.Attributes; @@ -59,6 +61,8 @@ import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.Invocable; import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; +import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.ShutdownThread; import org.eclipse.jetty.util.thread.ThreadPool; import org.slf4j.Logger; @@ -71,6 +75,8 @@ public class Server extends Handler.Wrapper implements Attributes private final AttributeContainerMap _attributes = new AttributeContainerMap(); private final ThreadPool _threadPool; + private final Scheduler _scheduler; + private final RetainableByteBufferPool _bufferPool; private final List _connectors = new CopyOnWriteArrayList<>(); private final Context _serverContext = new ServerContext(); private final AutoLock _dateLock = new AutoLock(); @@ -105,7 +111,7 @@ public class Server extends Handler.Wrapper implements Attributes this((ThreadPool)null); ServerConnector connector = new ServerConnector(this); connector.setPort(port); - setConnectors(new Connector[]{connector}); + addConnector(connector); addBean(_attributes); } @@ -122,13 +128,22 @@ public class Server extends Handler.Wrapper implements Attributes ServerConnector connector = new ServerConnector(this); connector.setHost(addr.getHostName()); connector.setPort(addr.getPort()); - setConnectors(new Connector[]{connector}); + addConnector(connector); } - public Server(@Name("threadpool") ThreadPool pool) + public Server(@Name("threadPool") ThreadPool pool) { - _threadPool = pool != null ? pool : new QueuedThreadPool(); + this(pool, null, null); + } + + public Server(@Name("threadPool") ThreadPool threadPool, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") RetainableByteBufferPool bufferPool) + { + _threadPool = threadPool != null ? threadPool : new QueuedThreadPool(); addBean(_threadPool); + _scheduler = scheduler != null ? scheduler : new ScheduledExecutorScheduler(); + addBean(_scheduler); + _bufferPool = bufferPool != null ? bufferPool : new ArrayRetainableByteBufferPool(); + addBean(_bufferPool); setServer(this); addBean(FileSystemPool.INSTANCE, false); } @@ -400,6 +415,16 @@ public class Server extends Handler.Wrapper implements Attributes return _threadPool; } + public Scheduler getScheduler() + { + return _scheduler; + } + + public RetainableByteBufferPool getRetainableByteBufferPool() + { + return _bufferPool; + } + /** * @return true if {@link #dumpStdErr()} is called after starting */ diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java index 8d688a74953..e7998bec1ed 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java @@ -30,10 +30,10 @@ import java.util.EventListener; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ManagedSelector; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.io.SocketChannelEndPoint; import org.eclipse.jetty.util.IO; @@ -204,7 +204,7 @@ public class ServerConnector extends AbstractNetworkConnector @Name("server") Server server, @Name("executor") Executor executor, @Name("scheduler") Scheduler scheduler, - @Name("bufferPool") ByteBufferPool bufferPool, + @Name("bufferPool") RetainableByteBufferPool bufferPool, @Name("acceptors") int acceptors, @Name("selectors") int selectors, @Name("factories") ConnectionFactory... factories) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index d5256392feb..407bacb068e 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -21,7 +21,6 @@ import javax.net.ssl.SSLSession; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; @@ -167,22 +166,17 @@ public class SslConnectionFactory extends AbstractConnectionFactory implements C protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = byteBufferPool.asRetainableByteBufferPool(); - return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); + RetainableByteBufferPool retainableByteBufferPool = connector.getRetainableByteBufferPool(); + return new SslConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } @Override protected AbstractConnection configure(AbstractConnection connection, Connector connector, EndPoint endPoint) { - if (connection instanceof SslConnection) + if (connection instanceof SslConnection sslConnection) { - SslConnection sslConnection = (SslConnection)connection; - if (connector instanceof ContainerLifeCycle) - { - ContainerLifeCycle container = (ContainerLifeCycle)connector; + if (connector instanceof ContainerLifeCycle container) container.getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener); - } getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener); } return super.configure(connection, connector, endPoint); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java index ae2c8697c9e..0f0dd3267fa 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java @@ -21,7 +21,8 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.pathmap.PathSpecSet; import org.eclipse.jetty.io.ByteBufferAccumulator; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.ConnectionMetaData; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; @@ -188,9 +189,9 @@ public class BufferedResponseHandler extends Handler.Wrapper if (shouldBuffer(this, last)) { ConnectionMetaData connectionMetaData = getRequest().getConnectionMetaData(); - ByteBufferPool byteBufferPool = connectionMetaData.getConnector().getByteBufferPool(); + RetainableByteBufferPool bufferPool = connectionMetaData.getConnector().getRetainableByteBufferPool(); boolean useOutputDirectByteBuffers = connectionMetaData.getHttpConfiguration().isUseOutputDirectByteBuffers(); - _accumulator = new CountingByteBufferAccumulator(byteBufferPool, useOutputDirectByteBuffers, getBufferSize()); + _accumulator = new CountingByteBufferAccumulator(bufferPool, useOutputDirectByteBuffers, getBufferSize()); } _firstWrite = false; } @@ -211,7 +212,8 @@ public class BufferedResponseHandler extends Handler.Wrapper complete = last && !current.hasRemaining(); if (write || complete) { - BufferedResponse.super.write(complete, _accumulator.takeByteBuffer(), this); + RetainableByteBuffer buffer = _accumulator.takeRetainableByteBuffer(); + BufferedResponse.super.write(complete, buffer.getByteBuffer(), Callback.from(this, buffer::release)); return Action.SCHEDULED; } return Action.SUCCEEDED; @@ -236,9 +238,18 @@ public class BufferedResponseHandler extends Handler.Wrapper { // TODO pass all accumulated buffers as an array instead of allocating & copying into a single one. if (_accumulator != null) - super.write(true, _accumulator.takeByteBuffer(), Callback.from(_callback, _accumulator::close)); + { + RetainableByteBuffer buffer = _accumulator.takeRetainableByteBuffer(); + super.write(true, buffer.getByteBuffer(), Callback.from(_callback, () -> + { + buffer.release(); + _accumulator.close(); + })); + } else + { _callback.succeeded(); + } } @Override @@ -246,9 +257,7 @@ public class BufferedResponseHandler extends Handler.Wrapper { if (_accumulator != null) _accumulator.close(); - else - // TODO: this callback should always be failed! - _callback.failed(x); + _callback.failed(x); } } @@ -258,7 +267,7 @@ public class BufferedResponseHandler extends Handler.Wrapper private final int _maxSize; private int _accumulatedCount; - private CountingByteBufferAccumulator(ByteBufferPool bufferPool, boolean direct, int maxSize) + private CountingByteBufferAccumulator(RetainableByteBufferPool bufferPool, boolean direct, int maxSize) { if (maxSize <= 0) throw new IllegalArgumentException("maxSize must be > 0, was: " + maxSize); @@ -290,10 +299,10 @@ public class BufferedResponseHandler extends Handler.Wrapper return _maxSize - _accumulatedCount; } - private ByteBuffer takeByteBuffer() + private RetainableByteBuffer takeRetainableByteBuffer() { _accumulatedCount = 0; - return _accumulator.takeByteBuffer(); + return _accumulator.takeRetainableByteBuffer(); } @Override diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java index 8d999a7cfa9..a66b69b2c36 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java @@ -32,11 +32,11 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ManagedSelector; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.io.SocketChannelEndPoint; import org.eclipse.jetty.server.Handler; @@ -48,7 +48,6 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.HostPort; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.Promise; -import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,7 +63,7 @@ public class ConnectHandler extends Handler.Wrapper private final Set blackList = new HashSet<>(); private Executor executor; private Scheduler scheduler; - private ByteBufferPool bufferPool; + private RetainableByteBufferPool bufferPool; private SelectorManager selector; private long connectTimeout = 15000; private long idleTimeout = 30000; @@ -101,12 +100,12 @@ public class ConnectHandler extends Handler.Wrapper this.scheduler = scheduler; } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { return bufferPool; } - public void setByteBufferPool(ByteBufferPool bufferPool) + public void setRetainableByteBufferPool(RetainableByteBufferPool bufferPool) { updateBean(this.bufferPool, bufferPool); this.bufferPool = bufferPool; @@ -162,15 +161,13 @@ public class ConnectHandler extends Handler.Wrapper if (scheduler == null) { - scheduler = getServer().getBean(Scheduler.class); - if (scheduler == null) - scheduler = new ScheduledExecutorScheduler(String.format("Proxy-Scheduler-%x", hashCode()), false); + scheduler = getServer().getScheduler(); addBean(scheduler); } if (bufferPool == null) { - bufferPool = new MappedByteBufferPool(); + bufferPool = getServer().getRetainableByteBufferPool(); addBean(bufferPool); } @@ -385,12 +382,12 @@ public class ConnectHandler extends Handler.Wrapper protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap context) { - return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context); + return new DownstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), context); } protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext) { - return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), connectContext); + return new UpstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), connectContext); } protected void prepareContext(Request request, ConcurrentMap context) @@ -556,7 +553,7 @@ public class ConnectHandler extends Handler.Wrapper { private final ConnectContext connectContext; - public UpstreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConnectContext connectContext) + public UpstreamConnection(EndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, ConnectContext connectContext) { super(endPoint, executor, bufferPool, connectContext.getContext()); this.connectContext = connectContext; @@ -592,7 +589,7 @@ public class ConnectHandler extends Handler.Wrapper { private ByteBuffer buffer; - public DownstreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConcurrentMap context) + public DownstreamConnection(EndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, ConcurrentMap context) { super(endPoint, executor, bufferPool, context); } @@ -659,18 +656,18 @@ public class ConnectHandler extends Handler.Wrapper private abstract static class TunnelConnection extends AbstractConnection { private final IteratingCallback pipe = new ProxyIteratingCallback(); - private final ByteBufferPool bufferPool; + private final RetainableByteBufferPool bufferPool; private final ConcurrentMap context; private TunnelConnection connection; - protected TunnelConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConcurrentMap context) + protected TunnelConnection(EndPoint endPoint, Executor executor, RetainableByteBufferPool bufferPool, ConcurrentMap context) { super(endPoint, executor); this.bufferPool = bufferPool; this.context = context; } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { return bufferPool; } @@ -718,7 +715,7 @@ public class ConnectHandler extends Handler.Wrapper private class ProxyIteratingCallback extends IteratingCallback { - private ByteBuffer buffer; + private RetainableByteBuffer buffer; private int filled; @Override @@ -727,21 +724,22 @@ public class ConnectHandler extends Handler.Wrapper buffer = bufferPool.acquire(getInputBufferSize(), true); try { - int filled = this.filled = read(getEndPoint(), buffer); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + int filled = this.filled = read(getEndPoint(), byteBuffer); if (filled > 0) { - write(connection.getEndPoint(), buffer, this); + write(connection.getEndPoint(), byteBuffer, this); return Action.SCHEDULED; } else if (filled == 0) { - bufferPool.release(buffer); + buffer.release(); fillInterested(); return Action.IDLE; } else { - bufferPool.release(buffer); + buffer.release(); connection.getEndPoint().shutdownOutput(); return Action.SUCCEEDED; } @@ -750,7 +748,7 @@ public class ConnectHandler extends Handler.Wrapper { if (LOG.isDebugEnabled()) LOG.debug("Could not fill {}", TunnelConnection.this, x); - bufferPool.release(buffer); + buffer.release(); disconnect(x); return Action.SUCCEEDED; } @@ -761,7 +759,7 @@ public class ConnectHandler extends Handler.Wrapper { if (LOG.isDebugEnabled()) LOG.debug("Wrote {} bytes {}", filled, TunnelConnection.this); - bufferPool.release(buffer); + buffer.release(); super.succeeded(); } @@ -775,7 +773,7 @@ public class ConnectHandler extends Handler.Wrapper { if (LOG.isDebugEnabled()) LOG.debug("Failed to write {} bytes {}", filled, TunnelConnection.this, x); - bufferPool.release(buffer); + buffer.release(); disconnect(x); } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorProcessor.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorProcessor.java index 2d8bdd8edc9..97aba33ae59 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorProcessor.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorProcessor.java @@ -41,6 +41,7 @@ import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.QuotedQualityCSV; import org.eclipse.jetty.io.ByteBufferOutputStream; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Server; @@ -202,74 +203,83 @@ public class ErrorProcessor implements Request.Processor int bufferSize = request.getConnectionMetaData().getHttpConfiguration().getOutputBufferSize(); bufferSize = Math.min(8192, bufferSize); // TODO ? - ByteBuffer buffer = request.getComponents().getByteBufferPool().acquire(bufferSize, false); + RetainableByteBuffer buffer = request.getComponents().getRetainableByteBufferPool().acquire(bufferSize, false); - // write into the response aggregate buffer and flush it asynchronously. - // Looping to reduce size if buffer overflows - boolean showStacks = _showStacks; - while (true) + try { - try + // write into the response aggregate buffer and flush it asynchronously. + // Looping to reduce size if buffer overflows + boolean showStacks = _showStacks; + while (true) { - BufferUtil.clear(buffer); - ByteBufferOutputStream out = new ByteBufferOutputStream(buffer); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, charset)); - - switch (type) + try { - case TEXT_HTML -> writeErrorHtml(request, writer, charset, code, message, cause, showStacks); - case TEXT_JSON -> writeErrorJson(request, writer, code, message, cause, showStacks); - case TEXT_PLAIN -> writeErrorPlain(request, writer, code, message, cause, showStacks); - default -> throw new IllegalStateException(); + buffer.clear(); + ByteBufferOutputStream out = new ByteBufferOutputStream(buffer.getByteBuffer()); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, charset)); + + switch (type) + { + case TEXT_HTML -> writeErrorHtml(request, writer, charset, code, message, cause, showStacks); + case TEXT_JSON -> writeErrorJson(request, writer, code, message, cause, showStacks); + case TEXT_PLAIN -> writeErrorPlain(request, writer, code, message, cause, showStacks); + default -> throw new IllegalStateException(); + } + + writer.flush(); + break; } - - writer.flush(); - break; - } - catch (BufferOverflowException e) - { - if (showStacks) + catch (BufferOverflowException e) { + if (showStacks) + { + if (LOG.isDebugEnabled()) + LOG.debug("Disable stacks for " + e.toString()); + + showStacks = false; + continue; + } if (LOG.isDebugEnabled()) - LOG.debug("Disable stacks for " + e.toString()); + LOG.warn("Error page too large: >{} {} {} {}", bufferSize, code, message, request, e); + else + LOG.warn("Error page too large: >{} {} {} {}", bufferSize, code, message, request); - showStacks = false; - continue; + break; } - if (LOG.isDebugEnabled()) - LOG.warn("Error page too large: >{} {} {} {}", bufferSize, code, message, request, e); - else - LOG.warn("Error page too large: >{} {} {} {}", bufferSize, code, message, request); - - break; } - } - if (!buffer.hasRemaining()) - { - callback.succeeded(); + if (!buffer.hasRemaining()) + { + buffer.release(); + callback.succeeded(); + return true; + } + + response.getHeaders().put(type.getContentTypeField(charset)); + response.write(true, buffer.getByteBuffer(), new Callback.Nested(callback) + { + @Override + public void succeeded() + { + buffer.release(); + super.succeeded(); + } + + @Override + public void failed(Throwable x) + { + buffer.release(); + super.failed(x); + } + }); + return true; } - - response.getHeaders().put(type.getContentTypeField(charset)); - response.write(true, buffer, new Callback.Nested(callback) + catch (Throwable x) { - @Override - public void succeeded() - { - request.getComponents().getByteBufferPool().release(buffer); - super.succeeded(); - } - - @Override - public void failed(Throwable x) - { - request.getComponents().getByteBufferPool().release(buffer); - super.failed(x); - } - }); - - return true; + buffer.release(); + throw x; + } } protected void writeErrorHtml(Request request, Writer writer, Charset charset, int code, String message, Throwable cause, boolean showStacks) throws IOException diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java index f43b7fb33f0..ba84e11e175 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -25,8 +25,7 @@ import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory; import org.eclipse.jetty.http.content.ResourceHttpContentFactory; import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory; import org.eclipse.jetty.http.content.VirtualHttpContentFactory; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NoopByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Context; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; @@ -58,7 +57,7 @@ public class ResourceHandler extends Handler.Wrapper { private final ResourceService _resourceService; - private ByteBufferPool _byteBufferPool; + private RetainableByteBufferPool _bufferPool; private Resource _resourceBase; private Resource _styleSheet; private MimeTypes _mimeTypes; @@ -81,22 +80,21 @@ public class ResourceHandler extends Handler.Wrapper _mimeTypes = context == null ? MimeTypes.DEFAULTS : context.getMimeTypes(); - _byteBufferPool = getByteBufferPool(context); + _bufferPool = getRetainableByteBufferPool(context); _resourceService.setHttpContentFactory(newHttpContentFactory()); _resourceService.setWelcomeFactory(setupWelcomeFactory()); super.doStart(); } - private ByteBufferPool getByteBufferPool(Context context) + private RetainableByteBufferPool getRetainableByteBufferPool(Context context) { if (context == null) - return new NoopByteBufferPool(); + return new RetainableByteBufferPool.NonPooling(); Server server = getServer(); if (server == null) - return new NoopByteBufferPool(); - ByteBufferPool byteBufferPool = server.getBean(ByteBufferPool.class); - return (byteBufferPool == null) ? new NoopByteBufferPool() : byteBufferPool; + return new RetainableByteBufferPool.NonPooling(); + return server.getRetainableByteBufferPool(); } public HttpContent.Factory getHttpContentFactory() @@ -110,7 +108,7 @@ public class ResourceHandler extends Handler.Wrapper contentFactory = new FileMappingHttpContentFactory(contentFactory); contentFactory = new VirtualHttpContentFactory(contentFactory, getStyleSheet(), "text/css"); contentFactory = new PreCompressedHttpContentFactory(contentFactory, getPrecompressedFormats()); - contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, Duration.ofSeconds(1).toMillis(), getByteBufferPool()); + contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, Duration.ofSeconds(1).toMillis(), getRetainableByteBufferPool()); return contentFactory; } @@ -161,9 +159,9 @@ public class ResourceHandler extends Handler.Wrapper return _resourceBase; } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return _byteBufferPool; + return _bufferPool; } /** diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipRequest.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipRequest.java index a0d8be26483..fecb4e308d9 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipRequest.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipRequest.java @@ -22,12 +22,12 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.PreEncodedHttpField; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.content.ContentSourceTransformer; import org.eclipse.jetty.server.Components; import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.compression.InflaterPool; public class GzipRequest extends Request.Wrapper @@ -50,7 +50,7 @@ public class GzipRequest extends Request.Wrapper if (inflateBufferSize > 0) { Components components = getComponents(); - _decoder = new Decoder(__inflaterPool, components.getByteBufferPool(), inflateBufferSize); + _decoder = new Decoder(__inflaterPool, components.getRetainableByteBufferPool(), inflateBufferSize); _gzipTransformer = new GzipTransformer(getWrapped()); } } @@ -167,17 +167,18 @@ public class GzipRequest extends Request.Wrapper // Retain the input chunk because its ByteBuffer will be referenced by the Inflater. if (retain) _chunk.retain(); - ByteBuffer decodedBuffer = _decoder.decode(_chunk); + RetainableByteBuffer decodedBuffer = _decoder.decode(_chunk); - if (BufferUtil.hasContent(decodedBuffer)) + if (decodedBuffer != null && decodedBuffer.hasRemaining()) { // The decoded ByteBuffer is a transformed "copy" of the // compressed one, so it has its own reference counter. - return Content.Chunk.from(decodedBuffer, _chunk.isLast() && !_chunk.hasRemaining(), _decoder::release); + return Content.Chunk.from(decodedBuffer.getByteBuffer(), _chunk.isLast() && !_chunk.hasRemaining(), decodedBuffer::release); } else { - _decoder.release(decodedBuffer); + if (decodedBuffer != null) + decodedBuffer.release(); // Could not decode more from this chunk, release it. Content.Chunk result = _chunk.isLast() ? Content.Chunk.EOF : null; _chunk.release(); @@ -189,24 +190,26 @@ public class GzipRequest extends Request.Wrapper private static class Decoder extends GZIPContentDecoder { - private ByteBuffer _decoded; + private RetainableByteBuffer _decoded; - private Decoder(InflaterPool inflaterPool, ByteBufferPool bufferPool, int bufferSize) + private Decoder(InflaterPool inflaterPool, RetainableByteBufferPool bufferPool, int bufferSize) { super(inflaterPool, bufferPool, bufferSize); } - public ByteBuffer decode(Content.Chunk chunk) + public RetainableByteBuffer decode(Content.Chunk chunk) { decodeChunks(chunk.getByteBuffer()); - ByteBuffer decoded = _decoded; + RetainableByteBuffer decoded = _decoded; _decoded = null; return decoded; } @Override - protected boolean decodedChunk(ByteBuffer decoded) + protected boolean decodedChunk(RetainableByteBuffer decoded) { + // Retain the chunk because it is stored for later use. + decoded.retain(); _decoded = decoded; return true; } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipResponseAndCallback.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipResponseAndCallback.java index 38af7c8a992..7eb03b43e6d 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipResponseAndCallback.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipResponseAndCallback.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.PreEncodedHttpField; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.BufferUtil; @@ -77,7 +78,7 @@ public class GzipResponseAndCallback extends Response.Wrapper implements Callbac private final boolean _syncFlush; private DeflaterPool.Entry _deflaterEntry; - private ByteBuffer _buffer; + private RetainableByteBuffer _buffer; public GzipResponseAndCallback(GzipHandler handler, Request request, Response response, Callback callback) { @@ -323,7 +324,7 @@ public class GzipResponseAndCallback extends Response.Wrapper implements Callbac protected Action process() throws Exception { if (LOG.isDebugEnabled()) - LOG.debug("GzipBufferCB.process(): _last={}, _buffer={}, _content={}", _last, BufferUtil.toDetailString(_buffer), BufferUtil.toDetailString(_content)); + LOG.debug("GzipBufferCB.process(): _last={}, _buffer={}, _content={}", _last, _buffer, BufferUtil.toDetailString(_content)); GZState gzstate = _state.get(); @@ -340,25 +341,26 @@ public class GzipResponseAndCallback extends Response.Wrapper implements Callbac // If we have no buffer if (_buffer == null) { - _buffer = getRequest().getComponents().getByteBufferPool().acquire(_bufferSize, false); + _buffer = getRequest().getComponents().getRetainableByteBufferPool().acquire(_bufferSize, false); + ByteBuffer byteBuffer = _buffer.getByteBuffer(); // Per RFC-1952, GZIP is LITTLE_ENDIAN - _buffer.order(ByteOrder.LITTLE_ENDIAN); - BufferUtil.flipToFill(_buffer); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + BufferUtil.flipToFill(byteBuffer); // Add GZIP Header - _buffer.put(GZIP_HEADER, 0, GZIP_HEADER.length); + byteBuffer.put(GZIP_HEADER, 0, GZIP_HEADER.length); } else { // otherwise clear the buffer as previous writes will always fully consume. - BufferUtil.clearToFill(_buffer); + BufferUtil.clearToFill(_buffer.getByteBuffer()); } Deflater deflater = _deflaterEntry.get(); return switch (gzstate) { - case COMPRESSING -> compressing(deflater, _buffer); - case FINISHING -> finishing(deflater, _buffer); + case COMPRESSING -> compressing(deflater, _buffer.getByteBuffer()); + case FINISHING -> finishing(deflater, _buffer.getByteBuffer()); default -> throw new IllegalStateException("Unexpected state [" + _state.get() + "]"); }; } @@ -373,7 +375,7 @@ public class GzipResponseAndCallback extends Response.Wrapper implements Callbac if (_buffer != null) { - getRequest().getComponents().getByteBufferPool().release(_buffer); + _buffer.release(); _buffer = null; } } @@ -483,7 +485,7 @@ public class GzipResponseAndCallback extends Response.Wrapper implements Callbac super.toString(), BufferUtil.toDetailString(_content), _last, - BufferUtil.toDetailString(_buffer), + _buffer, _deflaterEntry, _state.get()); } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java index a90e1f75723..b0b4dbbe0af 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java @@ -37,10 +37,10 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.Trailers; import org.eclipse.jetty.http.UriCompliance; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Components; import org.eclipse.jetty.server.ConnectionMetaData; import org.eclipse.jetty.server.Connector; @@ -220,15 +220,15 @@ public class HttpChannelState implements HttpChannel, Components } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return getConnectionMetaData().getConnector().getByteBufferPool(); + return getConnectionMetaData().getConnector().getRetainableByteBufferPool(); } @Override public Scheduler getScheduler() { - return getServer().getBean(Scheduler.class); + return getServer().getScheduler(); } @Override diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java index 5a075a265ee..c5f3a5bba86 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java @@ -46,7 +46,6 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.Trailers; import org.eclipse.jetty.http.UriCompliance; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; @@ -96,7 +95,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ private final RequestHandler _requestHandler; private final HttpParser _parser; private final HttpGenerator _generator; - private final ByteBufferPool _bufferPool; private final RetainableByteBufferPool _retainableByteBufferPool; private final AtomicReference _stream = new AtomicReference<>(); private final Lazy _attributes = new Lazy(); @@ -141,8 +139,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ _id = __connectionIdGenerator.getAndIncrement(); _configuration = configuration; _connector = connector; - _bufferPool = _connector.getByteBufferPool(); - _retainableByteBufferPool = _bufferPool.asRetainableByteBufferPool(); + _retainableByteBufferPool = _connector.getRetainableByteBufferPool(); _generator = newHttpGenerator(); _httpChannel = newHttpChannel(connector.getServer(), configuration); _requestHandler = newRequestHandler(); @@ -367,7 +364,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ if (!isRequestBufferEmpty()) { ByteBuffer unconsumed = ByteBuffer.allocateDirect(_retainableByteBuffer.remaining()); - unconsumed.put(_retainableByteBuffer.getBuffer()); + unconsumed.put(_retainableByteBuffer.getByteBuffer()); unconsumed.flip(); releaseRequestBuffer(); return unconsumed; @@ -405,12 +402,12 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ { if (_retainableByteBuffer == null) _retainableByteBuffer = _retainableByteBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); - return _retainableByteBuffer.getBuffer(); + return _retainableByteBuffer.getByteBuffer(); } public boolean isRequestBufferEmpty() { - return _retainableByteBuffer == null || _retainableByteBuffer.isEmpty(); + return _retainableByteBuffer == null || !_retainableByteBuffer.hasRemaining(); } @Override @@ -597,7 +594,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ if (LOG.isDebugEnabled()) LOG.debug("{} parse {}", this, _retainableByteBuffer); - boolean handle = _parser.parseNext(_retainableByteBuffer == null ? BufferUtil.EMPTY_BUFFER : _retainableByteBuffer.getBuffer()); + boolean handle = _parser.parseNext(_retainableByteBuffer == null ? BufferUtil.EMPTY_BUFFER : _retainableByteBuffer.getByteBuffer()); if (LOG.isDebugEnabled()) LOG.debug("{} parsed {} {}", this, handle, _parser); @@ -724,8 +721,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ private ByteBuffer _content; private boolean _lastContent; private Callback _callback; - private ByteBuffer _header; - private ByteBuffer _chunk; + private RetainableByteBuffer _header; + private RetainableByteBuffer _chunk; private boolean _shutdownOut; private SendCallback() @@ -780,12 +777,14 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ boolean useDirectByteBuffers = isUseOutputDirectByteBuffers(); while (true) { - HttpGenerator.Result result = _generator.generateResponse(_info, _head, _header, _chunk, _content, _lastContent); + ByteBuffer headerByteBuffer = _header == null ? null : _header.getByteBuffer(); + ByteBuffer chunkByteBuffer = _chunk == null ? null : _chunk.getByteBuffer(); + HttpGenerator.Result result = _generator.generateResponse(_info, _head, headerByteBuffer, chunkByteBuffer, _content, _lastContent); if (LOG.isDebugEnabled()) LOG.debug("generate: {} for {} ({},{},{})@{}", result, this, - BufferUtil.toSummaryString(_header), + BufferUtil.toSummaryString(headerByteBuffer), BufferUtil.toSummaryString(_content), _lastContent, _generator.getState()); @@ -797,7 +796,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ case NEED_HEADER: { - _header = _bufferPool.acquire(Math.min(_configuration.getResponseHeaderSize(), _configuration.getOutputBufferSize()), useDirectByteBuffers); + _header = _retainableByteBufferPool.acquire(Math.min(_configuration.getResponseHeaderSize(), _configuration.getOutputBufferSize()), useDirectByteBuffers); continue; } case HEADER_OVERFLOW: @@ -805,18 +804,18 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ if (_header.capacity() >= _configuration.getResponseHeaderSize()) throw new BadMessageException(INTERNAL_SERVER_ERROR_500, "Response header too large"); releaseHeader(); - _header = _bufferPool.acquire(_configuration.getResponseHeaderSize(), useDirectByteBuffers); + _header = _retainableByteBufferPool.acquire(_configuration.getResponseHeaderSize(), useDirectByteBuffers); continue; } case NEED_CHUNK: { - _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers); + _chunk = _retainableByteBufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers); continue; } case NEED_CHUNK_TRAILER: { releaseChunk(); - _chunk = _bufferPool.acquire(_configuration.getResponseHeaderSize(), useDirectByteBuffers); + _chunk = _retainableByteBufferPool.acquire(_configuration.getResponseHeaderSize(), useDirectByteBuffers); continue; } case FLUSH: @@ -824,18 +823,19 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ // Don't write the chunk or the content if this is a HEAD response, or any other type of response that should have no content if (_head || _generator.isNoContent()) { - BufferUtil.clear(_chunk); + if (_chunk != null) + _chunk.clear(); BufferUtil.clear(_content); } int gatherWrite = 0; long bytes = 0; - if (BufferUtil.hasContent(_header)) + if (BufferUtil.hasContent(headerByteBuffer)) { gatherWrite += 4; bytes += _header.remaining(); } - if (BufferUtil.hasContent(_chunk)) + if (BufferUtil.hasContent(chunkByteBuffer)) { gatherWrite += 2; bytes += _chunk.remaining(); @@ -849,22 +849,22 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ switch (gatherWrite) { case 7: - getEndPoint().write(this, _header, _chunk, _content); + getEndPoint().write(this, headerByteBuffer, chunkByteBuffer, _content); break; case 6: - getEndPoint().write(this, _header, _chunk); + getEndPoint().write(this, headerByteBuffer, chunkByteBuffer); break; case 5: - getEndPoint().write(this, _header, _content); + getEndPoint().write(this, headerByteBuffer, _content); break; case 4: - getEndPoint().write(this, _header); + getEndPoint().write(this, headerByteBuffer); break; case 3: - getEndPoint().write(this, _chunk, _content); + getEndPoint().write(this, chunkByteBuffer, _content); break; case 2: - getEndPoint().write(this, _chunk); + getEndPoint().write(this, chunkByteBuffer); break; case 1: getEndPoint().write(this, _content); @@ -916,14 +916,14 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ private void releaseHeader() { if (_header != null) - _bufferPool.release(_header); + _header.release(); _header = null; } private void releaseChunk() { if (_chunk != null) - _bufferPool.release(_chunk); + _chunk.release(); _chunk = null; } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java index 50b44670151..2d8577e6699 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java @@ -24,7 +24,7 @@ import java.util.Set; import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.internal.HttpChannelState; import org.eclipse.jetty.util.Callback; @@ -46,7 +46,7 @@ public abstract class AbstractHttpTest public void setUp() throws Exception { server = new Server(); - connector = new ServerConnector(server, null, null, new ArrayByteBufferPool(64, 2048, 64 * 1024), 1, 1, new HttpConnectionFactory()); + connector = new ServerConnector(server, null, null, new ArrayRetainableByteBufferPool(64, 2048, 64 * 1024), 1, 1, new HttpConnectionFactory()); connector.setIdleTimeout(100000); server.addConnector(connector); diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java index 379aca089f7..e9111b0032b 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.io.ByteBufferAccumulator; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.server.handler.EchoHandler; import org.eclipse.jetty.util.Blocker; @@ -651,7 +652,8 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture if (chunk.isLast()) { // write accumulated buffers - response.write(true, bufferAccumulator.toByteBuffer(), callback); + RetainableByteBuffer buffer = bufferAccumulator.toRetainableByteBuffer(); + response.write(true, buffer.getByteBuffer(), Callback.from(buffer::release, callback)); return; } } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java index 28fe12d78ed..e333b908a6d 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java @@ -375,10 +375,6 @@ public class DetectorConnectionTest { if (!detectionSuccessful.compareAndSet(true, false)) throw new AssertionError("DetectionUnsuccessful callback should only have been called once"); - - // omitting this will leak the buffer - connector.getByteBufferPool().release(buffer); - Callback.Completable.with(c -> endPoint.write(c, ByteBuffer.wrap("No upgrade for you".getBytes(StandardCharsets.US_ASCII)))) .whenComplete((r, x) -> endPoint.close()); } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index 36ea166e22a..706b07077ca 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -1766,7 +1766,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture long total = contents.stream().mapToLong(Content.Chunk::remaining).sum(); assertThat(total, equalTo(chunk.length * 4L)); - RetainableByteBufferPool rbbp = _connector.getBean(RetainableByteBufferPool.class); + RetainableByteBufferPool rbbp = _connector.getRetainableByteBufferPool(); if (rbbp instanceof ArrayRetainableByteBufferPool pool) { long buffersBeforeRelease = pool.getAvailableDirectByteBufferCount() + pool.getAvailableHeapByteBufferCount(); diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockConnector.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockConnector.java index fa65ad26171..82134dd418d 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockConnector.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockConnector.java @@ -15,17 +15,13 @@ package org.eclipse.jetty.server; import java.io.IOException; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.util.thread.Scheduler; +import org.eclipse.jetty.io.RetainableByteBufferPool; public class MockConnector extends AbstractConnector { - private final Server _server; - public MockConnector(Server server) { - super(server, server.getThreadPool(), server.getBean(Scheduler.class), ByteBufferPool.NOOP, 0); - _server = server; + super(server, server.getThreadPool(), server.getScheduler(), new RetainableByteBufferPool.NonPooling(), 0); } @Override @@ -33,21 +29,6 @@ public class MockConnector extends AbstractConnector { } - @Override - public Scheduler getScheduler() - { - return _server.getBean(Scheduler.class); - } - - @Override - public ByteBufferPool getByteBufferPool() - { - ByteBufferPool pool = _server.getBean(ByteBufferPool.class); - if (pool != null) - return pool; - return super.getByteBufferPool(); - } - @Override public Object getTransport() { diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockHttpStream.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockHttpStream.java index a2ccaf1f004..526d0129ae1 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockHttpStream.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MockHttpStream.java @@ -206,7 +206,7 @@ public class MockHttpStream implements HttpStream _responseTrailers = HttpFields.build(trailers); } - if (!_out.compareAndSet(null, _accumulator.takeByteBuffer())) + if (!_out.compareAndSet(null, _accumulator.takeRetainableByteBuffer().getByteBuffer())) { if (response != null || content != null) { diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java index d06ecce4fd3..faf34e7da80 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java @@ -31,9 +31,8 @@ import java.util.stream.Stream; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.LeakTrackingByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; @@ -84,7 +83,9 @@ public class ThreadStarvationTest SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("storepwd"); - ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + // TODO: restore leak tracking. +// ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + RetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(); ServerConnector connector = new ServerConnector(server, null, null, pool, acceptors, selectors, diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BufferedResponseHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BufferedResponseHandlerTest.java index 57ca9e0ece3..5b2798a8366 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BufferedResponseHandlerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BufferedResponseHandlerTest.java @@ -18,7 +18,6 @@ import java.util.Arrays; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.NoopByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -45,7 +44,6 @@ public class BufferedResponseHandlerTest public void setUp() throws Exception { _server = new Server(); - _server.addBean(new NoopByteBufferPool()); // Avoid giving larger buffers than requested HttpConfiguration config = new HttpConfiguration(); config.setOutputBufferSize(1024); config.setOutputAggregationSize(256); diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java index 141656a1129..85ba710e331 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java @@ -26,9 +26,8 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.LeakTrackingByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; @@ -71,7 +70,9 @@ public class DebugHandlerTest SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); sslContextFactory.setKeyStorePath(keystorePath.getAbsolutePath()); sslContextFactory.setKeyStorePassword("storepwd"); - ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + // TODO: restore leak tracking. +// ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + RetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); ServerConnector sslConnector = new ServerConnector(server, null, null, pool, 1, 1, AbstractConnectionFactory.getFactories(sslContextFactory, new HttpConnectionFactory())); diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java index 6a8f2aac41f..6c8a49e6dd9 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java @@ -670,7 +670,7 @@ public class ResourceHandlerTest contentFactory = new FileMappingHttpContentFactory(contentFactory); contentFactory = new VirtualHttpContentFactory(contentFactory, getStyleSheet(), "text/css"); contentFactory = new PreCompressedHttpContentFactory(contentFactory, getPrecompressedFormats()); - contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, 0, getByteBufferPool()); + contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, 0, getRetainableByteBufferPool()); return contentFactory; } }; diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java index 757bd6760e3..9690f293b68 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java @@ -203,7 +203,7 @@ public class SSLEngineTest @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) + return new SslConnection(connector.getRetainableByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) { @Override protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuffer output) throws SSLException diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/ServerConnectorSslServerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/ServerConnectorSslServerTest.java index cb6096b50a8..aff49dfb563 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/ServerConnectorSslServerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/ServerConnectorSslServerTest.java @@ -27,10 +27,9 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManagerFactory; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.LeakTrackingByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -74,7 +73,9 @@ public class ServerConnectorSslServerTest extends HttpServerTestBase SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); sslContextFactory.setKeyStorePath(keystorePath); sslContextFactory.setKeyStorePassword("storepwd"); - ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + // TODO: restore leak tracking. +// ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + RetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(); ServerConnector connector = new ServerConnector(_server, null, null, pool, 1, 1, AbstractConnectionFactory.getFactories(sslContextFactory, httpConnectionFactory)); diff --git a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java index 6586d8ee3aa..05261392f20 100644 --- a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java +++ b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java @@ -289,7 +289,7 @@ public abstract class AbstractSessionManager extends ContainerLifeCycle implemen addBean(_sessionIdManager, false); } - _scheduler = server.getBean(Scheduler.class); + _scheduler = server.getScheduler(); if (_scheduler == null) { _scheduler = new ScheduledExecutorScheduler(String.format("Session-Scheduler-%x", hashCode()), false); diff --git a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/HouseKeeper.java b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/HouseKeeper.java index 2aec6f87dba..5aedd172c72 100644 --- a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/HouseKeeper.java +++ b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/HouseKeeper.java @@ -43,12 +43,8 @@ public class HouseKeeper extends AbstractLifeCycle protected boolean _ownScheduler = false; private long _intervalMs = DEFAULT_PERIOD_MS; - /** - * Runner - */ protected class Runner implements Runnable { - @Override public void run() { @@ -104,7 +100,7 @@ public class HouseKeeper extends AbstractLifeCycle if (_sessionIdManager instanceof DefaultSessionIdManager) { //try and use a common scheduler, fallback to own - _scheduler = ((DefaultSessionIdManager)_sessionIdManager).getServer().getBean(Scheduler.class); + _scheduler = ((DefaultSessionIdManager)_sessionIdManager).getServer().getScheduler(); } if (_scheduler == null) diff --git a/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/HouseKeeperTest.java b/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/HouseKeeperTest.java index eddd4fedf5f..36f2d16f1bd 100644 --- a/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/HouseKeeperTest.java +++ b/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/HouseKeeperTest.java @@ -72,11 +72,14 @@ public class HouseKeeperTest @Test public void testHouseKeeper() throws Exception { + Server server = new Server(); + server.start(); + HouseKeeper t = new TestHouseKeeper(); assertThrows(IllegalStateException.class, () -> t.start()); TestHouseKeeper hk = new TestHouseKeeper(); - hk.setSessionIdManager(new TestSessionIdManager(new Server())); + hk.setSessionIdManager(new TestSessionIdManager(server)); hk.setIntervalSec(-1); hk.start(); //no scavenging @@ -103,8 +106,7 @@ public class HouseKeeperTest assertNotNull(hk.getRunner()); assertNotNull(hk.getTask()); assertNotNull(hk.getScheduler()); - assertTrue(hk.isOwnScheduler()); - + //stop it hk.stop(); assertNull(hk.getRunner()); @@ -124,6 +126,5 @@ public class HouseKeeperTest //provide that functionality. assertNotSame(oldTask, hk.getTask()); assertNotNull(hk.getScheduler()); - assertTrue(hk.isOwnScheduler()); } } diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java index 91889fcae2c..890a6bf86d8 100644 --- a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java +++ b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java @@ -37,6 +37,7 @@ import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3; import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory; import org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory; import org.eclipse.jetty.http3.server.HTTP3ServerConnector; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.quic.server.QuicServerConnector; import org.eclipse.jetty.server.AbstractConnector; @@ -138,7 +139,8 @@ public class AbstractTest { QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); - return new Server(serverThreads); + // TODO: restore leak tracking. + return new Server(serverThreads, null, new ArrayRetainableByteBufferPool()); } protected SslContextFactory.Server newSslContextFactoryServer() diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ForwardProxyWithDynamicTransportTest.java b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ForwardProxyWithDynamicTransportTest.java index e1298b228a6..d6fa6445444 100644 --- a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ForwardProxyWithDynamicTransportTest.java +++ b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ForwardProxyWithDynamicTransportTest.java @@ -411,7 +411,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap context) { - return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context) + return new DownstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), context) { @Override protected void close(Throwable failure) @@ -425,7 +425,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext) { - return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), connectContext) + return new UpstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), connectContext) { @Override protected void close(Throwable failure) @@ -462,7 +462,7 @@ public class ForwardProxyWithDynamicTransportTest public void onDataAvailable(Stream stream) { Stream.Data data = stream.readData(); - String response = BufferUtil.toString(data.frame().getData(), StandardCharsets.UTF_8); + String response = BufferUtil.toString(data.frame().getByteBuffer(), StandardCharsets.UTF_8); data.release(); if (response.startsWith("HTTP/1.1 200")) responseLatch.countDown(); @@ -494,7 +494,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap context) { - return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context) + return new DownstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), context) { @Override protected void close(Throwable failure) @@ -508,7 +508,7 @@ public class ForwardProxyWithDynamicTransportTest @Override protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext) { - return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), connectContext) + return new UpstreamConnection(endPoint, getExecutor(), getRetainableByteBufferPool(), connectContext) { @Override protected void close(Throwable failure) @@ -550,7 +550,7 @@ public class ForwardProxyWithDynamicTransportTest public void onDataAvailable(Stream stream) { Stream.Data data = stream.readData(); - String response = BufferUtil.toString(data.frame().getData(), StandardCharsets.UTF_8); + String response = BufferUtil.toString(data.frame().getByteBuffer(), StandardCharsets.UTF_8); data.release(); if (response.startsWith("HTTP/1.1 200")) responseLatch.countDown(); diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientDemandTest.java b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientDemandTest.java index 83d3bd6433c..d03171b4af6 100644 --- a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientDemandTest.java +++ b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientDemandTest.java @@ -33,8 +33,8 @@ import org.eclipse.jetty.client.Result; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.BufferUtil; @@ -140,7 +140,7 @@ public class HttpClientDemandTest extends AbstractTest }); startClient(transport); client.stop(); - client.setByteBufferPool(new MappedByteBufferPool(bufferSize)); + client.setRetainableByteBufferPool(new ArrayRetainableByteBufferPool(0, bufferSize, -1)); client.setResponseBufferSize(bufferSize); client.start(); @@ -322,7 +322,7 @@ public class HttpClientDemandTest extends AbstractTest }); startClient(transport); client.stop(); - client.setByteBufferPool(new MappedByteBufferPool(bufferSize)); + client.setRetainableByteBufferPool(new ArrayRetainableByteBufferPool(0, bufferSize, -1)); client.setResponseBufferSize(bufferSize); client.start(); diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientLoadTest.java b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientLoadTest.java index 22250afa4a1..6c079f2014e 100644 --- a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientLoadTest.java +++ b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientLoadTest.java @@ -34,11 +34,8 @@ import org.eclipse.jetty.client.Result; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.LeakTrackingByteBufferPool; -import org.eclipse.jetty.io.LogarithmicArrayByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.util.BufferUtil; @@ -67,11 +64,11 @@ public class HttpClientLoadTest extends AbstractTest public void testIterative(Transport transport) throws Exception { server = newServer(); - server.addBean(new LeakTrackingByteBufferPool(new LogarithmicArrayByteBufferPool())); start(transport, new LoadHandler()); setStreamIdleTimeout(120000); client.stop(); - client.setByteBufferPool(new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged())); + // TODO: restore leak tracking. + client.setRetainableByteBufferPool(new ArrayRetainableByteBufferPool()); client.setMaxConnectionsPerDestination(32768); client.setMaxRequestsQueuedPerDestination(1024 * 1024); client.setIdleTimeout(120000); @@ -120,7 +117,8 @@ public class HttpClientLoadTest extends AbstractTest start(transport, new LoadHandler()); client.stop(); - client.setByteBufferPool(new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged())); + // TODO: restore leak tracking. + client.setRetainableByteBufferPool(new ArrayRetainableByteBufferPool()); client.setMaxConnectionsPerDestination(32768); client.setMaxRequestsQueuedPerDestination(1024 * 1024); client.start(); @@ -138,24 +136,6 @@ public class HttpClientLoadTest extends AbstractTest { System.gc(); - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - if (byteBufferPool instanceof LeakTrackingByteBufferPool serverBufferPool) - { - assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), Matchers.is(0L)); - assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), Matchers.is(0L)); - assertThat("Server BufferPool - leaked removes", serverBufferPool.getLeakedRemoves(), Matchers.is(0L)); - assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), Matchers.is(0L)); - } - - byteBufferPool = client.getByteBufferPool(); - if (byteBufferPool instanceof LeakTrackingByteBufferPool clientBufferPool) - { - assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), Matchers.is(0L)); - assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), Matchers.is(0L)); - assertThat("Client BufferPool - leaked removes", clientBufferPool.getLeakedRemoves(), Matchers.is(0L)); - assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), Matchers.is(0L)); - } - assertThat("Connection Leaks", connectionLeaks.get(), Matchers.is(0L)); } diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientTimeoutTest.java b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientTimeoutTest.java index 5d56a172565..f8461a52f26 100644 --- a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientTimeoutTest.java +++ b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/HttpClientTimeoutTest.java @@ -35,10 +35,10 @@ import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.client.Result; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.server.Handler; @@ -238,12 +238,12 @@ public class HttpClientTimeoutTest extends AbstractTest { if (sslContextFactory == null) sslContextFactory = getSslContextFactory(); - return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory) + return new SslClientConnectionFactory(sslContextFactory, getRetainableByteBufferPool(), getExecutor(), connectionFactory) { @Override - protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) + protected SslConnection newSslConnection(RetainableByteBufferPool bufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(byteBufferPool, executor, endPoint, engine) + return new SslConnection(bufferPool, executor, endPoint, engine) { @Override protected boolean onReadTimeout(Throwable timeout) diff --git a/jetty-core/jetty-unixdomain-server/src/main/java/org/eclipse/jetty/unixdomain/server/UnixDomainServerConnector.java b/jetty-core/jetty-unixdomain-server/src/main/java/org/eclipse/jetty/unixdomain/server/UnixDomainServerConnector.java index dce9a1c80b3..b29dade57a0 100644 --- a/jetty-core/jetty-unixdomain-server/src/main/java/org/eclipse/jetty/unixdomain/server/UnixDomainServerConnector.java +++ b/jetty-core/jetty-unixdomain-server/src/main/java/org/eclipse/jetty/unixdomain/server/UnixDomainServerConnector.java @@ -29,10 +29,10 @@ import java.nio.file.Path; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ManagedSelector; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.io.SocketChannelEndPoint; import org.eclipse.jetty.server.AbstractConnector; @@ -78,9 +78,9 @@ public class UnixDomainServerConnector extends AbstractConnector this(server, null, null, null, acceptors, selectors, factories); } - public UnixDomainServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories) + public UnixDomainServerConnector(Server server, Executor executor, Scheduler scheduler, RetainableByteBufferPool bufferPool, int acceptors, int selectors, ConnectionFactory... factories) { - super(server, executor, scheduler, pool, acceptors, factories.length > 0 ? factories : new ConnectionFactory[]{new HttpConnectionFactory()}); + super(server, executor, scheduler, bufferPool, acceptors, factories.length > 0 ? factories : new ConnectionFactory[]{new HttpConnectionFactory()}); selectorManager = newSelectorManager(getExecutor(), getScheduler(), selectors); addBean(selectorManager, true); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-core/jetty-websocket/jetty-websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index cc681f3878a..b91222f4f78 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -37,7 +37,6 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; @@ -478,9 +477,8 @@ public abstract class CoreClientUpgradeRequest implements Response.CompleteListe customizer.customize(coreSession); HttpClient httpClient = wsClient.getHttpClient(); - ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = bufferPool.asRetainableByteBufferPool(); - WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); + RetainableByteBufferPool bufferPool = wsClient.getWebSocketComponents().getRetainableByteBufferPool(); + WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); Throwable listenerError = notifyUpgradeListeners((listener) -> listener.onHandshakeResponse(request, response)); diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java index 7baee9ddfcf..c9697965d12 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java @@ -13,7 +13,7 @@ package org.eclipse.jetty.websocket.core; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -27,7 +27,7 @@ public class AbstractExtension implements Extension { private final Logger log; private CoreSession coreSession; - private ByteBufferPool bufferPool; + private RetainableByteBufferPool bufferPool; private ExtensionConfig config; private OutgoingFrames nextOutgoing; private IncomingFrames nextIncoming; @@ -43,7 +43,7 @@ public class AbstractExtension implements Extension public void init(ExtensionConfig config, WebSocketComponents components) { this.config = config; - this.bufferPool = components.getBufferPool(); + this.bufferPool = components.getRetainableByteBufferPool(); this.deflaterPool = components.getDeflaterPool(); this.inflaterPool = components.getInflaterPool(); } @@ -60,7 +60,7 @@ public class AbstractExtension implements Extension nextOutgoingFrame(frame, callback, batch); } - public ByteBufferPool getBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { return bufferPool; } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/CoreSession.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/CoreSession.java index 0fb82bef2ea..37f72f2dedc 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/CoreSession.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/CoreSession.java @@ -19,7 +19,7 @@ import java.net.URI; import java.util.List; import java.util.Map; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; /** @@ -82,9 +82,9 @@ public interface CoreSession extends OutgoingFrames, Configuration WebSocketComponents getWebSocketComponents(); /** - * @return The shared ByteBufferPool + * @return The shared RetainableByteBufferPool */ - ByteBufferPool getByteBufferPool(); + RetainableByteBufferPool getRetainableByteBufferPool(); /** * The Local Socket Address for the connection @@ -241,7 +241,7 @@ public interface CoreSession extends OutgoingFrames, Configuration } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { return null; } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/WebSocketComponents.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/WebSocketComponents.java index 7d609e0a962..d32f59a7d6b 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/WebSocketComponents.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/WebSocketComponents.java @@ -16,8 +16,8 @@ package org.eclipse.jetty.websocket.core; import java.util.concurrent.Executor; import java.util.zip.Deflater; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.compression.CompressionPool; @@ -27,14 +27,14 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool; /** * A collection of components which are the resources needed for websockets such as - * {@link ByteBufferPool}, {@link WebSocketExtensionRegistry}, and {@link DecoratedObjectFactory}. + * {@link RetainableByteBufferPool}, {@link WebSocketExtensionRegistry}, and {@link DecoratedObjectFactory}. */ public class WebSocketComponents extends ContainerLifeCycle { private final DecoratedObjectFactory _objectFactory; private final WebSocketExtensionRegistry _extensionRegistry; private final Executor _executor; - private final ByteBufferPool _bufferPool; + private final RetainableByteBufferPool _bufferPool; private final InflaterPool _inflaterPool; private final DeflaterPool _deflaterPool; @@ -44,17 +44,17 @@ public class WebSocketComponents extends ContainerLifeCycle } public WebSocketComponents(WebSocketExtensionRegistry extensionRegistry, DecoratedObjectFactory objectFactory, - ByteBufferPool bufferPool, InflaterPool inflaterPool, DeflaterPool deflaterPool) + RetainableByteBufferPool bufferPool, InflaterPool inflaterPool, DeflaterPool deflaterPool) { this (extensionRegistry, objectFactory, bufferPool, inflaterPool, deflaterPool, null); } public WebSocketComponents(WebSocketExtensionRegistry extensionRegistry, DecoratedObjectFactory objectFactory, - ByteBufferPool bufferPool, InflaterPool inflaterPool, DeflaterPool deflaterPool, Executor executor) + RetainableByteBufferPool bufferPool, InflaterPool inflaterPool, DeflaterPool deflaterPool, Executor executor) { _extensionRegistry = (extensionRegistry == null) ? new WebSocketExtensionRegistry() : extensionRegistry; _objectFactory = (objectFactory == null) ? new DecoratedObjectFactory() : objectFactory; - _bufferPool = (bufferPool == null) ? new MappedByteBufferPool() : bufferPool; + _bufferPool = (bufferPool == null) ? new ArrayRetainableByteBufferPool() : bufferPool; _inflaterPool = (inflaterPool == null) ? new InflaterPool(CompressionPool.DEFAULT_CAPACITY, true) : inflaterPool; _deflaterPool = (deflaterPool == null) ? new DeflaterPool(CompressionPool.DEFAULT_CAPACITY, Deflater.DEFAULT_COMPRESSION, true) : deflaterPool; @@ -77,7 +77,7 @@ public class WebSocketComponents extends ContainerLifeCycle addBean(_executor); } - public ByteBufferPool getBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { return _bufferPool; } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java index 72104c288ef..f91ec562258 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java @@ -22,6 +22,7 @@ import java.nio.file.Path; import java.util.Calendar; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.StringUtil; @@ -105,14 +106,14 @@ public class FrameCaptureExtension extends AbstractExtension return; } - ByteBuffer buf = getBufferPool().acquire(BUFSIZE, false); - + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(BUFSIZE, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); try { Frame f = Frame.copy(frame); f.setMask(null); // TODO is this needed? - generator.generateHeader(f, buf); - channel.write(buf); + generator.generateHeader(f, byteBuffer); + channel.write(byteBuffer); if (frame.hasPayload()) { channel.write(frame.getPayload().slice()); @@ -127,7 +128,7 @@ public class FrameCaptureExtension extends AbstractExtension } finally { - getBufferPool().release(buf); + buffer.release(); } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java index 0f2199375c4..4c63f0853af 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java @@ -24,8 +24,9 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingCallback; @@ -50,7 +51,7 @@ public class FrameFlusher extends IteratingCallback private final AutoLock lock = new AutoLock(); private final LongAdder messagesOut = new LongAdder(); private final LongAdder bytesOut = new LongAdder(); - private final ByteBufferPool bufferPool; + private final RetainableByteBufferPool bufferPool; private final EndPoint endPoint; private final int bufferSize; private final Generator generator; @@ -62,15 +63,15 @@ public class FrameFlusher extends IteratingCallback private final List previousEntries; private final List failedEntries; - private List releasableBuffers = new ArrayList<>(); - private ByteBuffer batchBuffer; + private List releasableBuffers = new ArrayList<>(); + private RetainableByteBuffer batchBuffer; private boolean canEnqueue = true; private boolean flushed = true; private Throwable closedCause; private long idleTimeout; private boolean useDirectByteBuffers; - public FrameFlusher(ByteBufferPool bufferPool, Scheduler scheduler, Generator generator, EndPoint endPoint, int bufferSize, int maxGather) + public FrameFlusher(RetainableByteBufferPool bufferPool, Scheduler scheduler, Generator generator, EndPoint endPoint, int bufferSize, int maxGather) { this.bufferPool = bufferPool; this.endPoint = endPoint; @@ -209,7 +210,7 @@ public class FrameFlusher extends IteratingCallback entries.clear(); if (flushed && batchBuffer != null) - BufferUtil.clear(batchBuffer); + batchBuffer.clear(); while (!queue.isEmpty() && entries.size() <= maxGather) { @@ -223,7 +224,7 @@ public class FrameFlusher extends IteratingCallback messagesOut.increment(); - int batchSpace = batchBuffer == null ? bufferSize : BufferUtil.space(batchBuffer); + int batchSpace = batchBuffer == null ? bufferSize : BufferUtil.space(batchBuffer.getByteBuffer()); boolean batch = entry.batch && !entry.frame.isControlFrame() && @@ -236,26 +237,26 @@ public class FrameFlusher extends IteratingCallback if (batchBuffer == null) { batchBuffer = acquireBuffer(bufferSize); - buffers.add(batchBuffer); + buffers.add(batchBuffer.getByteBuffer()); } // Generate the frame into the batchBuffer. - generator.generateWholeFrame(entry.frame, batchBuffer); + generator.generateWholeFrame(entry.frame, batchBuffer.getByteBuffer()); } else { if (batchBuffer != null && batchSpace >= Generator.MAX_HEADER_LENGTH) { // Use the batch space for our header. - generator.generateHeader(entry.frame, batchBuffer); + generator.generateHeader(entry.frame, batchBuffer.getByteBuffer()); } else { // Add headers to the list of buffers. - ByteBuffer headerBuffer = acquireBuffer(Generator.MAX_HEADER_LENGTH); + RetainableByteBuffer headerBuffer = acquireBuffer(Generator.MAX_HEADER_LENGTH); releasableBuffers.add(headerBuffer); - generator.generateHeader(entry.frame, headerBuffer); - buffers.add(headerBuffer); + generator.generateHeader(entry.frame, headerBuffer.getByteBuffer()); + buffers.add(headerBuffer.getByteBuffer()); } // Add the payload to the list of buffers. @@ -264,11 +265,11 @@ public class FrameFlusher extends IteratingCallback { if (entry.frame.isMasked()) { - payload = acquireBuffer(entry.frame.getPayloadLength()); - releasableBuffers.add(payload); + RetainableByteBuffer masked = acquireBuffer(entry.frame.getPayloadLength()); + payload = masked.getByteBuffer(); + releasableBuffers.add(masked); generator.generatePayload(entry.frame, payload); } - buffers.add(payload.slice()); } flush = true; @@ -280,15 +281,9 @@ public class FrameFlusher extends IteratingCallback // If we are going to flush we should release any buffers we have allocated after the callback completes. if (flush) { - final List callbackBuffers = releasableBuffers; + List callbackBuffers = releasableBuffers; releasableBuffers = new ArrayList<>(); - releasingCallback = Callback.from(releasingCallback, () -> - { - for (ByteBuffer buffer : callbackBuffers) - { - bufferPool.release(buffer); - } - }); + releasingCallback = Callback.from(releasingCallback, () -> callbackBuffers.forEach(RetainableByteBuffer::release)); } } @@ -297,7 +292,7 @@ public class FrameFlusher extends IteratingCallback this, entries.size(), flush, - BufferUtil.toDetailString(batchBuffer), + batchBuffer, entries); // succeed previous entries @@ -339,7 +334,7 @@ public class FrameFlusher extends IteratingCallback return Action.SCHEDULED; } - private ByteBuffer acquireBuffer(int capacity) + private RetainableByteBuffer acquireBuffer(int capacity) { return bufferPool.acquire(capacity, isUseDirectByteBuffers()); } @@ -404,7 +399,8 @@ public class FrameFlusher extends IteratingCallback @Override public void onCompleteFailure(Throwable failure) { - BufferUtil.clear(batchBuffer); + if (batchBuffer != null) + batchBuffer.clear(); releaseAggregate(); try (AutoLock l = lock.lock()) { @@ -414,10 +410,7 @@ public class FrameFlusher extends IteratingCallback failedEntries.addAll(entries); entries.clear(); - for (ByteBuffer buffer : releasableBuffers) - { - bufferPool.release(buffer); - } + releasableBuffers.forEach(RetainableByteBuffer::release); releasableBuffers.clear(); if (closedCause == null) @@ -437,9 +430,9 @@ public class FrameFlusher extends IteratingCallback private void releaseAggregate() { - if (BufferUtil.isEmpty(batchBuffer)) + if (batchBuffer != null && !batchBuffer.hasRemaining()) { - bufferPool.release(batchBuffer); + batchBuffer.release(); batchBuffer = null; } } @@ -502,12 +495,11 @@ public class FrameFlusher extends IteratingCallback return String.format("%s[queueSize=%d,aggregate=%s]", super.toString(), getQueueSize(), - BufferUtil.toDetailString(batchBuffer)); + batchBuffer); } - private class Entry extends FrameEntry + private static class Entry extends FrameEntry { - private ByteBuffer headerBuffer; private final long timeOfCreation = System.currentTimeMillis(); private Entry(Frame frame, Callback callback, boolean batch) diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java index ad9c6454321..a1654ff7c84 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java @@ -16,9 +16,10 @@ package org.eclipse.jetty.websocket.core.internal; import java.io.Closeable; import java.nio.ByteBuffer; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.websocket.core.CloseStatus; import org.eclipse.jetty.websocket.core.Configuration; import org.eclipse.jetty.websocket.core.Frame; @@ -46,7 +47,8 @@ public class Parser } private static final Logger LOG = LoggerFactory.getLogger(Parser.class); - private final ByteBufferPool bufferPool; + + private final RetainableByteBufferPool bufferPool; private final Configuration configuration; // State specific @@ -55,14 +57,14 @@ public class Parser private int cursor; private byte[] mask; private int payloadLength; - private ByteBuffer aggregate; + private RetainableByteBuffer aggregate; - public Parser(ByteBufferPool bufferPool) + public Parser(RetainableByteBufferPool bufferPool) { this(bufferPool, new Configuration.ConfigurationCustomizer()); } - public Parser(ByteBufferPool bufferPool, Configuration configuration) + public Parser(RetainableByteBufferPool bufferPool, Configuration configuration) { this.bufferPool = bufferPool; this.configuration = configuration; @@ -135,7 +137,7 @@ public class Parser else if (payloadLength == 0) { state = State.START; - return newFrame(firstByte, mask, null, false); + return newFrame(firstByte, mask, null, null); } else { @@ -158,7 +160,7 @@ public class Parser else if (payloadLength == 0) { state = State.START; - return newFrame(firstByte, mask, null, false); + return newFrame(firstByte, mask, null, null); } else { @@ -176,7 +178,7 @@ public class Parser if (payloadLength == 0) { state = State.START; - return newFrame(firstByte, mask, null, false); + return newFrame(firstByte, mask, null, null); } state = State.PAYLOAD; } @@ -198,7 +200,7 @@ public class Parser if (payloadLength == 0) { state = State.START; - return newFrame(firstByte, mask, null, false); + return newFrame(firstByte, mask, null, null); } state = State.PAYLOAD; } @@ -263,7 +265,7 @@ public class Parser } } - protected ParsedFrame newFrame(byte firstByte, byte[] mask, ByteBuffer payload, boolean releaseable) + protected ParsedFrame newFrame(byte firstByte, byte[] mask, ByteBuffer payload, Runnable releaser) { // Validate OpCode byte opcode = OpCode.getOpCode(firstByte); @@ -275,7 +277,7 @@ public class Parser if (OpCode.isControlFrame(opcode) && !fin) throw new ProtocolException("Fragmented Control Frame [" + OpCode.name(opcode) + "]"); - return new ParsedFrame(firstByte, mask, payload, releaseable); + return new ParsedFrame(firstByte, mask, payload, releaser); } private ParsedFrame autoFragment(ByteBuffer buffer, int fragmentSize) @@ -297,7 +299,7 @@ public class Parser content.limit(fragmentSize); buffer.position(buffer.position() + fragmentSize); - final ParsedFrame frame = newFrame((byte)(firstByte & 0x7F), mask, content, false); + final ParsedFrame frame = newFrame((byte)(firstByte & 0x7F), mask, content, null); mask = nextMask; firstByte = (byte)((firstByte & 0x80) | OpCode.CONTINUATION); state = State.FRAGMENT; @@ -331,14 +333,14 @@ public class Parser // No space in the buffer, so we have to copy the partial payload aggregate = bufferPool.acquire(payloadLength, false); - BufferUtil.append(aggregate, buffer); + BufferUtil.append(aggregate.getByteBuffer(), buffer); return null; } if (available == payloadLength) { // All the available data is for this frame and completes it - ParsedFrame frame = newFrame(firstByte, mask, buffer.slice(), false); + ParsedFrame frame = newFrame(firstByte, mask, buffer.slice(), null); buffer.position(buffer.limit()); state = State.START; return frame; @@ -349,7 +351,7 @@ public class Parser int limit = buffer.limit(); int end = buffer.position() + payloadLength; buffer.limit(end); - final ParsedFrame frame = newFrame(firstByte, mask, buffer.slice(), false); + final ParsedFrame frame = newFrame(firstByte, mask, buffer.slice(), null); buffer.position(end); buffer.limit(limit); state = State.START; @@ -363,26 +365,30 @@ public class Parser if (available < expecting) { // not enough data to complete this frame, just copy it - BufferUtil.append(aggregate, buffer); + BufferUtil.append(aggregate.getByteBuffer(), buffer); return null; } if (available == expecting) { // All the available data is for this frame and completes it - BufferUtil.append(aggregate, buffer); + BufferUtil.append(aggregate.getByteBuffer(), buffer); state = State.START; - return newFrame(firstByte, mask, aggregate, true); + // Capture the current aggregate to release it. + RetainableByteBuffer aggregate = this.aggregate; + return newFrame(firstByte, mask, aggregate.getByteBuffer(), aggregate::release); } // The buffer contains data for this frame and subsequent frames // Copy the first part of the buffer to the frame and complete it int limit = buffer.limit(); buffer.limit(buffer.position() + expecting); - BufferUtil.append(aggregate, buffer); + BufferUtil.append(aggregate.getByteBuffer(), buffer); buffer.limit(limit); state = State.START; - return newFrame(firstByte, mask, aggregate, true); + // Capture the current aggregate to release it. + RetainableByteBuffer aggregate = this.aggregate; + return newFrame(firstByte, mask, aggregate.getByteBuffer(), aggregate::release); } } @@ -390,19 +396,19 @@ public class Parser public String toString() { return String - .format("Parser@%x[s=%s,c=%d,o=0x%x,m=%s,l=%d]", hashCode(), state, cursor, firstByte, mask == null ? "-" : TypeUtil.toHexString(mask), payloadLength); + .format("Parser@%x[s=%s,c=%d,o=0x%x,m=%s,l=%d]", hashCode(), state, cursor, firstByte, mask == null ? "-" : StringUtil.toHexString(mask), payloadLength); } - public class ParsedFrame extends Frame implements Closeable, CloseStatus.Supplier + public static class ParsedFrame extends Frame implements Closeable, CloseStatus.Supplier { final CloseStatus closeStatus; - final boolean releaseable; + final Runnable releaser; - public ParsedFrame(byte firstByte, byte[] mask, ByteBuffer payload, boolean releaseable) + public ParsedFrame(byte firstByte, byte[] mask, ByteBuffer payload, Runnable releaser) { super(firstByte, mask, payload); demask(); - this.releaseable = releaseable; + this.releaser = releaser; if (getOpCode() == OpCode.CLOSE) { if (hasPayload()) @@ -419,8 +425,8 @@ public class Parser @Override public void close() { - if (releaseable) - bufferPool.release(getPayload()); + if (releaser != null) + releaser.run(); } @Override @@ -431,7 +437,7 @@ public class Parser public boolean isReleaseable() { - return releaseable; + return releaser != null; } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/PerMessageDeflateExtension.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/PerMessageDeflateExtension.java index beac35ad459..c175ea4b668 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/PerMessageDeflateExtension.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/PerMessageDeflateExtension.java @@ -22,6 +22,7 @@ import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.compression.DeflaterPool; @@ -294,25 +295,26 @@ public class PerMessageDeflateExtension extends AbstractExtension implements Dem private boolean deflate(Callback callback) { - // Get a buffer for the inflated payload. + // Get a buffer for the deflated payload. long maxFrameSize = getConfiguration().getMaxFrameSize(); int bufferSize = (maxFrameSize <= 0) ? deflateBufferSize : (int)Math.min(maxFrameSize, deflateBufferSize); - final ByteBuffer buffer = getBufferPool().acquire(bufferSize, false); - callback = Callback.from(callback, () -> getBufferPool().release(buffer)); - BufferUtil.clear(buffer); + RetainableByteBuffer buffer = getRetainableByteBufferPool().acquire(bufferSize, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + callback = Callback.from(callback, buffer::release); + buffer.clear(); // Fill up the buffer with a max length of bufferSize; boolean finished = false; Deflater deflater = getDeflater(); while (true) { - int compressed = deflater.deflate(buffer.array(), buffer.arrayOffset() + buffer.position(), - bufferSize - buffer.position(), Deflater.SYNC_FLUSH); - buffer.limit(buffer.limit() + compressed); + int compressed = deflater.deflate(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), + bufferSize - byteBuffer.position(), Deflater.SYNC_FLUSH); + byteBuffer.limit(byteBuffer.limit() + compressed); if (LOG.isDebugEnabled()) LOG.debug("Compressed {} bytes {}", compressed, toDetail(deflater)); - if (buffer.limit() == bufferSize) + if (byteBuffer.limit() == bufferSize) { // We need to fragment. if (!getConfiguration().isAutoFragment()) @@ -327,7 +329,7 @@ public class PerMessageDeflateExtension extends AbstractExtension implements Dem } } - ByteBuffer payload = buffer; + ByteBuffer payload = byteBuffer; if (payload.hasRemaining()) { // Handle tail bytes generated by SYNC_FLUSH. @@ -361,7 +363,7 @@ public class PerMessageDeflateExtension extends AbstractExtension implements Dem private class IncomingFlusher extends DemandingFlusher { private boolean _tailBytes; - private AtomicReference _payloadRef = new AtomicReference<>(); + private AtomicReference _payloadRef; public IncomingFlusher() { @@ -423,21 +425,22 @@ public class PerMessageDeflateExtension extends AbstractExtension implements Dem // Get a buffer for the inflated payload. long maxFrameSize = getConfiguration().getMaxFrameSize(); int bufferSize = (maxFrameSize <= 0) ? inflateBufferSize : (int)Math.min(maxFrameSize, inflateBufferSize); - ByteBuffer payload = getBufferPool().acquire(bufferSize, false); + RetainableByteBuffer payload = getRetainableByteBufferPool().acquire(bufferSize, false); _payloadRef = new AtomicReference<>(payload); - BufferUtil.clear(payload); + ByteBuffer byteBuffer = payload.getByteBuffer(); + payload.clear(); // Fill up the ByteBuffer with a max length of bufferSize; Inflater inflater = getInflater(); boolean complete = false; while (true) { - int decompressed = inflater.inflate(payload.array(), payload.arrayOffset() + payload.position(), bufferSize - payload.position()); - payload.limit(payload.limit() + decompressed); + int decompressed = inflater.inflate(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), bufferSize - byteBuffer.position()); + byteBuffer.limit(byteBuffer.limit() + decompressed); if (LOG.isDebugEnabled()) LOG.debug("Decompress: read {} {}", decompressed, toDetail(inflater)); - if (payload.limit() == bufferSize) + if (byteBuffer.limit() == bufferSize) { // We need to fragment. if (!getConfiguration().isAutoFragment()) @@ -461,21 +464,17 @@ public class PerMessageDeflateExtension extends AbstractExtension implements Dem Frame chunk = new Frame(first ? frame.getOpCode() : OpCode.CONTINUATION); chunk.setRsv1(false); - chunk.setPayload(payload); + chunk.setPayload(byteBuffer); chunk.setFin(frame.isFin() && complete); + // Capture the current AtomicReference. + AtomicReference payloadRef = _payloadRef; boolean succeedCallback = complete; - AtomicReference payloadRef = _payloadRef; - Callback payloadCallback = Callback.from(() -> + Callback payloadCallback = Callback.from(() -> releasePayload(payloadRef), Callback.from(() -> { - getBufferPool().release(payloadRef.getAndSet(null)); if (succeedCallback) callback.succeeded(); - }, t -> - { - getBufferPool().release(payloadRef.getAndSet(null)); - failFlusher(t); - }); + }, this::failFlusher)); emitFrame(chunk, payloadCallback); if (LOG.isDebugEnabled()) @@ -486,8 +485,17 @@ public class PerMessageDeflateExtension extends AbstractExtension implements Dem @Override protected void onCompleteFailure(Throwable cause) { - getBufferPool().release(_payloadRef.getAndSet(null)); + releasePayload(_payloadRef); super.onCompleteFailure(cause); } + + private void releasePayload(AtomicReference reference) + { + if (reference == null) + return; + RetainableByteBuffer buffer = reference.getAndSet(null); + if (buffer != null) + buffer.release(); + } } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 61d32895807..896142046d6 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -24,7 +24,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.LongAdder; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; @@ -54,7 +53,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio private static final int MIN_BUFFER_SIZE = Generator.MAX_HEADER_LENGTH; private final AutoLock lock = new AutoLock(); - private final ByteBufferPool bufferPool; private final RetainableByteBufferPool retainableByteBufferPool; private final Generator generator; private final Parser parser; @@ -80,11 +78,10 @@ public class WebSocketConnection extends AbstractConnection implements Connectio public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, - ByteBufferPool bufferPool, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) { - this(endp, executor, scheduler, bufferPool, retainableByteBufferPool, coreSession, null); + this(endp, executor, scheduler, retainableByteBufferPool, coreSession, null); } /** @@ -96,7 +93,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio * @param endp The endpoint ever which Websockot is sent/received * @param executor A thread executor to use for WS callbacks. * @param scheduler A scheduler to use for timeouts - * @param bufferPool A pool of buffers to use. * @param retainableByteBufferPool A pool of retainable buffers to use. * @param coreSession The WC core session to which frames are delivered. * @param randomMask A Random used to mask frames. If null then SecureRandom will be created if needed. @@ -104,7 +100,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, - ByteBufferPool bufferPool, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession, Random randomMask) @@ -114,14 +109,12 @@ public class WebSocketConnection extends AbstractConnection implements Connectio Objects.requireNonNull(endp, "EndPoint"); Objects.requireNonNull(coreSession, "Session"); Objects.requireNonNull(executor, "Executor"); - Objects.requireNonNull(bufferPool, "ByteBufferPool"); Objects.requireNonNull(retainableByteBufferPool, "RetainableByteBufferPool"); - this.bufferPool = bufferPool; this.retainableByteBufferPool = retainableByteBufferPool; this.coreSession = coreSession; this.generator = new Generator(); - this.parser = new Parser(bufferPool, coreSession); + this.parser = new Parser(retainableByteBufferPool, coreSession); this.flusher = new Flusher(scheduler, coreSession.getOutputBufferSize(), generator, endp); this.setInputBufferSize(coreSession.getInputBufferSize()); @@ -136,11 +129,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio return super.getExecutor(); } - public ByteBufferPool getBufferPool() - { - return bufferPool; - } - public Generator getGenerator() { return generator; @@ -305,7 +293,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio if (networkBuffer == null) throw new IllegalStateException(); - if (networkBuffer.getBuffer().hasRemaining()) + if (networkBuffer.getByteBuffer().hasRemaining()) throw new IllegalStateException(); networkBuffer.release(); @@ -392,7 +380,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio return true; fillingAndParsing = false; - if (networkBuffer.isEmpty()) + if (!networkBuffer.hasRemaining()) releaseNetworkBuffer(); return false; @@ -437,9 +425,9 @@ public class WebSocketConnection extends AbstractConnection implements Connectio while (true) { // Parse and handle frames - while (!networkBuffer.isEmpty()) + while (networkBuffer.hasRemaining()) { - Parser.ParsedFrame frame = parser.parse(networkBuffer.getBuffer()); + Parser.ParsedFrame frame = parser.parse(networkBuffer.getByteBuffer()); if (frame == null) break; @@ -453,7 +441,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio } // buffer must be empty here because parser is fully consuming - assert (networkBuffer.isEmpty()); + assert (!networkBuffer.hasRemaining()); if (!getEndPoint().isOpen()) { @@ -465,7 +453,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio if (networkBuffer.isRetained()) reacquireNetworkBuffer(); - int filled = getEndPoint().fill(networkBuffer.getBuffer()); // TODO check if compact is possible. + int filled = getEndPoint().fill(networkBuffer.getByteBuffer()); // TODO check if compact is possible. if (LOG.isDebugEnabled()) LOG.debug("endpointFill() filled={}: {}", filled, networkBuffer); @@ -494,7 +482,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio if (networkBuffer != null) { - BufferUtil.clear(networkBuffer.getBuffer()); + BufferUtil.clear(networkBuffer.getByteBuffer()); releaseNetworkBuffer(); } coreSession.processConnectionError(t, Callback.NOOP); @@ -516,7 +504,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio { networkBuffer = newNetworkBuffer(initialBuffer.remaining()); } - ByteBuffer buffer = networkBuffer.getBuffer(); + ByteBuffer buffer = networkBuffer.getByteBuffer(); BufferUtil.clearToFill(buffer); BufferUtil.put(initialBuffer, buffer); BufferUtil.flipToFlush(buffer, 0); @@ -638,7 +626,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio { private Flusher(Scheduler scheduler, int bufferSize, Generator generator, EndPoint endpoint) { - super(bufferPool, scheduler, generator, endpoint, bufferSize, 8); + super(retainableByteBufferPool, scheduler, generator, endpoint, bufferSize, 8); setUseDirectByteBuffers(isUseOutputDirectByteBuffers()); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketCoreSession.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketCoreSession.java index 757c4a94768..3a3aa7121e2 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketCoreSession.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketCoreSession.java @@ -26,7 +26,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Utf8Appendable; import org.eclipse.jetty.util.component.Dumpable; @@ -240,9 +240,9 @@ public class WebSocketCoreSession implements IncomingFrames, CoreSession, Dumpab } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return this.connection.getBufferPool(); + return components.getRetainableByteBufferPool(); } public void onEof() diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/ByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/ByteBufferMessageSink.java index ea8aa44b0d7..a3b945494c0 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/ByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/ByteBufferMessageSink.java @@ -19,7 +19,8 @@ import java.nio.ByteBuffer; import java.util.Objects; import org.eclipse.jetty.io.ByteBufferCallbackAccumulator; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CoreSession; @@ -83,17 +84,18 @@ public class ByteBufferMessageSink extends AbstractMessageSink callback = Callback.NOOP; if (frame.isFin()) { - ByteBufferPool bufferPool = session.getByteBufferPool(); - ByteBuffer buffer = bufferPool.acquire(out.getLength(), false); - out.writeTo(buffer); + RetainableByteBufferPool bufferPool = session.getRetainableByteBufferPool(); + RetainableByteBuffer buffer = bufferPool.acquire(out.getLength(), false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + out.writeTo(byteBuffer); try { - methodHandle.invoke(buffer); + methodHandle.invoke(byteBuffer); } finally { - bufferPool.release(buffer); + buffer.release(); } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageOutputStream.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageOutputStream.java index cf6921f4ca2..2d17db34e70 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageOutputStream.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageOutputStream.java @@ -17,7 +17,8 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FutureCallback; @@ -37,19 +38,17 @@ public class MessageOutputStream extends OutputStream private final AutoLock lock = new AutoLock(); private final CoreSession coreSession; - private final ByteBufferPool bufferPool; private final int bufferSize; + private final RetainableByteBuffer buffer; private long frameCount; private long bytesSent; - private ByteBuffer buffer; private Callback callback; private boolean closed; private byte messageOpCode = OpCode.BINARY; - public MessageOutputStream(CoreSession coreSession, ByteBufferPool bufferPool) + public MessageOutputStream(CoreSession coreSession, RetainableByteBufferPool bufferPool) { this.coreSession = coreSession; - this.bufferPool = bufferPool; this.bufferSize = coreSession.getOutputBufferSize(); this.buffer = bufferPool.acquire(bufferSize, true); } @@ -120,14 +119,14 @@ public class MessageOutputStream extends OutputStream private void flush(boolean fin) throws IOException { - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { if (closed) throw new IOException("Stream is closed"); closed = fin; Frame frame = new Frame(frameCount == 0 ? messageOpCode : OpCode.CONTINUATION); - frame.setPayload(buffer); + frame.setPayload(buffer.getByteBuffer()); frame.setFin(fin); int initialBufferSize = buffer.remaining(); @@ -143,7 +142,7 @@ public class MessageOutputStream extends OutputStream try { assert buffer.remaining() == initialBufferSize; - BufferUtil.clear(buffer); + buffer.clear(); } catch (Throwable t) { @@ -154,7 +153,7 @@ public class MessageOutputStream extends OutputStream private void send(ByteBuffer data) throws IOException { - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { if (closed) throw new IOException("Stream is closed"); @@ -163,7 +162,7 @@ public class MessageOutputStream extends OutputStream { int bufferRemainingSpace = bufferSize - buffer.remaining(); int copied = Math.min(bufferRemainingSpace, data.remaining()); - BufferUtil.append(buffer, data.array(), data.arrayOffset() + data.position(), copied); + BufferUtil.append(buffer.getByteBuffer(), data.array(), data.arrayOffset() + data.position(), copied); data.position(data.position() + copied); if (data.hasRemaining()) @@ -178,7 +177,7 @@ public class MessageOutputStream extends OutputStream try { flush(true); - bufferPool.release(buffer); + buffer.release(); if (LOG.isDebugEnabled()) LOG.debug("Stream closed, {} frames ({} bytes) sent", frameCount, bytesSent); // Notify without holding locks. @@ -194,7 +193,7 @@ public class MessageOutputStream extends OutputStream public void setCallback(Callback callback) { - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { this.callback = callback; } @@ -203,7 +202,7 @@ public class MessageOutputStream extends OutputStream private void notifySuccess() { Callback callback; - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { callback = this.callback; } @@ -214,7 +213,7 @@ public class MessageOutputStream extends OutputStream private void notifyFailure(Throwable failure) { Callback callback; - try (AutoLock l = lock.lock()) + try (AutoLock ignored = lock.lock()) { callback = this.callback; } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageWriter.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageWriter.java index 9bef7d66454..952b28c948b 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageWriter.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/messages/MessageWriter.java @@ -19,7 +19,7 @@ import java.nio.CharBuffer; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.OpCode; @@ -38,7 +38,7 @@ public class MessageWriter extends Writer .onUnmappableCharacter(CodingErrorAction.REPORT) .onMalformedInput(CodingErrorAction.REPORT); - public MessageWriter(CoreSession coreSession, ByteBufferPool bufferPool) + public MessageWriter(CoreSession coreSession, RetainableByteBufferPool bufferPool) { this.outputStream = new MessageOutputStream(coreSession, bufferPool); this.outputStream.setMessageType(OpCode.TEXT); @@ -67,4 +67,4 @@ public class MessageWriter extends Writer { outputStream.setCallback(callback); } -} \ No newline at end of file +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java index dc34b96d129..d3ec78cfd92 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java @@ -15,7 +15,7 @@ package org.eclipse.jetty.websocket.core.server; import java.util.concurrent.Executor; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.DecoratedObjectFactory; @@ -27,7 +27,7 @@ import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry; /** * A collection of components which are the resources needed for websockets such as - * {@link ByteBufferPool}, {@link WebSocketExtensionRegistry}, and {@link DecoratedObjectFactory}. + * {@link RetainableByteBufferPool}, {@link WebSocketExtensionRegistry}, and {@link DecoratedObjectFactory}. * * These components should be accessed through {@link WebSocketServerComponents#getWebSocketComponents} so that * the instance can be shared by being stored as a bean on the ContextHandler. @@ -39,7 +39,7 @@ public class WebSocketServerComponents extends WebSocketComponents public static final String WEBSOCKET_DEFLATER_POOL_ATTRIBUTE = "jetty.websocket.deflater"; public static final String WEBSOCKET_BUFFER_POOL_ATTRIBUTE = "jetty.websocket.bufferPool"; - WebSocketServerComponents(InflaterPool inflaterPool, DeflaterPool deflaterPool, ByteBufferPool bufferPool, DecoratedObjectFactory objectFactory, Executor executor) + WebSocketServerComponents(InflaterPool inflaterPool, DeflaterPool deflaterPool, RetainableByteBufferPool bufferPool, DecoratedObjectFactory objectFactory, Executor executor) { super(null, objectFactory, bufferPool, inflaterPool, deflaterPool, executor); } @@ -53,7 +53,7 @@ public class WebSocketServerComponents extends WebSocketComponents *

    *

    * Servlet context attributes can be set with {@link #WEBSOCKET_BUFFER_POOL_ATTRIBUTE}, {@link #WEBSOCKET_INFLATER_POOL_ATTRIBUTE} - * and {@link #WEBSOCKET_DEFLATER_POOL_ATTRIBUTE} to override the {@link ByteBufferPool}, {@link DeflaterPool} or + * and {@link #WEBSOCKET_DEFLATER_POOL_ATTRIBUTE} to override the {@link RetainableByteBufferPool}, {@link DeflaterPool} or * {@link InflaterPool} used by the components, otherwise this will try to use the pools shared on the {@link Server}. *

    * @param server the server. @@ -75,9 +75,9 @@ public class WebSocketServerComponents extends WebSocketComponents if (deflaterPool == null) deflaterPool = DeflaterPool.ensurePool(server); - ByteBufferPool bufferPool = (ByteBufferPool)context.getAttribute(WEBSOCKET_BUFFER_POOL_ATTRIBUTE); + RetainableByteBufferPool bufferPool = (RetainableByteBufferPool)context.getAttribute(WEBSOCKET_BUFFER_POOL_ATTRIBUTE); if (bufferPool == null) - bufferPool = server.getBean(ByteBufferPool.class); + bufferPool = server.getRetainableByteBufferPool(); Executor executor = (Executor)context.getAttribute("org.eclipse.jetty.server.Executor"); if (executor == null) diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java index 70e743c79ed..6c6fac9d3c7 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java @@ -20,7 +20,6 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.PreEncodedHttpField; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.ConnectionMetaData; @@ -189,9 +188,9 @@ public abstract class AbstractHandshaker implements Handshaker protected abstract WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession); - protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) + protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) { - return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, retainableByteBufferPool, coreSession); + return new WebSocketConnection(endPoint, executor, scheduler, retainableByteBufferPool, coreSession); } protected abstract void prepareResponse(Response response, WebSocketNegotiation negotiation); diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index ee2dd2a546d..6b676064129 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -21,7 +21,6 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.ConnectionMetaData; import org.eclipse.jetty.server.Connector; @@ -80,9 +79,8 @@ public final class RFC6455Handshaker extends AbstractHandshaker { ConnectionMetaData connectionMetaData = baseRequest.getConnectionMetaData(); Connector connector = connectionMetaData.getConnector(); - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = byteBufferPool.asRetainableByteBufferPool(); - return newWebSocketConnection(connectionMetaData.getConnection().getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); + RetainableByteBufferPool retainableByteBufferPool = connector.getRetainableByteBufferPool(); + return newWebSocketConnection(connectionMetaData.getConnection().getEndPoint(), connector.getExecutor(), connector.getScheduler(), retainableByteBufferPool, coreSession); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index 0722faf9a3f..b838f05a939 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.websocket.core.server.internal; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; @@ -60,11 +59,10 @@ public class RFC8441Handshaker extends AbstractHandshaker protected WebSocketConnection createWebSocketConnection(Request request, WebSocketCoreSession coreSession) { Connector connector = request.getConnectionMetaData().getConnector(); - ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = byteBufferPool.asRetainableByteBufferPool(); + RetainableByteBufferPool retainableByteBufferPool = connector.getRetainableByteBufferPool(); TunnelSupport tunnelSupport = request.getTunnelSupport(); EndPoint endPoint = tunnelSupport.getEndPoint(); - return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); + return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), retainableByteBufferPool, coreSession); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/GeneratorParserRoundTripTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/GeneratorParserRoundTripTest.java index 1f792181125..725551fd793 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/GeneratorParserRoundTripTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/GeneratorParserRoundTripTest.java @@ -17,8 +17,9 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.websocket.core.internal.Generator; import org.junit.jupiter.api.Test; @@ -28,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class GeneratorParserRoundTripTest { - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testParserAndGenerator() throws Exception @@ -38,20 +39,21 @@ public class GeneratorParserRoundTripTest String message = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; - ByteBuffer out = bufferPool.acquire(8192, false); + RetainableByteBuffer out = bufferPool.acquire(8192, false); try { // Generate Buffer + ByteBuffer byteBuffer = out.getByteBuffer(); Frame frame = new Frame(OpCode.TEXT).setPayload(message); - gen.generateHeader(frame, out); - gen.generatePayload(frame, out); + gen.generateHeader(frame, byteBuffer); + gen.generatePayload(frame, byteBuffer); // Parse Buffer - capture.parse(out); + capture.parse(byteBuffer); } finally { - bufferPool.release(out); + out.release(); } // Validate @@ -67,9 +69,11 @@ public class GeneratorParserRoundTripTest String message = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; - ByteBuffer out = bufferPool.acquire(8192, false); + RetainableByteBuffer out = bufferPool.acquire(8192, false); try { + ByteBuffer byteBuffer = out.getByteBuffer(); + // Setup Frame Frame frame = new Frame(OpCode.TEXT).setPayload(message); @@ -79,15 +83,15 @@ public class GeneratorParserRoundTripTest frame.setMask(mask); // Generate Buffer - gen.generateHeader(frame, out); - gen.generatePayload(frame, out); + gen.generateHeader(frame, byteBuffer); + gen.generatePayload(frame, byteBuffer); // Parse Buffer - capture.parse(out); + capture.parse(byteBuffer); } finally { - bufferPool.release(out); + out.release(); } // Validate diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/MessageHandlerTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/MessageHandlerTest.java index 8149fb2e78e..04875c98dc0 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/MessageHandlerTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/MessageHandlerTest.java @@ -19,8 +19,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FutureCallback; @@ -61,7 +61,7 @@ public class MessageHandlerTest coreSession = new CoreSession.Empty() { - private final ByteBufferPool byteBufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Override public void sendFrame(Frame frame, Callback callback, boolean batch) @@ -71,9 +71,9 @@ public class MessageHandlerTest } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return byteBufferPool; + return bufferPool; } }; diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadCloseStatusCodesTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadCloseStatusCodesTest.java index b1cef8d7b7d..47a0e2d8f70 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadCloseStatusCodesTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadCloseStatusCodesTest.java @@ -16,8 +16,6 @@ package org.eclipse.jetty.websocket.core; import java.nio.ByteBuffer; import java.util.stream.Stream; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.core.exception.ProtocolException; @@ -60,8 +58,6 @@ public class ParserBadCloseStatusCodesTest ); } - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); - @ParameterizedTest(name = "closeCode={0} {1}") @MethodSource("data") public void testBadStatusCode(int closeCode, String description) diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadOpCodesTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadOpCodesTest.java index 8613e7fef16..5261233f707 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadOpCodesTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserBadOpCodesTest.java @@ -16,8 +16,6 @@ package org.eclipse.jetty.websocket.core; import java.nio.ByteBuffer; import java.util.stream.Stream; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.core.exception.ProtocolException; @@ -52,8 +50,6 @@ public class ParserBadOpCodesTest ); } - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); - @ParameterizedTest(name = "opcode={0} {1}") @MethodSource("data") public void testBadOpCode(byte opcode, String description) diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserCapture.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserCapture.java index 7062a12e308..ac53eedd30f 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserCapture.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/ParserCapture.java @@ -55,7 +55,7 @@ public class ParserCapture this.coreSession = new WebSocketCoreSession(new TestMessageHandler(), behavior, Negotiated.from(exStack), components); coreSession.setAutoFragment(false); coreSession.setMaxFrameSize(0); - this.parser = new Parser(components.getBufferPool(), coreSession); + this.parser = new Parser(components.getRetainableByteBufferPool(), coreSession); } public void parse(ByteBuffer buffer) diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketTester.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketTester.java index fd80a25ccfd..692cec81bd3 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketTester.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketTester.java @@ -24,8 +24,9 @@ import java.util.Base64; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.io.ArrayByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.websocket.core.internal.Parser; @@ -41,8 +42,8 @@ public class WebSocketTester { private static final String NON_RANDOM_KEY = Base64.getEncoder().encodeToString("0123456701234567".getBytes()); private static SslContextFactory.Client sslContextFactory; - protected ByteBufferPool bufferPool; - protected ByteBuffer buffer; + protected RetainableByteBufferPool bufferPool; + protected RetainableByteBuffer buffer; protected Parser parser; @BeforeAll @@ -62,7 +63,7 @@ public class WebSocketTester @BeforeEach public void before() { - bufferPool = new ArrayByteBufferPool(); + bufferPool = new ArrayRetainableByteBufferPool(); parser = new Parser(bufferPool); } @@ -160,26 +161,28 @@ public class WebSocketTester while (true) { - Parser.ParsedFrame frame = parser.parse(buffer); - if (!buffer.hasRemaining()) - BufferUtil.clear(buffer); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + Parser.ParsedFrame frame = parser.parse(byteBuffer); + if (!byteBuffer.hasRemaining()) + BufferUtil.clear(byteBuffer); if (frame != null) return frame; - int p = BufferUtil.flipToFill(buffer); - int len = in.read(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); + int p = BufferUtil.flipToFill(byteBuffer); + int len = in.read(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), byteBuffer.remaining()); if (len < 0) return null; - buffer.position(buffer.position() + len); - BufferUtil.flipToFlush(buffer, p); + byteBuffer.position(byteBuffer.position() + len); + BufferUtil.flipToFlush(byteBuffer, p); } } protected void receiveEof(InputStream in) throws IOException { - ByteBuffer buffer = bufferPool.acquire(4096, false); - BufferUtil.clearToFill(buffer); - int len = in.read(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); + RetainableByteBuffer buffer = bufferPool.acquire(4096, false); + ByteBuffer byteBuffer = buffer.getByteBuffer(); + BufferUtil.clearToFill(byteBuffer); + int len = in.read(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), buffer.remaining()); if (len < 0) return; diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/AbstractExtensionTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/AbstractExtensionTest.java index 160f56c46a4..07e0b4992d2 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/AbstractExtensionTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/AbstractExtensionTest.java @@ -26,7 +26,7 @@ public abstract class AbstractExtensionTest @BeforeEach public void init() { - clientExtensions = new ExtensionTool(components.getBufferPool()); - serverExtensions = new ExtensionTool(components.getBufferPool()); + clientExtensions = new ExtensionTool(); + serverExtensions = new ExtensionTool(); } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionTool.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionTool.java index eb51be10f02..99a53fea54c 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionTool.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionTool.java @@ -18,8 +18,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.toolchain.test.ByteBufferAssert; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.FutureCallback; @@ -63,7 +62,7 @@ public class ExtensionTool assertThat(extensionStack.getExtensions().size(), equalTo(1)); this.capture = new IncomingFramesCapture(); - this.parser = new Parser(new MappedByteBufferPool()); + this.parser = new Parser(new ArrayRetainableByteBufferPool()); } public String getRequestedExtParams() @@ -161,7 +160,7 @@ public class ExtensionTool private final WebSocketComponents components; - public ExtensionTool(ByteBufferPool bufferPool) + public ExtensionTool() { this.components = new WebSocketComponents(); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/internal/FrameFlusherTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/internal/FrameFlusherTest.java index 6677d875647..321d6cc78ef 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/internal/FrameFlusherTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/internal/FrameFlusherTest.java @@ -26,8 +26,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; @@ -52,7 +52,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class FrameFlusherTest { - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); private Scheduler scheduler; @BeforeEach @@ -231,7 +231,7 @@ public class FrameFlusherTest public Parser parser; public LinkedBlockingQueue incomingFrames = new LinkedBlockingQueue<>(); - public CapturingEndPoint(ByteBufferPool bufferPool) + public CapturingEndPoint(RetainableByteBufferPool bufferPool) { parser = new Parser(bufferPool); } @@ -282,7 +282,7 @@ public class FrameFlusherTest blockTime = time; } - public BlockingEndpoint(ByteBufferPool bufferPool) + public BlockingEndpoint(RetainableByteBufferPool bufferPool) { super(bufferPool); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/MessageWriterTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/MessageWriterTest.java index 865199f4cef..ca25168bef0 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/MessageWriterTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/MessageWriterTest.java @@ -20,8 +20,8 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.websocket.core.CoreSession; @@ -37,7 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; public class MessageWriterTest { private final CoreSession coreSession = new CoreSession.Empty(); - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testMultipleWrites() throws Exception diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java index ac35c0577f2..2e24e306c50 100644 --- a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java @@ -18,8 +18,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; @@ -146,9 +146,9 @@ public class MavenServerConnector extends ContainerLifeCycle implements Connecto } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return checkDelegate().getByteBufferPool(); + return checkDelegate().getRetainableByteBufferPool(); } @Override diff --git a/jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServlet.java b/jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServlet.java index b488e85a338..1adcff3860d 100644 --- a/jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServlet.java +++ b/jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServlet.java @@ -43,8 +43,9 @@ import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.client.Result; import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.server.handler.ConnectHandler; import org.eclipse.jetty.util.BufferUtil; @@ -774,8 +775,8 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet try { this.transformer = transformer; - ByteBufferPool byteBufferPool = httpClient == null ? null : httpClient.getByteBufferPool(); - this.decoder = new GZIPContentDecoder(byteBufferPool, GZIPContentDecoder.DEFAULT_BUFFER_SIZE); + RetainableByteBufferPool bufferPool = httpClient == null ? null : httpClient.getRetainableByteBufferPool(); + this.decoder = new GZIPContentDecoder(bufferPool, GZIPContentDecoder.DEFAULT_BUFFER_SIZE); this.out = new ByteArrayOutputStream(); this.gzipOut = new GZIPOutputStream(out); } @@ -791,7 +792,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet if (logger.isDebugEnabled()) logger.debug("Ungzipping {} bytes, finished={}", input.remaining(), finished); - List decodeds = Collections.emptyList(); + List decodeds = Collections.emptyList(); if (!input.hasRemaining()) { if (finished) @@ -802,14 +803,14 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet decodeds = new ArrayList<>(); while (true) { - ByteBuffer decoded = decoder.decode(input); + RetainableByteBuffer decoded = decoder.decode(input); decodeds.add(decoded); boolean decodeComplete = !input.hasRemaining() && !decoded.hasRemaining(); boolean complete = finished && decodeComplete; if (logger.isDebugEnabled()) logger.debug("Ungzipped {} bytes, complete={}", decoded.remaining(), complete); if (decoded.hasRemaining() || complete) - transformer.transform(decoded, complete, buffers); + transformer.transform(decoded.getByteBuffer(), complete, buffers); if (decodeComplete) break; } @@ -822,7 +823,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet output.add(result); } - decodeds.forEach(decoder::release); + decodeds.forEach(RetainableByteBuffer::release); } private ByteBuffer gzip(List buffers, boolean finished) throws IOException diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java index ef5f30e1468..114dd6aba07 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java @@ -60,8 +60,7 @@ import org.eclipse.jetty.http.content.ResourceHttpContentFactory; import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory; import org.eclipse.jetty.http.content.VirtualHttpContentFactory; import org.eclipse.jetty.io.ByteBufferInputStream; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NoopByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Context; import org.eclipse.jetty.server.HttpStream; import org.eclipse.jetty.server.Request; @@ -168,9 +167,9 @@ public class DefaultServlet extends HttpServlet long cacheValidationTime = getInitParameter("cacheValidationTime") != null ? Long.parseLong(getInitParameter("cacheValidationTime")) : -2; if (maxCachedFiles != -2 || maxCacheSize != -2 || maxCachedFileSize != -2 || cacheValidationTime != -2) { - ByteBufferPool byteBufferPool = getByteBufferPool(servletContextHandler); + RetainableByteBufferPool bufferPool = getRetainableByteBufferPool(servletContextHandler); ValidatingCachingHttpContentFactory cached = new ValidatingCachingHttpContentFactory(contentFactory, - (cacheValidationTime > -2) ? cacheValidationTime : Duration.ofSeconds(1).toMillis(), byteBufferPool); + (cacheValidationTime > -2) ? cacheValidationTime : Duration.ofSeconds(1).toMillis(), bufferPool); contentFactory = cached; if (maxCacheSize >= 0) cached.setMaxCacheSize(maxCacheSize); @@ -239,15 +238,14 @@ public class DefaultServlet extends HttpServlet } } - private static ByteBufferPool getByteBufferPool(ContextHandler contextHandler) + private static RetainableByteBufferPool getRetainableByteBufferPool(ContextHandler contextHandler) { if (contextHandler == null) - return new NoopByteBufferPool(); + return new RetainableByteBufferPool.NonPooling(); Server server = contextHandler.getServer(); if (server == null) - return new NoopByteBufferPool(); - ByteBufferPool byteBufferPool = server.getBean(ByteBufferPool.class); - return (byteBufferPool == null) ? new NoopByteBufferPool() : byteBufferPool; + return new RetainableByteBufferPool.NonPooling(); + return server.getRetainableByteBufferPool(); } private String getInitParameter(String name, String... deprecated) diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ErrorHandler.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ErrorHandler.java index 23b90d9e371..1b321076d8a 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ErrorHandler.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ErrorHandler.java @@ -293,7 +293,7 @@ public class ErrorHandler implements Request.Processor // TODO error page may cause a BufferOverflow. In which case we try // TODO again with stacks disabled. If it still overflows, it is // TODO written without a body. - ByteBuffer buffer = baseRequest.getResponse().getHttpOutput().getBuffer(); + ByteBuffer buffer = baseRequest.getResponse().getHttpOutput().getByteBuffer(); ByteBufferOutputStream out = new ByteBufferOutputStream(buffer); PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, charset)); diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java index 72fafee4ffd..c039c45d1ca 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java @@ -31,14 +31,16 @@ import jakarta.servlet.ServletOutputStream; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.WriteListener; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EofException; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.ConnectionMetaData; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.NanoTime; @@ -128,7 +130,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable private final ConnectionMetaData _connectionMetaData; private final ServletChannel _servletChannel; private final Response _response; - private final ByteBufferPool _byteBufferPool; + private final RetainableByteBufferPool _bufferPool; private final ServletRequestState _channelState; private final SharedBlockingCallback _writeBlocker; private ApiState _apiState = ApiState.BLOCKING; @@ -137,7 +139,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable private long _written; private long _flushed; private long _firstByteNanoTime = -1; - private ByteBuffer _aggregate; + private RetainableByteBuffer _aggregate; private int _bufferSize; private int _commitSize; private WriteListener _writeListener; @@ -149,7 +151,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _response = response; _servletChannel = channel; _connectionMetaData = _response.getRequest().getConnectionMetaData(); - _byteBufferPool = _response.getRequest().getComponents().getByteBufferPool(); + _bufferPool = _response.getRequest().getComponents().getRetainableByteBufferPool(); _channelState = _servletChannel.getState(); _writeBlocker = new WriteBlocker(_servletChannel); @@ -230,7 +232,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _state = State.CLOSED; closedCallback = _closedCallback; _closedCallback = null; - releaseBuffer(failure); + releaseBuffer(); wake = updateApiState(failure); } else if (_state == State.CLOSE) @@ -239,7 +241,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // We can now send a (probably empty) last buffer and then when it completes // onWriteComplete will be called again to actually execute the _completeCallback _state = State.CLOSING; - closeContent = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + closeContent = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; } else { @@ -314,10 +316,12 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (_aggregate == null) return getBufferSize(); - // compact to maximize space - BufferUtil.compact(_aggregate); + ByteBuffer byteBuffer = _aggregate.getByteBuffer(); - return BufferUtil.space(_aggregate); + // compact to maximize space + BufferUtil.compact(byteBuffer); + + return BufferUtil.space(byteBuffer); } public void softClose() @@ -397,7 +401,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Output is idle blocking state, but we still do an async close _apiState = ApiState.BLOCKED; _state = State.CLOSING; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case ASYNC: @@ -405,7 +409,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Output is idle in async state, so we can do an async close _apiState = ApiState.PENDING; _state = State.CLOSING; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case UNREADY: @@ -451,7 +455,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable try (AutoLock l = _channelState.lock()) { _state = State.CLOSED; - releaseBuffer(failure); + releaseBuffer(); } } @@ -503,7 +507,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _apiState = ApiState.BLOCKED; _state = State.CLOSING; blocker = _writeBlocker.acquire(); - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case BLOCKED: @@ -521,7 +525,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Output is idle in async state, so we can do an async close _apiState = ApiState.PENDING; _state = State.CLOSING; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case UNREADY: @@ -576,30 +580,27 @@ public class HttpOutput extends ServletOutputStream implements Runnable } } - public ByteBuffer getBuffer() + public ByteBuffer getByteBuffer() { try (AutoLock l = _channelState.lock()) { - return acquireBuffer(); + return acquireBuffer().getByteBuffer(); } } - private ByteBuffer acquireBuffer() + private RetainableByteBuffer acquireBuffer() { boolean useOutputDirectByteBuffers = _connectionMetaData.getHttpConfiguration().isUseOutputDirectByteBuffers(); if (_aggregate == null) - _aggregate = _byteBufferPool.acquire(getBufferSize(), useOutputDirectByteBuffers); + _aggregate = _bufferPool.acquire(getBufferSize(), useOutputDirectByteBuffers); return _aggregate; } - private void releaseBuffer(Throwable failure) + private void releaseBuffer() { if (_aggregate != null) { - if (failure == null) - _byteBufferPool.release(_aggregate); - else - _byteBufferPool.remove(_aggregate); + _aggregate.release(); _aggregate = null; } } @@ -647,7 +648,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { case BLOCKING: _apiState = ApiState.BLOCKED; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case ASYNC: @@ -727,7 +728,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Write will be aggregated if: // + it is smaller than the commitSize // + is not the last one, or is last but will fit in an already allocated aggregate buffer. - aggregate = len <= _commitSize && (!last || BufferUtil.hasContent(_aggregate) && len <= space); + aggregate = len <= _commitSize && (!last || (_aggregate != null && _aggregate.hasRemaining()) && len <= space); flush = last || !aggregate || len >= space; if (last && _state == State.OPEN) @@ -762,14 +763,14 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (aggregate) { acquireBuffer(); - int filled = BufferUtil.fill(_aggregate, b, off, len); + int filled = BufferUtil.fill(_aggregate.getByteBuffer(), b, off, len); // return if we are not complete, not full and filled all the content if (!flush) { if (LOG.isDebugEnabled()) LOG.debug("write(array) {} aggregated !flush {}", - stateString(), BufferUtil.toDetailString(_aggregate)); + stateString(), _aggregate); return; } @@ -781,7 +782,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (LOG.isDebugEnabled()) LOG.debug("write(array) {} last={} agg={} flush=true async={}, len={} {}", - stateString(), last, aggregate, async, len, BufferUtil.toDetailString(_aggregate)); + stateString(), last, aggregate, async, len, _aggregate); if (async) { @@ -795,15 +796,17 @@ public class HttpOutput extends ServletOutputStream implements Runnable { boolean complete = false; // flush any content from the aggregate - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { + ByteBuffer byteBuffer = _aggregate.getByteBuffer(); + complete = last && len == 0; - channelWrite(_aggregate, complete); + channelWrite(byteBuffer, complete); // should we fill aggregate again from the buffer? if (len > 0 && !last && len <= _commitSize && len <= maximizeAggregateSpace()) { - BufferUtil.append(_aggregate, b, off, len); + BufferUtil.append(byteBuffer, b, off, len); onWriteComplete(false, null); return; } @@ -856,7 +859,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable checkWritable(); long written = _written + len; last = _servletChannel.getResponse().isAllContentWritten(written); - flush = last || len > 0 || BufferUtil.hasContent(_aggregate); + flush = last || len > 0 || (_aggregate != null && _aggregate.hasRemaining()); if (last && _state == State.OPEN) _state = State.CLOSING; @@ -900,10 +903,10 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Blocking write // flush any content from the aggregate boolean complete = false; - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { complete = last && len == 0; - channelWrite(_aggregate, complete); + channelWrite(_aggregate.getByteBuffer(), complete); } // write any remaining content in the buffer directly @@ -965,7 +968,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _written = written; acquireBuffer(); - BufferUtil.append(_aggregate, (byte)b); + BufferUtil.append(_aggregate.getByteBuffer(), (byte)b); } // Check if all written or full @@ -979,7 +982,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { try { - channelWrite(_aggregate, last); + channelWrite(_aggregate.getByteBuffer(), last); onWriteComplete(last, null); } catch (Throwable t) @@ -1023,51 +1026,62 @@ public class HttpOutput extends ServletOutputStream implements Runnable encoder.reset(); } - CharBuffer in = CharBuffer.wrap(s); - CharBuffer crlf = eoln ? CharBuffer.wrap("\r\n") : null; - ByteBuffer out = _byteBufferPool.acquire((int)(1 + (s.length() + 2) * encoder.averageBytesPerChar()), false); - BufferUtil.flipToFill(out); - - while (true) + RetainableByteBuffer out = _bufferPool.acquire((int)(1 + (s.length() + 2) * encoder.averageBytesPerChar()), false); + try { - CoderResult result; - if (in.hasRemaining()) + CharBuffer in = CharBuffer.wrap(s); + CharBuffer crlf = eoln ? CharBuffer.wrap("\r\n") : null; + ByteBuffer byteBuffer = out.getByteBuffer(); + BufferUtil.flipToFill(byteBuffer); + + while (true) { - result = encoder.encode(in, out, crlf == null); - if (result.isUnderflow()) - if (crlf == null) - break; - else - continue; - } - else if (crlf != null && crlf.hasRemaining()) - { - result = encoder.encode(crlf, out, true); - if (result.isUnderflow()) + CoderResult result; + if (in.hasRemaining()) { - if (!encoder.flush(out).isUnderflow()) - result.throwException(); - break; + result = encoder.encode(in, byteBuffer, crlf == null); + if (result.isUnderflow()) + if (crlf == null) + break; + else + continue; } - } - else - break; + else if (crlf != null && crlf.hasRemaining()) + { + result = encoder.encode(crlf, byteBuffer, true); + if (result.isUnderflow()) + { + if (!encoder.flush(byteBuffer).isUnderflow()) + result.throwException(); + break; + } + } + else + break; - if (result.isOverflow()) - { - BufferUtil.flipToFlush(out, 0); - ByteBuffer bigger = BufferUtil.ensureCapacity(out, out.capacity() + s.length() + 2); - _byteBufferPool.release(out); - BufferUtil.flipToFill(bigger); - out = bigger; - continue; + if (result.isOverflow()) + { + BufferUtil.flipToFlush(byteBuffer, 0); + RetainableByteBuffer bigger = _bufferPool.acquire(out.capacity() + s.length() + 2, out.isDirect()); + BufferUtil.flipToFill(bigger.getByteBuffer()); + bigger.getByteBuffer().put(byteBuffer); + out.release(); + BufferUtil.flipToFill(bigger.getByteBuffer()); + out = bigger; + byteBuffer = bigger.getByteBuffer(); + continue; + } + + result.throwException(); } - result.throwException(); + BufferUtil.flipToFlush(byteBuffer, 0); + write(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.remaining()); + } + finally + { + out.release(); } - BufferUtil.flipToFlush(out, 0); - write(out.array(), out.arrayOffset(), out.remaining()); - _byteBufferPool.release(out); } /** @@ -1182,7 +1196,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { try (AutoLock l = _channelState.lock()) { - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { callback.failed(new IOException("cannot sendContent() after write()")); return false; @@ -1267,7 +1281,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _commitSize = config.getOutputAggregationSize(); if (_commitSize > _bufferSize) _commitSize = _bufferSize; - releaseBuffer(null); + releaseBuffer(); _written = 0; _writeListener = null; _onError = null; @@ -1281,8 +1295,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable { try (AutoLock l = _channelState.lock()) { - if (BufferUtil.hasContent(_aggregate)) - BufferUtil.clear(_aggregate); + if (_aggregate != null) + _aggregate.clear(); _written = 0; } } @@ -1462,7 +1476,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable } catch (Throwable t) { - if (t != e) + if (ExceptionUtil.areNotAssociated(e, t)) e.addSuppressed(t); } finally @@ -1484,10 +1498,10 @@ public class HttpOutput extends ServletOutputStream implements Runnable @Override protected Action process() throws Exception { - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { _flushed = true; - channelWrite(_aggregate, false, this); + channelWrite(_aggregate.getByteBuffer(), false, this); return Action.SCHEDULED; } @@ -1536,19 +1550,20 @@ public class HttpOutput extends ServletOutputStream implements Runnable protected Action process() throws Exception { // flush any content from the aggregate - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { _completed = _len == 0; - channelWrite(_aggregate, _last && _completed, this); + channelWrite(_aggregate.getByteBuffer(), _last && _completed, this); return Action.SCHEDULED; } // Can we just aggregate the remainder? if (!_last && _aggregate != null && _len < maximizeAggregateSpace() && _len < _commitSize) { - int position = BufferUtil.flipToFill(_aggregate); - BufferUtil.put(_buffer, _aggregate); - BufferUtil.flipToFlush(_aggregate, position); + ByteBuffer byteBuffer = _aggregate.getByteBuffer(); + int position = BufferUtil.flipToFill(byteBuffer); + BufferUtil.put(_buffer, byteBuffer); + BufferUtil.flipToFlush(byteBuffer, position); return Action.SUCCEEDED; } @@ -1602,7 +1617,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable private class InputStreamWritingCB extends NestedChannelWriteCB { private final InputStream _in; - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private boolean _eof; private boolean _closed; @@ -1611,7 +1626,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable super(callback, true); _in = in; // Reading from InputStream requires byte[], don't use direct buffers. - _buffer = _byteBufferPool.acquire(getBufferSize(), false); + _buffer = _bufferPool.acquire(getBufferSize(), false); } @Override @@ -1624,20 +1639,17 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (LOG.isDebugEnabled()) LOG.debug("EOF of {}", this); if (!_closed) - { _closed = true; - _byteBufferPool.release(_buffer); - IO.close(_in); - } - return Action.SUCCEEDED; } + ByteBuffer byteBuffer = _buffer.getByteBuffer(); + // Read until buffer full or EOF int len = 0; - while (len < _buffer.capacity() && !_eof) + while (len < byteBuffer.capacity() && !_eof) { - int r = _in.read(_buffer.array(), _buffer.arrayOffset() + len, _buffer.capacity() - len); + int r = _in.read(byteBuffer.array(), byteBuffer.arrayOffset() + len, byteBuffer.capacity() - len); if (r < 0) _eof = true; else @@ -1645,24 +1657,27 @@ public class HttpOutput extends ServletOutputStream implements Runnable } // write what we have - _buffer.position(0); - _buffer.limit(len); + byteBuffer.position(0); + byteBuffer.limit(len); _written += len; - channelWrite(_buffer, _eof, this); + channelWrite(byteBuffer, _eof, this); return Action.SCHEDULED; } + @Override + protected void onCompleteSuccess() + { + _buffer.release(); + IO.close(_in); + super.onCompleteSuccess(); + } + @Override public void onCompleteFailure(Throwable x) { - try - { - _byteBufferPool.release(_buffer); - } - finally - { - super.onCompleteFailure(x); - } + _buffer.release(); + IO.close(_in); + super.onCompleteFailure(x); } } @@ -1678,7 +1693,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable private class ReadableByteChannelWritingCB extends NestedChannelWriteCB { private final ReadableByteChannel _in; - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private boolean _eof; private boolean _closed; @@ -1687,7 +1702,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable super(callback, true); _in = in; boolean useOutputDirectByteBuffers = _connectionMetaData.getHttpConfiguration().isUseOutputDirectByteBuffers(); - _buffer = _byteBufferPool.acquire(getBufferSize(), useOutputDirectByteBuffers); + _buffer = _bufferPool.acquire(getBufferSize(), useOutputDirectByteBuffers); } @Override @@ -1700,33 +1715,39 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (LOG.isDebugEnabled()) LOG.debug("EOF of {}", this); if (!_closed) - { _closed = true; - _byteBufferPool.release(_buffer); - IO.close(_in); - } return Action.SUCCEEDED; } + ByteBuffer byteBuffer = _buffer.getByteBuffer(); + // Read from stream until buffer full or EOF - BufferUtil.clearToFill(_buffer); - while (_buffer.hasRemaining() && !_eof) + BufferUtil.clearToFill(byteBuffer); + while (byteBuffer.hasRemaining() && !_eof) { - _eof = (_in.read(_buffer)) < 0; + _eof = (_in.read(byteBuffer)) < 0; } // write what we have - BufferUtil.flipToFlush(_buffer, 0); - _written += _buffer.remaining(); - channelWrite(_buffer, _eof, this); + BufferUtil.flipToFlush(byteBuffer, 0); + _written += byteBuffer.remaining(); + channelWrite(byteBuffer, _eof, this); return Action.SCHEDULED; } + @Override + protected void onCompleteSuccess() + { + _buffer.release(); + IO.close(_in); + super.onCompleteSuccess(); + } + @Override public void onCompleteFailure(Throwable x) { - _byteBufferPool.release(_buffer); + _buffer.release(); IO.close(_in); super.onCompleteFailure(x); } diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannel.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannel.java index a7956fad06c..a906c5d5770 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannel.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannel.java @@ -743,7 +743,7 @@ public class ServletChannel try { _state.completing(); - getResponse().write(true, getResponse().getHttpOutput().getBuffer(), Callback.from(() -> _state.completed(null), _state::completed)); + getResponse().write(true, getResponse().getHttpOutput().getByteBuffer(), Callback.from(() -> _state.completed(null), _state::completed)); } catch (Throwable x) { diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketContainer.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketContainer.java index f5e9fcea88d..cd4ad79dc5b 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketContainer.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketContainer.java @@ -24,7 +24,7 @@ import java.util.function.Consumer; import jakarta.websocket.Extension; import jakarta.websocket.WebSocketContainer; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Dumpable; @@ -53,9 +53,9 @@ public abstract class JakartaWebSocketContainer extends ContainerLifeCycle imple protected abstract JakartaWebSocketFrameHandlerFactory getFrameHandlerFactory(); - public ByteBufferPool getBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return components.getBufferPool(); + return components.getRetainableByteBufferPool(); } public WebSocketExtensionRegistry getExtensionRegistry() diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java index 94c9c938bf5..70ef22ee04f 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java @@ -49,12 +49,12 @@ public class JakartaWebSocketRemoteEndpoint implements jakarta.websocket.RemoteE protected MessageWriter newMessageWriter() { - return new MessageWriter(coreSession, session.getContainerImpl().getBufferPool()); + return new MessageWriter(coreSession, session.getContainerImpl().getRetainableByteBufferPool()); } protected MessageOutputStream newMessageOutputStream() { - return new MessageOutputStream(coreSession, session.getContainerImpl().getBufferPool()); + return new MessageOutputStream(coreSession, session.getContainerImpl().getRetainableByteBufferPool()); } @Override diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/AbstractSessionTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/AbstractSessionTest.java index 565b4d9cd57..8a68ff66a04 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/AbstractSessionTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/AbstractSessionTest.java @@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit; import jakarta.websocket.Endpoint; import jakarta.websocket.EndpointConfig; import jakarta.websocket.Session; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.junit.jupiter.api.AfterAll; @@ -64,9 +64,9 @@ public abstract class AbstractSessionTest } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return components.getBufferPool(); + return components.getRetainableByteBufferPool(); } public void waitForDemand(long timeout, TimeUnit timeUnit) throws InterruptedException diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/MessageWriterTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/MessageWriterTest.java index 0a1872ef0e8..d44872ae988 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/MessageWriterTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/MessageWriterTest.java @@ -19,8 +19,8 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.websocket.core.CoreSession; @@ -34,7 +34,7 @@ import static org.hamcrest.Matchers.is; public class MessageWriterTest { - private ByteBufferPool bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testSingleByteArray512b() throws IOException, InterruptedException diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/LocalServer.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/LocalServer.java index b2b564569d6..ede2a226289 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/LocalServer.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/LocalServer.java @@ -28,8 +28,8 @@ import org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketSessionLi import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; import org.eclipse.jetty.ee10.websocket.jakarta.server.internal.JakartaWebSocketServerContainer; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -60,7 +60,7 @@ public class LocalServer extends ContainerLifeCycle implements LocalFuzzer.Provi } private static final Logger LOG = LoggerFactory.getLogger(LocalServer.class); - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); private Server server; private ServerConnector connector; private LocalConnector localConnector; diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientClassLoaderTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientClassLoaderTest.java index 2cfc3e33817..633d5653948 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientClassLoaderTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientClassLoaderTest.java @@ -38,7 +38,7 @@ import org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketContainer import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketConfiguration; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest; @@ -150,7 +150,7 @@ public class JakartaClientClassLoaderTest app.copyLib(CoreClientUpgradeRequest.class, "jetty-websocket-core-client.jar"); app.copyLib(WebSocketComponents.class, "jetty-websocket-core-common.jar"); app.copyLib(Response.class, "jetty-client.jar"); - app.copyLib(ByteBufferPool.class, "jetty-io.jar"); + app.copyLib(EndPoint.class, "jetty-io.jar"); app.copyLib(BadMessageException.class, "jetty-http.jar"); app.copyLib(XmlConfiguration.class, "jetty-xml.jar"); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java index 284ee1d3bc8..9eacc83d004 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java @@ -33,7 +33,7 @@ import org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketContainer import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketConfiguration; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest; @@ -99,7 +99,7 @@ public class JakartaClientShutdownWithServerWebAppTest app.copyLib(CoreClientUpgradeRequest.class, "jetty-websocket-core-client.jar"); app.copyLib(WebSocketComponents.class, "jetty-websocket-core-common.jar"); app.copyLib(Response.class, "jetty-client.jar"); - app.copyLib(ByteBufferPool.class, "jetty-io.jar"); + app.copyLib(EndPoint.class, "jetty-io.jar"); app.copyLib(BadMessageException.class, "jetty-http.jar"); return app; diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/WebSocketClient.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/WebSocketClient.java index c20c5c59846..b23fb35da40 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/WebSocketClient.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/WebSocketClient.java @@ -39,8 +39,8 @@ import org.eclipse.jetty.ee10.websocket.client.impl.JettyClientUpgradeRequest; import org.eclipse.jetty.ee10.websocket.common.JettyWebSocketFrameHandler; import org.eclipse.jetty.ee10.websocket.common.JettyWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee10.websocket.common.SessionTracker; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Graceful; @@ -333,9 +333,9 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli getHttpClient().setCookieStore(cookieStore); } - public ByteBufferPool getBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return getHttpClient().getByteBufferPool(); + return getHttpClient().getRetainableByteBufferPool(); } @Override diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageInputStreamTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageInputStreamTest.java index b8780329aff..c1659ae05be 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageInputStreamTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageInputStreamTest.java @@ -28,7 +28,6 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.internal.messages.MessageInputStream; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -38,14 +37,6 @@ import static org.junit.jupiter.api.Assertions.assertTimeout; public class MessageInputStreamTest { - public TestableLeakTrackingBufferPool bufferPool = new TestableLeakTrackingBufferPool("Test"); - - @AfterEach - public void afterEach() - { - bufferPool.assertNoLeaks(); - } - @Test public void testBasicAppendRead() throws IOException { diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageOutputStreamTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageOutputStreamTest.java index 41df82486a5..273de5d31e3 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageOutputStreamTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageOutputStreamTest.java @@ -16,7 +16,11 @@ package org.eclipse.jetty.ee10.websocket.common; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.core.internal.messages.MessageOutputStream; import org.junit.jupiter.api.AfterEach; @@ -28,29 +32,49 @@ import org.slf4j.LoggerFactory; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MessageOutputStreamTest { private static final Logger LOG = LoggerFactory.getLogger(MessageOutputStreamTest.class); private static final int OUTPUT_BUFFER_SIZE = 4096; - public TestableLeakTrackingBufferPool bufferPool = new TestableLeakTrackingBufferPool("Test"); - - @AfterEach - public void afterEach() - { - bufferPool.assertNoLeaks(); - } - + private final AtomicInteger leaks = new AtomicInteger(); + private RetainableByteBufferPool bufferPool; private OutgoingMessageCapture sessionCapture; @BeforeEach public void setupTest() throws Exception { + bufferPool = new ArrayRetainableByteBufferPool() + { + @Override + public RetainableByteBuffer acquire(int size, boolean direct) + { + leaks.incrementAndGet(); + return new RetainableByteBuffer.Wrapper(super.acquire(size, direct)) + { + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + leaks.decrementAndGet(); + return released; + } + }; + } + }; sessionCapture = new OutgoingMessageCapture(); sessionCapture.setOutputBufferSize(OUTPUT_BUFFER_SIZE); } + @AfterEach + public void afterEach() + { + assertEquals(0, leaks.get(), "leak detected"); + } + @Test public void testMultipleWrites() throws Exception { diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/OutgoingMessageCapture.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/OutgoingMessageCapture.java index 4bc43ce1972..e0ac3175b60 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/OutgoingMessageCapture.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/OutgoingMessageCapture.java @@ -20,8 +20,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NoopByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.toolchain.test.Hex; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CloseStatus; @@ -42,7 +41,7 @@ public class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes public BlockingQueue binaryMessages = new LinkedBlockingDeque<>(); public BlockingQueue events = new LinkedBlockingDeque<>(); - private final ByteBufferPool bufferPool = new NoopByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new RetainableByteBufferPool.NonPooling(); private final MethodHandle wholeTextHandle; private final MethodHandle wholeBinaryHandle; private MessageSink messageSink; @@ -129,7 +128,7 @@ public class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { return bufferPool; } diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/TestableLeakTrackingBufferPool.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/TestableLeakTrackingBufferPool.java deleted file mode 100644 index 373dcf8387d..00000000000 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/TestableLeakTrackingBufferPool.java +++ /dev/null @@ -1,43 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.ee10.websocket.common; - -import org.eclipse.jetty.io.LeakTrackingByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -public class TestableLeakTrackingBufferPool extends LeakTrackingByteBufferPool -{ - private final String id; - - public TestableLeakTrackingBufferPool(Class clazz) - { - this(clazz.getSimpleName()); - } - - public TestableLeakTrackingBufferPool(String id) - { - super(new MappedByteBufferPool.Tagged()); - this.id = id; - } - - public void assertNoLeaks() - { - assertThat("Leaked Acquires Count for [" + id + "]", getLeakedAcquires(), is(0L)); - assertThat("Leaked Releases Count for [" + id + "]", getLeakedReleases(), is(0L)); - assertThat("Leaked Resource Count for [" + id + "]", getLeakedResources(), is(0L)); - } -} diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyClientClassLoaderTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyClientClassLoaderTest.java index ff930762606..a6cd6984b92 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyClientClassLoaderTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyClientClassLoaderTest.java @@ -39,7 +39,7 @@ import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServletFactory; import org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketConfiguration; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest; @@ -169,7 +169,7 @@ public class JettyClientClassLoaderTest app.copyLib(CoreClientUpgradeRequest.class, "jetty-websocket-core-client.jar"); app.copyLib(WebSocketComponents.class, "jetty-websocket-core-common.jar"); app.copyLib(Response.class, "jetty-client.jar"); - app.copyLib(ByteBufferPool.class, "jetty-io.jar"); + app.copyLib(EndPoint.class, "jetty-io.jar"); app.copyLib(BadMessageException.class, "jetty-http.jar"); app.copyLib(XmlConfiguration.class, "jetty-xml.jar"); diff --git a/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/MavenServerConnector.java b/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/MavenServerConnector.java index b2b52b7d835..52e902574dc 100644 --- a/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/MavenServerConnector.java +++ b/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/MavenServerConnector.java @@ -18,8 +18,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; @@ -149,9 +149,9 @@ public class MavenServerConnector extends ContainerLifeCycle implements Connecto } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return checkDelegate().getByteBufferPool(); + return checkDelegate().getRetainableByteBufferPool(); } @Override diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java index 14a8c8a2084..881480d6bfc 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java @@ -266,7 +266,7 @@ public class BufferedResponseHandler extends HandlerWrapper // Do we need a new aggregate buffer. if (BufferUtil.space(_aggregate) == 0) { - // TODO: use a buffer pool always allocating with outputBufferSize to avoid polluting the ByteBufferPool. + // TODO: use a buffer pool always allocating with outputBufferSize to avoid polluting the ByteBuffer pool. int size = Math.max(_channel.getHttpConfiguration().getOutputBufferSize(), BufferUtil.length(content)); _aggregate = BufferUtil.allocate(size); _buffers.offer(_aggregate); diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ErrorHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ErrorHandler.java index da03475014b..cb7a81af27d 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ErrorHandler.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ErrorHandler.java @@ -282,7 +282,7 @@ public class ErrorHandler extends AbstractHandler // write into the response aggregate buffer and flush it asynchronously. while (true) { - ByteBuffer buffer = baseRequest.getResponse().getHttpOutput().getBuffer(); + ByteBuffer buffer = baseRequest.getResponse().getHttpOutput().getByteBuffer(); try { ByteBufferOutputStream out = new ByteBufferOutputStream(buffer); diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannel.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannel.java index dc1fae1ab26..400b670ca92 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannel.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannel.java @@ -43,11 +43,11 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.Trailers; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.QuietException; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.ConnectionMetaData; import org.eclipse.jetty.server.Connector; @@ -312,9 +312,9 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor _endPoint.setIdleTimeout(timeoutMs); } - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return _connector.getByteBufferPool(); + return _connector.getRetainableByteBufferPool(); } public HttpConfiguration getHttpConfiguration() @@ -897,7 +897,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor { _request.setHandled(true); _state.completing(); - sendResponse(null, _response.getHttpOutput().getBuffer(), true, Callback.from(() -> _state.completed(null), _state::completed)); + sendResponse(null, _response.getHttpOutput().getByteBuffer(), true, Callback.from(() -> _state.completed(null), _state::completed)); } catch (Throwable x) { diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java index e971b5fcaa6..045368e06af 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java @@ -33,11 +33,12 @@ import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.WriteListener; import org.eclipse.jetty.http.content.HttpContent; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EofException; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.NanoTime; @@ -188,7 +189,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable private long _written; private long _flushed; private long _firstByteNanoTime = -1; - private ByteBuffer _aggregate; + private RetainableByteBuffer _aggregate; private int _bufferSize; private int _commitSize; private WriteListener _writeListener; @@ -289,7 +290,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _state = State.CLOSED; closedCallback = _closedCallback; _closedCallback = null; - releaseBuffer(failure); + releaseBuffer(); wake = updateApiState(failure); } else if (_state == State.CLOSE) @@ -298,7 +299,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // We can now send a (probably empty) last buffer and then when it completes // onWriteComplete will be called again to actually execute the _completeCallback _state = State.CLOSING; - closeContent = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + closeContent = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; } else { @@ -373,10 +374,10 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (_aggregate == null) return getBufferSize(); + ByteBuffer byteBuffer = _aggregate.getByteBuffer(); // compact to maximize space - BufferUtil.compact(_aggregate); - - return BufferUtil.space(_aggregate); + BufferUtil.compact(byteBuffer); + return BufferUtil.space(byteBuffer); } public void softClose() @@ -456,7 +457,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Output is idle blocking state, but we still do an async close _apiState = ApiState.BLOCKED; _state = State.CLOSING; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case ASYNC: @@ -464,7 +465,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Output is idle in async state, so we can do an async close _apiState = ApiState.PENDING; _state = State.CLOSING; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case UNREADY: @@ -510,7 +511,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable try (AutoLock l = _channelState.lock()) { _state = State.CLOSED; - releaseBuffer(failure); + releaseBuffer(); } } @@ -562,7 +563,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _apiState = ApiState.BLOCKED; _state = State.CLOSING; blocker = _writeBlocker.acquire(); - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case BLOCKED: @@ -580,7 +581,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Output is idle in async state, so we can do an async close _apiState = ApiState.PENDING; _state = State.CLOSING; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case UNREADY: @@ -635,30 +636,26 @@ public class HttpOutput extends ServletOutputStream implements Runnable } } - public ByteBuffer getBuffer() + public ByteBuffer getByteBuffer() { try (AutoLock l = _channelState.lock()) { - return acquireBuffer(); + return acquireBuffer().getByteBuffer(); } } - private ByteBuffer acquireBuffer() + private RetainableByteBuffer acquireBuffer() { if (_aggregate == null) - _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers()); + _aggregate = _channel.getRetainableByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers()); return _aggregate; } - private void releaseBuffer(Throwable failure) + private void releaseBuffer() { if (_aggregate != null) { - ByteBufferPool bufferPool = _channel.getConnector().getByteBufferPool(); - if (failure == null) - bufferPool.release(_aggregate); - else - bufferPool.remove(_aggregate); + _aggregate.release(); _aggregate = null; } } @@ -706,7 +703,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { case BLOCKING: _apiState = ApiState.BLOCKED; - content = BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER; + content = _aggregate != null && _aggregate.hasRemaining() ? _aggregate.getByteBuffer() : BufferUtil.EMPTY_BUFFER; break; case ASYNC: @@ -786,7 +783,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Write will be aggregated if: // + it is smaller than the commitSize // + is not the last one, or is last but will fit in an already allocated aggregate buffer. - aggregate = len <= _commitSize && (!last || BufferUtil.hasContent(_aggregate) && len <= space); + aggregate = len <= _commitSize && (!last || (_aggregate != null && _aggregate.hasRemaining()) && len <= space); flush = last || !aggregate || len >= space; if (last && _state == State.OPEN) @@ -821,14 +818,14 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (aggregate) { acquireBuffer(); - int filled = BufferUtil.fill(_aggregate, b, off, len); + int filled = BufferUtil.fill(_aggregate.getByteBuffer(), b, off, len); // return if we are not complete, not full and filled all the content if (!flush) { if (LOG.isDebugEnabled()) LOG.debug("write(array) {} aggregated !flush {}", - stateString(), BufferUtil.toDetailString(_aggregate)); + stateString(), _aggregate); return; } @@ -840,7 +837,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (LOG.isDebugEnabled()) LOG.debug("write(array) {} last={} agg={} flush=true async={}, len={} {}", - stateString(), last, aggregate, async, len, BufferUtil.toDetailString(_aggregate)); + stateString(), last, aggregate, async, len, _aggregate); if (async) { @@ -854,15 +851,17 @@ public class HttpOutput extends ServletOutputStream implements Runnable { boolean complete = false; // flush any content from the aggregate - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { + ByteBuffer byteBuffer = _aggregate.getByteBuffer(); + complete = last && len == 0; - channelWrite(_aggregate, complete); + channelWrite(byteBuffer, complete); // should we fill aggregate again from the buffer? if (len > 0 && !last && len <= _commitSize && len <= maximizeAggregateSpace()) { - BufferUtil.append(_aggregate, b, off, len); + BufferUtil.append(byteBuffer, b, off, len); onWriteComplete(false, null); return; } @@ -915,7 +914,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable checkWritable(); long written = _written + len; last = _channel.getResponse().isAllContentWritten(written); - flush = last || len > 0 || BufferUtil.hasContent(_aggregate); + flush = last || len > 0 || (_aggregate != null && _aggregate.hasRemaining()); if (last && _state == State.OPEN) _state = State.CLOSING; @@ -959,10 +958,10 @@ public class HttpOutput extends ServletOutputStream implements Runnable // Blocking write // flush any content from the aggregate boolean complete = false; - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { complete = last && len == 0; - channelWrite(_aggregate, complete); + channelWrite(_aggregate.getByteBuffer(), complete); } // write any remaining content in the buffer directly @@ -1024,7 +1023,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _written = written; acquireBuffer(); - BufferUtil.append(_aggregate, (byte)b); + BufferUtil.append(_aggregate.getByteBuffer(), (byte)b); } // Check if all written or full @@ -1032,13 +1031,15 @@ public class HttpOutput extends ServletOutputStream implements Runnable return; if (async) + { // Do the asynchronous writing from the callback new AsyncFlush(last).iterate(); + } else { try { - channelWrite(_aggregate, last); + channelWrite(_aggregate.getByteBuffer(), last); onWriteComplete(last, null); } catch (Throwable t) @@ -1082,51 +1083,61 @@ public class HttpOutput extends ServletOutputStream implements Runnable encoder.reset(); } - CharBuffer in = CharBuffer.wrap(s); - CharBuffer crlf = eoln ? CharBuffer.wrap("\r\n") : null; - ByteBuffer out = getHttpChannel().getByteBufferPool().acquire((int)(1 + (s.length() + 2) * encoder.averageBytesPerChar()), false); - BufferUtil.flipToFill(out); - - while (true) + RetainableByteBuffer out = getHttpChannel().getRetainableByteBufferPool().acquire((int)(1 + (s.length() + 2) * encoder.averageBytesPerChar()), false); + try { - CoderResult result; - if (in.hasRemaining()) + CharBuffer in = CharBuffer.wrap(s); + CharBuffer crlf = eoln ? CharBuffer.wrap("\r\n") : null; + ByteBuffer byteBuffer = out.getByteBuffer(); + BufferUtil.flipToFill(byteBuffer); + + while (true) { - result = encoder.encode(in, out, crlf == null); - if (result.isUnderflow()) - if (crlf == null) - break; - else - continue; - } - else if (crlf != null && crlf.hasRemaining()) - { - result = encoder.encode(crlf, out, true); - if (result.isUnderflow()) + CoderResult result; + if (in.hasRemaining()) { - if (!encoder.flush(out).isUnderflow()) - result.throwException(); - break; + result = encoder.encode(in, byteBuffer, crlf == null); + if (result.isUnderflow()) + if (crlf == null) + break; + else + continue; } - } - else - break; + else if (crlf != null && crlf.hasRemaining()) + { + result = encoder.encode(crlf, byteBuffer, true); + if (result.isUnderflow()) + { + if (!encoder.flush(byteBuffer).isUnderflow()) + result.throwException(); + break; + } + } + else + break; - if (result.isOverflow()) - { - BufferUtil.flipToFlush(out, 0); - ByteBuffer bigger = BufferUtil.ensureCapacity(out, out.capacity() + s.length() + 2); - getHttpChannel().getByteBufferPool().release(out); - BufferUtil.flipToFill(bigger); - out = bigger; - continue; - } + if (result.isOverflow()) + { + BufferUtil.flipToFlush(byteBuffer, 0); + RetainableByteBuffer bigger = _channel.getRetainableByteBufferPool().acquire(out.capacity() + s.length() + 2, out.isDirect()); + BufferUtil.flipToFill(bigger.getByteBuffer()); + bigger.getByteBuffer().put(byteBuffer); + out.release(); + BufferUtil.flipToFill(bigger.getByteBuffer()); + out = bigger; + byteBuffer = bigger.getByteBuffer(); + continue; + } - result.throwException(); + result.throwException(); + } + BufferUtil.flipToFlush(byteBuffer, 0); + write(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.remaining()); + } + finally + { + out.release(); } - BufferUtil.flipToFlush(out, 0); - write(out.array(), out.arrayOffset(), out.remaining()); - getHttpChannel().getByteBufferPool().release(out); } /** @@ -1256,7 +1267,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { try (AutoLock l = _channelState.lock()) { - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { callback.failed(new IOException("cannot sendContent() after write()")); return false; @@ -1379,7 +1390,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _commitSize = config.getOutputAggregationSize(); if (_commitSize > _bufferSize) _commitSize = _bufferSize; - releaseBuffer(null); + releaseBuffer(); _written = 0; _writeListener = null; _onError = null; @@ -1394,8 +1405,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable try (AutoLock l = _channelState.lock()) { _interceptor.resetBuffer(); - if (BufferUtil.hasContent(_aggregate)) - BufferUtil.clear(_aggregate); + if (_aggregate != null) + _aggregate.clear(); _written = 0; } } @@ -1575,7 +1586,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable } catch (Throwable t) { - if (t != e) + if (ExceptionUtil.areNotAssociated(e, t)) e.addSuppressed(t); } finally @@ -1597,10 +1608,10 @@ public class HttpOutput extends ServletOutputStream implements Runnable @Override protected Action process() throws Exception { - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { _flushed = true; - channelWrite(_aggregate, false, this); + channelWrite(_aggregate.getByteBuffer(), false, this); return Action.SCHEDULED; } @@ -1649,19 +1660,20 @@ public class HttpOutput extends ServletOutputStream implements Runnable protected Action process() throws Exception { // flush any content from the aggregate - if (BufferUtil.hasContent(_aggregate)) + if (_aggregate != null && _aggregate.hasRemaining()) { _completed = _len == 0; - channelWrite(_aggregate, _last && _completed, this); + channelWrite(_aggregate.getByteBuffer(), _last && _completed, this); return Action.SCHEDULED; } // Can we just aggregate the remainder? if (!_last && _aggregate != null && _len < maximizeAggregateSpace() && _len < _commitSize) { - int position = BufferUtil.flipToFill(_aggregate); - BufferUtil.put(_buffer, _aggregate); - BufferUtil.flipToFlush(_aggregate, position); + ByteBuffer byteBuffer = _aggregate.getByteBuffer(); + int position = BufferUtil.flipToFill(byteBuffer); + BufferUtil.put(_buffer, byteBuffer); + BufferUtil.flipToFlush(byteBuffer, position); return Action.SUCCEEDED; } @@ -1715,7 +1727,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable private class InputStreamWritingCB extends NestedChannelWriteCB { private final InputStream _in; - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private boolean _eof; private boolean _closed; @@ -1724,7 +1736,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable super(callback, true); _in = in; // Reading from InputStream requires byte[], don't use direct buffers. - _buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false); + _buffer = _channel.getRetainableByteBufferPool().acquire(getBufferSize(), false); } @Override @@ -1737,20 +1749,17 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (LOG.isDebugEnabled()) LOG.debug("EOF of {}", this); if (!_closed) - { _closed = true; - _channel.getByteBufferPool().release(_buffer); - IO.close(_in); - } - return Action.SUCCEEDED; } + ByteBuffer byteBuffer = _buffer.getByteBuffer(); + // Read until buffer full or EOF int len = 0; - while (len < _buffer.capacity() && !_eof) + while (len < byteBuffer.capacity() && !_eof) { - int r = _in.read(_buffer.array(), _buffer.arrayOffset() + len, _buffer.capacity() - len); + int r = _in.read(byteBuffer.array(), byteBuffer.arrayOffset() + len, byteBuffer.capacity() - len); if (r < 0) _eof = true; else @@ -1758,24 +1767,27 @@ public class HttpOutput extends ServletOutputStream implements Runnable } // write what we have - _buffer.position(0); - _buffer.limit(len); + byteBuffer.position(0); + byteBuffer.limit(len); _written += len; - channelWrite(_buffer, _eof, this); + channelWrite(byteBuffer, _eof, this); return Action.SCHEDULED; } + @Override + protected void onCompleteSuccess() + { + _buffer.release(); + IO.close(_in); + super.onCompleteSuccess(); + } + @Override public void onCompleteFailure(Throwable x) { - try - { - _channel.getByteBufferPool().release(_buffer); - } - finally - { - super.onCompleteFailure(x); - } + _buffer.release(); + IO.close(_in); + super.onCompleteFailure(x); } } @@ -1791,7 +1803,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable private class ReadableByteChannelWritingCB extends NestedChannelWriteCB { private final ReadableByteChannel _in; - private final ByteBuffer _buffer; + private final RetainableByteBuffer _buffer; private boolean _eof; private boolean _closed; @@ -1799,7 +1811,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { super(callback, true); _in = in; - _buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers()); + _buffer = _channel.getRetainableByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers()); } @Override @@ -1812,33 +1824,39 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (LOG.isDebugEnabled()) LOG.debug("EOF of {}", this); if (!_closed) - { _closed = true; - _channel.getByteBufferPool().release(_buffer); - IO.close(_in); - } return Action.SUCCEEDED; } + ByteBuffer byteBuffer = _buffer.getByteBuffer(); + // Read from stream until buffer full or EOF - BufferUtil.clearToFill(_buffer); - while (_buffer.hasRemaining() && !_eof) + BufferUtil.clearToFill(byteBuffer); + while (byteBuffer.hasRemaining() && !_eof) { - _eof = (_in.read(_buffer)) < 0; + _eof = (_in.read(byteBuffer)) < 0; } // write what we have - BufferUtil.flipToFlush(_buffer, 0); - _written += _buffer.remaining(); - channelWrite(_buffer, _eof, this); + BufferUtil.flipToFlush(byteBuffer, 0); + _written += byteBuffer.remaining(); + channelWrite(byteBuffer, _eof, this); return Action.SCHEDULED; } + @Override + protected void onCompleteSuccess() + { + _buffer.release(); + IO.close(_in); + super.onCompleteSuccess(); + } + @Override public void onCompleteFailure(Throwable x) { - _channel.getByteBufferPool().release(_buffer); + _buffer.release(); IO.close(_in); super.onCompleteFailure(x); } diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceHandler.java index e76679016c4..ec4913c5b7f 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceHandler.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceHandler.java @@ -35,8 +35,7 @@ import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory; import org.eclipse.jetty.http.content.ResourceHttpContentFactory; import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory; import org.eclipse.jetty.http.content.VirtualHttpContentFactory; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NoopByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.resource.Resource; @@ -55,7 +54,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory, { private static final Logger LOG = LoggerFactory.getLogger(ResourceHandler.class); - private ByteBufferPool _byteBufferPool; + private RetainableByteBufferPool _bufferPool; Resource _baseResource; ContextHandler _context; Resource _defaultStyleSheet; @@ -106,7 +105,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory, if (_mimeTypes == null) _mimeTypes = _context == null ? MimeTypes.DEFAULTS : _context.getMimeTypes(); - _byteBufferPool = getByteBufferPool(_context); + _bufferPool = getRetainableByteBufferPool(_context); if (_resourceService.getHttpContentFactory() == null) _resourceService.setHttpContentFactory(newHttpContentFactory()); _resourceService.setWelcomeFactory(this); @@ -114,15 +113,14 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory, super.doStart(); } - private static ByteBufferPool getByteBufferPool(ContextHandler contextHandler) + private static RetainableByteBufferPool getRetainableByteBufferPool(ContextHandler contextHandler) { if (contextHandler == null) - return new NoopByteBufferPool(); + return new RetainableByteBufferPool.NonPooling(); Server server = contextHandler.getServer(); if (server == null) - return new NoopByteBufferPool(); - ByteBufferPool byteBufferPool = server.getBean(ByteBufferPool.class); - return (byteBufferPool == null) ? new NoopByteBufferPool() : byteBufferPool; + return new RetainableByteBufferPool.NonPooling(); + return server.getRetainableByteBufferPool(); } public HttpContent.Factory getHttpContentFactory() @@ -136,7 +134,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory, contentFactory = new FileMappingHttpContentFactory(contentFactory); contentFactory = new VirtualHttpContentFactory(contentFactory, getStyleSheet(), "text/css"); contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats()); - contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, Duration.ofSeconds(1).toMillis(), _byteBufferPool); + contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, Duration.ofSeconds(1).toMillis(), _bufferPool); return contentFactory; } diff --git a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/AbstractHttpTest.java b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/AbstractHttpTest.java index e059eed6ac4..1b82586e407 100644 --- a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/AbstractHttpTest.java +++ b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/AbstractHttpTest.java @@ -24,7 +24,7 @@ import java.util.Set; import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -51,7 +51,7 @@ public abstract class AbstractHttpTest public void setUp() throws Exception { server = new Server(); - connector = new ServerConnector(server, null, null, new ArrayByteBufferPool(64, 2048, 64 * 1024), 1, 1, new HttpConnectionFactory()); + connector = new ServerConnector(server, null, null, new ArrayRetainableByteBufferPool(64, 2048, 64 * 1024), 1, 1, new HttpConnectionFactory()); connector.setIdleTimeout(100000); server.addConnector(connector); diff --git a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpOutputTest.java b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpOutputTest.java index 82a4821a057..6ad0c8ce7e4 100644 --- a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpOutputTest.java +++ b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpOutputTest.java @@ -31,7 +31,6 @@ import jakarta.servlet.ServletException; import jakarta.servlet.WriteListener; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.eclipse.jetty.io.NoopByteBufferPool; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.LocalConnector.LocalEndPoint; @@ -73,8 +72,6 @@ public class HttpOutputTest _server = new Server(); _contextHandler = new ContextHandler(_server, "/"); - _server.addBean(new NoopByteBufferPool()); - HttpConnectionFactory http = new HttpConnectionFactory(); http.getHttpConfiguration().setRequestHeaderSize(1024); http.getHttpConfiguration().setResponseHeaderSize(1024); diff --git a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpWriterTest.java b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpWriterTest.java index edfa61863df..081fd7cad18 100644 --- a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpWriterTest.java +++ b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/HttpWriterTest.java @@ -17,8 +17,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import org.eclipse.jetty.io.ArrayByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.StringUtil; @@ -42,25 +40,16 @@ public class HttpWriterTest { _bytes = BufferUtil.allocate(2048); - final ByteBufferPool pool = new ArrayByteBufferPool(); Server server = new Server(); ContextHandler contextHandler = new ContextHandler(server); HttpChannel channel = new HttpChannel(contextHandler, new MockConnectionMetaData()) { - /* @Override public boolean failAllContent(Throwable failure) { return false; } - */ - - @Override - public ByteBufferPool getByteBufferPool() - { - return pool; - } @Override public boolean failed(Throwable x) diff --git a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/MockConnector.java b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/MockConnector.java index ed201ae5d73..a247a95607b 100644 --- a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/MockConnector.java +++ b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/MockConnector.java @@ -15,15 +15,11 @@ package org.eclipse.jetty.ee9.nested; import java.io.IOException; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.thread.Scheduler; public class MockConnector extends AbstractConnector { - private final Server _server; - public MockConnector() { this(new Server()); @@ -31,8 +27,7 @@ public class MockConnector extends AbstractConnector public MockConnector(Server server) { - super(server, server.getThreadPool(), server.getBean(Scheduler.class), ByteBufferPool.NOOP, 0); - _server = server; + super(server, server.getThreadPool(), server.getScheduler(), server.getRetainableByteBufferPool(), 0); } @Override @@ -40,25 +35,9 @@ public class MockConnector extends AbstractConnector { } - @Override - public Scheduler getScheduler() - { - return _server.getBean(Scheduler.class); - } - - @Override - public ByteBufferPool getByteBufferPool() - { - ByteBufferPool pool = _server.getBean(ByteBufferPool.class); - if (pool != null) - return pool; - return super.getByteBufferPool(); - } - @Override public Object getTransport() { return null; } } - diff --git a/jetty-ee9/jetty-ee9-nested/src/test/resources/jetty-logging.properties b/jetty-ee9/jetty-ee9-nested/src/test/resources/jetty-logging.properties index b7300b187d1..130634ccf5f 100644 --- a/jetty-ee9/jetty-ee9-nested/src/test/resources/jetty-logging.properties +++ b/jetty-ee9/jetty-ee9-nested/src/test/resources/jetty-logging.properties @@ -1,4 +1,3 @@ -# Jetty Logging using jetty-slf4j-impl #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.ee9.handler.LEVEL=DEBUG #org.eclipse.jetty.server.LEVEL=DEBUG diff --git a/jetty-ee9/jetty-ee9-proxy/src/main/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServlet.java b/jetty-ee9/jetty-ee9-proxy/src/main/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServlet.java index 0dd92330322..74f4e6b9aec 100644 --- a/jetty-ee9/jetty-ee9-proxy/src/main/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServlet.java +++ b/jetty-ee9/jetty-ee9-proxy/src/main/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServlet.java @@ -43,8 +43,9 @@ import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.client.Result; import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.server.handler.ConnectHandler; import org.eclipse.jetty.util.BufferUtil; @@ -774,7 +775,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet try { this.transformer = transformer; - ByteBufferPool byteBufferPool = httpClient == null ? null : httpClient.getByteBufferPool(); + RetainableByteBufferPool byteBufferPool = httpClient == null ? null : httpClient.getRetainableByteBufferPool(); this.decoder = new GZIPContentDecoder(byteBufferPool, GZIPContentDecoder.DEFAULT_BUFFER_SIZE); this.out = new ByteArrayOutputStream(); this.gzipOut = new GZIPOutputStream(out); @@ -791,7 +792,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet if (logger.isDebugEnabled()) logger.debug("Ungzipping {} bytes, finished={}", input.remaining(), finished); - List decodeds = Collections.emptyList(); + List decodeds = Collections.emptyList(); if (!input.hasRemaining()) { if (finished) @@ -802,14 +803,14 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet decodeds = new ArrayList<>(); while (true) { - ByteBuffer decoded = decoder.decode(input); + RetainableByteBuffer decoded = decoder.decode(input); decodeds.add(decoded); boolean decodeComplete = !input.hasRemaining() && !decoded.hasRemaining(); boolean complete = finished && decodeComplete; if (logger.isDebugEnabled()) logger.debug("Ungzipped {} bytes, complete={}", decoded.remaining(), complete); if (decoded.hasRemaining() || complete) - transformer.transform(decoded, complete, buffers); + transformer.transform(decoded.getByteBuffer(), complete, buffers); if (decodeComplete) break; } @@ -822,7 +823,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet output.add(result); } - decodeds.forEach(decoder::release); + decodeds.forEach(RetainableByteBuffer::release); } private ByteBuffer gzip(List buffers, boolean finished) throws IOException diff --git a/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/DefaultServlet.java b/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/DefaultServlet.java index e88832fde11..d47b8f18e91 100644 --- a/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/DefaultServlet.java +++ b/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/DefaultServlet.java @@ -41,8 +41,7 @@ import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory; import org.eclipse.jetty.http.content.ResourceHttpContentFactory; import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory; import org.eclipse.jetty.http.content.VirtualHttpContentFactory; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NoopByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.URIUtil; @@ -262,9 +261,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc long cacheValidationTime = getInitParameter("cacheValidationTime") != null ? Long.parseLong(getInitParameter("cacheValidationTime")) : -2; if (maxCachedFiles != -2 || maxCacheSize != -2 || maxCachedFileSize != -2 || cacheValidationTime != -2) { - ByteBufferPool byteBufferPool = getByteBufferPool(_contextHandler); + RetainableByteBufferPool bufferPool = getRetainableByteBufferPool(_contextHandler); _cachingContentFactory = new ValidatingCachingHttpContentFactory(contentFactory, - (cacheValidationTime > -2) ? cacheValidationTime : Duration.ofSeconds(1).toMillis(), byteBufferPool); + (cacheValidationTime > -2) ? cacheValidationTime : Duration.ofSeconds(1).toMillis(), bufferPool); contentFactory = _cachingContentFactory; if (maxCacheSize >= 0) _cachingContentFactory.setMaxCacheSize(maxCacheSize); @@ -302,15 +301,14 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc LOG.debug("resource base = {}", _baseResource); } - private static ByteBufferPool getByteBufferPool(ContextHandler contextHandler) + private static RetainableByteBufferPool getRetainableByteBufferPool(ContextHandler contextHandler) { if (contextHandler == null) - return new NoopByteBufferPool(); + return new RetainableByteBufferPool.NonPooling(); Server server = contextHandler.getServer(); if (server == null) - return new NoopByteBufferPool(); - ByteBufferPool byteBufferPool = server.getBean(ByteBufferPool.class); - return (byteBufferPool == null) ? new NoopByteBufferPool() : byteBufferPool; + return new RetainableByteBufferPool.NonPooling(); + return server.getRetainableByteBufferPool(); } private String getInitParameter(String name, String... deprecated) diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketContainer.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketContainer.java index 38f8bffc36e..50a2e85ef83 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketContainer.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketContainer.java @@ -24,7 +24,7 @@ import java.util.function.Consumer; import jakarta.websocket.Extension; import jakarta.websocket.WebSocketContainer; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Dumpable; @@ -53,9 +53,9 @@ public abstract class JakartaWebSocketContainer extends ContainerLifeCycle imple protected abstract JakartaWebSocketFrameHandlerFactory getFrameHandlerFactory(); - public ByteBufferPool getBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return components.getBufferPool(); + return components.getRetainableByteBufferPool(); } public WebSocketExtensionRegistry getExtensionRegistry() diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java index 515acec5505..fdb264cc1e5 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java @@ -49,12 +49,12 @@ public class JakartaWebSocketRemoteEndpoint implements jakarta.websocket.RemoteE protected MessageWriter newMessageWriter() { - return new MessageWriter(coreSession, session.getContainerImpl().getBufferPool()); + return new MessageWriter(coreSession, session.getContainerImpl().getRetainableByteBufferPool()); } protected MessageOutputStream newMessageOutputStream() { - return new MessageOutputStream(coreSession, session.getContainerImpl().getBufferPool()); + return new MessageOutputStream(coreSession, session.getContainerImpl().getRetainableByteBufferPool()); } @Override diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/AbstractSessionTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/AbstractSessionTest.java index 23ae5753085..38ee8256593 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/AbstractSessionTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/AbstractSessionTest.java @@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit; import jakarta.websocket.Endpoint; import jakarta.websocket.EndpointConfig; import jakarta.websocket.Session; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.junit.jupiter.api.AfterAll; @@ -64,9 +64,9 @@ public abstract class AbstractSessionTest } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return components.getBufferPool(); + return components.getRetainableByteBufferPool(); } public void waitForDemand(long timeout, TimeUnit timeUnit) throws InterruptedException diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/MessageWriterTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/MessageWriterTest.java index d10645d5fe6..9bfc413c904 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/MessageWriterTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/MessageWriterTest.java @@ -19,8 +19,8 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.websocket.core.CoreSession; @@ -34,7 +34,7 @@ import static org.hamcrest.Matchers.is; public class MessageWriterTest { - private ByteBufferPool bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); @Test public void testSingleByteArray512b() throws IOException, InterruptedException diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/LocalServer.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/LocalServer.java index e2ae15be0a0..e0a1700b5d2 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/LocalServer.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/LocalServer.java @@ -28,8 +28,8 @@ import org.eclipse.jetty.ee9.websocket.jakarta.common.JakartaWebSocketSessionLis import org.eclipse.jetty.ee9.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; import org.eclipse.jetty.ee9.websocket.jakarta.server.internal.JakartaWebSocketServerContainer; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -60,7 +60,7 @@ public class LocalServer extends ContainerLifeCycle implements LocalFuzzer.Provi } private static final Logger LOG = LoggerFactory.getLogger(LocalServer.class); - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new ArrayRetainableByteBufferPool(); private Server server; private ServerConnector connector; private LocalConnector localConnector; diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientClassLoaderTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientClassLoaderTest.java index e1d24dfea14..88931c4fa7f 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientClassLoaderTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientClassLoaderTest.java @@ -38,7 +38,7 @@ import org.eclipse.jetty.ee9.websocket.jakarta.common.JakartaWebSocketContainer; import org.eclipse.jetty.ee9.websocket.jakarta.server.config.JakartaWebSocketConfiguration; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest; @@ -150,7 +150,7 @@ public class JakartaClientClassLoaderTest app.copyLib(CoreClientUpgradeRequest.class, "jetty-websocket-core-client.jar"); app.copyLib(WebSocketComponents.class, "jetty-websocket-core-common.jar"); app.copyLib(Response.class, "jetty-client.jar"); - app.copyLib(ByteBufferPool.class, "jetty-io.jar"); + app.copyLib(EndPoint.class, "jetty-io.jar"); app.copyLib(BadMessageException.class, "jetty-http.jar"); app.copyLib(XmlConfiguration.class, "jetty-xml.jar"); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java index e1fa02a93d5..fd1af5245da 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java @@ -33,7 +33,7 @@ import org.eclipse.jetty.ee9.websocket.jakarta.common.JakartaWebSocketContainer; import org.eclipse.jetty.ee9.websocket.jakarta.server.config.JakartaWebSocketConfiguration; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest; @@ -99,7 +99,7 @@ public class JakartaClientShutdownWithServerWebAppTest app.copyLib(CoreClientUpgradeRequest.class, "jetty-websocket-core-client.jar"); app.copyLib(WebSocketComponents.class, "jetty-websocket-core-common.jar"); app.copyLib(Response.class, "jetty-client.jar"); - app.copyLib(ByteBufferPool.class, "jetty-io.jar"); + app.copyLib(EndPoint.class, "jetty-io.jar"); app.copyLib(BadMessageException.class, "jetty-http.jar"); return app; diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/WebSocketClient.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/WebSocketClient.java index 9d70f6eb01f..d83492838d6 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/WebSocketClient.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/WebSocketClient.java @@ -40,8 +40,8 @@ import org.eclipse.jetty.ee9.websocket.client.impl.JettyClientUpgradeRequest; import org.eclipse.jetty.ee9.websocket.common.JettyWebSocketFrameHandler; import org.eclipse.jetty.ee9.websocket.common.JettyWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee9.websocket.common.SessionTracker; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Graceful; @@ -334,9 +334,9 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli getHttpClient().setCookieStore(cookieStore); } - public ByteBufferPool getBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { - return getHttpClient().getByteBufferPool(); + return getHttpClient().getRetainableByteBufferPool(); } @Override diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageInputStreamTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageInputStreamTest.java index 91cb935b060..c2a02c9d3f2 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageInputStreamTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageInputStreamTest.java @@ -28,7 +28,6 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.internal.messages.MessageInputStream; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -39,14 +38,6 @@ import static org.junit.jupiter.api.Assertions.assertTimeout; public class MessageInputStreamTest { - public TestableLeakTrackingBufferPool bufferPool = new TestableLeakTrackingBufferPool("Test"); - - @AfterEach - public void afterEach() - { - bufferPool.assertNoLeaks(); - } - @Test public void testBasicAppendRead() throws IOException { diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageOutputStreamTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageOutputStreamTest.java index 27d487ae1f6..eb43d0acf25 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageOutputStreamTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/MessageOutputStreamTest.java @@ -16,7 +16,11 @@ package org.eclipse.jetty.ee9.websocket.common; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.core.internal.messages.MessageOutputStream; import org.junit.jupiter.api.AfterEach; @@ -28,27 +32,47 @@ import org.slf4j.LoggerFactory; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MessageOutputStreamTest { private static final Logger LOG = LoggerFactory.getLogger(MessageOutputStreamTest.class); private static final int OUTPUT_BUFFER_SIZE = 4096; - public TestableLeakTrackingBufferPool bufferPool = new TestableLeakTrackingBufferPool("Test"); + private final AtomicInteger leaks = new AtomicInteger(); + private RetainableByteBufferPool bufferPool; + private OutgoingMessageCapture sessionCapture; + + @BeforeEach + public void beforeEach() + { + bufferPool = new ArrayRetainableByteBufferPool() + { + @Override + public RetainableByteBuffer acquire(int size, boolean direct) + { + leaks.incrementAndGet(); + return new RetainableByteBuffer.Wrapper(super.acquire(size, direct)) + { + @Override + public boolean release() + { + boolean released = super.release(); + if (released) + leaks.decrementAndGet(); + return released; + } + }; + } + }; + sessionCapture = new OutgoingMessageCapture(); + sessionCapture.setOutputBufferSize(OUTPUT_BUFFER_SIZE); + } @AfterEach public void afterEach() { - bufferPool.assertNoLeaks(); - } - - private OutgoingMessageCapture sessionCapture; - - @BeforeEach - public void setupTest() throws Exception - { - sessionCapture = new OutgoingMessageCapture(); - sessionCapture.setOutputBufferSize(OUTPUT_BUFFER_SIZE); + assertEquals(0, leaks.get(), "leak detected"); } @Test diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java index 64ab28a0eb5..7e0a2c3853f 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java @@ -20,8 +20,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NoopByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.toolchain.test.Hex; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CloseStatus; @@ -42,7 +41,7 @@ public class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes public BlockingQueue binaryMessages = new LinkedBlockingDeque<>(); public BlockingQueue events = new LinkedBlockingDeque<>(); - private final ByteBufferPool bufferPool = new NoopByteBufferPool(); + private final RetainableByteBufferPool bufferPool = new RetainableByteBufferPool.NonPooling(); private final MethodHandle wholeTextHandle; private final MethodHandle wholeBinaryHandle; private MessageSink messageSink; @@ -129,7 +128,7 @@ public class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes } @Override - public ByteBufferPool getByteBufferPool() + public RetainableByteBufferPool getRetainableByteBufferPool() { return bufferPool; } diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/TestableLeakTrackingBufferPool.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/TestableLeakTrackingBufferPool.java deleted file mode 100644 index f9bff85ed32..00000000000 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/TestableLeakTrackingBufferPool.java +++ /dev/null @@ -1,43 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.ee9.websocket.common; - -import org.eclipse.jetty.io.LeakTrackingByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -public class TestableLeakTrackingBufferPool extends LeakTrackingByteBufferPool -{ - private final String id; - - public TestableLeakTrackingBufferPool(Class clazz) - { - this(clazz.getSimpleName()); - } - - public TestableLeakTrackingBufferPool(String id) - { - super(new MappedByteBufferPool.Tagged()); - this.id = id; - } - - public void assertNoLeaks() - { - assertThat("Leaked Acquires Count for [" + id + "]", getLeakedAcquires(), is(0L)); - assertThat("Leaked Releases Count for [" + id + "]", getLeakedReleases(), is(0L)); - assertThat("Leaked Resource Count for [" + id + "]", getLeakedResources(), is(0L)); - } -} diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee9/websocket/tests/JettyClientClassLoaderTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee9/websocket/tests/JettyClientClassLoaderTest.java index 57cd40075ea..0d78984e790 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee9/websocket/tests/JettyClientClassLoaderTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee9/websocket/tests/JettyClientClassLoaderTest.java @@ -39,7 +39,7 @@ import org.eclipse.jetty.ee9.websocket.server.JettyWebSocketServletFactory; import org.eclipse.jetty.ee9.websocket.server.config.JettyWebSocketConfiguration; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest; @@ -169,7 +169,7 @@ public class JettyClientClassLoaderTest app.copyLib(CoreClientUpgradeRequest.class, "jetty-websocket-core-client.jar"); app.copyLib(WebSocketComponents.class, "jetty-websocket-core-common.jar"); app.copyLib(Response.class, "jetty-client.jar"); - app.copyLib(ByteBufferPool.class, "jetty-io.jar"); + app.copyLib(EndPoint.class, "jetty-io.jar"); app.copyLib(BadMessageException.class, "jetty-http.jar"); app.copyLib(XmlConfiguration.class, "jetty-xml.jar"); diff --git a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/util/ArrayByteBufferPoolBenchmark.java b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/util/ArrayByteBufferPoolBenchmark.java deleted file mode 100644 index 11a4b1d1ff7..00000000000 --- a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/util/ArrayByteBufferPoolBenchmark.java +++ /dev/null @@ -1,67 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.util; - -import java.nio.ByteBuffer; - -import org.eclipse.jetty.io.ArrayByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -@State(Scope.Benchmark) -public class ArrayByteBufferPoolBenchmark -{ - private ByteBufferPool pool; - - @Setup - public void setUp() throws Exception - { - pool = new ArrayByteBufferPool(); - } - - @TearDown - public void tearDown() - { - pool = null; - } - - @Benchmark - public void testAcquireRelease() - { - ByteBuffer buffer = pool.acquire(2048, true); - pool.release(buffer); - } - - public static void main(String[] args) throws RunnerException - { - Options opt = new OptionsBuilder() - .include(ArrayByteBufferPoolBenchmark.class.getSimpleName()) - .warmupIterations(3) - .measurementIterations(3) - .forks(1) - .threads(8) - // .addProfiler(GCProfiler.class) - .build(); - - new Runner(opt).run(); - } -}