private void useCustomBufferSizeSelector(final Integer netReadBufSizeStart, final Integer netWriteBufSizeStart,
      final Integer appReadBufSizeStart) throws Exception {
    // close existing selector
    selector.close();
    NetworkMetrics metrics = new NetworkMetrics(new MetricRegistry());
    Time time = SystemTime.getInstance();
    selector = new Selector(metrics, time, clientSSLFactory, networkConfig) {
      @Override
      protected Transmission createTransmission(String connectionId, SelectionKey key, String hostname, int port,
          PortType portType, SSLFactory.Mode mode) throws IOException {
        AtomicReference<Integer> netReadBufSizeOverride = new AtomicReference<>(netReadBufSizeStart);
        AtomicReference<Integer> netWriteBufSizeOverride = new AtomicReference<>(netWriteBufSizeStart);
        AtomicReference<Integer> appReadBufSizeOverride = new AtomicReference<>(appReadBufSizeStart);
        return new SSLTransmission(clientSSLFactory, connectionId, (SocketChannel) key.channel(), key, hostname, port,
            time, metrics, mode, networkConfig) {
          @Override
          protected int netReadBufferSize() {
            // netReadBufferSize() is invoked in SSLTransportLayer.read() prior to the read
            // operation. To avoid the read buffer being expanded too early, increase buffer size
            // only when read buffer is full. This ensures that BUFFER_UNDERFLOW is always
            // triggered in testNetReadBufferResize().
            boolean updateAllowed = netReadBuffer() != null && !netReadBuffer().hasRemaining();
            return getAndUpdateSize(super.netReadBufferSize(), netReadBufSizeOverride, updateAllowed);
          }

          @Override
          protected int netWriteBufferSize() {
            return getAndUpdateSize(super.netWriteBufferSize(), netWriteBufSizeOverride, true);
          }

          @Override
          protected int appReadBufferSize() {
            return getAndUpdateSize(super.appReadBufferSize(), appReadBufSizeOverride, true);
          }

          /**
           * Helper method for incrementing the recommended buffer size for buffer expansion testing.
           * @param defaultSize the default size to use if an override is not present.
           * @param sizeOverride a reference to the current override value, the reference can be null if the override
           *                     is not enabled.
           * @param updateAllowed if the override size should be increased. If {@code true}, the size returned will
           *                      be double the last, capping out at {@code defaultSize}. Otherwise, the size returned
           *                      will be the same as last.
           * @return the recommended buffer size to return.
           */
          private int getAndUpdateSize(int defaultSize, AtomicReference<Integer> sizeOverride, boolean updateAllowed) {
            return sizeOverride.get() == null ? defaultSize
                : sizeOverride.updateAndGet(i -> updateAllowed ? Math.min(i * 2, defaultSize) : i);
          }
        };
      }
    };
  }
Replace the {@link #selector} instance with that overrides buffer sizing logic to induce BUFFER_OVERFLOW and
BUFFER_UNDERFLOW cases. This overrides the methods used to get the new size for the buffers used by
{@link javax.net.ssl.SSLEngine}, doubling the size of the buffer each time until the size recommended by the
engine is reached.
@param netReadBufSizeStart if non-null, initialize the net read buffer with this size.
@param netWriteBufSizeStart if non-null, initialize the net write buffer with this size.
@param appReadBufSizeStart if non-null, initialize the app buffer with this size.
ambry-network/src/test/java/com/github/ambry/network/SSLSelectorTest.java