protected void notifyHeaders(IStream stream, HeadersFrame frame) { Stream.Listener listener = stream.getListener(); if (listener == null) return; try { listener.onHeaders(stream, frame); } catch (Throwable x) { LOG.info("Failure while notifying listener " + listener, x); } }
@Override public void failed(Throwable x) { if (stream != null) { stream.close(); stream.getSession().removeStream(stream); } super.failed(x); }
@Override public void abort(Throwable failure) { IStream stream = this.stream; if (LOG.isDebugEnabled()) LOG.debug("HTTP2 Response #{}/{} aborted", stream == null ? -1 : stream.getId(), stream == null ? -1 : Integer.toHexString(stream.getSession().hashCode())); if (stream != null) stream.reset(new ResetFrame(stream.getId(), ErrorCode.INTERNAL_ERROR.code), Callback.NOOP); }
private void sendTrailersFrame(MetaData metaData, Callback callback) { if (LOG.isDebugEnabled()) { LOG.debug("HTTP2 Response #{}/{}: trailers", stream.getId(), Integer.toHexString(stream.getSession().hashCode())); } HeadersFrame frame = new HeadersFrame(stream.getId(), metaData, null, true); stream.headers(frame, callback); }
private void sendDataFrame(ByteBuffer content, boolean lastContent, boolean endStream, Callback callback) { if (LOG.isDebugEnabled()) { LOG.debug("HTTP2 Response #{}/{}: {} content bytes{}", stream.getId(), Integer.toHexString(stream.getSession().hashCode()), content.remaining(), lastContent ? " (last chunk)" : ""); } DataFrame frame = new DataFrame(stream.getId(), content, endStream); stream.data(frame, callback); }
if (stream != null) if (stream.isRemotelyClosed()) streamFrame[0] = new WindowUpdateFrame(stream.getId(), length); stream.updateRecvWindow(length); if (LOG.isDebugEnabled()) LOG.debug("Data consumed, increased stream recv window by {} for {}", length, stream);
@Override public void onPushPromise(PushPromiseFrame frame) { if (LOG.isDebugEnabled()) LOG.debug("Received {}", frame); int streamId = frame.getStreamId(); int pushStreamId = frame.getPromisedStreamId(); IStream stream = getStream(streamId); if (stream == null) { if (LOG.isDebugEnabled()) LOG.debug("Ignoring {}, stream #{} not found", frame, streamId); } else { IStream pushStream = createRemoteStream(pushStreamId); if (pushStream != null) { pushStream.process(frame, Callback.NOOP); Stream.Listener listener = notifyPush(stream, pushStream, frame); pushStream.setListener(listener); } } }
@Override public void onCompleted() { // If the stream is not closed, it is still reading the request content. // Send a reset to the other end so that it stops sending data. if (!stream.isClosed()) { if (LOG.isDebugEnabled()) LOG.debug("HTTP2 Response #{}: unconsumed request content, resetting stream", stream.getId()); stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP); } // Consume the existing queued data frames to // avoid stalling the session flow control. HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttachment(); if (channel != null) channel.consumeInput(); }
@Override public void onStreamFailure(int streamId, int error, String reason) { Callback callback = new ResetCallback(streamId, error, Callback.NOOP); IStream stream = getStream(streamId); if (stream != null) stream.process(new FailureFrame(error, reason), callback); else callback.succeeded(); }
if (stream != null) int streamSendWindow = stream.updateSendWindow(0); if (sumOverflows(streamSendWindow, windowDelta)) stream.process(frame, Callback.NOOP); onWindowUpdate(stream, frame);
@Override public void succeeded() { boolean commit; Callback callback = null; synchronized (this) { commit = this.commit; if (state == State.WRITING) { this.state = State.IDLE; callback = this.callback; this.callback = null; this.commit = false; } } if (LOG.isDebugEnabled()) LOG.debug("HTTP2 Response #{}/{} {} {}", stream.getId(), Integer.toHexString(stream.getSession().hashCode()), commit ? "commit" : "flush", callback == null ? "failure" : "success"); if (callback != null) callback.succeeded(); }
@Override public void push(IStream stream, Promise<Stream> promise, PushPromiseFrame frame, Stream.Listener listener) { try { // Synchronization is necessary to atomically create // the stream id and enqueue the frame to be sent. boolean queued; synchronized (this) { int streamId = localStreamIds.getAndAdd(2); frame = new PushPromiseFrame(frame.getStreamId(), streamId, frame.getMetaData()); IStream pushStream = createLocalStream(streamId); pushStream.setListener(listener); ControlEntry entry = new ControlEntry(frame, pushStream, new StreamPromiseCallback(promise, pushStream)); queued = flusher.append(entry); } // Iterate outside the synchronized block. if (queued) flusher.iterate(); } catch (Throwable x) { promise.failed(x); } }
@Override public void push(final MetaData.Request request) { if (!stream.getSession().isPushEnabled()) { if (LOG.isDebugEnabled()) LOG.debug("HTTP/2 Push disabled for {}", request); return; } if (LOG.isDebugEnabled()) LOG.debug("HTTP/2 Push {}", request); stream.push(new PushPromiseFrame(stream.getId(), 0, request), new Promise<Stream>() { @Override public void succeeded(Stream pushStream) { connection.push(connector, (IStream)pushStream, request); } @Override public void failed(Throwable x) { if (LOG.isDebugEnabled()) LOG.debug("Could not push " + request, x); } }, new Stream.Listener.Adapter()); // TODO: handle reset from the client ? }
@Override public void onDataSending(IStream stream, int length) { if (length == 0) return; ISession session = stream.getSession(); int oldSessionWindow = session.updateSendWindow(-length); int newSessionWindow = oldSessionWindow - length; if (LOG.isDebugEnabled()) LOG.debug("Sending, session send window {} -> {} for {}", oldSessionWindow, newSessionWindow, session); if (newSessionWindow <= 0) onSessionStalled(session); int oldStreamWindow = stream.updateSendWindow(-length); int newStreamWindow = oldStreamWindow - length; if (LOG.isDebugEnabled()) LOG.debug("Sending, stream send window {} -> {} for {}", oldStreamWindow, newStreamWindow, stream); if (newStreamWindow <= 0) onStreamStalled(stream); }
if (stream.updateClose(headersFrame.isEndStream(), CloseState.Event.AFTER_SEND)) removeStream(stream); break; stream.close(); removeStream(stream); stream.updateClose(true, CloseState.Event.RECEIVED); break;
public boolean onSessionTimeout(Throwable failure) { ISession session = getSession(); // Compute whether all requests are idle. boolean result = session.getStreams().stream() .map(stream -> (IStream)stream) .map(stream -> (HttpChannelOverHTTP2)stream.getAttachment()) .filter(Objects::nonNull) .map(HttpChannelOverHTTP2::isRequestIdle) .reduce(true, Boolean::logicalAnd); if (LOG.isDebugEnabled()) LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", session, failure); return result; }
private boolean isStale() { return !isProtocol() && stream != null && stream.isReset(); }
void onStreamClosed(IStream stream, HttpChannelOverHTTP2 channel) { if (LOG.isDebugEnabled()) LOG.debug("{} closed for {}", stream, channel); channel.setStream(null); // Only non-push channels are released. if (stream.isLocal()) getHttpDestination().release(this); }
@Override public String toString() { IStream stream = getStream(); long streamId = stream == null ? -1 : stream.getId(); return String.format("%s#%d", super.toString(), streamId); }