[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