]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Do read retries in quic hq-interop server
authorNeil Horman <nhorman@openssl.org>
Sun, 15 Dec 2024 20:26:41 +0000 (15:26 -0500)
committerNeil Horman <nhorman@openssl.org>
Mon, 17 Feb 2025 16:27:33 +0000 (11:27 -0500)
Normally the throughput test in the interop harness requests several
hundred very small files, resulting in lots of small stream packets from
the client, which are nominally read in a single read operation (as they
typically fit into a single stream frame), and the server was written to
expect that.  However, its still possible, if a stream frame is packed
to the end of a datagram, that only part of its content is carried,
finished in a subsequent stream packet, which leads to a short read.

Augment the server to properly handle SSL_read transient failures so
that such an occurance is handled properly.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26198)

demos/guide/quic-hq-interop-server.c

index 226d9d10c2f5177544a4a12ebb2101da0e9e19db..bea9c44438bea0942a41684d30786925dfaf2dfb 100644 (file)
@@ -332,6 +332,72 @@ err:
     return NULL;
 }
 
+/**
+ * @brief Handles I/O failures on an SSL stream based on the result code.
+ *
+ * This function processes the result of an SSL I/O operation and handles
+ * different types of errors that may occur during the operation. It takes
+ * appropriate actions such as retrying the operation, reporting errors, or
+ * returning specific status codes based on the error type.
+ *
+ * @param ssl A pointer to the SSL object representing the stream.
+ * @param res The result code from the SSL I/O operation.
+ * @return An integer indicating the outcome:
+ *         - 0: EOF, indicating the stream has been closed.
+ *         - -1: A fatal error occurred or the stream has been reset.
+ *
+ *
+ * @note If the failure is due to an SSL verification error, additional
+ * information will be logged to stderr.
+ */
+static int handle_io_failure(SSL *ssl, int res)
+{
+    switch (SSL_get_error(ssl, res)) {
+    case SSL_ERROR_ZERO_RETURN:
+        /* EOF */
+        return 0;
+
+    case SSL_ERROR_SYSCALL:
+        return -1;
+
+    case SSL_ERROR_SSL:
+        /*
+         * Some stream fatal error occurred. This could be because of a
+         * stream reset - or some failure occurred on the underlying
+         * connection.
+         */
+        switch (SSL_get_stream_read_state(ssl)) {
+        case SSL_STREAM_STATE_RESET_REMOTE:
+            fprintf(stderr, "Stream reset occurred\n");
+            /*
+             * The stream has been reset but the connection is still
+             * healthy.
+             */
+            break;
+
+        case SSL_STREAM_STATE_CONN_CLOSED:
+            fprintf(stderr, "Connection closed\n");
+            /* Connection is already closed. */
+            break;
+
+        default:
+            fprintf(stderr, "Unknown stream failure\n");
+            break;
+        }
+        /*
+         * If the failure is due to a verification error we can get more
+         * information about it from SSL_get_verify_result().
+         */
+        if (SSL_get_verify_result(ssl) != X509_V_OK)
+            fprintf(stderr, "Verify error: %s\n",
+                    X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
+        return -1;
+
+    default:
+        return -1;
+    }
+}
+
 /**
  * @brief Processes a new incoming QUIC stream for an HTTP/0.9 GET request.
  *
@@ -375,10 +441,27 @@ static void process_new_stream(SSL *stream)
     size_t bytes_written = 0;
     size_t offset = 0;
     int rc;
+    int ret;
+    size_t total_read = 0;
 
     memset(buf, 0, BUF_SIZE);
-    if (SSL_read_ex(stream, buf, sizeof(buf) - 1, &nread) <= 0)
-        return;
+    for (;;) {
+        nread = 0;
+        ret = SSL_read_ex(stream, &buf[total_read],
+                          sizeof(buf) - total_read - 1, &nread);
+        total_read += nread;
+        if (ret <= 0) {
+            ret = handle_io_failure(stream, ret);
+            if (ret == 0) {
+                /* EOF condition, fin bit set, we got the whole request */
+                break;
+            } else {
+                /* permanent failure, abort */
+                fprintf(stderr, "Failure on stream\n");
+                return;
+            }
+        }
+    }
 
     /* We should have a valid http 0.9 GET request here */
     fprintf(stderr, "Request is %s\n", req);