[llvm] [llvm][SupportHTTP] Apply WinHTTP timeout setting after WinHttpOpen (PR #188969)

Stefan Gränitz via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 15 04:50:05 PDT 2026


https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/188969

>From 94cc8b29c476c8b72f080d7e13c2dc69443d893f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Fri, 27 Mar 2026 11:41:18 +0100
Subject: [PATCH] [llvm][HTTP] Apply WinHTTP timeout setting after WinHttpOpen

---
 llvm/include/llvm/Debuginfod/BuildIDFetcher.h |  5 ++-
 llvm/lib/Debuginfod/BuildIDFetcher.cpp        | 14 ++++++--
 llvm/lib/HTTP/HTTPClient.cpp                  | 33 +++++++++++--------
 .../llvm-debuginfod-find/Inputs/delay_req.py  | 26 +++++++++++++++
 .../tools/llvm-debuginfod-find/timeout.test   | 12 +++++++
 .../llvm-debuginfod-find.cpp                  |  7 ++--
 6 files changed, 77 insertions(+), 20 deletions(-)
 create mode 100644 llvm/test/tools/llvm-debuginfod-find/Inputs/delay_req.py
 create mode 100644 llvm/test/tools/llvm-debuginfod-find/timeout.test

diff --git a/llvm/include/llvm/Debuginfod/BuildIDFetcher.h b/llvm/include/llvm/Debuginfod/BuildIDFetcher.h
index 8f9c2aa8722ad..d78f30782814f 100644
--- a/llvm/include/llvm/Debuginfod/BuildIDFetcher.h
+++ b/llvm/include/llvm/Debuginfod/BuildIDFetcher.h
@@ -27,7 +27,10 @@ class DebuginfodFetcher : public object::BuildIDFetcher {
   ~DebuginfodFetcher() override = default;
 
   /// Fetches the given Build ID using debuginfod and returns a local path to
-  /// the resulting file.
+  /// the resulting file. If \p ErrMsg is non-null and the fetch fails, it
+  /// returns the error description.
+  std::optional<std::string> fetch(object::BuildIDRef BuildID,
+                                   std::string *ErrMsg) const;
   std::optional<std::string> fetch(object::BuildIDRef BuildID) const override;
 };
 
diff --git a/llvm/lib/Debuginfod/BuildIDFetcher.cpp b/llvm/lib/Debuginfod/BuildIDFetcher.cpp
index a7f13104abee1..08c0282f17dc0 100644
--- a/llvm/lib/Debuginfod/BuildIDFetcher.cpp
+++ b/llvm/lib/Debuginfod/BuildIDFetcher.cpp
@@ -18,14 +18,22 @@
 
 using namespace llvm;
 
-std::optional<std::string>
-DebuginfodFetcher::fetch(ArrayRef<uint8_t> BuildID) const {
+std::optional<std::string> DebuginfodFetcher::fetch(ArrayRef<uint8_t> BuildID,
+                                                    std::string *ErrMsg) const {
   if (std::optional<std::string> Path = BuildIDFetcher::fetch(BuildID))
     return std::move(*Path);
 
   Expected<std::string> PathOrErr = getCachedOrDownloadDebuginfo(BuildID);
   if (PathOrErr)
     return *PathOrErr;
-  consumeError(PathOrErr.takeError());
+  if (ErrMsg)
+    *ErrMsg = toString(PathOrErr.takeError());
+  else
+    consumeError(PathOrErr.takeError());
   return std::nullopt;
 }
+
+std::optional<std::string>
+DebuginfodFetcher::fetch(ArrayRef<uint8_t> BuildID) const {
+  return fetch(BuildID, nullptr);
+}
diff --git a/llvm/lib/HTTP/HTTPClient.cpp b/llvm/lib/HTTP/HTTPClient.cpp
index 8517c8cc7c7a0..a3d5daff0948e 100644
--- a/llvm/lib/HTTP/HTTPClient.cpp
+++ b/llvm/lib/HTTP/HTTPClient.cpp
@@ -154,6 +154,7 @@ struct WinHTTPSession {
   HINTERNET ConnectHandle = nullptr;
   HINTERNET RequestHandle = nullptr;
   DWORD ResponseCode = 0;
+  DWORD TimeoutMs = 30000;
 
   ~WinHTTPSession() {
     if (RequestHandle)
@@ -225,15 +226,7 @@ void HTTPClient::cleanup() {
 
 void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
   WinHTTPSession *Session = static_cast<WinHTTPSession *>(Handle);
-  if (Session && Session->SessionHandle) {
-    DWORD TimeoutMs = static_cast<DWORD>(Timeout.count());
-    WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_CONNECT_TIMEOUT,
-                     &TimeoutMs, sizeof(TimeoutMs));
-    WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_RECEIVE_TIMEOUT,
-                     &TimeoutMs, sizeof(TimeoutMs));
-    WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_SEND_TIMEOUT,
-                     &TimeoutMs, sizeof(TimeoutMs));
-  }
+  Session->TimeoutMs = static_cast<DWORD>(Timeout.count());
 }
 
 Error HTTPClient::perform(const HTTPRequest &Request,
@@ -265,6 +258,12 @@ Error HTTPClient::perform(const HTTPRequest &Request,
   if (!Session->SessionHandle)
     return createStringError(errc::io_error, "Failed to open WinHTTP session");
 
+  // Set timeouts for all 4 phases: resolve, connect, send and receive. Resolve
+  // and connect are hard-coded since they don't vary with different payloads.
+  // Send and receive is configurable and defaults to 30000.
+  WinHttpSetTimeouts(Session->SessionHandle, 5000, 10000, Session->TimeoutMs,
+                     Session->TimeoutMs);
+
   // Prevent fallback to TLS 1.0/1.1
   DWORD SecureProtocols =
       WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3;
@@ -326,12 +325,20 @@ Error HTTPClient::perform(const HTTPRequest &Request,
 
   // Send request
   if (!WinHttpSendRequest(Session->RequestHandle, WINHTTP_NO_ADDITIONAL_HEADERS,
-                          0, nullptr, 0, 0, 0))
-    return createStringError(errc::io_error, "Failed to send HTTP request");
+                          0, nullptr, 0, 0, 0)) {
+    bool TimedOut = GetLastError() == ERROR_WINHTTP_TIMEOUT;
+    return createStringError(errc::io_error,
+                             TimedOut ? "Timeout was reached"
+                                      : "Failed to send HTTP request");
+  }
 
   // Receive response
-  if (!WinHttpReceiveResponse(Session->RequestHandle, nullptr))
-    return createStringError(errc::io_error, "Failed to receive HTTP response");
+  if (!WinHttpReceiveResponse(Session->RequestHandle, nullptr)) {
+    bool TimedOut = GetLastError() == ERROR_WINHTTP_TIMEOUT;
+    return createStringError(errc::io_error,
+                             TimedOut ? "Timeout was reached"
+                                      : "Failed to receive HTTP response");
+  }
 
   // Get response code
   DWORD CodeSize = sizeof(Session->ResponseCode);
diff --git a/llvm/test/tools/llvm-debuginfod-find/Inputs/delay_req.py b/llvm/test/tools/llvm-debuginfod-find/Inputs/delay_req.py
new file mode 100644
index 0000000000000..ea76e2eb8c5a8
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfod-find/Inputs/delay_req.py
@@ -0,0 +1,26 @@
+import http.server
+import os
+import subprocess
+import sys
+import threading
+import time
+
+
+class DelayingHandler(http.server.BaseHTTPRequestHandler):
+    def do_GET(self):
+        # The test sets DEBUGINFOD_TIMEOUT=1
+        time.sleep(2)
+        self.send_response(501)
+
+
+httpd = http.server.HTTPServer(("localhost", 0), DelayingHandler)
+port = httpd.socket.getsockname()[1]
+
+try:
+    t = threading.Thread(target=httpd.serve_forever).start()
+    os.environ["DEBUGINFOD_URLS"] = f"http://localhost:{port}"
+    result = subprocess.run(sys.argv[1:], capture_output=True)
+    # e.g. Build ID 00: curl_easy_perform() failed: Timeout was reached
+    print(result.stderr.decode())
+finally:
+    httpd.shutdown()
diff --git a/llvm/test/tools/llvm-debuginfod-find/timeout.test b/llvm/test/tools/llvm-debuginfod-find/timeout.test
new file mode 100644
index 0000000000000..0949accd27ad4
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfod-find/timeout.test
@@ -0,0 +1,12 @@
+REQUIRES: curl || system-windows, http-server
+
+RUN: rm -rf %t
+RUN: mkdir -p %t/debuginfod-cache
+
+# We hit the timeout because the Python script delays the response by 2 seconds
+RUN: env DEBUGINFOD_TIMEOUT=1 \
+RUN: env DEBUGINFOD_CACHE=%t/debuginfod-cache \
+RUN: env DEBUGINFOD_HEADERS_FILE=%S/Inputs/headers \
+RUN:   %python %S/Inputs/delay_req.py llvm-debuginfod-find --debuginfo 0 | FileCheck %s
+
+CHECK: Timeout was reached
diff --git a/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp b/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp
index b6a5efd3e162c..beca86567d99f 100644
--- a/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp
+++ b/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp
@@ -152,10 +152,11 @@ int llvm_debuginfod_find_main(int argc, char **argv,
 
 // Find a debug file in local build ID directories and via debuginfod.
 std::string fetchDebugInfo(object::BuildIDRef BuildID) {
+  std::string ErrMsg;
   if (std::optional<std::string> Path =
-          DebuginfodFetcher(DebugFileDirectory).fetch(BuildID))
+          DebuginfodFetcher(DebugFileDirectory).fetch(BuildID, &ErrMsg))
     return *Path;
-  errs() << "Build ID " << llvm::toHex(BuildID, /*Lowercase=*/true)
-         << " could not be found.\n";
+  errs() << "Build ID " << llvm::toHex(BuildID, /*Lowercase=*/true) << ": "
+         << ErrMsg << "\n";
   exit(1);
 }



More information about the llvm-commits mailing list