mirror of
https://github.com/openssl/openssl.git
synced 2024-11-21 01:15:20 +08:00
QUIC APL: Implement backpressure on stream creation
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/21811)
This commit is contained in:
parent
10536b7f5b
commit
9d6bd3d30f
@ -1563,6 +1563,7 @@ SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH:303:ssl session id has bad length
|
||||
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
|
||||
|
@ -2,13 +2,16 @@
|
||||
|
||||
=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
|
||||
@ -38,6 +41,27 @@ L<SSL_accept_stream(3)>.
|
||||
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.
|
||||
|
@ -411,6 +411,12 @@ uint16_t ossl_quic_channel_get_diag_num_rx_ack(QUIC_CHANNEL *ch);
|
||||
*/
|
||||
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
|
||||
|
@ -607,6 +607,17 @@ void ossl_quic_stream_map_update_state(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s);
|
||||
*/
|
||||
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
|
||||
* ================
|
||||
|
@ -2284,7 +2284,9 @@ __owur uint64_t SSL_get_stream_id(SSL *s);
|
||||
#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
|
||||
|
@ -290,6 +290,7 @@
|
||||
# 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
|
||||
|
@ -3434,6 +3434,23 @@ err:
|
||||
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;
|
||||
@ -3443,13 +3460,12 @@ QUIC_STREAM *ossl_quic_channel_new_stream_local(QUIC_CHANNEL *ch, int is_uni)
|
||||
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;
|
||||
|
@ -1717,13 +1717,35 @@ err:
|
||||
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);
|
||||
@ -1733,6 +1755,33 @@ static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock)
|
||||
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);
|
||||
|
@ -311,19 +311,31 @@ static int qsm_ready_for_gc(QUIC_STREAM_MAP *qsm, QUIC_STREAM *qs)
|
||||
|| 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;
|
||||
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;
|
||||
|
||||
stream_limit
|
||||
= qsm->get_stream_limit_cb(uni, qsm->get_stream_limit_cb_arg);
|
||||
|
||||
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
|
||||
|
@ -466,6 +466,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
|
||||
{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"},
|
||||
|
@ -647,6 +647,8 @@ SSLv23_client_method define
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user