Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package aws-c-io for openSUSE:Factory 
checked in at 2024-06-06 12:34:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/aws-c-io (Old)
 and      /work/SRC/openSUSE:Factory/.aws-c-io.new.24587 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "aws-c-io"

Thu Jun  6 12:34:24 2024 rev:7 rq:1178905 version:0.14.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/aws-c-io/aws-c-io.changes        2024-05-16 
17:15:40.909023698 +0200
+++ /work/SRC/openSUSE:Factory/.aws-c-io.new.24587/aws-c-io.changes     
2024-06-06 12:35:01.617740112 +0200
@@ -1,0 +2,12 @@
+Wed Jun  5 08:50:30 UTC 2024 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- Update to version 0.14.9
+  * Fix tests that require a valid cert to use add_net_test_case
+    by @waahm7 in (#637)
+  * Fix signed/unsigned bug with aws_future_wait() timeout value
+    by @graebm in (#638)
+  * Adds Host Resolver IPv6 variations test by @waahm7 in (#639)
+  * Fix bug where last few bytes on socket go unread
+    by @graebm in (#642)
+
+-------------------------------------------------------------------

Old:
----
  v0.14.8.tar.gz

New:
----
  v0.14.9.tar.gz

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

Other differences:
------------------
++++++ aws-c-io.spec ++++++
--- /var/tmp/diff_new_pack.T3k2Mf/_old  2024-06-06 12:35:03.365803815 +0200
+++ /var/tmp/diff_new_pack.T3k2Mf/_new  2024-06-06 12:35:03.373804107 +0200
@@ -20,7 +20,7 @@
 %define library_version 1.0.0
 %define library_soversion 0unstable
 Name:           aws-c-io
-Version:        0.14.8
+Version:        0.14.9
 Release:        0
 Summary:        I/O and TLS package AWS SDK for C
 License:        Apache-2.0

++++++ v0.14.8.tar.gz -> v0.14.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/source/future.c 
new/aws-c-io-0.14.9/source/future.c
--- old/aws-c-io-0.14.8/source/future.c 2024-05-04 21:28:06.000000000 +0200
+++ new/aws-c-io-0.14.9/source/future.c 2024-06-04 18:27:58.000000000 +0200
@@ -453,13 +453,16 @@
     /* this function is conceptually const, but we need to use synchronization 
primitives */
     struct aws_future_impl *mutable_future = (struct aws_future_impl *)future;
 
+    /* condition-variable takes signed timeout, so clamp to INT64_MAX (292+ 
years) */
+    int64_t timeout_i64 = aws_min_u64(timeout_ns, INT64_MAX);
+
     /* BEGIN CRITICAL SECTION */
     aws_mutex_lock(&mutable_future->lock);
 
     bool is_done = aws_condition_variable_wait_for_pred(
                        &mutable_future->wait_cvar,
                        &mutable_future->lock,
-                       (int64_t)timeout_ns,
+                       timeout_i64,
                        s_future_impl_is_done_pred,
                        mutable_future) == AWS_OP_SUCCESS;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/source/posix/socket.c 
new/aws-c-io-0.14.9/source/posix/socket.c
--- old/aws-c-io-0.14.8/source/posix/socket.c   2024-05-04 21:28:06.000000000 
+0200
+++ new/aws-c-io-0.14.9/source/posix/socket.c   2024-06-04 18:27:58.000000000 
+0200
@@ -1668,6 +1668,23 @@
      * subscribed is set to false. */
     aws_ref_count_acquire(&socket_impl->internal_refcount);
 
+    /* NOTE: READABLE|WRITABLE|HANG_UP events might arrive simultaneously
+     * (e.g. peer sends last few bytes and immediately hangs up).
+     * Notify user of READABLE|WRITABLE events first, so they try to read any 
remaining bytes. */
+
+    if (socket_impl->currently_subscribed && events & 
AWS_IO_EVENT_TYPE_READABLE) {
+        AWS_LOGF_TRACE(AWS_LS_IO_SOCKET, "id=%p fd=%d: is readable", (void 
*)socket, socket->io_handle.data.fd);
+        if (socket->readable_fn) {
+            socket->readable_fn(socket, AWS_OP_SUCCESS, 
socket->readable_user_data);
+        }
+    }
+    /* if socket closed in between these branches, the currently_subscribed 
will be false and socket_impl will not
+     * have been cleaned up, so this next branch is safe. */
+    if (socket_impl->currently_subscribed && events & 
AWS_IO_EVENT_TYPE_WRITABLE) {
+        AWS_LOGF_TRACE(AWS_LS_IO_SOCKET, "id=%p fd=%d: is writable", (void 
*)socket, socket->io_handle.data.fd);
+        s_process_socket_write_requests(socket, NULL);
+    }
+
     if (events & AWS_IO_EVENT_TYPE_REMOTE_HANG_UP || events & 
AWS_IO_EVENT_TYPE_CLOSED) {
         aws_raise_error(AWS_IO_SOCKET_CLOSED);
         AWS_LOGF_TRACE(AWS_LS_IO_SOCKET, "id=%p fd=%d: closed remotely", (void 
*)socket, socket->io_handle.data.fd);
@@ -1688,19 +1705,6 @@
         goto end_check;
     }
 
-    if (socket_impl->currently_subscribed && events & 
AWS_IO_EVENT_TYPE_READABLE) {
-        AWS_LOGF_TRACE(AWS_LS_IO_SOCKET, "id=%p fd=%d: is readable", (void 
*)socket, socket->io_handle.data.fd);
-        if (socket->readable_fn) {
-            socket->readable_fn(socket, AWS_OP_SUCCESS, 
socket->readable_user_data);
-        }
-    }
-    /* if socket closed in between these branches, the currently_subscribed 
will be false and socket_impl will not
-     * have been cleaned up, so this next branch is safe. */
-    if (socket_impl->currently_subscribed && events & 
AWS_IO_EVENT_TYPE_WRITABLE) {
-        AWS_LOGF_TRACE(AWS_LS_IO_SOCKET, "id=%p fd=%d: is writable", (void 
*)socket, socket->io_handle.data.fd);
-        s_process_socket_write_requests(socket, NULL);
-    }
-
 end_check:
     aws_ref_count_release(&socket_impl->internal_refcount);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/source/socket_channel_handler.c 
new/aws-c-io-0.14.9/source/socket_channel_handler.c
--- old/aws-c-io-0.14.8/source/socket_channel_handler.c 2024-05-04 
21:28:06.000000000 +0200
+++ new/aws-c-io-0.14.9/source/socket_channel_handler.c 2024-06-04 
18:27:58.000000000 +0200
@@ -122,6 +122,10 @@
  */
 static void s_do_read(struct socket_handler *socket_handler) {
 
+    if (socket_handler->shutdown_in_progress) {
+        return;
+    }
+
     size_t downstream_window = 
aws_channel_slot_downstream_read_window(socket_handler->slot);
     size_t max_to_read =
         downstream_window > socket_handler->max_rw_size ? 
socket_handler->max_rw_size : downstream_window;
@@ -139,17 +143,20 @@
 
     size_t total_read = 0;
     size_t read = 0;
-    while (total_read < max_to_read && !socket_handler->shutdown_in_progress) {
+    int last_error = 0;
+    while (total_read < max_to_read) {
         size_t iter_max_read = max_to_read - total_read;
 
         struct aws_io_message *message = aws_channel_acquire_message_from_pool(
             socket_handler->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, 
iter_max_read);
 
         if (!message) {
+            last_error = aws_last_error();
             break;
         }
 
         if (aws_socket_read(socket_handler->socket, &message->message_data, 
&read)) {
+            last_error = aws_last_error();
             aws_mem_release(message->allocator, message);
             break;
         }
@@ -162,6 +169,7 @@
             (unsigned long long)read);
 
         if (aws_channel_slot_send_message(socket_handler->slot, message, 
AWS_CHANNEL_DIR_READ)) {
+            last_error = aws_last_error();
             aws_mem_release(message->allocator, message);
             break;
         }
@@ -170,30 +178,29 @@
     AWS_LOGF_TRACE(
         AWS_LS_IO_SOCKET_HANDLER,
         "id=%p: total read on this tick %llu",
-        (void *)&socket_handler->slot->handler,
+        (void *)socket_handler->slot->handler,
         (unsigned long long)total_read);
 
     socket_handler->stats.bytes_read += total_read;
 
     /* resubscribe as long as there's no error, just return if we're in a 
would block scenario. */
     if (total_read < max_to_read) {
-        int last_error = aws_last_error();
+        AWS_ASSERT(last_error != 0);
 
-        if (last_error != AWS_IO_READ_WOULD_BLOCK && 
!socket_handler->shutdown_in_progress) {
+        if (last_error != AWS_IO_READ_WOULD_BLOCK) {
             aws_channel_shutdown(socket_handler->slot->channel, last_error);
+        } else {
+            AWS_LOGF_TRACE(
+                AWS_LS_IO_SOCKET_HANDLER,
+                "id=%p: out of data to read on socket. "
+                "Waiting on event-loop notification.",
+                (void *)socket_handler->slot->handler);
         }
-
-        AWS_LOGF_TRACE(
-            AWS_LS_IO_SOCKET_HANDLER,
-            "id=%p: out of data to read on socket. "
-            "Waiting on event-loop notification.",
-            (void *)socket_handler->slot->handler);
         return;
     }
     /* in this case, everything was fine, but there's still pending reads. We 
need to schedule a task to do the read
      * again. */
-    if (!socket_handler->shutdown_in_progress && total_read == 
socket_handler->max_rw_size &&
-        !socket_handler->read_task_storage.task_fn) {
+    if (total_read == socket_handler->max_rw_size && 
!socket_handler->read_task_storage.task_fn) {
 
         AWS_LOGF_TRACE(
             AWS_LS_IO_SOCKET_HANDLER,
@@ -212,17 +219,29 @@
     (void)socket;
 
     struct socket_handler *socket_handler = user_data;
-    AWS_LOGF_TRACE(AWS_LS_IO_SOCKET_HANDLER, "id=%p: socket is now readable", 
(void *)socket_handler->slot->handler);
+    AWS_LOGF_TRACE(
+        AWS_LS_IO_SOCKET_HANDLER,
+        "id=%p: socket on-readable with error code %d(%s)",
+        (void *)socket_handler->slot->handler,
+        error_code,
+        aws_error_name(error_code));
 
-    /* read regardless so we can pick up data that was sent prior to the 
close. For example, peer sends a TLS ALERT
-     * then immediately closes the socket. On some platforms, we'll never see 
the readable flag. So we want to make
+    /* Regardless of error code call read() until it reports error or EOF,
+     * so we can pick up data that was sent prior to the close.
+     *
+     * For example, if peer closes the socket immediately after sending the 
last
+     * bytes of data, the READABLE and HANGUP events arrive simultaneously.
+     *
+     * Another example, peer sends a TLS ALERT then immediately closes the 
socket.
+     * On some platforms, we'll never see the readable flag. So we want to make
      * sure we read the ALERT, otherwise, we'll end up telling the user that 
the channel shutdown because of a socket
-     * closure, when in reality it was a TLS error */
+     * closure, when in reality it was a TLS error
+     *
+     * It may take more than one read() to get all remaining data.
+     * Also, if the downstream read-window reaches 0, we need to patiently
+     * wait until the window opens before we can call read() again. */
+    (void)error_code;
     s_do_read(socket_handler);
-
-    if (error_code && !socket_handler->shutdown_in_progress) {
-        aws_channel_shutdown(socket_handler->slot->channel, error_code);
-    }
 }
 
 /* Either the result of a context switch (for fairness in the event loop), or 
a window update. */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/source/windows/iocp/socket.c 
new/aws-c-io-0.14.9/source/windows/iocp/socket.c
--- old/aws-c-io-0.14.8/source/windows/iocp/socket.c    2024-05-04 
21:28:06.000000000 +0200
+++ new/aws-c-io-0.14.9/source/windows/iocp/socket.c    2024-06-04 
18:27:58.000000000 +0200
@@ -636,6 +636,7 @@
         case IO_STATUS_TIMEOUT:
             return AWS_IO_SOCKET_TIMEOUT;
         case IO_PIPE_BROKEN:
+        case ERROR_BROKEN_PIPE:
             return AWS_IO_SOCKET_CLOSED;
         case STATUS_INVALID_ADDRESS_COMPONENT:
         case WSAEADDRNOTAVAIL:
@@ -2970,7 +2971,7 @@
 
     AWS_LOGF_ERROR(
         AWS_LS_IO_SOCKET,
-        "id=%p handle=%p: ReadFile() failed with error %d",
+        "id=%p handle=%p: recv() failed with error %d",
         (void *)socket,
         (void *)socket->io_handle.data.handle,
         error);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/tests/CMakeLists.txt 
new/aws-c-io-0.14.9/tests/CMakeLists.txt
--- old/aws-c-io-0.14.8/tests/CMakeLists.txt    2024-05-04 21:28:06.000000000 
+0200
+++ new/aws-c-io-0.14.9/tests/CMakeLists.txt    2024-06-04 18:27:58.000000000 
+0200
@@ -94,6 +94,7 @@
 add_net_test_case(channel_connect_some_hosts_timeout)
 
 add_net_test_case(test_default_with_ipv6_lookup)
+add_net_test_case(test_default_host_resolver_ipv6_address_variations)
 add_test_case(test_resolver_ipv6_address_lookup)
 add_net_test_case(test_default_with_multiple_lookups)
 add_test_case(test_resolver_ipv4_address_lookup)
@@ -122,6 +123,28 @@
 
 add_test_case(socket_handler_echo_and_backpressure)
 add_test_case(socket_handler_close)
+# These tests fail on Windows due to some bug in our server code where, if the 
socket is closed
+# immediately after data is written, that data does not flush cleanly to the 
client.
+# I've lost days to this bug, and no one is using our Windows server 
funcionality,
+# so disabling these tests on Windows and moving along for now.
+# I tried the following:
+# 1) Wrote 2 simple standalone Windows programs, server and client, using 
simple synchronous socket code.
+#    WORKED PERFECTLY. So it's not a fundamental issue with Windows.
+# 2) Commented out server part of this failing test, and used the simple 
standalone server instead.
+#    WORKED PERFECTLY. So it's not a problem with our actual client code.
+# 3) Copy/pasted the simple standlone server code into this test, and used 
that instead of our actual server code.
+#    WORKED PERFECTLY. So it's not a problem with the server and client 
sockets being in the same process.
+# 4) Commented out the client part of this failing test, and used the simple 
standalone client instead.
+#    FAILED. The standalone client got WSAECONNRESET (Connection reset by 
peer) before receiving all the data.
+#    So it's something with our complicated non-blocking server code.
+#    The last interesting thing I noticed before giving up was: we call 
shutdown() immediately
+#    before calling closesocket() but shutdown() gets error WSAENOTCONN, even
+#    though, at that moment, the socket should be connected just fine.
+if(NOT WIN32)
+    add_net_test_case(socket_handler_read_to_eof_after_peer_hangup)
+    add_net_test_case(socket_handler_ipv4_read_to_eof_after_peer_hangup)
+    add_net_test_case(socket_handler_ipv6_read_to_eof_after_peer_hangup)
+endif()
 add_test_case(socket_pinned_event_loop)
 add_net_test_case(socket_pinned_event_loop_dns_failure)
 
@@ -209,7 +232,7 @@
     # Misc non-badssl tls tests
     add_net_test_case(test_concurrent_cert_import)
     add_net_test_case(test_duplicate_cert_import)
-    add_test_case(tls_channel_echo_and_backpressure_test)
+    add_net_test_case(tls_channel_echo_and_backpressure_test)
     add_net_test_case(tls_client_channel_negotiation_error_socket_closed)
     add_net_test_case(tls_client_channel_negotiation_success)
     add_net_test_case(tls_server_multiple_connections)
@@ -222,8 +245,8 @@
 
     add_test_case(alpn_error_creating_handler)
     add_test_case(tls_destroy_null_context)
-    add_test_case(tls_channel_statistics_test)
-    add_test_case(tls_certificate_chain_test)
+    add_net_test_case(tls_channel_statistics_test)
+    add_net_test_case(tls_certificate_chain_test)
 else()
     add_test_case(byo_tls_handler_test)
 endif()
@@ -238,6 +261,7 @@
 add_test_case(future_register_event_loop_callback_always_scheduled)
 add_test_case(future_register_channel_callback)
 add_test_case(future_wait_timeout)
+add_test_case(future_wait_timeout_max)
 add_test_case(future_pointer_with_destroy)
 add_test_case(future_pointer_with_release)
 add_test_case(future_get_result_by_move)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/tests/default_host_resolver_test.c 
new/aws-c-io-0.14.9/tests/default_host_resolver_test.c
--- old/aws-c-io-0.14.8/tests/default_host_resolver_test.c      2024-05-04 
21:28:06.000000000 +0200
+++ new/aws-c-io-0.14.9/tests/default_host_resolver_test.c      2024-06-04 
18:27:58.000000000 +0200
@@ -160,6 +160,103 @@
 
 AWS_TEST_CASE(test_default_with_ipv6_lookup, 
s_test_default_with_ipv6_lookup_fn)
 
+static int s_test_default_host_resolver_ipv6_address_variations_fn(struct 
aws_allocator *allocator, void *ctx) {
+    (void)ctx;
+
+    aws_io_library_init(allocator);
+
+    const struct test_case {
+        const char *ip_address;
+        const char *expected_resolved_ip_address;
+    } test_cases[] = {
+        /* simple full uri*/
+        {
+            .ip_address = "0:0::1",
+            .expected_resolved_ip_address = "::1",
+        },
+        {
+            .ip_address = "::1",
+            .expected_resolved_ip_address = "::1",
+        },
+        {
+            .ip_address = "0:0:0:0:0:0:0:1",
+            .expected_resolved_ip_address = "::1",
+        },
+        {
+            .ip_address = "fd00:ec2:0:0:0:0:0:23",
+            .expected_resolved_ip_address = "fd00:ec2::23",
+        },
+
+    };
+
+    struct aws_event_loop_group *el_group = 
aws_event_loop_group_new_default(allocator, 1, NULL);
+
+    struct aws_host_resolver_default_options resolver_options = {
+        .el_group = el_group,
+        .max_entries = 10,
+    };
+    struct aws_host_resolver *resolver = 
aws_host_resolver_new_default(allocator, &resolver_options);
+
+    struct aws_host_resolution_config config = {
+        .max_ttl = 10,
+        .impl = aws_default_dns_resolve,
+        .impl_data = NULL,
+    };
+
+    struct aws_mutex mutex = AWS_MUTEX_INIT;
+    struct default_host_callback_data callback_data = {
+        .condition_variable = AWS_CONDITION_VARIABLE_INIT,
+        .invoked = false,
+        .has_aaaa_address = false,
+        .has_a_address = false,
+        .mutex = &mutex,
+    };
+
+    for (size_t case_idx = 0; case_idx < AWS_ARRAY_SIZE(test_cases); 
++case_idx) {
+        struct test_case case_i = test_cases[case_idx];
+        printf(
+            "CASE[%zu]: ip_address=%s expected_resolved_ip_address=%s\n, ",
+            case_idx,
+            case_i.ip_address,
+            case_i.expected_resolved_ip_address);
+        struct aws_string *address = aws_string_new_from_c_str(allocator, 
case_i.ip_address);
+        struct aws_string *expected_address = 
aws_string_new_from_c_str(allocator, case_i.expected_resolved_ip_address);
+
+        ASSERT_SUCCESS(aws_host_resolver_resolve_host(
+            resolver, address, s_default_host_resolved_test_callback, &config, 
&callback_data));
+
+        ASSERT_SUCCESS(aws_mutex_lock(&mutex));
+        aws_condition_variable_wait_pred(
+            &callback_data.condition_variable, &mutex, 
s_default_host_resolved_predicate, &callback_data);
+
+        callback_data.invoked = false;
+        ASSERT_TRUE(callback_data.has_aaaa_address);
+        ASSERT_INT_EQUALS(AWS_ADDRESS_RECORD_TYPE_AAAA, 
callback_data.aaaa_address.record_type);
+        ASSERT_BIN_ARRAYS_EQUALS(
+            aws_string_bytes(expected_address),
+            expected_address->len,
+            aws_string_bytes(callback_data.aaaa_address.address),
+            callback_data.aaaa_address.address->len);
+
+        aws_host_address_clean_up(&callback_data.aaaa_address);
+        aws_host_address_clean_up(&callback_data.a_address);
+        ASSERT_SUCCESS(aws_mutex_unlock(&mutex));
+        aws_string_destroy(address);
+        aws_string_destroy(expected_address);
+    }
+
+    aws_host_resolver_release(resolver);
+    aws_event_loop_group_release(el_group);
+
+    aws_io_library_clean_up();
+
+    return 0;
+}
+
+AWS_TEST_CASE(
+    test_default_host_resolver_ipv6_address_variations,
+    s_test_default_host_resolver_ipv6_address_variations_fn)
+
 /* just FYI, this test assumes that "s3.us-east-1.amazonaws.com" does not 
return IPv6 addresses. */
 static int s_test_default_with_ipv4_only_lookup_fn(struct aws_allocator 
*allocator, void *ctx) {
     (void)ctx;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/tests/future_test.c 
new/aws-c-io-0.14.9/tests/future_test.c
--- old/aws-c-io-0.14.8/tests/future_test.c     2024-05-04 21:28:06.000000000 
+0200
+++ new/aws-c-io-0.14.9/tests/future_test.c     2024-06-04 18:27:58.000000000 
+0200
@@ -440,6 +440,29 @@
 }
 AWS_TEST_CASE(future_wait_timeout, s_test_future_wait_timeout)
 
+/* This is a regression test */
+static int s_test_future_wait_timeout_max(struct aws_allocator *alloc, void 
*ctx) {
+    (void)ctx;
+    aws_io_library_init(alloc);
+
+    /* Thread will complete the future in 1sec */
+    struct aws_future_size *future = s_start_thread_job(alloc, ONE_SEC_IN_NS);
+
+    /* Wait for future to complete, with timeout of UINT64_MAX.
+     * Once upon a time, there was a bug where this became a negative number 
and immediately timed out. */
+    bool completed_before_timeout = aws_future_size_wait(future, UINT64_MAX);
+    ASSERT_TRUE(completed_before_timeout);
+
+    /* Wait until other thread joins, at which point the future is complete 
and the callback has fired */
+    aws_thread_set_managed_join_timeout_ns(MAX_TIMEOUT_NS);
+    ASSERT_SUCCESS(aws_thread_join_all_managed());
+
+    aws_future_size_release(future);
+    aws_io_library_clean_up();
+    return 0;
+}
+AWS_TEST_CASE(future_wait_timeout_max, s_test_future_wait_timeout_max)
+
 struct aws_destroyme {
     struct aws_allocator *alloc;
     bool *set_true_on_death;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/tests/read_write_test_handler.c 
new/aws-c-io-0.14.9/tests/read_write_test_handler.c
--- old/aws-c-io-0.14.8/tests/read_write_test_handler.c 2024-05-04 
21:28:06.000000000 +0200
+++ new/aws-c-io-0.14.9/tests/read_write_test_handler.c 2024-06-04 
18:27:58.000000000 +0200
@@ -190,42 +190,60 @@
     struct aws_channel_slot *slot;
     struct aws_byte_buf *buffer;
     struct aws_channel_task task;
+    aws_channel_on_message_write_completed_fn *on_completion;
+    void *user_data;
 };
 
-static void s_rw_handler_write_task(struct aws_channel_task *task, void *arg, 
enum aws_task_status task_status) {
-    (void)task;
-    (void)task_status;
-    struct rw_handler_write_task_args *write_task_args = arg;
+static void s_rw_handler_write_now(
+    struct aws_channel_slot *slot,
+    struct aws_byte_buf *buffer,
+    aws_channel_on_message_write_completed_fn *on_completion,
+    void *user_data) {
+
+    struct aws_io_message *msg =
+        aws_channel_acquire_message_from_pool(slot->channel, 
AWS_IO_MESSAGE_APPLICATION_DATA, buffer->len);
 
-    struct aws_io_message *msg = aws_channel_acquire_message_from_pool(
-        write_task_args->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, 
write_task_args->buffer->len);
+    msg->on_completion = on_completion;
+    msg->user_data = user_data;
 
-    struct aws_byte_cursor write_buffer = 
aws_byte_cursor_from_buf(write_task_args->buffer);
-    aws_byte_buf_append(&msg->message_data, &write_buffer);
+    struct aws_byte_cursor write_buffer = aws_byte_cursor_from_buf(buffer);
+    AWS_FATAL_ASSERT(aws_byte_buf_append(&msg->message_data, &write_buffer) == 
AWS_OP_SUCCESS);
 
-    aws_channel_slot_send_message(write_task_args->slot, msg, 
AWS_CHANNEL_DIR_WRITE);
+    AWS_FATAL_ASSERT(aws_channel_slot_send_message(slot, msg, 
AWS_CHANNEL_DIR_WRITE) == AWS_OP_SUCCESS);
+}
 
+static void s_rw_handler_write_task(struct aws_channel_task *task, void *arg, 
enum aws_task_status task_status) {
+    (void)task;
+    (void)task_status;
+    struct rw_handler_write_task_args *write_task_args = arg;
+    s_rw_handler_write_now(
+        write_task_args->slot, write_task_args->buffer, 
write_task_args->on_completion, write_task_args->user_data);
     aws_mem_release(write_task_args->handler->alloc, write_task_args);
 }
 
 void rw_handler_write(struct aws_channel_handler *handler, struct 
aws_channel_slot *slot, struct aws_byte_buf *buffer) {
+    rw_handler_write_with_callback(handler, slot, buffer, NULL 
/*on_completion*/, NULL /*user_data*/);
+}
+
+void rw_handler_write_with_callback(
+    struct aws_channel_handler *handler,
+    struct aws_channel_slot *slot,
+    struct aws_byte_buf *buffer,
+    aws_channel_on_message_write_completed_fn *on_completion,
+    void *user_data) {
 
     struct rw_test_handler_impl *handler_impl = handler->impl;
 
     if (!handler_impl->event_loop_driven || 
aws_channel_thread_is_callers_thread(slot->channel)) {
-        struct aws_io_message *msg =
-            aws_channel_acquire_message_from_pool(slot->channel, 
AWS_IO_MESSAGE_APPLICATION_DATA, buffer->len);
-
-        struct aws_byte_cursor write_buffer = aws_byte_cursor_from_buf(buffer);
-        aws_byte_buf_append(&msg->message_data, &write_buffer);
-
-        aws_channel_slot_send_message(slot, msg, AWS_CHANNEL_DIR_WRITE);
+        s_rw_handler_write_now(slot, buffer, on_completion, user_data);
     } else {
         struct rw_handler_write_task_args *write_task_args =
             aws_mem_acquire(handler->alloc, sizeof(struct 
rw_handler_write_task_args));
         write_task_args->handler = handler;
         write_task_args->buffer = buffer;
         write_task_args->slot = slot;
+        write_task_args->on_completion = on_completion;
+        write_task_args->user_data = user_data;
         aws_channel_task_init(&write_task_args->task, s_rw_handler_write_task, 
write_task_args, "rw_handler_write");
 
         aws_channel_schedule_task_now(slot->channel, &write_task_args->task);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/tests/read_write_test_handler.h 
new/aws-c-io-0.14.9/tests/read_write_test_handler.h
--- old/aws-c-io-0.14.8/tests/read_write_test_handler.h 2024-05-04 
21:28:06.000000000 +0200
+++ new/aws-c-io-0.14.9/tests/read_write_test_handler.h 2024-06-04 
18:27:58.000000000 +0200
@@ -36,6 +36,13 @@
 
 void rw_handler_write(struct aws_channel_handler *handler, struct 
aws_channel_slot *slot, struct aws_byte_buf *buffer);
 
+void rw_handler_write_with_callback(
+    struct aws_channel_handler *handler,
+    struct aws_channel_slot *slot,
+    struct aws_byte_buf *buffer,
+    aws_channel_on_message_write_completed_fn *on_completion,
+    void *user_data);
+
 void rw_handler_trigger_read(struct aws_channel_handler *handler, struct 
aws_channel_slot *slot);
 
 bool rw_handler_shutdown_called(struct aws_channel_handler *handler);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-io-0.14.8/tests/socket_handler_test.c 
new/aws-c-io-0.14.9/tests/socket_handler_test.c
--- old/aws-c-io-0.14.8/tests/socket_handler_test.c     2024-05-04 
21:28:06.000000000 +0200
+++ new/aws-c-io-0.14.9/tests/socket_handler_test.c     2024-06-04 
18:27:58.000000000 +0200
@@ -17,6 +17,13 @@
 #include "statistics_handler_test.h"
 #include <read_write_test_handler.h>
 
+#ifdef _MSC_VER
+#    pragma warning(disable : 4996) /* allow strncpy() */
+#endif
+
+#define NANOS_PER_SEC ((uint64_t)AWS_TIMESTAMP_NANOS)
+#define TIMEOUT (10 * NANOS_PER_SEC)
+
 struct socket_test_args {
     struct aws_allocator *allocator;
     struct aws_mutex *mutex;
@@ -24,7 +31,7 @@
     struct aws_channel *channel;
     struct aws_channel_handler *rw_handler;
 
-    struct aws_atomic_var rw_slot; /* pointer-to struct aws_channel_slot */
+    struct aws_channel_slot *rw_slot;
     int error_code;
     bool shutdown_invoked;
     bool error_invoked;
@@ -37,6 +44,7 @@
     struct aws_mutex mutex;
     struct aws_condition_variable condition_variable;
     struct aws_event_loop_group *el_group;
+    struct aws_host_resolver *resolver;
     struct aws_atomic_var current_time_ns;
     struct aws_atomic_var stats_handler;
 
@@ -52,6 +60,13 @@
     aws_io_library_init(allocator);
 
     tester->el_group = aws_event_loop_group_new_default(allocator, 0, NULL);
+
+    struct aws_host_resolver_default_options resolver_options = {
+        .el_group = tester->el_group,
+        .max_entries = 8,
+    };
+    tester->resolver = aws_host_resolver_new_default(allocator, 
&resolver_options);
+
     struct aws_mutex mutex = AWS_MUTEX_INIT;
     struct aws_condition_variable condition_variable = 
AWS_CONDITION_VARIABLE_INIT;
     tester->mutex = mutex;
@@ -63,6 +78,7 @@
 }
 
 static int s_socket_common_tester_clean_up(struct socket_common_tester 
*tester) {
+    aws_host_resolver_release(tester->resolver);
     aws_event_loop_group_release(tester->el_group);
 
     aws_mutex_clean_up(&tester->mutex);
@@ -87,7 +103,7 @@
 
 static bool s_channel_setup_predicate(void *user_data) {
     struct socket_test_args *setup_test_args = (struct socket_test_args 
*)user_data;
-    return aws_atomic_load_ptr(&setup_test_args->rw_slot) != NULL;
+    return setup_test_args->rw_slot != NULL;
 }
 
 static bool s_channel_shutdown_predicate(void *user_data) {
@@ -120,7 +136,7 @@
     aws_channel_slot_insert_end(channel, rw_slot);
 
     aws_channel_slot_set_handler(rw_slot, setup_test_args->rw_handler);
-    aws_atomic_store_ptr(&setup_test_args->rw_slot, rw_slot);
+    setup_test_args->rw_slot = rw_slot;
 
     aws_mutex_unlock(setup_test_args->mutex);
 
@@ -147,7 +163,7 @@
         aws_channel_slot_insert_end(channel, rw_slot);
 
         aws_channel_slot_set_handler(rw_slot, setup_test_args->rw_handler);
-        aws_atomic_store_ptr(&setup_test_args->rw_slot, rw_slot);
+        setup_test_args->rw_slot = rw_slot;
     }
 
     aws_mutex_unlock(setup_test_args->mutex);
@@ -198,6 +214,7 @@
     struct aws_byte_buf received_message;
     size_t amount_read;
     size_t expected_read;
+    size_t amount_written;
     bool invocation_happened;
     bool shutdown_finished;
 };
@@ -224,8 +241,7 @@
     struct socket_test_rw_args *rw_args = (struct socket_test_rw_args 
*)user_data;
 
     aws_mutex_lock(rw_args->mutex);
-    memcpy(rw_args->received_message.buffer + rw_args->received_message.len, 
data_read->buffer, data_read->len);
-    rw_args->received_message.len += data_read->len;
+    
AWS_FATAL_ASSERT(aws_byte_buf_write_from_whole_buffer(&rw_args->received_message,
 *data_read) == true);
     rw_args->amount_read += data_read->len;
     rw_args->invocation_happened = true;
     aws_condition_variable_notify_one(rw_args->condition_variable);
@@ -234,6 +250,23 @@
     return rw_args->received_message;
 }
 
+void s_socket_test_handle_on_write_completed(
+    struct aws_channel *channel,
+    struct aws_io_message *message,
+    int error_code,
+    void *user_data) {
+
+    (void)channel;
+    AWS_FATAL_ASSERT(error_code == 0);
+    struct socket_test_rw_args *rw_args = (struct socket_test_rw_args 
*)user_data;
+
+    aws_mutex_lock(rw_args->mutex);
+    rw_args->amount_written += message->message_data.len;
+    rw_args->invocation_happened = true;
+    aws_condition_variable_notify_one(rw_args->condition_variable);
+    aws_mutex_unlock(rw_args->mutex);
+}
+
 static struct aws_byte_buf s_socket_test_handle_write(
     struct aws_channel_handler *handler,
     struct aws_channel_slot *slot,
@@ -291,13 +324,27 @@
     struct local_server_tester *tester,
     struct socket_test_args *args,
     struct socket_common_tester *s_c_tester,
+    enum aws_socket_domain socket_domain,
     bool enable_back_pressure) {
+
     AWS_ZERO_STRUCT(*tester);
     tester->socket_options.connect_timeout_ms = 3000;
     tester->socket_options.type = AWS_SOCKET_STREAM;
-    tester->socket_options.domain = AWS_SOCKET_LOCAL;
-
-    aws_socket_endpoint_init_local_address_for_test(&tester->endpoint);
+    tester->socket_options.domain = socket_domain;
+    switch (socket_domain) {
+        case AWS_SOCKET_LOCAL:
+            aws_socket_endpoint_init_local_address_for_test(&tester->endpoint);
+            break;
+        case AWS_SOCKET_IPV4:
+            strncpy(tester->endpoint.address, "127.0.0.1", 
sizeof(tester->endpoint.address));
+            break;
+        case AWS_SOCKET_IPV6:
+            strncpy(tester->endpoint.address, "::1", 
sizeof(tester->endpoint.address));
+            break;
+        default:
+            ASSERT_TRUE(false);
+            break;
+    }
 
     tester->server_bootstrap = aws_server_bootstrap_new(allocator, 
s_c_tester->el_group);
     ASSERT_NOT_NULL(tester->server_bootstrap);
@@ -316,6 +363,9 @@
     tester->listener = 
aws_server_bootstrap_new_socket_listener(&bootstrap_options);
     ASSERT_NOT_NULL(tester->listener);
 
+    /* find out which port the socket is bound to */
+    ASSERT_SUCCESS(aws_socket_get_bound_address(tester->listener, 
&tester->endpoint));
+
     return AWS_OP_SUCCESS;
 }
 
@@ -344,11 +394,12 @@
     ASSERT_SUCCESS(s_socket_test_args_init(&client_args, &c_tester, 
client_rw_handler));
 
     struct local_server_tester local_server_tester;
-    ASSERT_SUCCESS(s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, true));
+    ASSERT_SUCCESS(
+        s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, AWS_SOCKET_LOCAL, true));
 
     struct aws_client_bootstrap_options client_bootstrap_options = {
         .event_loop_group = c_tester.el_group,
-        .host_resolver = NULL,
+        .host_resolver = c_tester.resolver,
     };
     struct aws_client_bootstrap *client_bootstrap = 
aws_client_bootstrap_new(allocator, &client_bootstrap_options);
     ASSERT_NOT_NULL(client_bootstrap);
@@ -359,7 +410,7 @@
     AWS_ZERO_STRUCT(client_channel_options);
     client_channel_options.bootstrap = client_bootstrap;
     client_channel_options.host_name = local_server_tester.endpoint.address;
-    client_channel_options.port = 0;
+    client_channel_options.port = local_server_tester.endpoint.port;
     client_channel_options.socket_options = 
&local_server_tester.socket_options;
     client_channel_options.setup_callback = 
s_socket_handler_test_client_setup_callback;
     client_channel_options.shutdown_callback = 
s_socket_handler_test_client_shutdown_callback;
@@ -371,10 +422,10 @@
 
     ASSERT_SUCCESS(aws_mutex_lock(&c_tester.mutex));
     /* wait for both ends to setup */
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_pinned_channel_setup_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_pinned_channel_setup_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_pinned_channel_setup_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_pinned_channel_setup_predicate, &client_args));
 
     /* Verify the client channel was placed on the requested event loop */
     ASSERT_PTR_EQUALS(pinned_event_loop, 
aws_channel_get_event_loop(client_args.channel));
@@ -382,13 +433,13 @@
     ASSERT_SUCCESS(aws_channel_shutdown(server_args.channel, AWS_OP_SUCCESS));
     ASSERT_SUCCESS(aws_channel_shutdown(client_args.channel, AWS_OP_SUCCESS));
 
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &client_args));
     
aws_server_bootstrap_destroy_socket_listener(local_server_tester.server_bootstrap,
 local_server_tester.listener);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_listener_destroy_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_listener_destroy_predicate, &server_args));
 
     aws_mutex_unlock(&c_tester.mutex);
 
@@ -450,15 +501,9 @@
 
     s_socket_common_tester_init(allocator, &c_tester);
 
-    struct aws_host_resolver_default_options resolver_options = {
-        .el_group = c_tester.el_group,
-        .max_entries = 8,
-    };
-    struct aws_host_resolver *resolver = 
aws_host_resolver_new_default(allocator, &resolver_options);
-
     struct aws_client_bootstrap_options client_bootstrap_options = {
         .event_loop_group = c_tester.el_group,
-        .host_resolver = resolver,
+        .host_resolver = c_tester.resolver,
     };
     struct aws_client_bootstrap *client_bootstrap = 
aws_client_bootstrap_new(allocator, &client_bootstrap_options);
     ASSERT_NOT_NULL(client_bootstrap);
@@ -487,8 +532,8 @@
     
ASSERT_SUCCESS(aws_client_bootstrap_new_socket_channel(&client_channel_options));
 
     ASSERT_SUCCESS(aws_mutex_lock(&c_tester.mutex));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_dns_failure_channel_setup_predicate, &c_tester));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_dns_failure_channel_setup_predicate, &c_tester));
 
     /* Verify the setup callback failure was on the requested event loop */
     ASSERT_TRUE(c_tester.setup_error_code != 0);
@@ -496,7 +541,6 @@
     aws_mutex_unlock(&c_tester.mutex);
 
     aws_client_bootstrap_release(client_bootstrap);
-    aws_host_resolver_release(resolver);
     ASSERT_SUCCESS(s_socket_common_tester_clean_up(&c_tester));
 
     return AWS_OP_SUCCESS;
@@ -556,11 +600,12 @@
     ASSERT_SUCCESS(s_socket_test_args_init(&client_args, &c_tester, 
client_rw_handler));
 
     struct local_server_tester local_server_tester;
-    ASSERT_SUCCESS(s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, true));
+    ASSERT_SUCCESS(
+        s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, AWS_SOCKET_LOCAL, true));
 
     struct aws_client_bootstrap_options client_bootstrap_options = {
         .event_loop_group = c_tester.el_group,
-        .host_resolver = NULL,
+        .host_resolver = c_tester.resolver,
     };
     struct aws_client_bootstrap *client_bootstrap = 
aws_client_bootstrap_new(allocator, &client_bootstrap_options);
     ASSERT_NOT_NULL(client_bootstrap);
@@ -569,7 +614,7 @@
     AWS_ZERO_STRUCT(client_channel_options);
     client_channel_options.bootstrap = client_bootstrap;
     client_channel_options.host_name = local_server_tester.endpoint.address;
-    client_channel_options.port = 0;
+    client_channel_options.port = local_server_tester.endpoint.port;
     client_channel_options.socket_options = 
&local_server_tester.socket_options;
     client_channel_options.setup_callback = 
s_socket_handler_test_client_setup_callback;
     client_channel_options.shutdown_callback = 
s_socket_handler_test_client_shutdown_callback;
@@ -581,20 +626,20 @@
     ASSERT_SUCCESS(aws_mutex_lock(&c_tester.mutex));
 
     /* wait for both ends to setup */
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_setup_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_setup_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &client_args));
 
     /* send msg from client to server, and wait for some bytes to be received 
*/
-    rw_handler_write(client_args.rw_handler, 
aws_atomic_load_ptr(&client_args.rw_slot), &msg_from_client);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_socket_test_read_predicate, &server_rw_args));
+    rw_handler_write(client_args.rw_handler, client_args.rw_slot, 
&msg_from_client);
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_socket_test_read_predicate, &server_rw_args));
 
     /* send msg from server to client, and wait for some bytes to be received 
*/
-    rw_handler_write(server_args.rw_handler, 
aws_atomic_load_ptr(&server_args.rw_slot), &msg_from_server);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_socket_test_read_predicate, &client_rw_args));
+    rw_handler_write(server_args.rw_handler, server_args.rw_slot, 
&msg_from_server);
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_socket_test_read_predicate, &client_rw_args));
 
     /* confirm that the initial read window was respected */
     server_rw_args.invocation_happened = false;
@@ -604,13 +649,13 @@
     ASSERT_INT_EQUALS(s_server_initial_read_window, 
server_rw_args.amount_read);
 
     /* increment the read window on both sides and confirm they receive the 
remainder of their message */
-    rw_handler_trigger_increment_read_window(server_args.rw_handler, 
aws_atomic_load_ptr(&server_args.rw_slot), 100);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_socket_test_full_read_predicate, &server_rw_args));
-
-    rw_handler_trigger_increment_read_window(client_args.rw_handler, 
aws_atomic_load_ptr(&client_args.rw_slot), 100);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_socket_test_full_read_predicate, &client_rw_args));
+    rw_handler_trigger_increment_read_window(server_args.rw_handler, 
server_args.rw_slot, 100);
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_socket_test_full_read_predicate, &server_rw_args));
+
+    rw_handler_trigger_increment_read_window(client_args.rw_handler, 
client_args.rw_slot, 100);
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_socket_test_full_read_predicate, &client_rw_args));
 
     ASSERT_INT_EQUALS(msg_from_server.len, client_rw_args.amount_read);
     ASSERT_INT_EQUALS(msg_from_client.len, server_rw_args.amount_read);
@@ -630,13 +675,13 @@
     ASSERT_SUCCESS(aws_channel_shutdown(server_args.channel, AWS_OP_SUCCESS));
     ASSERT_SUCCESS(aws_channel_shutdown(client_args.channel, AWS_OP_SUCCESS));
 
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &client_args));
     
aws_server_bootstrap_destroy_socket_listener(local_server_tester.server_bootstrap,
 local_server_tester.listener);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_listener_destroy_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_listener_destroy_predicate, &server_args));
 
     aws_mutex_unlock(&c_tester.mutex);
 
@@ -687,11 +732,12 @@
     ASSERT_SUCCESS(s_socket_test_args_init(&client_args, &c_tester, 
client_rw_handler));
 
     struct local_server_tester local_server_tester;
-    ASSERT_SUCCESS(s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, false));
+    ASSERT_SUCCESS(
+        s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, AWS_SOCKET_LOCAL, false));
 
     struct aws_client_bootstrap_options client_bootstrap_options = {
         .event_loop_group = c_tester.el_group,
-        .host_resolver = NULL,
+        .host_resolver = c_tester.resolver,
     };
     struct aws_client_bootstrap *client_bootstrap = 
aws_client_bootstrap_new(allocator, &client_bootstrap_options);
     ASSERT_NOT_NULL(client_bootstrap);
@@ -700,7 +746,7 @@
     AWS_ZERO_STRUCT(client_channel_options);
     client_channel_options.bootstrap = client_bootstrap;
     client_channel_options.host_name = local_server_tester.endpoint.address;
-    client_channel_options.port = 0;
+    client_channel_options.port = local_server_tester.endpoint.port;
     client_channel_options.socket_options = 
&local_server_tester.socket_options;
     client_channel_options.setup_callback = 
s_socket_handler_test_client_setup_callback;
     client_channel_options.shutdown_callback = 
s_socket_handler_test_client_shutdown_callback;
@@ -711,24 +757,24 @@
     ASSERT_SUCCESS(aws_mutex_lock(&c_tester.mutex));
 
     /* wait for both ends to setup */
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_setup_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_setup_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &client_args));
 
     aws_channel_shutdown(server_args.channel, AWS_OP_SUCCESS);
 
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &client_args));
 
     ASSERT_INT_EQUALS(AWS_OP_SUCCESS, server_args.error_code);
     ASSERT_TRUE(
         AWS_IO_SOCKET_CLOSED == client_args.error_code || 
AWS_IO_SOCKET_NOT_CONNECTED == client_args.error_code);
     
aws_server_bootstrap_destroy_socket_listener(local_server_tester.server_bootstrap,
 local_server_tester.listener);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_listener_destroy_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_listener_destroy_predicate, &server_args));
 
     aws_mutex_unlock(&c_tester.mutex);
 
@@ -743,6 +789,182 @@
 
 AWS_TEST_CASE(socket_handler_close, s_socket_close_test)
 
+/* This is a regression test.
+ * Once upon a time, if the socket-handler received READABLE and HANGUP events 
simultaneously,
+ * it would read one last time from the socket before closing it. But one read 
may
+ * not be enough to get all remaining data. The correct thing is to do is
+ * repeatedly read until the read() call itself reports EOF or an error.
+ *
+ * Anyway, this test establishes a connection between server and client.
+ * The server sends a big chunk of data, and closes the socket immediately
+ * after the write completes. The client should still be able to read all the 
data. */
+static int s_socket_read_to_eof_after_peer_hangup_test_common(
+    struct aws_allocator *allocator,
+    void *ctx,
+    enum aws_socket_domain socket_domain) {
+
+    (void)ctx;
+    s_socket_common_tester_init(allocator, &c_tester);
+
+    const size_t total_bytes_to_send_from_server = 
g_aws_channel_max_fragment_size;
+
+    struct aws_byte_buf client_received_message;
+    ASSERT_SUCCESS(aws_byte_buf_init(&client_received_message, allocator, 
total_bytes_to_send_from_server));
+
+    struct aws_byte_buf msg_from_server;
+    ASSERT_SUCCESS(aws_byte_buf_init(&msg_from_server, allocator, 
total_bytes_to_send_from_server));
+
+    struct socket_test_rw_args server_rw_args;
+    ASSERT_SUCCESS(s_rw_args_init(&server_rw_args, &c_tester, 
aws_byte_buf_from_empty_array(NULL, 0), 0));
+
+    struct socket_test_rw_args client_rw_args;
+    ASSERT_SUCCESS(s_rw_args_init(&client_rw_args, &c_tester, 
client_received_message, 0));
+
+    /* NOTE: client starts with window=0, so we can VERY CAREFULLY control 
when it reads data from the socket */
+    struct aws_channel_handler *client_rw_handler = rw_handler_new(
+        allocator, s_socket_test_handle_read, s_socket_test_handle_write, 
true, 0 /*window*/, &client_rw_args);
+    ASSERT_NOT_NULL(client_rw_handler);
+
+    struct aws_channel_handler *server_rw_handler =
+        rw_handler_new(allocator, s_socket_test_handle_read, 
s_socket_test_handle_write, true, 10000, &server_rw_args);
+    ASSERT_NOT_NULL(server_rw_handler);
+
+    struct socket_test_args server_args;
+    ASSERT_SUCCESS(s_socket_test_args_init(&server_args, &c_tester, 
server_rw_handler));
+
+    struct socket_test_args client_args;
+    ASSERT_SUCCESS(s_socket_test_args_init(&client_args, &c_tester, 
client_rw_handler));
+
+    struct local_server_tester local_server_tester;
+    if (s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, socket_domain, false)) {
+        /* Skip test if server can't bind to address (e.g. Gith9ub's ubuntu 
runners don't allow IPv6) */
+        if (aws_last_error() == AWS_IO_SOCKET_INVALID_ADDRESS) {
+            return AWS_OP_SKIP;
+        } else {
+            ASSERT_TRUE(false, "s_local_server_tester_init() failed");
+        }
+    }
+
+    struct aws_client_bootstrap_options client_bootstrap_options = {
+        .event_loop_group = c_tester.el_group,
+        .host_resolver = c_tester.resolver,
+    };
+    struct aws_client_bootstrap *client_bootstrap = 
aws_client_bootstrap_new(allocator, &client_bootstrap_options);
+    ASSERT_NOT_NULL(client_bootstrap);
+
+    struct aws_socket_channel_bootstrap_options client_channel_options = {
+        .bootstrap = client_bootstrap,
+        .host_name = local_server_tester.endpoint.address,
+        .port = local_server_tester.endpoint.port,
+        .socket_options = &local_server_tester.socket_options,
+        .setup_callback = s_socket_handler_test_client_setup_callback,
+        .shutdown_callback = s_socket_handler_test_client_shutdown_callback,
+        .user_data = &client_args,
+        .enable_read_back_pressure = true,
+    };
+
+    
ASSERT_SUCCESS(aws_client_bootstrap_new_socket_channel(&client_channel_options));
+
+    ASSERT_SUCCESS(aws_mutex_lock(&c_tester.mutex));
+
+    /* wait for both ends to setup */
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &client_args));
+
+    /* We want the server to send some data and hang up IMMEDIATELY after,
+     * before the client has fully read the data. This is tricky to do in a 
test.
+     *
+     * First, have the server send data... */
+    ASSERT_TRUE(aws_byte_buf_write_u8_n(&msg_from_server, 's', 
total_bytes_to_send_from_server));
+    rw_handler_write_with_callback(
+        server_rw_handler,
+        server_args.rw_slot,
+        &msg_from_server,
+        s_socket_test_handle_on_write_completed,
+        &server_rw_args);
+
+    /* ...now have the client open its read window and receive data in tiny 
chunks,
+     * stopping once the server has sent all data, but BEFORE the client has 
read all data.
+     * This is possible because the client's OS will buffer a certain amount of
+     * incoming data, before the client application calls read() on it. */
+    while (server_rw_args.amount_written < total_bytes_to_send_from_server) {
+        const size_t client_read_chunk_size = 128;
+        client_rw_args.expected_read += client_read_chunk_size;
+        rw_handler_trigger_increment_read_window(client_args.rw_handler, 
client_args.rw_slot, client_read_chunk_size);
+        ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+            &c_tester.condition_variable,
+            &c_tester.mutex,
+            TIMEOUT,
+            s_socket_test_full_read_predicate,
+            &client_rw_args));
+    }
+
+    /* Now close the server's socket.*/
+    ASSERT_SUCCESS(aws_channel_shutdown(server_args.channel, AWS_OP_SUCCESS));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &server_args));
+
+    /* Now sleep a moment to 100% guarantee the OS propagates the socket-close 
event to the client-side. */
+    aws_mutex_unlock(&c_tester.mutex);
+    aws_thread_current_sleep(NANOS_PER_SEC / 4);
+    aws_mutex_lock(&c_tester.mutex);
+
+    /* Ensure the client hasn't shut down before reading all the data. */
+    ASSERT_FALSE(client_args.shutdown_invoked, "Client should read all data 
before shutting down.");
+
+    /* Ensure the client hasn't read all data yet */
+    ASSERT_TRUE(
+        client_rw_args.amount_read < total_bytes_to_send_from_server,
+        "If this fails, then we're not truly reproducing the regression test."
+        " The server needs to finish sending data, and close the socket,"
+        " BEFORE the client reads all the data.");
+
+    /* Have the client open its window more-than-enough to receive the rest of 
the data.
+     * If the client socket closes before all the data is received, then we 
still have the bug. */
+    rw_handler_trigger_increment_read_window(
+        client_args.rw_handler, client_args.rw_slot, 
total_bytes_to_send_from_server * 3 /*more-than-enough*/);
+    client_rw_args.expected_read = total_bytes_to_send_from_server;
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_socket_test_full_read_predicate, &client_rw_args));
+
+    /* Wait for client to shutdown, due to the server having closed the socket 
*/
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &client_args));
+
+    
aws_server_bootstrap_destroy_socket_listener(local_server_tester.server_bootstrap,
 local_server_tester.listener);
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_listener_destroy_predicate, &server_args));
+
+    ASSERT_INT_EQUALS(AWS_IO_SOCKET_CLOSED, client_args.error_code);
+
+    aws_mutex_unlock(&c_tester.mutex);
+
+    /* clean up */
+    ASSERT_SUCCESS(s_local_server_tester_clean_up(&local_server_tester));
+    aws_byte_buf_clean_up(&client_received_message);
+    aws_byte_buf_clean_up(&msg_from_server);
+    aws_client_bootstrap_release(client_bootstrap);
+    ASSERT_SUCCESS(s_socket_common_tester_clean_up(&c_tester));
+
+    return AWS_OP_SUCCESS;
+}
+static int s_socket_read_to_eof_after_peer_hangup_test(struct aws_allocator 
*allocator, void *ctx) {
+    return s_socket_read_to_eof_after_peer_hangup_test_common(allocator, ctx, 
AWS_SOCKET_LOCAL);
+}
+AWS_TEST_CASE(socket_handler_read_to_eof_after_peer_hangup, 
s_socket_read_to_eof_after_peer_hangup_test)
+
+static int s_socket_ipv4_read_to_eof_after_peer_hangup_test(struct 
aws_allocator *allocator, void *ctx) {
+    return s_socket_read_to_eof_after_peer_hangup_test_common(allocator, ctx, 
AWS_SOCKET_IPV4);
+}
+AWS_TEST_CASE(socket_handler_ipv4_read_to_eof_after_peer_hangup, 
s_socket_ipv4_read_to_eof_after_peer_hangup_test)
+
+static int s_socket_ipv6_read_to_eof_after_peer_hangup_test(struct 
aws_allocator *allocator, void *ctx) {
+    return s_socket_read_to_eof_after_peer_hangup_test_common(allocator, ctx, 
AWS_SOCKET_IPV6);
+}
+AWS_TEST_CASE(socket_handler_ipv6_read_to_eof_after_peer_hangup, 
s_socket_ipv6_read_to_eof_after_peer_hangup_test)
+
 static void s_creation_callback_test_channel_creation_callback(
     struct aws_client_bootstrap *bootstrap,
     int error_code,
@@ -843,12 +1065,13 @@
     ASSERT_SUCCESS(s_socket_test_args_init(&client_args, &c_tester, 
client_rw_handler));
 
     struct local_server_tester local_server_tester;
-    ASSERT_SUCCESS(s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, false));
+    ASSERT_SUCCESS(
+        s_local_server_tester_init(allocator, &local_server_tester, 
&server_args, &c_tester, AWS_SOCKET_LOCAL, false));
 
     struct aws_client_bootstrap_options client_bootstrap_options;
     AWS_ZERO_STRUCT(client_bootstrap_options);
     client_bootstrap_options.event_loop_group = c_tester.el_group;
-    client_bootstrap_options.host_resolver = NULL;
+    client_bootstrap_options.host_resolver = c_tester.resolver;
 
     struct aws_client_bootstrap *client_bootstrap = 
aws_client_bootstrap_new(allocator, &client_bootstrap_options);
     ASSERT_NOT_NULL(client_bootstrap);
@@ -857,7 +1080,7 @@
     AWS_ZERO_STRUCT(client_channel_options);
     client_channel_options.bootstrap = client_bootstrap;
     client_channel_options.host_name = local_server_tester.endpoint.address;
-    client_channel_options.port = 0;
+    client_channel_options.port = local_server_tester.endpoint.port;
     client_channel_options.socket_options = 
&local_server_tester.socket_options;
     client_channel_options.creation_callback = 
s_creation_callback_test_channel_creation_callback;
     client_channel_options.setup_callback = 
s_socket_handler_test_client_setup_callback;
@@ -869,22 +1092,20 @@
     ASSERT_SUCCESS(aws_mutex_lock(&c_tester.mutex));
 
     /* wait for both ends to setup */
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_setup_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_setup_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_setup_predicate, &client_args));
 
     ASSERT_TRUE(client_args.creation_callback_invoked);
 
-    struct aws_channel_slot *client_rw_slot = 
aws_atomic_load_ptr(&client_args.rw_slot);
-    rw_handler_write(client_args.rw_handler, client_rw_slot, &msg_from_client);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_socket_test_read_predicate, &server_rw_args));
-
-    struct aws_channel_slot *server_rw_slot = 
aws_atomic_load_ptr(&server_args.rw_slot);
-    rw_handler_write(server_args.rw_handler, server_rw_slot, &msg_from_server);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_socket_test_read_predicate, &client_rw_args));
+    rw_handler_write(client_args.rw_handler, client_args.rw_slot, 
&msg_from_client);
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_socket_test_read_predicate, &server_rw_args));
+
+    rw_handler_write(server_args.rw_handler, server_args.rw_slot, 
&msg_from_server);
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_socket_test_read_predicate, &client_rw_args));
 
     uint64_t ms_to_ns = aws_timestamp_convert(1, AWS_TIMESTAMP_MILLIS, 
AWS_TIMESTAMP_NANOS, NULL);
 
@@ -894,8 +1115,8 @@
     struct aws_statistics_handler_test_impl *stats_impl = stats_handler->impl;
 
     aws_mutex_lock(&stats_impl->lock);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &stats_impl->signal, &stats_impl->lock, s_stats_processed_predicate, 
stats_handler));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &stats_impl->signal, &stats_impl->lock, TIMEOUT, 
s_stats_processed_predicate, stats_handler));
 
     ASSERT_TRUE(stats_impl->total_bytes_read == msg_from_server.len);
     ASSERT_TRUE(stats_impl->total_bytes_written == msg_from_client.len);
@@ -904,14 +1125,14 @@
 
     aws_channel_shutdown(server_args.channel, AWS_OP_SUCCESS);
 
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &server_args));
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_channel_shutdown_predicate, &client_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_channel_shutdown_predicate, &client_args));
 
     
aws_server_bootstrap_destroy_socket_listener(local_server_tester.server_bootstrap,
 local_server_tester.listener);
-    ASSERT_SUCCESS(aws_condition_variable_wait_pred(
-        &c_tester.condition_variable, &c_tester.mutex, 
s_listener_destroy_predicate, &server_args));
+    ASSERT_SUCCESS(aws_condition_variable_wait_for_pred(
+        &c_tester.condition_variable, &c_tester.mutex, TIMEOUT, 
s_listener_destroy_predicate, &server_args));
 
     aws_mutex_unlock(&c_tester.mutex);
 

Reply via email to