SSL_R_SSL_SESSION_ID_TOO_LONG:408:ssl session id too long
SSL_R_SSL_SESSION_VERSION_MISMATCH:210:ssl session version mismatch
SSL_R_STILL_IN_INIT:121:still in init
+SSL_R_STREAM_COUNT_LIMITED:395:stream count limited
SSL_R_STREAM_FINISHED:365:stream finished
SSL_R_STREAM_RECV_ONLY:366:stream recv only
SSL_R_STREAM_RESET:375:stream reset
=head1 NAME
-SSL_new_stream, SSL_STREAM_FLAG_UNI - create a new locally-initiated QUIC stream
+SSL_new_stream, SSL_STREAM_FLAG_UNI, SSL_STREAM_FLAG_NO_BLOCK,
+SSL_STREAM_FLAG_ADVANCE - create a new locally-initiated QUIC stream
=head1 SYNOPSIS
#include <openssl/ssl.h>
- #define SSL_STREAM_FLAG_UNI (1U << 0)
+ #define SSL_STREAM_FLAG_UNI (1U << 0)
+ #define SSL_STREAM_FLAG_NO_BLOCK (1U << 1)
+ #define SSL_STREAM_FLAG_ADVANCE (1U << 2)
SSL *SSL_new_stream(SSL *ssl, uint64_t flags);
=head1 DESCRIPTION
Calling SSL_new_stream() if there is no default stream already present
inhibits the future creation of a default stream. See L<openssl-quic(7)>.
+The creation of new streams is subject to flow control by the QUIC protocol. If
+it is currently not possible to create a new locally initiated stream of the
+specified type, a call to SSL_new_stream() will either block (if the connection
+is configured in blocking mode) until a new stream can be created, or otherwise
+return NULL.
+
+This function operates in blocking mode if the QUIC connection SSL object is
+configured in blocking mode (see L<SSL_set_blocking_mode(3)>). It may also be
+used in nonblocking mode on a connection configured in blocking mode by passing
+the flag B<SSL_STREAM_FLAG_NO_BLOCK>.
+
+The flag B<SSL_STREAM_FLAG_ADVANCE> may be used to create a QUIC stream SSL
+object even if a new QUIC stream cannot yet be opened due to flow control. The
+caller may begin to use the new stream and fill the write buffer of the stream
+by calling L<SSL_write(3)>. However, no actual stream data (or QUIC frames
+regarding the stream) will be sent until QUIC flow control allows it. Any queued
+data will be sent as soon as a peer permits it. There is no guarantee the stream
+will be eventually created; for example, the connection could fail, or a peer
+might simply decide never to increase the number of allowed streams for the
+remainder of the connection lifetime.
+
=head1 RETURN VALUES
SSL_new_stream() returns a new stream object, or NULL on error.
*/
void ossl_quic_channel_get_diag_local_cid(QUIC_CHANNEL *ch, QUIC_CONN_ID *cid);
+/*
+ * Returns 1 if stream count flow control allows us to create a new
+ * locally-initiated stream.
+ */
+int ossl_quic_channel_is_new_local_stream_admissible(QUIC_CHANNEL *ch, int is_uni);
+
# endif
#endif
*/
void ossl_quic_stream_map_set_rr_stepping(QUIC_STREAM_MAP *qsm, size_t stepping);
+/*
+ * Returns 1 if the stream ordinal given is allowed by the current stream count
+ * flow control limit, assuming a locally initiated stream of a type described
+ * by is_uni.
+ *
+ * Note that stream_ordinal is a stream ordinal, not a stream ID.
+ */
+int ossl_quic_stream_map_is_local_allowed_by_stream_limit(QUIC_STREAM_MAP *qsm,
+ uint64_t stream_ordinal,
+ int is_uni);
+
/*
* Stream Send Part
* ================
#define SSL_DEFAULT_STREAM_MODE_AUTO_UNI 2
__owur int SSL_set_default_stream_mode(SSL *s, uint32_t mode);
-#define SSL_STREAM_FLAG_UNI (1U << 0)
+#define SSL_STREAM_FLAG_UNI (1U << 0)
+#define SSL_STREAM_FLAG_NO_BLOCK (1U << 1)
+#define SSL_STREAM_FLAG_ADVANCE (1U << 2)
__owur SSL *SSL_new_stream(SSL *s, uint64_t flags);
#define SSL_INCOMING_STREAM_POLICY_AUTO 0
# define SSL_R_SSL_SESSION_ID_TOO_LONG 408
# define SSL_R_SSL_SESSION_VERSION_MISMATCH 210
# define SSL_R_STILL_IN_INIT 121
+# define SSL_R_STREAM_COUNT_LIMITED 395
# define SSL_R_STREAM_FINISHED 365
# define SSL_R_STREAM_RECV_ONLY 366
# define SSL_R_STREAM_RESET 375
return 0;
}
+static uint64_t *ch_get_local_stream_next_ordinal_ptr(QUIC_CHANNEL *ch,
+ int is_uni)
+{
+ return is_uni ? &ch->next_local_stream_ordinal_uni
+ : &ch->next_local_stream_ordinal_bidi;
+}
+
+int ossl_quic_channel_is_new_local_stream_admissible(QUIC_CHANNEL *ch,
+ int is_uni)
+{
+ uint64_t *p_next_ordinal = ch_get_local_stream_next_ordinal_ptr(ch, is_uni);
+
+ return ossl_quic_stream_map_is_local_allowed_by_stream_limit(&ch->qsm,
+ *p_next_ordinal,
+ is_uni);
+}
+
QUIC_STREAM *ossl_quic_channel_new_stream_local(QUIC_CHANNEL *ch, int is_uni)
{
QUIC_STREAM *qs;
type = ch->is_server ? QUIC_STREAM_INITIATOR_SERVER
: QUIC_STREAM_INITIATOR_CLIENT;
- if (is_uni) {
- p_next_ordinal = &ch->next_local_stream_ordinal_uni;
+ p_next_ordinal = ch_get_local_stream_next_ordinal_ptr(ch, is_uni);
+
+ if (is_uni)
type |= QUIC_STREAM_DIR_UNI;
- } else {
- p_next_ordinal = &ch->next_local_stream_ordinal_bidi;
+ else
type |= QUIC_STREAM_DIR_BIDI;
- }
if (*p_next_ordinal >= ((uint64_t)1) << 62)
return NULL;
return NULL;
}
+struct quic_new_stream_wait_args {
+ QUIC_CONNECTION *qc;
+ int is_uni;
+};
+
+static int quic_new_stream_wait(void *arg)
+{
+ struct quic_new_stream_wait_args *args = arg;
+ QUIC_CONNECTION *qc = args->qc;
+
+ if (!quic_mutation_allowed(qc, /*req_active=*/1))
+ return -1;
+
+ if (ossl_quic_channel_is_new_local_stream_admissible(qc->ch, args->is_uni))
+ return 1;
+
+ return 0;
+}
+
/* locking depends on need_lock */
static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock)
{
+ int ret;
QUIC_CONNECTION *qc = ctx->qc;
QUIC_XSO *xso = NULL;
QUIC_STREAM *qs = NULL;
int is_uni = ((flags & SSL_STREAM_FLAG_UNI) != 0);
+ int no_blocking = ((flags & SSL_STREAM_FLAG_NO_BLOCK) != 0);
+ int advance = ((flags & SSL_STREAM_FLAG_ADVANCE) != 0);
if (need_lock)
quic_lock(qc);
goto err;
}
+ if (!advance
+ && !ossl_quic_channel_is_new_local_stream_admissible(qc->ch, is_uni)) {
+ struct quic_new_stream_wait_args args;
+
+ /*
+ * Stream count flow control currently doesn't permit this stream to be
+ * opened.
+ */
+ if (no_blocking || !qc_blocking_mode(qc)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_STREAM_COUNT_LIMITED, NULL);
+ goto err;
+ }
+
+ args.qc = qc;
+ args.is_uni = is_uni;
+
+ /* Blocking mode - wait until we can get a stream. */
+ ret = block_until_pred(ctx->qc, quic_new_stream_wait, &args, 0);
+ if (!quic_mutation_allowed(qc, /*req_active=*/1)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
+ goto err; /* Shutdown before completion */
+ } else if (ret <= 0) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
+ goto err; /* Non-protocol error */
+ }
+ }
+
qs = ossl_quic_channel_new_stream_local(qc->ch, is_uni);
if (qs == NULL) {
QUIC_RAISE_NON_IO_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
|| qs->send_state == QUIC_SSTREAM_STATE_RESET_RECVD);
}
+int ossl_quic_stream_map_is_local_allowed_by_stream_limit(QUIC_STREAM_MAP *qsm,
+ uint64_t stream_ordinal,
+ int is_uni)
+{
+ uint64_t stream_limit;
+
+ if (qsm->get_stream_limit_cb == NULL)
+ return 1;
+
+ stream_limit = qsm->get_stream_limit_cb(is_uni, qsm->get_stream_limit_cb_arg);
+ return stream_ordinal < stream_limit;
+}
+
void ossl_quic_stream_map_update_state(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
{
int should_be_active, allowed_by_stream_limit = 1;
- if (qsm->get_stream_limit_cb != NULL
- && ossl_quic_stream_is_server_init(s) == qsm->is_server) {
- int uni = !ossl_quic_stream_is_bidi(s);
- uint64_t stream_limit, stream_ordinal = s->id >> 2;
-
- stream_limit
- = qsm->get_stream_limit_cb(uni, qsm->get_stream_limit_cb_arg);
+ if (ossl_quic_stream_is_server_init(s) == qsm->is_server) {
+ int is_uni = !ossl_quic_stream_is_bidi(s);
+ uint64_t stream_ordinal = s->id >> 2;
- allowed_by_stream_limit = (stream_ordinal < stream_limit);
+ allowed_by_stream_limit
+ = ossl_quic_stream_map_is_local_allowed_by_stream_limit(qsm,
+ stream_ordinal,
+ is_uni);
}
if (s->send_state == QUIC_SSTREAM_STATE_DATA_SENT
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_VERSION_MISMATCH),
"ssl session version mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STILL_IN_INIT), "still in init"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_COUNT_LIMITED),
+ "stream count limited"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_FINISHED), "stream finished"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_RECV_ONLY), "stream recv only"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_RESET), "stream reset"},
SSLv23_method define
SSLv23_server_method define
SSL_STREAM_FLAG_UNI define
+SSL_STREAM_FLAG_NO_BLOCK define
+SSL_STREAM_FLAG_ADVANCE define
SSL_STREAM_TYPE_NONE define
SSL_STREAM_TYPE_READ define
SSL_STREAM_TYPE_WRITE define