]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
quic: make eyeballers connect retries stop at weird replies
authorStefan Eissing <stefan@eissing.org>
Fri, 24 Nov 2023 12:45:55 +0000 (13:45 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 24 Nov 2023 19:58:54 +0000 (20:58 +0100)
- when a connect immediately goes into DRAINING state, do
  not attempt retries in the QUIC connection filter. Instead,
  return CURLE_WEIRD_SERVER_REPLY
- When eyeballing, interpret CURLE_WEIRD_SERVER_REPLY as an
  inconclusive answer. When all addresses have been attempted,
  rewind the address list once on an inconclusive answer.
- refs #11832 where connects were retried indefinitely until
  the overall timeout fired

Closes #12400

lib/connect.c
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_quiche.c

index 5845e7d3b3106f8c92c578d9c67451df075c4f01..c1e49ae29cd4aa4dc1ea005689c9af7f22e61861 100644 (file)
@@ -351,6 +351,7 @@ void Curl_conncontrol(struct connectdata *conn,
  */
 struct eyeballer {
   const char *name;
+  const struct Curl_addrinfo *first; /* complete address list, not owned */
   const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
   int ai_family;                     /* matching address family only */
   cf_ip_connect_create *cf_create;   /* for creating cf */
@@ -362,9 +363,12 @@ struct eyeballer {
   expire_id timeout_id;              /* ID for Curl_expire() */
   CURLcode result;
   int error;
+  BIT(rewinded);                     /* if we rewinded the addr list */
   BIT(has_started);                  /* attempts have started */
   BIT(is_done);                      /* out of addresses/time */
   BIT(connected);                    /* cf has connected */
+  BIT(inconclusive);                 /* connect was not a hard failure, we
+                                      * might talk to a restarting server */
 };
 
 
@@ -411,7 +415,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer,
 #endif
                   "ip"));
   baller->cf_create = cf_create;
-  baller->addr = addr;
+  baller->first = baller->addr = addr;
   baller->ai_family = ai_family;
   baller->primary = primary;
   baller->delay_ms = delay_ms;
@@ -441,6 +445,13 @@ static void baller_free(struct eyeballer *baller,
   }
 }
 
+static void baller_rewind(struct eyeballer *baller)
+{
+  baller->rewinded = TRUE;
+  baller->addr = baller->first;
+  baller->inconclusive = FALSE;
+}
+
 static void baller_next_addr(struct eyeballer *baller)
 {
   baller->addr = addr_next_match(baller->addr, baller->ai_family);
@@ -531,6 +542,10 @@ static CURLcode baller_start_next(struct Curl_cfilter *cf,
 {
   if(cf->sockindex == FIRSTSOCKET) {
     baller_next_addr(baller);
+    /* If we get inconclusive answers from the server(s), we make
+     * a second iteration over the address list */
+    if(!baller->addr && baller->inconclusive && !baller->rewinded)
+      baller_rewind(baller);
     baller_start(cf, data, baller, timeoutms);
   }
   else {
@@ -569,6 +584,8 @@ static CURLcode baller_connect(struct Curl_cfilter *cf,
         baller->result = CURLE_OPERATION_TIMEDOUT;
       }
     }
+    else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
+      baller->inconclusive = TRUE;
   }
   return baller->result;
 }
index 33f837711e152c5ccaa20db99377f1b06d422a00..300a5ba4610718a31aae0e9b3a1c107c84adad59 100644 (file)
@@ -2617,27 +2617,9 @@ out:
      ngtcp2_conn_in_draining_period(ctx->qconn)) {
     /* When a QUIC server instance is shutting down, it may send us a
      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
-     * state.
-     * This may be a stopping of the service or it may be that the server
-     * is reloading and a new instance will start serving soon.
-     * In any case, we tear down our socket and start over with a new one.
-     * We re-open the underlying UDP cf right now, but do not start
-     * connecting until called again.
-     */
-    int reconn_delay_ms = 200;
-
-    CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
-                reconn_delay_ms);
-    Curl_conn_cf_close(cf->next, data);
-    cf_ngtcp2_ctx_clear(ctx);
-    result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
-    if(!result && *done) {
-      *done = FALSE;
-      ctx->reconnect_at = now;
-      ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
-      Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
-      result = CURLE_OK;
-    }
+     * state. The CONNECT may work in the near future again. Indicate
+     * that as a "weird" reply. */
+    result = CURLE_WEIRD_SERVER_REPLY;
   }
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
index d584cc0cd509682bb655c3517f2888c31e0c2916..7123d63ca4ad65145ba4b426fe1daca1abb27be6 100644 (file)
@@ -1501,27 +1501,9 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
   else if(quiche_conn_is_draining(ctx->qconn)) {
     /* When a QUIC server instance is shutting down, it may send us a
      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
-     * state.
-     * This may be a stopping of the service or it may be that the server
-     * is reloading and a new instance will start serving soon.
-     * In any case, we tear down our socket and start over with a new one.
-     * We re-open the underlying UDP cf right now, but do not start
-     * connecting until called again.
-     */
-    int reconn_delay_ms = 200;
-
-    CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
-                reconn_delay_ms);
-    Curl_conn_cf_close(cf->next, data);
-    cf_quiche_ctx_clear(ctx);
-    result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
-    if(!result && *done) {
-      *done = FALSE;
-      ctx->reconnect_at = Curl_now();
-      ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
-      Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
-      result = CURLE_OK;
-    }
+     * state. The CONNECT may work in the near future again. Indicate
+     * that as a "weird" reply. */
+    result = CURLE_WEIRD_SERVER_REPLY;
   }
 
 out: