@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { queue = new CoalescingBufferQueue(ctx.channel()); }
@Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof ByteBuf) { queue.add((ByteBuf) msg, promise); } else { ctx.write(msg, promise); } }
@Override public void flush(ChannelHandlerContext ctx) throws Exception { if (queue.isEmpty()) { return; } ByteBuf buf = null; try { ChannelPromise promise = ctx.newPromise(); int readableBytes = queue.readableBytes(); buf = queue.remove(readableBytes, promise); byte[] bytes = new byte[readableBytes]; buf.readBytes(bytes); byte[] wrapperBytes = saslClient.wrap(bytes, 0, bytes.length); ChannelPromise lenPromise = ctx.newPromise(); ctx.write(ctx.alloc().buffer(4).writeInt(wrapperBytes.length), lenPromise); ChannelPromise contentPromise = ctx.newPromise(); ctx.write(Unpooled.wrappedBuffer(wrapperBytes), contentPromise); PromiseCombiner combiner = new PromiseCombiner(); combiner.addAll(lenPromise, contentPromise); combiner.finish(promise); ctx.flush(); } finally { if (buf != null) { ReferenceCountUtil.safeRelease(buf); } } }
@Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { if (!queue.isEmpty()) { queue.releaseAndFailAll(new ConnectionClosedException("Connection closed")); } ctx.close(promise); } }
FlowControlledData(Http2Stream stream, ByteBuf buf, int padding, boolean endOfStream, ChannelPromise promise) { super(stream, padding, endOfStream, promise); queue = new CoalescingBufferQueue(promise.channel()); queue.add(buf, promise); dataSize = queue.readableBytes(); }
@Override public void write(ChannelHandlerContext ctx, int allowedBytes) { int queuedData = queue.readableBytes(); if (!endOfStream) { if (queuedData == 0) { // There's no need to write any data frames because there are only empty data frames in the queue // and it is not end of stream yet. Just complete their promises by getting the buffer corresponding // to 0 bytes and writing it to the channel (to preserve notification order). ChannelPromise writePromise = ctx.newPromise().addListener(this); ctx.write(queue.remove(0, writePromise), writePromise); return; } if (allowedBytes == 0) { return; } } // Determine how much data to write. int writableData = min(queuedData, allowedBytes); ChannelPromise writePromise = ctx.newPromise().addListener(this); ByteBuf toWrite = queue.remove(writableData, writePromise); dataSize = queue.readableBytes(); // Determine how much padding to write. int writablePadding = min(allowedBytes - writableData, padding); padding -= writablePadding; // Write the frame(s). frameWriter().writeData(ctx, stream.id(), toWrite, writablePadding, endOfStream && size() == 0, writePromise); }
@Override public boolean merge(ChannelHandlerContext ctx, Http2RemoteFlowController.FlowControlled next) { FlowControlledData nextData; if (FlowControlledData.class != next.getClass() || MAX_VALUE - (nextData = (FlowControlledData) next).size() < size()) { return false; } nextData.queue.copyTo(queue); dataSize = queue.readableBytes(); // Given that we're merging data into a frame it doesn't really make sense to accumulate padding. padding = Math.max(padding, nextData.padding); endOfStream = nextData.endOfStream; return true; } }
/** * Release all buffers in the queue and complete all listeners and promises. */ public void releaseAndFailAll(Throwable cause) { releaseAndFailAll(channel, cause); }
/** * Remove a {@link ByteBuf} from the queue with the specified number of bytes. Any added buffer who's bytes are * fully consumed during removal will have it's promise completed when the passed aggregate {@link ChannelPromise} * completes. * * @param bytes the maximum number of readable bytes in the returned {@link ByteBuf}, if {@code bytes} is greater * than {@link #readableBytes} then a buffer of length {@link #readableBytes} is returned. * @param aggregatePromise used to aggregate the promises and listeners for the constituent buffers. * @return a {@link ByteBuf} composed of the enqueued buffers. */ public ByteBuf remove(int bytes, ChannelPromise aggregatePromise) { return remove(channel.alloc(), bytes, aggregatePromise); }
@Override protected ByteBuf compose(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf next) { if (cumulation instanceof CompositeByteBuf) { CompositeByteBuf composite = (CompositeByteBuf) cumulation; composite.addComponent(true, next); return composite; } return composeIntoComposite(alloc, cumulation, next); }
@Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { if (!queue.isEmpty()) { queue.releaseAndFailAll(new ConnectionClosedException("Connection closed")); } ctx.close(promise); } }
@Override public void error(ChannelHandlerContext ctx, Throwable cause) { queue.releaseAndFailAll(cause); // Don't update dataSize because we need to ensure the size() method returns a consistent size even after // error so we don't invalidate flow control when returning bytes to flow control. lifecycleManager.onError(ctx, cause); }
@Override public void flush(ChannelHandlerContext ctx) throws Exception { if (queue.isEmpty()) { return; } ByteBuf buf = null; try { ChannelPromise promise = ctx.newPromise(); int readableBytes = queue.readableBytes(); buf = queue.remove(readableBytes, promise); byte[] bytes = new byte[readableBytes]; buf.readBytes(bytes); byte[] wrapperBytes = cryptoAES.wrap(bytes, 0, bytes.length); ChannelPromise lenPromise = ctx.newPromise(); ctx.write(ctx.alloc().buffer(4).writeInt(wrapperBytes.length), lenPromise); ChannelPromise contentPromise = ctx.newPromise(); ctx.write(Unpooled.wrappedBuffer(wrapperBytes), contentPromise); PromiseCombiner combiner = new PromiseCombiner(); combiner.addAll(lenPromise, contentPromise); combiner.finish(promise); ctx.flush(); } finally { if (buf != null) { ReferenceCountUtil.safeRelease(buf); } } }
@Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { if (!queue.isEmpty()) { queue.releaseAndFailAll(new ConnectionClosedException("Connection closed")); } ctx.close(promise); } }
@Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof ByteBuf) { queue.add((ByteBuf) msg, promise); } else { ctx.write(msg, promise); } }
@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { queue = new CoalescingBufferQueue(ctx.channel()); }
@Override public void flush(ChannelHandlerContext ctx) throws Exception { if (queue.isEmpty()) { return; } ByteBuf buf = null; try { ChannelPromise promise = ctx.newPromise(); int readableBytes = queue.readableBytes(); buf = queue.remove(readableBytes, promise); byte[] bytes = new byte[readableBytes]; buf.readBytes(bytes); byte[] wrapperBytes = saslClient.wrap(bytes, 0, bytes.length); ChannelPromise lenPromise = ctx.newPromise(); ctx.write(ctx.alloc().buffer(4).writeInt(wrapperBytes.length), lenPromise); ChannelPromise contentPromise = ctx.newPromise(); ctx.write(Unpooled.wrappedBuffer(wrapperBytes), contentPromise); PromiseCombiner combiner = new PromiseCombiner(); combiner.addAll(lenPromise, contentPromise); combiner.finish(promise); ctx.flush(); } finally { if (buf != null) { ReferenceCountUtil.safeRelease(buf); } } }
@Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { if (!queue.isEmpty()) { queue.releaseAndFailAll(new ConnectionClosedException("Connection closed")); } ctx.close(promise); } }
@Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof ByteBuf) { queue.add((ByteBuf) msg, promise); } else { ctx.write(msg, promise); } }
@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { queue = new CoalescingBufferQueue(ctx.channel()); }