This is a QUIC specific module that retains the received stream data until the application reads it with SSL_read() or any future stream read calls.
These are the requirements that were identified for MVP:
These are optional features of the stream receive buffers implementation. They are not required for MVP but they are otherwise desirable:
int SSL_set_max_stored_stream_data(SSL *stream, size_t length);
This function adjusts the current data flow control limit on the stream
to allow storing length
bytes of quic stream data before it is read by
the application.
OpenSSL handles sending MAX_STREAM_DATA frames appropriately when the application reads the stored data.
int SSL_set_max_unprocessed_packet_data(SSL *connection,
size_t length);
This sets the limit on unprocessed quic packet data length
in bytes that
is allowed to be allocated for the connection
.
See the Other considerations section below.
SSL_read() copies data out of the stored buffers if available and eventually triggers release of stored unprocessed packet(s).
SSL_peek(), SSL_pending(), SSL_has_pending() peek into the stored buffers for any information about the stored data.
The Receive Buffers module obtains the stream data via the ssl_queue_data() callback.
The module uses ossl_qrx_pkt_wrap_up_ref() and ossl_qrx_pkt_wrap_release() functions to keep and release decrypted packets with unprocessed data.
The Receive Buffers module provides an appropriate value for the Flow Control module to send MAX_DATA and MAX_STREAM_DATA frames. Details TBD.
The Receive Buffers module needs to know whether it should stop holding
the decrypted quic packets and start copying the stream data due to
the limit reached. See the SSL_set_max_unprocessed_quic_packet_data()
function above and the Other considerations section
below. Details TBD.
The QUIC_RSTREAM object holds the received stream data in the SFRAME_LIST structure. This is a sorted list of partially (never fully) overlapping data frames. Each list item holds a pointer to the received packet wrapper for refcounting and proper release of the received packet data once the stream data is read by the application.
Each SFRAME_LIST item has range.start and range.end values greater than the range.start and range.end values of the previous item in the list. This invariant is ensured on the insertion of overlapping stream frames. Any redundant frames are released. Insertion at the end of the list is optimised as in the ideal situation when no packets are lost we always just append new frames.
See include/internal/quic_stream.h
and include/internal/quic_sf_list.h
for internal API details.
The peer is allowed to recreate the stream data frames. As we aim for a single-copy operation a rogue peer could use this to override the stored data limits by sending duplicate frames with only slight changes in the offset. For example: 1st frame - offset 0 length 1000, 2nd frame - offset 1 length 1000, 3rd frame - offset 2 length 1000, and so on. We would have to keep the packet data for all these frames which would effectively raise the stream data flow control limit quadratically.
And this is not the only way how a rogue peer could make us occupy much more data than what is allowed by the stream data flow control limit in the single-copy scenario.
Although intuitively the MAX_DATA flow control limit might be used to somehow limit the allocated packet buffer size, it is defined as sum of allowed data to be sent across all the streams in the connection instead. The packet buffer will contain much more data than just the stream frames especially with a rogue peer, that means MAX_DATA limit cannot be used to limit the memory occupied by packet buffers.
To resolve this problem, we fall back to copying the data off the decrypted packet buffer once we reach a limit on unprocessed decrypted packets. We might also consider falling back to copying the data in case we receive stream data frames that are partially overlapping and one frame not being a subrange of the other.
Because in MVP only a single bidirectional stream to receive any data will be supported, the MAX_DATA flow control limit should be equal to MAX_STREAM_DATA limit for that stream.