]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
ngtcp2: check error code on connect failure
authorStefan Eissing <stefan@eissing.org>
Thu, 11 Sep 2025 12:12:04 +0000 (14:12 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 11 Sep 2025 14:00:58 +0000 (16:00 +0200)
Access the error codes of ngtcp2 when a connect attempt failes. Trace
the information for analysis. Treat errors as permanent failure by
default, trigger retrying only when the server refused without
indicating an error.

Closes #18521

lib/vquic/curl_ngtcp2.c
tests/http/testenv/nghttpx.py

index 6470f1506d15f65c9d763c31dd40acdb3d639270..3f7c58d08af230d2fb70a7c7141bb7123bb6f170 100644 (file)
@@ -2578,11 +2578,31 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
 out:
   if(result == CURLE_RECV_ERROR && ctx->qconn &&
      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. The CONNECT may work in the near future again. Indicate
-     * that as a "weird" reply. */
-    result = CURLE_WEIRD_SERVER_REPLY;
+    const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn);
+
+    result = CURLE_COULDNT_CONNECT;
+    if(cerr) {
+      CURL_TRC_CF(data, cf, "connect error, type=%d, code=%"
+                  FMT_PRIu64,
+                  cerr->type, (curl_uint64_t)cerr->error_code);
+      switch(cerr->type) {
+      case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION:
+        CURL_TRC_CF(data, cf, "error in version negotiation");
+        break;
+      default:
+        if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) {
+          CURL_TRC_CF(data, cf, "crypto error, tls alert=%u",
+                      (unsigned int)(cerr->error_code & 0xffu));
+        }
+        else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) {
+          CURL_TRC_CF(data, cf, "connection refused by server");
+          /* When a QUIC server instance is shutting down, it may send us a
+           * CONNECTION_CLOSE with this code right away. We want
+            * to keep on trying in this case. */
+          result = CURLE_WEIRD_SERVER_REPLY;
+        }
+      }
+    }
   }
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
index 0bd46ac3603cc0cea6ce4b058e9d1bbcc033d5d3..dfb416334c5aa455c628c4aefb80a46451f9c22a 100644 (file)
@@ -124,15 +124,16 @@ class Nghttpx:
             running = self._process
             self._process = None
             os.kill(running.pid, signal.SIGQUIT)
-            end_wait = datetime.now() + timeout
+            end_wait = datetime.now() + timedelta(seconds=5)
             if not self.start(wait_live=False):
                 self._process = running
                 return False
             while datetime.now() < end_wait:
                 try:
                     log.debug(f'waiting for nghttpx({running.pid}) to exit.')
-                    running.wait(2)
+                    running.wait(1)
                     log.debug(f'nghttpx({running.pid}) terminated -> {running.returncode}')
+                    running = None
                     break
                 except subprocess.TimeoutExpired:
                     log.warning(f'nghttpx({running.pid}), not shut down yet.')
@@ -142,7 +143,7 @@ class Nghttpx:
                 os.kill(running.pid, signal.SIGKILL)
                 running.terminate()
                 running.wait(1)
-            return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT))
+            return self.wait_live(timeout=timeout)
         return False
 
     def wait_dead(self, timeout: timedelta):