patch 9.2.0021: channel: connection timeout fails to fall back to IPv4

Commit: 
https://github.com/vim/vim/commit/dd989ec9ca3654584e2813f26a322f9de18406bf
Author: thinca <[email protected]>
Date:   Wed Feb 18 21:34:57 2026 +0000

    patch 9.2.0021: channel: connection timeout fails to fall back to IPv4
    
    Problem:  When ch_open() tries to connect to a hostname that resolves to
              multiple addresses (e.g., both IPv6 and IPv4), it uses a
              single waittime for all connection attempts. If the first IPv6
              connection attempt times out, it consumes almost all of the
              waittime, leaving insufficient time (often just 1ms) for the
              IPv4 attempt to succeed. (reporter)
    Solution: Implement a simplified version of Happy Eyeballs (RFC 8305) to
              improve connection fallback behavior when IPv6 is unavailable
              or slow (thinca).
    
    Distribute the waittime across multiple addresses:
    - First address: use up to 250ms (RFC 8305 Connection Attempt Delay) or
      half of the total waittime, whichever is smaller
    - Middle addresses: divide remaining time equally
    - Last address: use all remaining time
    
    This ensures that IPv4 fallback has sufficient time to succeed even when
    IPv6 connection attempts fail or timeout.
    
    closes: #19233
    
    Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
    Signed-off-by: thinca <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt
index e2259823b..5c1de248d 100644
--- a/runtime/doc/channel.txt
+++ b/runtime/doc/channel.txt
@@ -1,4 +1,4 @@
-*channel.txt*  For Vim version 9.2.  Last change: 2026 Feb 14
+*channel.txt*  For Vim version 9.2.  Last change: 2026 Feb 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -130,6 +130,11 @@ a Unix-domain socket path prefixed by "unix:".  E.g. >
     [2001:db8::1]:8765   " IPv6 + port
     unix:/tmp/my-socket  " Unix-domain socket path
 
+When a domain name resolves to multiple addresses (e.g., both IPv6 and IPv4),
+Vim tries each address in order.  If a connection is slow or unreachable, it
+quickly falls back to the next address.  This helps when IPv6 or IPv4 is
+unreachable on the network.
+
 {options} is a dictionary with optional entries:       *channel-open-options*
 
 "mode" can be:                                         *channel-mode*
diff --git a/src/channel.c b/src/channel.c
index 5c7fb07bd..a1c9bb7a9 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -935,13 +935,31 @@ channel_open(
        return NULL;
     }
 
+    // Count the number of addresses for timeout distribution
+    int addr_count = 0;
     for (addr = res; addr != NULL; addr = addr->ai_next)
+       addr_count++;
+
+    // On Mac and Solaris a zero timeout almost never works.  Waiting for
+    // one millisecond already helps a lot.  Later Mac systems (using IPv6)
+    // need more time, 15 milliseconds appears to work well.
+    // Let's do it for all systems, because we don't know why this is
+    // needed.
+    if (waittime == 0)
+       waittime = 15;
+
+    int addr_index = 0;
+    for (addr = res; addr != NULL; addr = addr->ai_next, addr_index++)
     {
        const char  *dst = hostname;
 # ifdef HAVE_INET_NTOP
        const void  *src = NULL;
        char        buf[NUMBUFLEN];
 # endif
+       int         try_waittime;
+       int         before_waittime;
+       int         consumed;
+       int         remaining_addrs;
 
        if (addr->ai_family == AF_INET6)
        {
@@ -974,18 +992,44 @@ channel_open(
 
        ch_log(channel, "Trying to connect to %s port %d", dst, port);
 
-       // On Mac and Solaris a zero timeout almost never works.  Waiting for
-       // one millisecond already helps a lot.  Later Mac systems (using IPv6)
-       // need more time, 15 milliseconds appears to work well.
-       // Let's do it for all systems, because we don't know why this is
-       // needed.
-       if (waittime == 0)
-           waittime = 15;
+       // Distribute the timeout across addresses for better fallback behavior.
+       // This implements a simplified version of Happy Eyeballs (RFC 8305).
+       if (addr->ai_next == NULL)
+           try_waittime = waittime;
+       else if (addr_index == 0)
+       {
+           if (waittime > 500)
+               try_waittime = 250;
+           else if (waittime > 30)
+               try_waittime = waittime / 2;
+           else
+               try_waittime = waittime;
+       }
+       else
+       {
+           remaining_addrs = addr_count - addr_index;
+           try_waittime = waittime / remaining_addrs;
+       }
 
+       before_waittime = try_waittime;
        sd = channel_connect(channel, addr->ai_addr, (int)addr->ai_addrlen,
-                                                                  &waittime);
+                                                          &try_waittime);
+
+       // Update the overall waittime based on consumed time
+       consumed = before_waittime - try_waittime;
+       waittime -= consumed;
+       if (waittime < 0)
+           waittime = 0;
+
        if (sd >= 0)
            break;
+
+       // If we have no time left, stop trying
+       if (waittime <= 0 && addr->ai_next != NULL)
+       {
+           ch_log(channel, "Out of time, stopping connection attempts");
+           break;
+       }
     }
 
     freeaddrinfo(res);
diff --git a/src/version.c b/src/version.c
index 394a536da..53c825fd0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    21,
 /**/
     20,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1vspLz-001VoC-OW%40256bit.org.

Raspunde prin e-mail lui