Class StreamsState

java.lang.Object
io.github.mahmoudimus.quic.connection.StreamsState

public final class StreamsState extends Object
The streams of one connection (RFC 9000 sections 2 to 4): opening and accepting streams, stream and connection flow control, the send and receive state machines, RESET_STREAM and STOP_SENDING, priorities, and the control and STREAM frames they produce.

The application side (open(io.github.mahmoudimus.quic.connection.Dir), accept(io.github.mahmoudimus.quic.connection.Dir), write(io.github.mahmoudimus.quic.connection.StreamId, java.nio.ByteBuffer, io.github.mahmoudimus.quic.connection.Retransmits), finish(io.github.mahmoudimus.quic.connection.StreamId), reset(io.github.mahmoudimus.quic.connection.StreamId, long, io.github.mahmoudimus.quic.connection.Retransmits), read(io.github.mahmoudimus.quic.connection.StreamId, boolean, io.github.mahmoudimus.quic.connection.Retransmits), stop(io.github.mahmoudimus.quic.connection.StreamId, long, io.github.mahmoudimus.quic.connection.Retransmits)) never blocks: it reports StreamException.Kind.BLOCKED and later a StreamEvent from poll() says when to retry. That is what a blocking stream layer waits on.

Frames the operations need go into a Retransmits the caller passes, normally the application data space's pending frames; writeControlFrames(java.nio.ByteBuffer, io.github.mahmoudimus.quic.connection.Retransmits, io.github.mahmoudimus.quic.connection.Retransmits, int) and writeStreamFrames(java.nio.ByteBuffer, int, boolean) put them into packets.

Flow control. Credit is issued as quinn does: a stream's MAX_STREAM_DATA moves with what the application reads, to bytes read + stream receive window, and is sent once it has moved by an eighth of the window; MAX_DATA likewise with the connection receive window; MAX_STREAMS keeps the configured number of remote streams open and is sent once an eighth of them have closed. The windows can be changed at run time; quinn does not auto-tune them, and neither does this port. Writes are limited by the peer's credit and by a local send window of buffered, unacknowledged data.

Threads. Not thread-safe and takes no locks: one connection owns it and serializes every call, including the blocking stream layer's.

  • Field Details

    • MAX_STREAM_COUNT

      public static final long MAX_STREAM_COUNT
      The largest stream count MAX_STREAMS may carry, 260.
      See Also:
  • Constructor Details

    • StreamsState

      public StreamsState(Side side, long maxRemoteUni, long maxRemoteBi, long sendWindow, long receiveWindow, long streamReceiveWindow)
      The stream state of a new connection.
      Parameters:
      side - this endpoint's role
      maxRemoteUni - how many unidirectional streams the peer may have open
      maxRemoteBi - how many bidirectional streams the peer may have open
      sendWindow - the most outgoing data to buffer, in bytes
      receiveWindow - the connection receive window, in bytes
      streamReceiveWindow - the per-stream receive window, in bytes
  • Method Details

    • setParams

      public void setParams(TransportParameters params)
      Applies the peer's transport parameters: initial stream and connection limits.
      Parameters:
      params - the peer's parameters
    • onConnectionClosed

      public void onConnectionClosed()
      Marks the connection closed: writes are refused from now on.
    • zeroRttRejected

      public void zeroRttRejected()
      Forgets every local stream after the server rejected 0-RTT.
    • setMaxConcurrent

      public void setMaxConcurrent(Dir dir, long count)
      Changes how many remote streams of a directionality may be open at once.
      Parameters:
      dir - the directionality
      count - the number of concurrent streams
    • maxConcurrent

      public long maxConcurrent(Dir dir)
      How many remote streams of a directionality are allowed and not fully closed.
      Parameters:
      dir - the directionality
      Returns:
      the count
    • setSendWindow

      public void setSendWindow(long sendWindow)
      Changes the send window, the most outgoing data to buffer.
      Parameters:
      sendWindow - bytes; may be below what is already buffered
    • setReceiveWindow

      public boolean setReceiveWindow(long receiveWindow)
      Changes the connection receive window. A larger window raises the limit at once; a smaller one is absorbed from credit as data is read.
      Parameters:
      receiveWindow - bytes
      Returns:
      true if the window grew, so a MAX_DATA is worth sending
    • open

      public StreamId open(Dir dir)
      Opens a local stream.
      Parameters:
      dir - the directionality
      Returns:
      the new stream, or null if the peer's limit is reached (a STREAMS_BLOCKED is then queued) or the connection is closed
    • accept

      public StreamId accept(Dir dir)
      Accepts a stream the peer opened. Does not change flow control or stream limits.
      Parameters:
      dir - the directionality
      Returns:
      the next stream not yet accepted, or null if there is none
    • sendStreams

      public int sendStreams()
      The number of streams that may have unacknowledged outgoing data.
      Returns:
      the count
    • remoteOpenStreams

      public long remoteOpenStreams(Dir dir)
      The number of open remote streams, accepted or not, which count against the concurrency limit.
      Parameters:
      dir - the directionality
      Returns:
      the count
    • write

      public int write(StreamId id, ByteBuffer data, Retransmits pending) throws StreamException
      Writes data, copying as much as flow control and the send window accept.
      Parameters:
      id - the stream
      data - the bytes between the position and limit; the position advances past the bytes taken
      pending - where to queue DATA_BLOCKED and STREAM_DATA_BLOCKED
      Returns:
      the number of bytes taken, at least 1 unless data is empty
      Throws:
      StreamException - BLOCKED if no byte could be taken, STOPPED after STOP_SENDING, CLOSED_STREAM if the stream is not writable
    • writeChunks

      public StreamsState.Written writeChunks(StreamId id, ByteBuffer[] chunks, Retransmits pending) throws StreamException
      Writes buffers without copying them: the stream keeps views until the data is acknowledged, so their contents must not change. A buffer only partly taken has its position advanced.
      Parameters:
      id - the stream
      chunks - the data
      pending - where to queue DATA_BLOCKED and STREAM_DATA_BLOCKED
      Returns:
      the bytes and whole chunks taken
      Throws:
      StreamException - as for write(io.github.mahmoudimus.quic.connection.StreamId, java.nio.ByteBuffer, io.github.mahmoudimus.quic.connection.Retransmits)
    • stopped

      public long stopped(StreamId id) throws StreamException
      Whether the peer stopped a stream.
      Parameters:
      id - the stream
      Returns:
      the STOP_SENDING error code, or -1 if not stopped
      Throws:
      StreamException - CLOSED_STREAM if the stream's send side is gone
    • finish

      public void finish(StreamId id) throws StreamException
      Finishes a stream: no more data will be written, and a FIN is queued. StreamEvent.Finished follows once everything is acknowledged.
      Parameters:
      id - the stream
      Throws:
      StreamException - STOPPED after STOP_SENDING, CLOSED_STREAM if already finished or reset
    • reset

      public void reset(StreamId id, long errorCode, Retransmits retransmits) throws StreamException
      Abandons sending on a stream (RESET_STREAM). Unsent and unacknowledged data is discarded.
      Parameters:
      id - the stream
      errorCode - the application error code
      retransmits - where to queue the RESET_STREAM
      Throws:
      StreamException - CLOSED_STREAM if the stream was already reset or is gone
    • setPriority

      public void setPriority(StreamId id, int priority) throws StreamException
      Sets a stream's priority; streams with a higher priority send first.
      Parameters:
      id - the stream
      priority - the priority, 0 by default
      Throws:
      StreamException - CLOSED_STREAM if the stream's send side is gone
    • priority

      public int priority(StreamId id) throws StreamException
      A stream's priority.
      Parameters:
      id - the stream
      Returns:
      the priority
      Throws:
      StreamException - CLOSED_STREAM if the stream's send side is gone
    • sendState

      public SendState sendState(StreamId id)
      The state of a stream's sending part.
      Parameters:
      id - the stream
      Returns:
      the state, or null if the stream has no sending part or it was released
    • read

      public Chunks read(StreamId id, boolean ordered, Retransmits retransmits) throws StreamException
      Starts reading a stream. Call Chunks.next(int) for data, then Chunks.close() (or use try-with-resources) to issue the flow control credit for what was read.
      Parameters:
      id - the stream
      ordered - true to read bytes in order; false for chunks in any order, after which ordered reads are refused
      retransmits - where to queue MAX_DATA, MAX_STREAM_DATA and MAX_STREAMS
      Returns:
      the reader
      Throws:
      StreamException - CLOSED_STREAM if the stream is gone or stopped, ILLEGAL_ORDERED_READ for an ordered read after an unordered one
    • stop

      public void stop(StreamId id, long errorCode, Retransmits retransmits) throws StreamException
      Stops receiving a stream (STOP_SENDING). Unread data is discarded and its connection credit returned.
      Parameters:
      id - the stream
      errorCode - the application error code
      retransmits - where to queue STOP_SENDING and MAX_DATA
      Throws:
      StreamException - CLOSED_STREAM if the stream is gone or already stopped
    • takeReset

      public long takeReset(StreamId id, Retransmits retransmits) throws StreamException
      Whether the peer reset a stream. Once a reset is returned the stream is released, and later calls fail with CLOSED_STREAM.
      Parameters:
      id - the stream
      retransmits - where to queue MAX_STREAMS
      Returns:
      the RESET_STREAM error code, or -1 if the stream was not reset
      Throws:
      StreamException - CLOSED_STREAM if the stream is gone or stopped
    • recvState

      public RecvState recvState(StreamId id)
      The state of a stream's receiving part.
      Parameters:
      id - the stream
      Returns:
      the state, or null if the stream has no receiving part or it was released
    • received

      public boolean received(Frame.Stream frame, int payloadLength) throws QuicException
      Processes a STREAM frame. The stream keeps a view of the frame's data until it is read, so copy the data out of a reusable packet buffer first.
      Parameters:
      frame - the frame
      payloadLength - the size of the buffer behind the frame's data, for memory accounting
      Returns:
      true if a MAX_DATA should be queued
      Throws:
      QuicException - STREAM_STATE_ERROR for a send-only or unopened stream, STREAM_LIMIT_ERROR beyond the stream limit, FLOW_CONTROL_ERROR beyond a credit limit, FINAL_SIZE_ERROR for a changed or exceeded final size
    • receivedReset

      public boolean receivedReset(Frame.ResetStream frame) throws QuicException
      Processes a RESET_STREAM frame.
      Parameters:
      frame - the frame
      Returns:
      true if a MAX_DATA should be queued
      Throws:
      QuicException - as for received(io.github.mahmoudimus.quic.frame.Frame.Stream, int)
    • receivedStopSending

      public void receivedStopSending(Frame.StopSending frame) throws QuicException
      Processes a STOP_SENDING frame.
      Parameters:
      frame - the frame
      Throws:
      QuicException - STREAM_STATE_ERROR for a receive-only or unopened stream, STREAM_LIMIT_ERROR beyond the stream limit
    • receivedStreamDataBlocked

      public void receivedStreamDataBlocked(Frame.StreamDataBlocked frame) throws QuicException
      Processes a STREAM_DATA_BLOCKED frame, which needs no response.
      Parameters:
      frame - the frame
      Throws:
      QuicException - STREAM_STATE_ERROR for a stream this endpoint only sends on
    • receivedStreamsBlocked

      public void receivedStreamsBlocked(Frame.StreamsBlocked frame) throws QuicException
      Processes a STREAMS_BLOCKED frame, which needs no response.
      Parameters:
      frame - the frame
      Throws:
      QuicException - FRAME_ENCODING_ERROR for a limit above 260
    • receivedMaxStreams

      public void receivedMaxStreams(Dir dir, long count) throws QuicException
      Processes a MAX_STREAMS frame.
      Parameters:
      dir - the directionality
      count - the new limit
      Throws:
      QuicException - FRAME_ENCODING_ERROR for a limit above 260
    • receivedMaxData

      public void receivedMaxData(long n)
      Processes a MAX_DATA frame; smaller limits than the current one are ignored.
      Parameters:
      n - the new connection-level limit
    • receivedMaxStreamData

      public void receivedMaxStreamData(StreamId id, long offset) throws QuicException
      Processes a MAX_STREAM_DATA frame.
      Parameters:
      id - the stream
      offset - the new limit
      Throws:
      QuicException - STREAM_STATE_ERROR for a receive-only or unopened stream, STREAM_LIMIT_ERROR beyond the stream limit
    • resetAcked

      public void resetAcked(StreamId id)
      Releases a stream whose RESET_STREAM was acknowledged.
      Parameters:
      id - the stream
    • receivedAckOf

      public void receivedAckOf(StreamMeta frame)
      Records that a STREAM frame was acknowledged; may release the stream and report StreamEvent.Finished.
      Parameters:
      frame - what the frame carried
    • retransmit

      public void retransmit(StreamMeta frame)
      Queues a lost STREAM frame's data to be sent again.
      Parameters:
      frame - what the frame carried
    • retransmitAfterZeroRttRejected

      public boolean retransmitAfterZeroRttRejected(TransportParameters params)
      After the server rejected 0-RTT: if what the local streams used fits the server's actual limits, takes those limits and queues everything written to be sent again in 1-RTT packets, so the streams carry on as if the early data had been lost; otherwise forgets the local streams, as quinn does (zeroRttRejected()).
      Parameters:
      params - the server's transport parameters from this handshake
      Returns:
      true if the streams carry on, false if they were forgotten
    • retransmitAllForZeroRtt

      public void retransmitAllForZeroRtt()
      Queues everything written on local streams to be sent again after 0-RTT was rejected.
    • writeLimit

      public long writeLimit()
      How much the application may write now: the peer's remaining connection credit, limited by the send window.
      Returns:
      bytes
    • poll

      public StreamEvent poll()
      The next stream event.
      Returns:
      the event, or null if there is none
    • queueMaxStreamId

      public boolean queueMaxStreamId(Retransmits retransmits)
      Queues MAX_STREAMS if enough remote streams closed: more than an eighth of the concurrency limit.
      Parameters:
      retransmits - where to queue the frames
      Returns:
      true if any were queued
    • canSendStreamData

      public boolean canSendStreamData()
      Whether any stream has data to send, ignoring control frames.
      Returns:
      true if writeStreamFrames(java.nio.ByteBuffer, int, boolean) would write a frame
    • canSendFlowControl

      public boolean canSendFlowControl(StreamId id)
      Whether a MAX_STREAM_DATA for a stream would still be useful.
      Parameters:
      id - the stream
      Returns:
      true if the stream is still receiving with an unknown final size
    • canSendDataBlocked

      public boolean canSendDataBlocked()
      Whether a queued DATA_BLOCKED is still accurate.
      Returns:
      true if the peer has not raised MAX_DATA since it was queued
    • streamDataBlockedLimit

      public long streamDataBlockedLimit(StreamId id)
      The limit to put in a STREAM_DATA_BLOCKED for a stream.
      Parameters:
      id - the stream
      Returns:
      the limit, or -1 if the frame is no longer accurate: the stream is not writable, was stopped, or the peer raised its limit
    • isLocalUnopened

      public boolean isLocalUnopened(StreamId id)
      Whether a local stream has never been opened.
      Parameters:
      id - a locally initiated stream
      Returns:
      true if its index is beyond those opened
    • writeControlFrames

      public void writeControlFrames(ByteBuffer buf, Retransmits retransmits, Retransmits sent, int maxSize)
      Writes pending stream control frames: RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS, STREAMS_BLOCKED, DATA_BLOCKED and STREAM_DATA_BLOCKED, as many as fit. Each one written is recorded in sent, to be queued again if the packet is lost.
      Parameters:
      buf - the packet payload, written at its position
      retransmits - the pending frames; written ones are removed
      sent - the frames this packet carries
      maxSize - the position the frames must stay below
    • writeStreamFrames

      public List<StreamMeta> writeStreamFrames(ByteBuffer buf, int maxBufSize, boolean fair)
      Writes STREAM frames from the pending streams, highest priority first, as many as fit.
      Parameters:
      buf - the packet payload, written at its position
      maxBufSize - the position the frames must stay below
      fair - true to requeue a partly written stream behind others of its priority, false to finish it first
      Returns:
      what each frame carried, to keep with the sent packet