diff --git a/llvm/include/llvm/Debuginfod/HTTPClient.h b/llvm/include/llvm/Debuginfod/HTTPClient.h --- a/llvm/include/llvm/Debuginfod/HTTPClient.h +++ b/llvm/include/llvm/Debuginfod/HTTPClient.h @@ -27,6 +27,7 @@ /// A stateless description of an outbound HTTP request. struct HTTPRequest { SmallString<128> Url; + SmallVector Headers; HTTPMethod Method = HTTPMethod::GET; bool FollowRedirects = true; HTTPRequest(StringRef Url); diff --git a/llvm/lib/Debuginfod/Debuginfod.cpp b/llvm/lib/Debuginfod/Debuginfod.cpp --- a/llvm/lib/Debuginfod/Debuginfod.cpp +++ b/llvm/lib/Debuginfod/Debuginfod.cpp @@ -22,6 +22,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Debuginfod/Debuginfod.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" @@ -34,6 +35,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/xxhash.h" @@ -169,6 +171,37 @@ return Error::success(); } +// An over-accepting simplification of the HTTP RFC 7230 spec. +static bool isHeader(StringRef S) { + StringRef Name; + StringRef Value; + std::tie(Name, Value) = S.split(':'); + if (Name.empty() || Value.empty()) + return false; + return all_of(Name, [](char C) { return isPrint(C) && C != ' '; }) && + all_of(Value, [](char C) { return isPrint(C) || C == '\t'; }); +} + +static SmallVector getHeaders() { + const char *Filename = getenv("DEBUGINFOD_HEADERS_FILE"); + if (!Filename) + return {}; + ErrorOr> HeadersFile = + MemoryBuffer::getFile(Filename, /*IsText=*/true); + if (!HeadersFile) + return {}; + + SmallVector Headers; + for (StringRef Line : split((*HeadersFile)->getBuffer(), '\n')) { + if (!isHeader(Line)) + continue; + if (Line.back() == '\r') + Line = Line.drop_back(); + Headers.emplace_back(Line); + } + return Headers; +} + Expected getCachedOrDownloadArtifact( StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath, ArrayRef DebuginfodUrls, std::chrono::milliseconds Timeout) { @@ -214,6 +247,7 @@ StreamedHTTPResponseHandler Handler([&]() { return CacheAddStream(Task); }, Client); HTTPRequest Request(ArtifactUrl); + Request.Headers = getHeaders(); Error Err = Client.perform(Request, Handler); if (Err) return std::move(Err); diff --git a/llvm/lib/Debuginfod/HTTPClient.cpp b/llvm/lib/Debuginfod/HTTPClient.cpp --- a/llvm/lib/Debuginfod/HTTPClient.cpp +++ b/llvm/lib/Debuginfod/HTTPClient.cpp @@ -111,9 +111,15 @@ curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str()); curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects); + curl_slist *Headers = nullptr; + for (const std::string &Header : Request.Headers) + Headers = curl_slist_append(Headers, Header.c_str()); + curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers); + CurlHTTPRequest CurlRequest(Handler); curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest); CURLcode CurlRes = curl_easy_perform(Curl); + curl_slist_free_all(Headers); if (CurlRes != CURLE_OK) return joinErrors(std::move(CurlRequest.ErrorState), createStringError(errc::io_error, diff --git a/llvm/test/tools/llvm-debuginfod-find/Inputs/capture_req.py b/llvm/test/tools/llvm-debuginfod-find/Inputs/capture_req.py new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-debuginfod-find/Inputs/capture_req.py @@ -0,0 +1,23 @@ +import http.server +import os +import subprocess +import sys +import threading + +class TrivialHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(501) + + def log_request(self, *args, **kwargs): + print(self.requestline) + print(self.headers) + +httpd = http.server.HTTPServer(('', 0), TrivialHandler) +port = httpd.socket.getsockname()[1] + +try: + t = threading.Thread(target=httpd.serve_forever).start() + os.environ['DEBUGINFOD_URLS'] =f"http://localhost:{port}" + subprocess.run(sys.argv[1:], capture_output = True) +finally: + httpd.shutdown() diff --git a/llvm/test/tools/llvm-debuginfod-find/Inputs/headers b/llvm/test/tools/llvm-debuginfod-find/Inputs/headers new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-debuginfod-find/Inputs/headers @@ -0,0 +1,12 @@ + + +A: +:A +: +A :B + +A:B +C: D +E:F +hi!$: j k + diff --git a/llvm/test/tools/llvm-debuginfod-find/headers.test b/llvm/test/tools/llvm-debuginfod-find/headers.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-debuginfod-find/headers.test @@ -0,0 +1,16 @@ +REQUIRES: curl + +RUN: %python %S/Inputs/capture_req.py llvm-debuginfod-find --debuginfo 0 \ +RUN: | FileCheck --check-prefix NO-HEADERS %s +RUN: DEBUGINFOD_HEADERS_FILE=bad %python %S/Inputs/capture_req.py \ +RUN: llvm-debuginfod-find --debuginfo 0 \ +RUN: | FileCheck --check-prefix NO-HEADERS %s +RUN: DEBUGINFOD_HEADERS_FILE=%S/Inputs/headers %python %S/Inputs/capture_req.py \ +RUN: llvm-debuginfod-find --debuginfo 0 \ +RUN: | FileCheck --check-prefix HEADERS %s + +NO-HEADERS-NOT: A: B +HEADERS: A: B +HEADERS: C: D +HEADERS: E: F +HEADERS: hi!$: j k