quic-hq-interop: Allow for retries if we've reached our max stream limit
Several servers defer the sending of max stream frames. For instance quic-go uses a go-routine to do the sending after sufficient existing streams have finished, while mvfst seems to wait for all outstanding streams to be closed before issuing a new batch. This result in the client, if all streams are in use, getting a transient NULL return from SSL_new_stream(). Check for the stream limit being reached and allow a number of retries before giving up to give the server a chance to issue us more streams. Also dead-reckon the batch count of streams we use in parallel to be 1/4 of our total number of available streams (generally hard coded to 100 for most servers) to avoid using all our streams at once. It would be really nice to have an api to expose our negotiated transport parameters so that the application can know what this limit is, but until then we have to just guess. Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26527)
This commit is contained in:
parent
5569e170ee
commit
17dc32c51b
2 changed files with 36 additions and 5 deletions
|
@ -529,6 +529,8 @@ static size_t build_request_set(SSL *ssl)
|
|||
char req_string[REQ_STRING_SZ];
|
||||
SSL *new_stream;
|
||||
size_t written;
|
||||
unsigned long error;
|
||||
size_t retry_count;
|
||||
|
||||
/*
|
||||
* Free any previous poll list
|
||||
|
@ -606,12 +608,29 @@ static size_t build_request_set(SSL *ssl)
|
|||
new_stream = NULL;
|
||||
|
||||
/*
|
||||
* We don't strictly have to do this check, but our quic client limits
|
||||
* our max data streams to 100, so we're just batching in groups of 100
|
||||
* for now
|
||||
* NOTE: We are doing groups of 25 because thats 1/4 of the initial max
|
||||
* stream count that most servers advertise. This gives the server an
|
||||
* opportunity to send us updated MAX_STREAM frames to extend our stream
|
||||
* allotment before we run out, which many servers defer doing.
|
||||
*/
|
||||
if (poll_count <= 99)
|
||||
new_stream = SSL_new_stream(ssl, 0);
|
||||
if (poll_count <= 25) {
|
||||
for (retry_count = 0; retry_count < 10; retry_count++) {
|
||||
ERR_clear_error();
|
||||
new_stream = SSL_new_stream(ssl, 0);
|
||||
if (new_stream == NULL
|
||||
&& (error = ERR_get_error()) != 0
|
||||
&& ERR_GET_REASON(error) == SSL_R_STREAM_COUNT_LIMITED) {
|
||||
/*
|
||||
* Kick the SSL state machine in the hopes that
|
||||
* the server has a MAX_STREAM frame for us to process
|
||||
*/
|
||||
fprintf(stderr, "Stream limit reached, retrying\n");
|
||||
SSL_handle_events(ssl);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_stream == NULL) {
|
||||
/*
|
||||
|
|
|
@ -69,6 +69,18 @@ SSL_new_stream() returns a new stream object, or NULL on error.
|
|||
This function fails if called on a QUIC stream SSL object or on a non-QUIC SSL
|
||||
object.
|
||||
|
||||
SSL_new_stream() may also fail if the connection that the stream is being
|
||||
allocated in relation to has reached the maximum number of streams allowed by
|
||||
the connection (as dictated by the B<max_streams_bidi> or B<max_streams_uni>
|
||||
transport parameter values negotiated by the connection with the server. In
|
||||
this event the NULL return will be accompanied by an error on the error stack
|
||||
(obtainable via ERR_get_error()), which will contain a reason code of
|
||||
B<SSL_R_STREAM_COUNT_LIMITED>. When this error is encountered, The operation
|
||||
may be retried. It is recommended that, prior to retry, the error stack be
|
||||
cleared via a call to ERR_clear_error(), and that the TLS state machine be
|
||||
triggered via a call to SSL_handle_events() to process any potential updates
|
||||
from the server allowing additional streams to be created.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<SSL_accept_stream(3)>, L<SSL_free(3)>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue