Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package aws-c-http for openSUSE:Factory 
checked in at 2026-03-29 20:00:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/aws-c-http (Old)
 and      /work/SRC/openSUSE:Factory/.aws-c-http.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "aws-c-http"

Sun Mar 29 20:00:49 2026 rev:26 rq:1343395 version:0.10.12

Changes:
--------
--- /work/SRC/openSUSE:Factory/aws-c-http/aws-c-http.changes    2026-03-05 
17:32:49.282614670 +0100
+++ /work/SRC/openSUSE:Factory/.aws-c-http.new.8177/aws-c-http.changes  
2026-03-29 20:01:10.078607915 +0200
@@ -1,0 +2,6 @@
+Mon Mar 23 11:01:59 UTC 2026 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to 0.10.12
+  * Introduce max concurrent streams for stream manager by @TingDaoK in (#553)
+
+-------------------------------------------------------------------

Old:
----
  v0.10.11.tar.gz

New:
----
  v0.10.12.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ aws-c-http.spec ++++++
--- /var/tmp/diff_new_pack.8ZVs5s/_old  2026-03-29 20:01:10.674632471 +0200
+++ /var/tmp/diff_new_pack.8ZVs5s/_new  2026-03-29 20:01:10.674632471 +0200
@@ -20,7 +20,7 @@
 %define library_version 1.0.0
 %define library_soversion 1_0_0
 Name:           aws-c-http
-Version:        0.10.11
+Version:        0.10.12
 Release:        0
 Summary:        C99 implementation of the HTTP/1.1 and HTTP/2 specifications
 License:        Apache-2.0

++++++ v0.10.11.tar.gz -> v0.10.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-http-0.10.11/.github/workflows/ci.yml 
new/aws-c-http-0.10.12/.github/workflows/ci.yml
--- old/aws-c-http-0.10.11/.github/workflows/ci.yml     2026-03-02 
18:28:16.000000000 +0100
+++ new/aws-c-http-0.10.12/.github/workflows/ci.yml     2026-03-20 
23:33:19.000000000 +0100
@@ -6,7 +6,7 @@
       - 'main'
 
 env:
-  BUILDER_VERSION: v0.9.72
+  BUILDER_VERSION: v0.9.90
   BUILDER_SOURCE: releases
   BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net
   PACKAGE_NAME: aws-c-http
@@ -135,6 +135,7 @@
 
   # Test downstream repos.
   # This should not be required because we can run into a chicken and egg 
problem if there is a change that needs some fix in a downstream repo.
+  # bump to ubuntu-22 since ubuntu 18 failed to install the mock server deps 
with python3.6
   downstream:
     runs-on: ubuntu-24.04 # latest
     steps:
@@ -146,7 +147,7 @@
     - name: Build ${{ env.PACKAGE_NAME }}
       run: |
         aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION 
}}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x 
./linux-container-ci.sh
-        ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ 
env.LINUX_BASE_IMAGE }} build downstream -p ${{ env.PACKAGE_NAME }}
+        ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} 
aws-crt-ubuntu-22-x64 build downstream -p ${{ env.PACKAGE_NAME }}
 
   windows:
     runs-on: windows-2025 # latest
@@ -203,7 +204,7 @@
         python 
.\aws-c-http\build\deps\aws-c-common\scripts\appverifier_ctest.py 
--build_directory .\aws-c-http\build\aws-c-http
 
   macos:
-    runs-on: macos-14
+    runs-on: macos-15
     strategy:
       fail-fast: false
       matrix:
@@ -220,7 +221,7 @@
         ./builder build -p ${{ env.PACKAGE_NAME }} 
--cmake-extra=-DAWS_USE_APPLE_NETWORK_FRAMEWORK=${{ matrix.eventloop == 
'dispatch_queue' && 'ON' || 'OFF' }}
 
   macos-x64:
-    runs-on: macos-14-large
+    runs-on: macos-15-large
     steps:
     - uses: aws-actions/configure-aws-credentials@v4
       with:
@@ -247,7 +248,7 @@
         python3 builder.pyz build -p aws-c-http 
--cmake-extra=-DENABLE_LOCALHOST_INTEGRATION_TESTS=ON --config Debug
 
   localhost-test-macos:
-    runs-on: macos-14
+    runs-on: macos-15
     strategy:
       fail-fast: false
       matrix:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aws-c-http-0.10.11/include/aws/http/http2_stream_manager.h 
new/aws-c-http-0.10.12/include/aws/http/http2_stream_manager.h
--- old/aws-c-http-0.10.11/include/aws/http/http2_stream_manager.h      
2026-03-02 18:28:16.000000000 +0100
+++ new/aws-c-http-0.10.12/include/aws/http/http2_stream_manager.h      
2026-03-20 23:33:19.000000000 +0100
@@ -154,9 +154,17 @@
     size_t max_concurrent_streams_per_connection;
     /**
      * Required.
-     * The max number of connections will be open at same time. If all the 
connections are full, manager will wait until
-     * available to vender more streams */
+     * The max number of connections that will be open at the same time. If 
all the connections are full, the manager
+     * will wait until a connection is available to vend more streams.
+     */
     size_t max_connections;
+    /**
+     * Optional.
+     * The max number of concurrent streams that can be active across all 
connections at the same time.
+     * 0 means no limit (default). When this limit is reached, the stream 
manager will wait for
+     * existing streams to complete before creating new ones, even if 
connections have available capacity.
+     */
+    size_t max_concurrent_streams;
 };
 
 struct aws_http2_stream_manager_acquire_stream_options {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aws-c-http-0.10.11/include/aws/http/private/http2_stream_manager_impl.h 
new/aws-c-http-0.10.12/include/aws/http/private/http2_stream_manager_impl.h
--- old/aws-c-http-0.10.11/include/aws/http/private/http2_stream_manager_impl.h 
2026-03-02 18:28:16.000000000 +0100
+++ new/aws-c-http-0.10.12/include/aws/http/private/http2_stream_manager_impl.h 
2026-03-20 23:33:19.000000000 +0100
@@ -43,6 +43,12 @@
     } thread_data;
 
     enum aws_h2_sm_connection_state_type state;
+
+    /**
+     * Node for tracking in the all_held_connections list,
+     * NOTE: lock required to alter the state of the list.
+     */
+    struct aws_linked_list_node node;
 };
 
 /* Live from the user request to acquire a stream to the stream completed. */
@@ -115,6 +121,14 @@
     size_t max_concurrent_streams_per_connection;
 
     /**
+     * Optional. 0 means no limit (default).
+     * The max number of concurrent streams that can be active across all 
connections at the same time.
+     * When this limit is reached, the stream manager will wait for existing 
streams to complete
+     * before creating new ones, even if connections have available capacity.
+     */
+    size_t max_concurrent_streams;
+
+    /**
      * Task to invoke pending acquisition callbacks asynchronously if stream 
manager is shutting.
      */
     struct aws_event_loop *finish_pending_stream_acquisitions_task_event_loop;
@@ -129,19 +143,21 @@
         enum aws_h2_sm_state_type state;
 
         /**
-         * A set of all connections that meet all requirement to use. Note: 
there will be connections not in this set,
-         * but hold by the stream manager, which can be tracked by the streams 
created on it. Set of `struct
-         * aws_h2_sm_connection *`
+         * A set of all connections that meet all requirement to use. Doesn't 
own the connection.
+         *
+         * Note: there will be connections not in this set, but hold by the 
stream manager, which can be tracked by the
+         * all_held_connections. Set of `struct aws_h2_sm_connection *`
          */
         struct aws_random_access_set ideal_available_set;
         /**
-         * A set of all available connections that exceed the soft limits set 
by users. Note: there will be connections
-         * not in this set, but hold by the stream manager, which can be 
tracked by the streams created. Set of `struct
-         * aws_h2_sm_connection *`
+         * A set of all available connections that exceed the soft limits set 
by users. Doesn't own the connection.
+         *
+         * Note: there will be connections not in this set, but hold by the 
stream manager, which can be tracked by the
+         * all_held_connections. Set of `struct aws_h2_sm_connection *`
          */
         struct aws_random_access_set nonideal_available_set;
-        /* We don't mantain set for connections that is full or "dead" (Cannot 
make any new streams). We have streams
-         * opening from the connection tracking them */
+        /* We don't mantain set for connections that is full or "dead" (Cannot 
make any new streams). We have
+         * all_held_connections tracking them */
 
         /**
          * The set of all incomplete stream acquisition requests (haven't 
decide what connection to make the request
@@ -150,6 +166,14 @@
         struct aws_linked_list pending_stream_acquisitions;
 
         /**
+         * List of all aws_h2_sm_connection that holding the HTTP connection 
from connection manager.
+         * This list tracks all aws_h2_sm_connection from getting the 
connection from connection manager until release
+         * it back.
+         * list of `struct aws_h2_sm_connection*`
+         */
+        struct aws_linked_list all_held_connections;
+
+        /**
          * The number of connections acquired from connection manager and not 
released yet.
          */
         size_t holding_connections_count;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-http-0.10.11/source/http2_stream_manager.c 
new/aws-c-http-0.10.12/source/http2_stream_manager.c
--- old/aws-c-http-0.10.11/source/http2_stream_manager.c        2026-03-02 
18:28:16.000000000 +0100
+++ new/aws-c-http-0.10.12/source/http2_stream_manager.c        2026-03-20 
23:33:19.000000000 +0100
@@ -93,6 +93,20 @@
         stream_manager->synced_data.holding_connections_count);
 }
 
+/* Check against the max concurrent streams limit, return true if more streams 
are allowed. */
+static bool s_sm_allows_more_concurrent_streams(struct 
aws_http2_stream_manager *stream_manager) {
+    if (stream_manager->max_concurrent_streams > 0) {
+        size_t total_active_streams =
+            
stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_OPEN_STREAM] +
+            
stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_PENDING_MAKE_REQUESTS];
+        if (total_active_streams >= stream_manager->max_concurrent_streams) {
+            /* total active streams are equal or more than the setting */
+            return false;
+        }
+    }
+    return true;
+}
+
 /* The count acquire and release all needs to be invoked helding the lock */
 static void s_sm_count_increase_synced(
     struct aws_http2_stream_manager *stream_manager,
@@ -151,6 +165,18 @@
     struct aws_h2_sm_pending_stream_acquisition *pending_stream_acquisition) {
 
     AWS_ASSERT(pending_stream_acquisition->sm_connection == NULL);
+
+    /* Check if we've reached the max_concurrent_streams limit */
+    if (!s_sm_allows_more_concurrent_streams(stream_manager)) {
+        /* We've reached the limit, cannot assign a connection yet */
+        STREAM_MANAGER_LOGF(
+            DEBUG,
+            stream_manager,
+            "acquisition:%p waiting - max_concurrent_streams limit reached (%" 
PRIu64 ")",
+            (void *)pending_stream_acquisition,
+            (uint64_t)stream_manager->max_concurrent_streams);
+        return;
+    }
     int errored = 0;
     if 
(aws_random_access_set_get_size(&stream_manager->synced_data.ideal_available_set))
 {
         /**
@@ -160,6 +186,10 @@
             
s_get_best_sm_connection_from_set(&stream_manager->synced_data.ideal_available_set);
         AWS_ASSERT(chosen_connection);
         pending_stream_acquisition->sm_connection = chosen_connection;
+        if (chosen_connection->num_streams_assigned == 0) {
+            /* If bump from 0, acquire the refcount for streams */
+            aws_ref_count_acquire(&chosen_connection->ref_count);
+        }
         chosen_connection->num_streams_assigned++;
 
         STREAM_MANAGER_LOGF(
@@ -212,6 +242,10 @@
                 
s_get_best_sm_connection_from_set(&stream_manager->synced_data.nonideal_available_set);
             AWS_ASSERT(chosen_connection);
             pending_stream_acquisition->sm_connection = chosen_connection;
+            if (chosen_connection->num_streams_assigned == 0) {
+                /* If bump from 0, acquire the refcount for streams */
+                aws_ref_count_acquire(&chosen_connection->ref_count);
+            }
             chosen_connection->num_streams_assigned++;
 
             STREAM_MANAGER_LOGF(
@@ -299,6 +333,14 @@
 /* helper function for building the transaction: how many new connections we 
should request */
 static void s_check_new_connections_needed_synced(struct 
aws_http2_stream_management_transaction *work) {
     struct aws_http2_stream_manager *stream_manager = work->stream_manager;
+
+    /* avoid acquiring for connection when no more streams are allowed */
+    if (!s_sm_allows_more_concurrent_streams(stream_manager)) {
+        /* We've reached the limit, avoid creating new connection in case of 
having connection not being tracked by
+         * streams. */
+        return;
+    }
+
     /* The ideal new connection we need to fit all the pending stream 
acquisitions */
     size_t ideal_new_connection_count =
         
stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_PENDING_ACQUISITION]
 /
@@ -342,6 +384,17 @@
     struct aws_http2_stream_manager *stream_manager = work->stream_manager;
     if (stream_manager->synced_data.state == AWS_H2SMST_READY) {
 
+        /* Steps 0: Check if we've reached the max_concurrent_streams limit */
+        if (!s_sm_allows_more_concurrent_streams(stream_manager)) {
+            /* We've reached the limit, skip building the transactions */
+            STREAM_MANAGER_LOGF(
+                DEBUG,
+                stream_manager,
+                "stream manager waiting - max_concurrent_streams limit reached 
(%" PRIu64 ")",
+                (uint64_t)stream_manager->max_concurrent_streams);
+            return;
+        }
+
         /* Steps 1: Pending acquisitions of stream */
         while 
(!aws_linked_list_empty(&stream_manager->synced_data.pending_stream_acquisitions))
 {
             struct aws_linked_list_node *node =
@@ -495,6 +548,7 @@
 
 static void s_sm_connection_destroy(void *user_data) {
     struct aws_h2_sm_connection *sm_connection = user_data;
+
     aws_mem_release(sm_connection->allocator, sm_connection);
 }
 
@@ -515,6 +569,7 @@
     sm_connection->stream_manager = stream_manager;
     sm_connection->state = AWS_H2SMCST_IDEAL;
     aws_ref_count_init(&sm_connection->ref_count, sm_connection, 
s_sm_connection_destroy);
+
     if (stream_manager->connection_ping_period_ns) {
         struct aws_channel *channel = 
aws_http_connection_get_channel(connection);
         uint64_t schedule_time = 0;
@@ -531,15 +586,20 @@
 
 static void s_sm_connection_release_connection(struct aws_h2_sm_connection 
*sm_connection) {
     AWS_ASSERT(sm_connection->num_streams_assigned == 0);
-    if (sm_connection->connection) {
-        /* Should only be invoked from the connection thread. */
-        
AWS_ASSERT(aws_channel_thread_is_callers_thread(aws_http_connection_get_channel(sm_connection->connection)));
-        int error = aws_http_connection_manager_release_connection(
-            sm_connection->stream_manager->connection_manager, 
sm_connection->connection);
-        AWS_ASSERT(!error);
-        (void)error;
-        sm_connection->connection = NULL;
-    }
+    AWS_ASSERT(sm_connection->connection != NULL);
+    /* Should only be invoked from the connection thread. */
+    
AWS_ASSERT(aws_channel_thread_is_callers_thread(aws_http_connection_get_channel(sm_connection->connection)));
+
+    s_lock_synced_data(sm_connection->stream_manager);
+    /* Remove this connection from the connection tracking list within the 
lock as altering the list in synced_data. */
+    aws_linked_list_remove(&sm_connection->node);
+    s_unlock_synced_data(sm_connection->stream_manager);
+
+    int error = aws_http_connection_manager_release_connection(
+        sm_connection->stream_manager->connection_manager, 
sm_connection->connection);
+    AWS_ASSERT(!error);
+    (void)error;
+    sm_connection->connection = NULL;
     aws_ref_count_release(&sm_connection->ref_count);
 }
 
@@ -612,6 +672,8 @@
             should_release_connection = true;
         } else {
             struct aws_h2_sm_connection *sm_connection = 
s_sm_connection_new(stream_manager, connection);
+            /* Add this connection to the all_held_connections tracking list */
+            
aws_linked_list_push_back(&stream_manager->synced_data.all_held_connections, 
&sm_connection->node);
             bool added = false;
             re_error |=
                 
aws_random_access_set_add(&stream_manager->synced_data.ideal_available_set, 
sm_connection, &added);
@@ -769,6 +831,10 @@
         s_lock_synced_data(stream_manager);
         s_sm_count_decrease_synced(stream_manager, AWS_SMCT_OPEN_STREAM, 1);
         --sm_connection->num_streams_assigned;
+        if (sm_connection->num_streams_assigned == 0) {
+            /* Release the refcount for streams */
+            aws_ref_count_release(&sm_connection->ref_count);
+        }
         if (!connection_available) {
             /* It might be removed already, but, it's fine */
             
aws_random_access_set_remove(&stream_manager->synced_data.ideal_available_set, 
sm_connection);
@@ -777,8 +843,8 @@
             
s_update_sm_connection_set_on_stream_finishes_synced(sm_connection, 
stream_manager);
         }
         s_aws_http2_stream_manager_build_transaction_synced(&work);
-        /* After we build transaction, if the sm_connection still have zero 
assigned stream, we can kill the
-         * sm_connection */
+        /* After we build transaction, if the sm_connection still have zero 
assigned stream, we can release the
+         * sm_connection back to the pool */
         if (sm_connection->num_streams_assigned == 0) {
             /* It might be removed already, but, it's fine */
             
aws_random_access_set_remove(&stream_manager->synced_data.ideal_available_set, 
sm_connection);
@@ -1019,12 +1085,43 @@
     struct aws_http2_stream_manager *stream_manager = user_data;
     STREAM_MANAGER_LOG(TRACE, stream_manager, "Stream Manager reaches the 
condition to destroy, start to destroy");
     /* If there is no outstanding streams, the connections set should be 
empty. */
-    
AWS_ASSERT(aws_random_access_set_get_size(&stream_manager->synced_data.ideal_available_set)
 == 0);
-    
AWS_ASSERT(aws_random_access_set_get_size(&stream_manager->synced_data.nonideal_available_set)
 == 0);
     
AWS_ASSERT(stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_CONNECTIONS_ACQUIRING]
 == 0);
     
AWS_ASSERT(stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_OPEN_STREAM]
 == 0);
     
AWS_ASSERT(stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_PENDING_MAKE_REQUESTS]
 == 0);
     
AWS_ASSERT(stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_PENDING_ACQUISITION]
 == 0);
+
+    /* Iterate through all connections and clean up any that remain */
+    struct aws_linked_list_node *node = 
aws_linked_list_begin(&stream_manager->synced_data.all_held_connections);
+
+    /* No lock needed when stream manager started destroying process. Since 
there should have no other threads still
+     * working. */
+    const struct aws_linked_list_node *end_node =
+        aws_linked_list_end(&stream_manager->synced_data.all_held_connections);
+    while (node != end_node) {
+        struct aws_h2_sm_connection *sm_connection = AWS_CONTAINER_OF(node, 
struct aws_h2_sm_connection, node);
+        /* Move to next node before potentially destroying current connection 
*/
+        node = aws_linked_list_next(node);
+
+        /* Verify this connection has no outstanding streams, which should be 
the precondition before stream manager
+         * start the destroy process. */
+        AWS_ASSERT(sm_connection->num_streams_assigned == 0);
+        /* Release the connection back to connection manager */
+        if (sm_connection->connection) {
+            aws_http_connection_manager_release_connection(
+                stream_manager->connection_manager, sm_connection->connection);
+            sm_connection->connection = NULL;
+            --stream_manager->synced_data.holding_connections_count;
+        }
+
+        /* Remove this connection from the connection tracking list */
+        aws_linked_list_remove(&sm_connection->node);
+        aws_ref_count_release(&sm_connection->ref_count);
+    }
+
+    /* All connections should be cleaned up now */
+    
AWS_ASSERT(aws_linked_list_empty(&stream_manager->synced_data.all_held_connections));
+    AWS_ASSERT(stream_manager->synced_data.holding_connections_count == 0);
+
     AWS_ASSERT(stream_manager->connection_manager);
     struct aws_http_connection_manager *cm = 
stream_manager->connection_manager;
     stream_manager->connection_manager = NULL;
@@ -1069,6 +1166,7 @@
         aws_mem_calloc(allocator, 1, sizeof(struct aws_http2_stream_manager));
     stream_manager->allocator = allocator;
     
aws_linked_list_init(&stream_manager->synced_data.pending_stream_acquisitions);
+    aws_linked_list_init(&stream_manager->synced_data.all_held_connections);
 
     if (aws_mutex_init(&stream_manager->synced_data.lock)) {
         goto on_error;
@@ -1156,6 +1254,7 @@
     stream_manager->max_concurrent_streams_per_connection =
         options->max_concurrent_streams_per_connection ? 
options->max_concurrent_streams_per_connection : UINT32_MAX;
     stream_manager->max_connections = options->max_connections;
+    stream_manager->max_concurrent_streams = options->max_concurrent_streams; 
/* 0 means no limit */
     stream_manager->close_connection_on_server_error = 
options->close_connection_on_server_error;
 
     return stream_manager;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-http-0.10.11/tests/CMakeLists.txt 
new/aws-c-http-0.10.12/tests/CMakeLists.txt
--- old/aws-c-http-0.10.11/tests/CMakeLists.txt 2026-03-02 18:28:16.000000000 
+0100
+++ new/aws-c-http-0.10.12/tests/CMakeLists.txt 2026-03-20 23:33:19.000000000 
+0100
@@ -694,6 +694,9 @@
 add_net_test_case(h2_sm_connection_ping)
 add_net_test_case(h2_sm_with_flow_control_err)
 add_net_test_case(h2_sm_with_initial_settings)
+add_net_test_case(h2_sm_mock_max_concurrent_streams)
+add_net_test_case(h2_sm_mock_max_concurrent_streams_multiple_connections)
+add_net_test_case(h2_sm_mock_max_concurrent_streams_zero_means_no_limit)
 
 # Tests against real world server
 add_net_test_case(h2_sm_acquire_stream)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-http-0.10.11/tests/test_stream_manager.c 
new/aws-c-http-0.10.12/tests/test_stream_manager.c
--- old/aws-c-http-0.10.11/tests/test_stream_manager.c  2026-03-02 
18:28:16.000000000 +0100
+++ new/aws-c-http-0.10.12/tests/test_stream_manager.c  2026-03-20 
23:33:19.000000000 +0100
@@ -128,6 +128,7 @@
     struct h2_fake_peer peer;
     struct aws_http_client_connection_options options;
     struct aws_http_connection *connection;
+    size_t last_completed_frame_index; /* Track the last frame index we've 
completed */
 };
 
 static void s_testing_channel_shutdown(int error_code, void *user_data) {
@@ -358,7 +359,8 @@
     aws_http_headers_add_array(response_headers, response_headers_src, 
AWS_ARRAY_SIZE(response_headers_src));
     size_t frames_count = 
h2_decode_tester_frame_count(&fake_connection->peer.decode);
     int streams_completed = 0;
-    for (size_t i = 0; i < frames_count; ++i) {
+    /* Start from the last completed frame index to avoid re-completing 
streams */
+    for (size_t i = fake_connection->last_completed_frame_index; i < 
frames_count; ++i) {
         struct h2_decoded_frame *frame = 
h2_decode_tester_get_frame(&fake_connection->peer.decode, i);
         if (frame->end_stream) {
             struct aws_h2_frame *response_frame = aws_h2_frame_new_headers(
@@ -372,6 +374,8 @@
                         aws_byte_cursor_from_c_str("tests"),
                         true /*end_stream*/) == AWS_OP_SUCCESS);
             }
+            /* Update the last completed frame index */
+            fake_connection->last_completed_frame_index = i + 1;
             if (num_streams_to_complete && ++streams_completed >= 
num_streams_to_complete) {
                 break;
             }
@@ -1292,6 +1296,148 @@
 
     return s_tester_clean_up();
 }
+
+/* Test that max_concurrent_streams limits the total number of active streams 
*/
+TEST_CASE(h2_sm_mock_max_concurrent_streams) {
+    (void)ctx;
+    size_t max_concurrent_streams = 5;
+    struct sm_tester_options options = {
+        .max_connections = 3,
+        .max_concurrent_streams_per_connection = 10,
+        .alloc = allocator,
+    };
+    ASSERT_SUCCESS(s_tester_init(&options));
+
+    /* Set max_concurrent_streams on the stream manager */
+    s_tester.stream_manager->max_concurrent_streams = max_concurrent_streams;
+
+    
s_override_cm_connect_function(s_aws_http_connection_manager_create_connection_sync_mock);
+
+    /* Try to acquire 10 streams, but only 5 should be created initially */
+    int num_to_acquire = 10;
+    ASSERT_SUCCESS(s_sm_stream_acquiring(num_to_acquire));
+
+    /* Wait for connection to be made */
+    ASSERT_SUCCESS(s_wait_on_fake_connection_count(1));
+    s_drain_all_fake_connection_testing_channel();
+
+    /* Should have acquired only max_concurrent_streams streams */
+    ASSERT_SUCCESS(s_wait_on_streams_acquired_count(max_concurrent_streams));
+    ASSERT_UINT_EQUALS(max_concurrent_streams, 
aws_array_list_length(&s_tester.streams));
+
+    /* Complete 3 streams */
+    struct sm_fake_connection *fake_connection = s_get_fake_connection(0);
+    s_fake_connection_complete_streams(fake_connection, 3, false);
+    s_drain_all_fake_connection_testing_channel();
+
+    /* Now 3 more streams should be created (total of 8 acquired) */
+    ASSERT_SUCCESS(s_wait_on_streams_acquired_count(8));
+    ASSERT_UINT_EQUALS(8, aws_array_list_length(&s_tester.streams));
+
+    /* Complete 2 more streams */
+    s_fake_connection_complete_streams(fake_connection, 2, false);
+    s_drain_all_fake_connection_testing_channel();
+
+    /* The remaining 2 streams should now be created (total of 10) */
+    ASSERT_SUCCESS(s_wait_on_streams_acquired_count(10));
+    ASSERT_UINT_EQUALS(10, aws_array_list_length(&s_tester.streams));
+
+    /* Complete all remaining streams */
+    ASSERT_SUCCESS(s_complete_all_fake_connection_streams());
+    s_drain_all_fake_connection_testing_channel();
+    /* Should complete without error */
+    ASSERT_INT_EQUALS(0, s_tester.stream_complete_errors);
+    ASSERT_INT_EQUALS(AWS_ERROR_SUCCESS, s_tester.stream_completed_error_code);
+
+    return s_tester_clean_up();
+}
+
+/* Test that max_concurrent_streams works with multiple connections */
+TEST_CASE(h2_sm_mock_max_concurrent_streams_multiple_connections) {
+    (void)ctx;
+    size_t max_concurrent_streams = 8;
+    struct sm_tester_options options = {
+        .max_connections = 4,
+        .max_concurrent_streams_per_connection = 3,
+        .alloc = allocator,
+    };
+    ASSERT_SUCCESS(s_tester_init(&options));
+
+    /* Set max_concurrent_streams */
+    s_tester.stream_manager->max_concurrent_streams = max_concurrent_streams;
+
+    
s_override_cm_connect_function(s_aws_http_connection_manager_create_connection_sync_mock);
+
+    /* Try to acquire 15 streams */
+    int num_to_acquire = 15;
+    ASSERT_SUCCESS(s_sm_stream_acquiring(num_to_acquire));
+
+    /* all 4 connections should be created for the pending streams, but only 8 
streams will be created in total */
+    ASSERT_SUCCESS(s_wait_on_fake_connection_count(3));
+    s_drain_all_fake_connection_testing_channel();
+
+    /* Should have acquired only max_concurrent_streams streams */
+    ASSERT_SUCCESS(s_wait_on_streams_acquired_count(max_concurrent_streams));
+    ASSERT_UINT_EQUALS(max_concurrent_streams, 
aws_array_list_length(&s_tester.streams));
+
+    /* Complete 4 streams from first 2 connections, since they must have at 
least 2 streams. */
+    for (size_t i = 0; i < 2; ++i) {
+        struct sm_fake_connection *fake_connection = s_get_fake_connection(i);
+        s_fake_connection_complete_streams(fake_connection, 2, false);
+    }
+    s_drain_all_fake_connection_testing_channel();
+
+    /* 4 more streams should be created (total of 12) */
+    ASSERT_SUCCESS(s_wait_on_streams_acquired_count(12));
+    ASSERT_UINT_EQUALS(12, aws_array_list_length(&s_tester.streams));
+
+    /* Complete remaining streams */
+    ASSERT_SUCCESS(s_complete_all_fake_connection_streams());
+    s_drain_all_fake_connection_testing_channel();
+
+    /* All 15 streams should eventually be acquired */
+    ASSERT_SUCCESS(s_wait_on_streams_acquired_count(15));
+    ASSERT_UINT_EQUALS(15, aws_array_list_length(&s_tester.streams));
+
+    ASSERT_SUCCESS(s_complete_all_fake_connection_streams());
+    s_drain_all_fake_connection_testing_channel();
+    /* Should complete without error */
+    ASSERT_INT_EQUALS(0, s_tester.stream_complete_errors);
+    ASSERT_INT_EQUALS(AWS_ERROR_SUCCESS, s_tester.stream_completed_error_code);
+
+    return s_tester_clean_up();
+}
+
+/* Test that max_concurrent_streams = 0 means no limit (default behavior) */
+TEST_CASE(h2_sm_mock_max_concurrent_streams_zero_means_no_limit) {
+    (void)ctx;
+    struct sm_tester_options options = {
+        .max_connections = 2,
+        .max_concurrent_streams_per_connection = 5,
+        .alloc = allocator,
+    };
+    ASSERT_SUCCESS(s_tester_init(&options));
+
+    /* max_concurrent_streams defaults to 0 (no limit) */
+    ASSERT_UINT_EQUALS(0, s_tester.stream_manager->max_concurrent_streams);
+
+    
s_override_cm_connect_function(s_aws_http_connection_manager_create_connection_sync_mock);
+
+    /* Acquire 10 streams - all should be created immediately */
+    int num_to_acquire = 10;
+    ASSERT_SUCCESS(s_sm_stream_acquiring(num_to_acquire));
+
+    ASSERT_SUCCESS(s_wait_on_fake_connection_count(2)); /* 2 connections 
needed for 10 streams */
+    s_drain_all_fake_connection_testing_channel();
+
+    /* All 10 streams should be acquired without waiting */
+    ASSERT_SUCCESS(s_wait_on_streams_acquired_count(num_to_acquire));
+    ASSERT_UINT_EQUALS(num_to_acquire, 
aws_array_list_length(&s_tester.streams));
+
+    ASSERT_SUCCESS(s_complete_all_fake_connection_streams());
+
+    return s_tester_clean_up();
+}
 
 
/*******************************************************************************
  * Net test, that makes real HTTP/2 connection and requests

Reply via email to