private ChannelFuture resetStream(final ChannelHandlerContext ctx, final Http2Stream stream, long errorCode, ChannelPromise promise) { promise = promise.unvoid(); if (stream.isResetSent()) { return promise.setSuccess(); if (stream.state() == IDLE || connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) { future = promise.setSuccess(); } else { future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise); stream.resetSent(); if (future.isDone()) { processRstStreamWriteResult(ctx, stream, future); } else { future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception {
@Override public int onDataRead(final ChannelHandlerContext context, final int streamId, final ByteBuf data, final int padding, final boolean endOfStream) { final int bytesProcessed = data.readableBytes() + padding; final Http2Stream stream = this.connection().stream(streamId); if (stream.getProperty(this.payloadPropertyKey) == null) { stream.setProperty(this.payloadPropertyKey, data.alloc().heapBuffer(MAX_CONTENT_LENGTH)); } ((ByteBuf) stream.getProperty(this.payloadPropertyKey)).writeBytes(data); if (endOfStream) { this.handleEndOfStream(context, stream); } return bytesProcessed; }
@Override public void closeStream(final Http2Stream stream, ChannelFuture future) { stream.close(); if (future.isDone()) { checkCloseConnection(future); } else { future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { checkCloseConnection(future); } }); } }
DefaultHttp2FrameStream setStreamAndProperty(PropertyKey streamKey, Http2Stream stream) { assert id == -1 || stream.id() == id; this.stream = stream; stream.setProperty(streamKey, this); return this; }
private static boolean isWritable(Http2Stream stream) { switch (stream.state()) { case OPEN: case HALF_CLOSED_REMOTE: return !stream.isHeadersSent(); default: return false; } }
switch (stream.state()) { case RESERVED_LOCAL: stream.open(endOfStream); break; case OPEN: throw new IllegalStateException("Stream " + stream.id() + " in unexpected state " + stream.state()); promise = promise.unvoid().addListener(closeStreamLocalListener); weight, exclusive, padding, endOfStream, promise); Throwable failureCause = future.cause(); if (failureCause == null) { stream.headersSent(isInformational); if (!future.isSuccess()) {
@Override public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) throws Http2Exception { final Http2Stream stream = connection.stream(streamId); final Http2Decompressor decompressor = decompressor(stream); if (decompressor == null) { final int compressedBytes = data.readableBytes() + padding; decompressor.incrementCompressedBytes(compressedBytes); try { channel.writeInbound(data.retain()); ByteBuf buf = nextReadableBuf(channel); if (buf == null && endOfStream && channel.finish()) { Http2LocalFlowController flowController = connection.local().flowController(); decompressor.incrementDecompressedBytes(padding); for (;;) { decompressor.incrementDecompressedBytes(buf.readableBytes()); throw e; } catch (Throwable t) { throw streamError(stream.id(), INTERNAL_ERROR, t, "Decompressor error detected while delegating data read on streamId %d", stream.id());
if (streamCreatedAfterGoAwaySent(streamId)) { logger.info("{} ignoring {} frame for stream {}. Stream sent after GOAWAY sent", ctx.channel(), frameName, streamId); return true; } else if (stream.isResetSent() || streamCreatedAfterGoAwaySent(streamId)) { logger.info("{} ignoring {} frame for stream {} {}", ctx.channel(), frameName, stream.isResetSent() ? "RST_STREAM sent." : ("Stream created after GOAWAY sent. Last known stream by peer " + connection.remote().lastStreamKnownByPeer()));
@Override public int onDataRead(final ChannelHandlerContext context, final int streamId, final ByteBuf data, final int padding, final boolean endOfStream) { log.trace("Received data from APNs gateway on stream {}: {}", streamId, data.toString(StandardCharsets.UTF_8)); final int bytesProcessed = data.readableBytes() + padding; if (endOfStream) { final Http2Stream stream = this.connection().stream(streamId); this.handleEndOfStream(context, this.connection().stream(streamId), (Http2Headers) stream.getProperty(this.responseHeadersPropertyKey), data); } else { log.error("Gateway sent a DATA frame that was not the end of a stream."); } return bytesProcessed; }
@Override public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) { int processed = data.readableBytes() + padding; if (endOfStream) { Http2Stream http2Stream = connection().stream(streamId); // read cached http2 header from stream Http2Headers headers = http2Stream.getProperty(headerKey); handleRequest(ctx, streamId, headers, data); } return processed; }
@Override public int onDataRead(final ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) throws Http2Exception { Http2Stream stream = connection.stream(streamId); Http2LocalFlowController flowController = flowController(); int bytesToReturn = data.readableBytes() + padding; switch (stream.state()) { case OPEN: case HALF_CLOSED_LOCAL: case HALF_CLOSED_REMOTE: case CLOSED: error = streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s", stream.id(), stream.state()); break; default: error = streamError(stream.id(), PROTOCOL_ERROR, "Stream %d in unexpected state: %s", stream.id(), stream.state()); break; lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
void retryPushNotificationFromStream(final ChannelHandlerContext context, final int streamId) { final Http2Stream stream = this.connection().stream(streamId); final PushNotificationPromise responsePromise = stream.removeProperty(this.responsePromisePropertyKey); final ChannelPromise writePromise = context.channel().newPromise(); this.writePushNotification(context, responsePromise, writePromise); }
void retryPushNotificationFromStream(final ChannelHandlerContext context, final int streamId) { final Http2Stream stream = this.connection().stream(streamId); final PushNotificationPromise responsePromise = stream.removeProperty(this.responsePromisePropertyKey); final ChannelPromise writePromise = context.channel().newPromise(); this.writePushNotification(context, responsePromise, writePromise); writePromise.addListener(new GenericFutureListener<Future<Void>>() { @Override public void operationComplete(final Future<Void> writeFuture) { if (!writeFuture.isSuccess()) { responsePromise.tryFailure(writeFuture.cause()); } } }); }
private void _writeData(Http2Stream stream, ByteBuf chunk, boolean end, ChannelPromise promise) { encoder().writeData(chctx, stream.id(), chunk, 0, end, promise); Http2RemoteFlowController controller = encoder().flowController(); if (!controller.isWritable(stream) || end) { try { encoder().flowController().writePendingBytes(); } catch (Http2Exception e) { onError(chctx, true, e); } } chctx.channel().flush(); }
@Override public void write(ChannelHandlerContext ctx, int allowedBytes) { boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream); if (promise.isVoid()) { promise = ctx.newPromise(); } promise.addListener(this); ChannelFuture f = frameWriter.writeHeaders(ctx, stream.id(), headers, streamDependency, weight, exclusive, padding, endOfStream, promise); // Writing headers may fail during the encode state if they violate HPACK limits. Throwable failureCause = f.cause(); if (failureCause == null) { // This just sets internal stream state which is used elsewhere in the codec and doesn't // necessarily mean the write will complete successfully. stream.headersSent(isInformational); } }
@Override protected ChannelFuture doWriteReset(int id, int streamId, Http2Error error) { final Http2Stream stream = encoder.connection().stream(streamId); // Send a RST_STREAM frame only for an active stream which did not send a RST_STREAM frame already. if (stream != null && !stream.isResetSent()) { return encoder.writeRstStream(ctx, streamId, error.code(), ctx.newPromise()); } return ctx.writeAndFlush(Unpooled.EMPTY_BUFFER); }
@Override public ChannelFuture writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding, final boolean endOfStream, ChannelPromise promise) { final Http2Stream stream; try { stream = requireStream(streamId); // Verify that the stream is in the appropriate state for sending DATA frames. switch (stream.state()) { case OPEN: case HALF_CLOSED_REMOTE: // Allowed sending DATA frames in these states. break; default: throw new IllegalStateException("Stream " + stream.id() + " in unexpected state " + stream.state()); } } catch (Throwable e) { data.release(); return promise.setFailure(e); } // Hand control of the frame to the flow controller. flowController().addFlowControlled(stream, new FlowControlledData(stream, data, padding, endOfStream, promise)); return promise; }
/** * Release remaining content from {@link EmbeddedChannel} and remove the compressor from the {@link Http2Stream}. * * @param stream The stream for which {@code compressor} is the compressor for * @param compressor The compressor for {@code stream} */ void cleanup(Http2Stream stream, EmbeddedChannel compressor) { if (compressor.finish()) { for (;;) { final ByteBuf buf = compressor.readOutbound(); if (buf == null) { break; } buf.release(); } } stream.removeProperty(propertyKey); }
private void handleEndOfStream(final ChannelHandlerContext context, final Http2Stream stream) { final Http2Headers headers = stream.getProperty(this.headersPropertyKey); final ByteBuf payload = stream.getProperty(this.payloadPropertyKey); final ChannelPromise writePromise = context.newPromise(); this.pushNotificationHandler.handlePushNotification(headers, payload); this.write(context, new AcceptNotificationResponse(stream.id(), apnsId), writePromise); this.listener.handlePushNotificationAccepted(headers, payload); } catch (final RejectedNotificationException e) { ((UnregisteredDeviceTokenException) e).getDeviceTokenExpirationTimestamp() : null; this.write(context, new RejectNotificationResponse(stream.id(), apnsId, e.getRejectionReason(), deviceTokenExpirationTimestamp), writePromise); this.listener.handlePushNotificationRejected(headers, payload, e.getRejectionReason(), deviceTokenExpirationTimestamp); } catch (final Exception e) { this.write(context, new RejectNotificationResponse(stream.id(), apnsId, RejectionReason.INTERNAL_SERVER_ERROR, null), writePromise); this.listener.handlePushNotificationRejected(headers, payload, RejectionReason.INTERNAL_SERVER_ERROR, null); } finally { if (stream.getProperty(this.payloadPropertyKey) != null) { ((ByteBuf) stream.getProperty(this.payloadPropertyKey)).release();