Commit 8776ba0fb3 for openssl.org

commit 8776ba0fb36a164302a31e374742183e2242639a
Author: Alexandr Nedvedicky <sashan@openssl.org>
Date:   Wed Apr 22 14:17:39 2026 +0200

    Port script_2 from test/quic_multistream.c to test/radix/quic_tests.c

    The multistream tests use so-called t-server to test QUIC connection
    and stream functionality. With introduction of QUIC SSL listener
    object and QUIC TLS server method, using t-server is no longer
    necessary (and welcomed). All multisttream tests should be
    ported to QUIC radix test infratructure.

    Co-authored-by: Matt Caswell <matt@openssl.foundation>

    Reviewed-by: Norbert Pocs <norbertp@openssl.org>
    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    Reviewed-by: Tom Cosgrove <tom.cosgrove@arm.com>
    MergeDate: Mon Jun 15 14:38:19 2026
    (Merged from https://github.com/openssl/openssl/pull/30935)

diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c
index a45a9407a3..de7ea27a49 100644
--- a/test/quic_multistream_test.c
+++ b/test/quic_multistream_test.c
@@ -373,19 +373,6 @@ static void s_unlock(struct helper *h, struct helper_local *hl);
 #define ACQUIRE_S() s_lock(h, hl)
 #define ACQUIRE_S_NOHL() s_lock(h, NULL)

-static int check_rejected(struct helper *h, struct helper_local *hl)
-{
-    uint64_t stream_id = hl->check_op->arg2;
-
-    if (!ossl_quic_tserver_stream_has_peer_stop_sending(ACQUIRE_S(), stream_id, NULL)
-        || !ossl_quic_tserver_stream_has_peer_reset_stream(ACQUIRE_S(), stream_id, NULL)) {
-        h->check_spin_again = 1;
-        return 0;
-    }
-
-    return 1;
-}
-
 static int check_stream_reset(struct helper *h, struct helper_local *hl)
 {
     uint64_t stream_id = hl->check_op->arg2, aec = 0;
@@ -2048,88 +2035,7 @@ static const struct script_op script_1[] = {

 /* 2. Multi-stream test */
 static const struct script_op script_2[] = {
-    OP_C_SET_ALPN("ossltest"),
-    OP_C_CONNECT_WAIT(),
-    OP_C_SET_INCOMING_STREAM_POLICY(SSL_INCOMING_STREAM_POLICY_ACCEPT),
-    OP_C_WRITE(DEFAULT, "apple", 5),
-    OP_S_BIND_STREAM_ID(a, C_BIDI_ID(0)),
-    OP_S_READ_EXPECT(a, "apple", 5),
-    OP_S_WRITE(a, "orange", 6),
-    OP_C_READ_EXPECT(DEFAULT, "orange", 6),
-
-    OP_C_NEW_STREAM_BIDI(b, C_BIDI_ID(1)),
-    OP_C_WRITE(b, "flamingo", 8),
-    OP_C_CONCLUDE(b),
-    OP_S_BIND_STREAM_ID(b, C_BIDI_ID(1)),
-    OP_S_READ_EXPECT(b, "flamingo", 8),
-    OP_S_EXPECT_FIN(b),
-    OP_S_WRITE(b, "gargoyle", 8),
-    OP_S_CONCLUDE(b),
-    OP_C_READ_EXPECT(b, "gargoyle", 8),
-    OP_C_EXPECT_FIN(b),
-
-    OP_C_NEW_STREAM_UNI(c, C_UNI_ID(0)),
-    OP_C_WRITE(c, "elephant", 8),
-    OP_C_CONCLUDE(c),
-    OP_S_BIND_STREAM_ID(c, C_UNI_ID(0)),
-    OP_S_READ_EXPECT(c, "elephant", 8),
-    OP_S_EXPECT_FIN(c),
-    OP_S_WRITE_FAIL(c),
-
-    OP_C_ACCEPT_STREAM_NONE(),
-
-    OP_S_NEW_STREAM_BIDI(d, S_BIDI_ID(0)),
-    OP_S_WRITE(d, "frog", 4),
-    OP_S_CONCLUDE(d),
-
-    OP_C_ACCEPT_STREAM_WAIT(d),
-    OP_C_ACCEPT_STREAM_NONE(),
-    OP_C_READ_EXPECT(d, "frog", 4),
-    OP_C_EXPECT_FIN(d),
-
-    OP_S_NEW_STREAM_BIDI(e, S_BIDI_ID(1)),
-    OP_S_WRITE(e, "mixture", 7),
-    OP_S_CONCLUDE(e),
-
-    OP_C_ACCEPT_STREAM_WAIT(e),
-    OP_C_READ_EXPECT(e, "mixture", 7),
-    OP_C_EXPECT_FIN(e),
-    OP_C_WRITE(e, "ramble", 6),
-    OP_S_READ_EXPECT(e, "ramble", 6),
-    OP_C_CONCLUDE(e),
-    OP_S_EXPECT_FIN(e),
-
-    OP_S_NEW_STREAM_UNI(f, S_UNI_ID(0)),
-    OP_S_WRITE(f, "yonder", 6),
-    OP_S_CONCLUDE(f),
-
-    OP_C_ACCEPT_STREAM_WAIT(f),
-    OP_C_ACCEPT_STREAM_NONE(),
-    OP_C_READ_EXPECT(f, "yonder", 6),
-    OP_C_EXPECT_FIN(f),
-    OP_C_WRITE_FAIL(f),
-
-    OP_C_SET_INCOMING_STREAM_POLICY(SSL_INCOMING_STREAM_POLICY_REJECT),
-    OP_S_NEW_STREAM_BIDI(g, S_BIDI_ID(2)),
-    OP_S_WRITE(g, "unseen", 6),
-    OP_S_CONCLUDE(g),
-
-    OP_C_ACCEPT_STREAM_NONE(),
-
-    OP_C_SET_INCOMING_STREAM_POLICY(SSL_INCOMING_STREAM_POLICY_AUTO),
-    OP_S_NEW_STREAM_BIDI(h, S_BIDI_ID(3)),
-    OP_S_WRITE(h, "UNSEEN", 6),
-    OP_S_CONCLUDE(h),
-
-    OP_C_ACCEPT_STREAM_NONE(),
-
-    /*
-     * Streams g, h should have been rejected, so server should have got
-     * STOP_SENDING/RESET_STREAM.
-     */
-    OP_CHECK(check_rejected, S_BIDI_ID(2)),
-    OP_CHECK(check_rejected, S_BIDI_ID(3)),
-
+    /* test moved to test/radix/quic_tests.c */
     OP_END
 };

diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c
index 87c3f77084..11cf93be30 100644
--- a/test/radix/quic_ops.c
+++ b/test/radix/quic_ops.c
@@ -1010,8 +1010,10 @@ err:
         OP_PUSH_U64(1),                                      \
         OP_FUNC(hf_new_stream))

-#define OP_ACCEPT_STREAM_NONE(conn_name) \
-    (OP_SELECT_SSL(0, conn_name),        \
+#define OP_ACCEPT_STREAM_NONE(conn_name, flags) \
+    (OP_SELECT_SSL(0, conn_name),               \
+        OP_PUSH_PZ(#conn_name),                 \
+        OP_PUSH_U64(flags),                     \
         OP_FUNC(hf_accept_stream_none))

 #define OP_ACCEPT_CONN_WAIT(listener_name, conn_name, flags) \
@@ -1071,7 +1073,7 @@ err:
 #define OP_READ_EXPECT_B(name, buf) \
     OP_READ_EXPECT(name, (buf), sizeof(buf))

-#define OP_READ_FAIL()       \
+#define OP_READ_FAIL(name)   \
     (OP_SELECT_SSL(0, name), \
         OP_PUSH_U64(0),      \
         OP_FUNC(hf_read_fail))
diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c
index 90b8727bdf..a840c02ee8 100644
--- a/test/radix/quic_tests.c
+++ b/test/radix/quic_tests.c
@@ -7,6 +7,8 @@
  * https://www.openssl.org/source/license.html
  */

+#include "internal/quic_stream_map.h"
+
 #if defined(_AIX)
 /*
  * Some versions of AIX define macros for events and revents for use when
@@ -22,6 +24,128 @@
  * ============================================================================
  */

+DEF_FUNC(check_rejected)
+{
+    QUIC_CHANNEL *ch;
+    SSL *ssl, *stream;
+    QUIC_STREAM *qs;
+    uint64_t stream_id;
+    int ok = 0;
+
+    REQUIRE_SSL_2(ssl, stream);
+    ch = ossl_quic_conn_get_channel(ssl);
+    if (!TEST_ptr(ch))
+        goto err;
+
+    stream_id = SSL_get_stream_id(stream);
+    qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(ch), stream_id);
+    if (!TEST_ptr(qs))
+        goto err;
+
+    if (qs->peer_stop_sending)
+        ok = 1;
+    else
+        F_SPIN_AGAIN();
+
+err:
+
+    return ok;
+}
+
+/*
+ * Multi-stream test
+ */
+DEF_SCRIPT(multi_stream, "multi stream test")
+{
+    OP_SIMPLE_PAIR_CONN();
+    OP_WRITE_B(C, "apple");
+    OP_ACCEPT_CONN_WAIT(L, S, 0);
+    OP_SET_INCOMING_STREAM_POLICY(C, SSL_INCOMING_STREAM_POLICY_ACCEPT, 42 /* error code */);
+    OP_SET_INCOMING_STREAM_POLICY(S, SSL_INCOMING_STREAM_POLICY_ACCEPT, 42 /* error code */);
+    OP_READ_EXPECT_B(S, "apple");
+    OP_WRITE_B(S, "orange");
+    OP_READ_EXPECT_B(C, "orange");
+
+    OP_NEW_STREAM(C, C0, 0 /* bidirectional stream */);
+    OP_WRITE_B(C0, "flamingo");
+    OP_ACCEPT_STREAM_WAIT(S, S0, 0 /* bidirectional stream */);
+    OP_READ_EXPECT_B(S0, "flamingo");
+    OP_CONCLUDE(C0);
+    OP_EXPECT_FIN(S0);
+    OP_WRITE_B(S0, "gargoyle");
+    OP_READ_EXPECT_B(C0, "gargoyle");
+    OP_CONCLUDE(S0);
+    OP_EXPECT_FIN(C0);
+
+    OP_NEW_STREAM(C, C1, SSL_STREAM_FLAG_UNI);
+    OP_WRITE_B(C1, "elephant");
+    OP_ACCEPT_STREAM_WAIT(S, S1, SSL_STREAM_FLAG_UNI);
+    OP_READ_EXPECT_B(S1, "elephant");
+    OP_CONCLUDE(C1);
+    OP_EXPECT_FIN(S1);
+    OP_READ_FAIL(S1);
+    OP_WRITE_FAIL(S1);
+
+    OP_ACCEPT_STREAM_NONE(C, SSL_STREAM_FLAG_UNI);
+
+    OP_NEW_STREAM(S, S2, 0 /* bidirectional stream */);
+    OP_WRITE_B(S2, "frog");
+    OP_ACCEPT_STREAM_WAIT(C, C2, 0 /* bidirectional stream */);
+    OP_READ_EXPECT_B(C2, "frog");
+    OP_CONCLUDE(S2);
+    OP_EXPECT_FIN(C2);
+
+    OP_ACCEPT_STREAM_NONE(C, 0);
+
+    OP_NEW_STREAM(S, S3, 0 /* bidirectional stream */);
+    OP_WRITE_B(S3, "mixture");
+    OP_CONCLUDE(S3);
+
+    OP_ACCEPT_STREAM_WAIT(C, C3, 0 /* bidirectional stream */);
+    OP_READ_EXPECT_B(C3, "mixture");
+    OP_EXPECT_FIN(C3);
+    OP_WRITE_B(C3, "ramble");
+    OP_READ_EXPECT_B(S3, "ramble");
+    OP_CONCLUDE(C3);
+    OP_EXPECT_FIN(S3);
+
+    OP_NEW_STREAM(S, S4, SSL_STREAM_FLAG_UNI);
+    OP_WRITE_B(S4, "yonder");
+    OP_CONCLUDE(S4);
+    OP_ACCEPT_STREAM_WAIT(C, C4, SSL_STREAM_FLAG_UNI);
+    OP_ACCEPT_STREAM_NONE(C, SSL_STREAM_FLAG_UNI);
+    OP_READ_EXPECT_B(C4, "yonder");
+    OP_EXPECT_FIN(C4);
+    OP_WRITE_FAIL(C4);
+
+    OP_SET_INCOMING_STREAM_POLICY(C, SSL_INCOMING_STREAM_POLICY_REJECT, 42 /* application error code */);
+    OP_NEW_STREAM(S, S5, 0 /* bidirectional stream */);
+    OP_WRITE_B(S5, "unseen");
+    OP_ACCEPT_STREAM_NONE(C, 0);
+    OP_SELECT_SSL(0, S);
+    OP_SELECT_SSL(1, S5);
+    /*
+     * Stream S5 is rejected because of reject policy on client side.
+     */
+    OP_FUNC(check_rejected);
+
+    OP_SET_INCOMING_STREAM_POLICY(C, SSL_INCOMING_STREAM_POLICY_AUTO, 0 /* app. error code */);
+    OP_NEW_STREAM(S, S6, 0 /* bidirectional stream */);
+    OP_WRITE_B(S6, "UNSEEN");
+    OP_ACCEPT_STREAM_NONE(C, 0);
+    OP_SELECT_SSL(0, S);
+    OP_SELECT_SSL(1, S6);
+    /*
+     * Remember the client `C` and server `S` got created by
+     * OP_SIMPLE_PAIR_CON() which creates QUIC connection objects switched to
+     * default (implicit) stream mode (see SSL_set_default_stream_mode(3ossl)).
+     * The stream policy on client `C` is AUTO now which in combination with
+     * default stream mode makes `C` to reject incoming stream `S6`
+     * (see SSL_set_incoming_stream_policy(3ossl) for details).
+     */
+    OP_FUNC(check_rejected);
+}
+
 /*
  * Simple single-stream test
  */
@@ -639,6 +763,7 @@ DEF_SCRIPT(check_ctx_cbks, "Check new_pending and client_hello callbacks")
  */
 static SCRIPT_INFO *const scripts[] = {
     USE(simple_stream),
+    USE(multi_stream),
     USE(simple_conn),
     USE(simple_thread),
     USE(ssl_poll),