@Override public String toString() { return String.format("%s@%x{l:%s <-> r:%s,sendWindow=%s,recvWindow=%s,streams=%d,%s,%s}", getClass().getSimpleName(), hashCode(), getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress(), sendWindow, recvWindow, streams.size(), closed, closeFrame); }
@Override protected void onStreamClosed(IStream stream) { super.onStreamClosed(stream); streamsClosed.incrementAndGet(); }
@Override protected void onStreamOpened(IStream stream) { super.onStreamOpened(stream); streamsOpened.incrementAndGet(); }
public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId) { this.scheduler = scheduler; this.endPoint = endPoint; this.generator = generator; this.listener = listener; this.flowControl = flowControl; this.flusher = new HTTP2Flusher(this); this.maxLocalStreams = -1; this.maxRemoteStreams = -1; this.localStreamIds.set(initialStreamId); this.lastRemoteStreamId.set(isClientStream(initialStreamId) ? 0 : -1); this.streamIdleTimeout = endPoint.getIdleTimeout(); this.sendWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE); this.recvWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE); this.writeThreshold = 32 * 1024; this.pushEnabled = true; // SPEC: by default, push is enabled. this.idleTime = System.nanoTime(); addBean(flowControl); addBean(flusher); }
onStreamOpened(stream); HeadersFrame headersFrame = (HeadersFrame)frame; if (stream.updateClose(headersFrame.isEndStream(), CloseState.Event.AFTER_SEND)) removeStream(stream); break; removeStream(stream); getEndPoint().shutdownOutput(); break;
if (session.getSendWindow() <= 0 && stalledEntry == null) break; int writeThreshold = session.getWriteThreshold(); if (lease.getTotalLength() >= writeThreshold) pendingEntries); session.getEndPoint().write(this, byteBuffers.toArray(EMPTY_BYTE_BUFFERS)); return Action.SCHEDULED;
protected IStream createLocalStream(int streamId) { while (true) { int localCount = localStreamCount.get(); int maxCount = getMaxLocalStreams(); if (maxCount >= 0 && localCount >= maxCount) // TODO: remove the dump() in the exception message. throw new IllegalStateException("Max local stream count " + maxCount + " exceeded" + System.lineSeparator() + dump()); if (localStreamCount.compareAndSet(localCount, localCount + 1)) break; } IStream stream = newStream(streamId, true); if (streams.putIfAbsent(streamId, stream) == null) { stream.setIdleTimeout(getStreamIdleTimeout()); flowControl.onStreamCreated(stream); if (LOG.isDebugEnabled()) LOG.debug("Created local {}", stream); return stream; } else { localStreamCount.decrementAndGet(); throw new IllegalStateException("Duplicate stream " + streamId); } }
@Override public void onPing(PingFrame frame) { if (LOG.isDebugEnabled()) LOG.debug("Received {}", frame); if (frame.isReply()) { notifyPing(this, frame); } else { PingFrame reply = new PingFrame(frame.getPayload(), true); control(null, Callback.NOOP, reply); } }
if (elapsed < endPoint.getIdleTimeout()) return false; return notifyIdleTimeout(this); abort(new TimeoutException("Idle timeout " + endPoint.getIdleTimeout() + " ms")); return false;
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, error, reason); control(null, callback, closeFrame); return true;
@Override public void settings(SettingsFrame frame, Callback callback) { control(null, callback, frame); }
@Override public void failed(Throwable x) { disconnect(); }
private void complete() { close(error, reason, getCallback()); } }
@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 protected void onCompleteFailure(Throwable x) { lease.recycle(); Throwable closed; Set<Entry> allEntries; synchronized (this) { closed = terminated; terminated = x; if (LOG.isDebugEnabled()) LOG.debug(String.format("%s, entries processed/pending/queued=%d/%d/%d", closed != null ? "Closing" : "Failing", processedEntries.size(), pendingEntries.size(), entries.size()), x); allEntries = new HashSet<>(entries); entries.clear(); } allEntries.addAll(processedEntries); processedEntries.clear(); allEntries.addAll(pendingEntries); pendingEntries.clear(); allEntries.forEach(entry -> entry.failed(x)); // If the failure came from within the // flusher, we need to close the connection. if (closed == null) session.abort(x); }
@Override public void dump(Appendable out, String indent) throws IOException { super.dump(out, indent); dump(out, indent, Collections.singleton(new DumpableCollection("streams", streams.values()))); }
onStreamOpened(stream); HeadersFrame headersFrame = (HeadersFrame)frame; if (stream.updateClose(headersFrame.isEndStream(), CloseState.Event.AFTER_SEND)) removeStream(stream); break; removeStream(stream); getEndPoint().shutdownOutput(); break;
if (session.getSendWindow() <= 0 && stalledEntry == null) break; int writeThreshold = session.getWriteThreshold(); if (lease.getTotalLength() >= writeThreshold) pendingEntries); session.getEndPoint().write(this, byteBuffers.toArray(EMPTY_BYTE_BUFFERS)); return Action.SCHEDULED;