Index: llvm/include/llvm/Support/HTTPClient.h
===================================================================
--- /dev/null
+++ llvm/include/llvm/Support/HTTPClient.h
@@ -0,0 +1,96 @@
+//===-- llvm/Support/HTTPClient.h - HTTP client library ---*- C++ -------*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declarations of the HTTPClient, HTTPMethod,
+/// HTTPResponseHandler, and BufferedHTTPResponseHandler classes, as well as
+/// the HTTPResponseBuffer and HTTPRequest structs.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_HTTP_CLIENT_H
+#define LLVM_SUPPORT_HTTP_CLIENT_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace llvm {
+
+enum class HTTPMethod { GET };
+
+/// A stateless description of the configuration of an outbound HTTP Request.
+/// An HTTPRequest can be performed any number of times by HTTPClient::perform.
+struct HTTPRequest {
+  SmallString<128> Url;
+  HTTPMethod Method = HTTPMethod::GET;
+  bool FollowRedirects = true;
+  bool operator==(const HTTPRequest &O) const {
+    return Url == O.Url && Method == O.Method &&
+           FollowRedirects == O.FollowRedirects;
+  }
+  HTTPRequest(const Twine &Url);
+};
+
+/// A handler for state updates during the lifecycle of a request performed
+/// by HTTPClient::perform.
+class HTTPResponseHandler {
+public:
+  /// Processes one line of HTTP response headers.
+  virtual Expected<size_t> handleHeaderLine(StringRef HeaderLine) = 0;
+  /// Processes an additional chunk of bytes of the HTTP response body.
+  /// Offset indicates the location of the chunk in the response body stream.
+  virtual Expected<size_t> handleBodyChunk(StringRef BodyChunk,
+                                           size_t Offset) = 0;
+  /// Processes the HTTP response status code.
+  virtual void handleStatusCode(unsigned Code) = 0;
+  virtual ~HTTPResponseHandler() {}
+};
+
+/// An HTTP response status code bundled with a buffer which can store the
+/// body.
+struct HTTPResponseBuffer {
+  unsigned Code = 0;
+  std::unique_ptr<WritableMemoryBuffer> Body;
+};
+
+/// A simple handler which writes returned data to an HTTPResponseBuffer.
+/// This handler discards all headers except the Content-Length, which
+/// it uses to allocate an appropriately-sized Body buffer.
+class BufferedHTTPResponseHandler : public HTTPResponseHandler {
+public:
+  HTTPResponseBuffer ResponseBuffer;
+  Expected<size_t> handleHeaderLine(StringRef HeaderLine) override;
+  Expected<size_t> handleBodyChunk(StringRef BodyChunk, size_t Offset) override;
+  void handleStatusCode(unsigned Code) override;
+};
+
+/// A reusable client for performing outbound HTTP requests.
+class HTTPClient {
+public:
+  /// Performs the Request and passes response data to the Handler.
+  /// Returns any errors which occur during request.
+  /// If any of the callbacks to Handler return an Error, a Client
+  /// should stop performing the request and return an Error.
+  virtual Error perform(const HTTPRequest &Request,
+                        HTTPResponseHandler &Handler);
+  /// Performs the Request with the default BufferedHTTPResponseHandler,
+  /// and returns its HTTPResponseBuffer or an Error.
+  Expected<HTTPResponseBuffer> perform(const HTTPRequest &Request);
+
+  /// Performs an HTTPRequest with the default configuration to make a GET
+  /// request to the given Url. Returns an HTTPResponseBuffer or an Error.
+  Expected<HTTPResponseBuffer> get(const Twine &Url);
+
+  /// Children of HTTPClient are expected to clean up their resources
+  /// upon destruction.
+  virtual ~HTTPClient() {}
+};
+
+} // end namespace llvm
+
+#endif // LLVM_SUPPORT_HTTP_CLIENT_H
Index: llvm/lib/Support/CMakeLists.txt
===================================================================
--- llvm/lib/Support/CMakeLists.txt
+++ llvm/lib/Support/CMakeLists.txt
@@ -155,6 +155,7 @@
   GlobPattern.cpp
   GraphWriter.cpp
   Hashing.cpp
+  HTTPClient.cpp
   InitLLVM.cpp
   InstructionCost.cpp
   IntEqClasses.cpp
Index: llvm/lib/Support/HTTPClient.cpp
===================================================================
--- /dev/null
+++ llvm/lib/Support/HTTPClient.cpp
@@ -0,0 +1,80 @@
+//===-- llvm/Support/HTTPClient.cpp - HTTP client library -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// This file defines the methods of the HTTPRequest, HTTPClient, and
+/// BufferedHTTPResponseHandler classes.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/HTTPClient.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "HTTPClient"
+
+HTTPRequest::HTTPRequest(const Twine &Url) { this->Url = Url.str(); }
+
+Expected<HTTPResponseBuffer> HTTPClient::perform(const HTTPRequest &Request) {
+  BufferedHTTPResponseHandler Handler;
+  if (Error Err = perform(Request, Handler))
+    return Err;
+  return std::move(Handler.ResponseBuffer);
+}
+
+Expected<HTTPResponseBuffer> HTTPClient::get(const Twine &Url) {
+  HTTPRequest Request(Url);
+  return perform(Request);
+}
+
+static bool parseContentLengthHeader(StringRef LineRef,
+                                     unsigned long long &ContentLength) {
+  // Content-Length is a mandatory header, and the only one we handle.
+  return LineRef.consume_front("Content-Length: ") &&
+         !getAsUnsignedInteger(LineRef.trim(), 10, ContentLength);
+}
+
+Expected<size_t>
+BufferedHTTPResponseHandler::handleHeaderLine(StringRef HeaderLine) {
+  if (ResponseBuffer.Body)
+    return HeaderLine.size();
+
+  unsigned long long ContentLength;
+  if (parseContentLengthHeader(HeaderLine, ContentLength))
+    ResponseBuffer.Body =
+        WritableMemoryBuffer::getNewUninitMemBuffer(ContentLength);
+
+  return HeaderLine.size();
+}
+
+Expected<size_t>
+BufferedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk,
+                                             size_t Offset) {
+  if (!ResponseBuffer.Body)
+    return createStringError(errc::io_error,
+                             "Unallocated response buffer -- HTTP Body data "
+                             "recieved before Content-Length Header.");
+
+  if (Offset + BodyChunk.size() > ResponseBuffer.Body->getBufferSize())
+    return createStringError(errc::io_error,
+                             "Content is larger than response buffer.");
+
+  memcpy(ResponseBuffer.Body->getBufferStart() + Offset, BodyChunk.data(),
+         BodyChunk.size());
+  return BodyChunk.size();
+}
+
+void BufferedHTTPResponseHandler::handleStatusCode(unsigned Code) {
+  ResponseBuffer.Code = Code;
+}
Index: llvm/unittests/Support/CMakeLists.txt
===================================================================
--- llvm/unittests/Support/CMakeLists.txt
+++ llvm/unittests/Support/CMakeLists.txt
@@ -41,6 +41,7 @@
   GlobPatternTest.cpp
   HashBuilderTest.cpp
   Host.cpp
+  HTTPClient.cpp
   IndexedAccessorTest.cpp
   InstructionCostTest.cpp
   ItaniumManglingCanonicalizerTest.cpp
Index: llvm/unittests/Support/HTTPClient.cpp
===================================================================
--- /dev/null
+++ llvm/unittests/Support/HTTPClient.cpp
@@ -0,0 +1,136 @@
+//===-- llvm/unittest/Support/HTTPClient.cpp - unit tests -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/HTTPClient.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(HTTPClientTests, bufferedHTTPResponseHandlerLifecycleTest) {
+  {
+    BufferedHTTPResponseHandler Handler;
+    // test initial state
+    EXPECT_EQ(Handler.ResponseBuffer.Code, 0u);
+    EXPECT_EQ(Handler.ResponseBuffer.Body, nullptr);
+
+    // a body chunk passed before the content-length header should return Error
+    EXPECT_THAT_EXPECTED(Handler.handleBodyChunk("body", 0),
+                         Failed<llvm::StringError>());
+    EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("a header line"),
+                         HasValue(13));
+    EXPECT_THAT_EXPECTED(Handler.handleBodyChunk("body", 4),
+                         Failed<llvm::StringError>());
+  }
+
+  BufferedHTTPResponseHandler Handler;
+  EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("Content-Length: 36\r\n"),
+                       HasValue(20));
+  EXPECT_THAT_EXPECTED(Handler.handleBodyChunk("body:", 0), HasValue(5));
+  EXPECT_THAT_EXPECTED(
+      Handler.handleBodyChunk("this puts the total at 36 chars", 5),
+      HasValue(31));
+  EXPECT_EQ(memcmp(Handler.ResponseBuffer.Body->getBufferStart(),
+                   "body:this puts the total at 36 chars",
+                   Handler.ResponseBuffer.Body->getBufferSize()),
+            0);
+
+  // additional content should be rejected by the handler
+  EXPECT_THAT_EXPECTED(
+      Handler.handleBodyChunk("extra content past the content-length", 36),
+      Failed<llvm::StringError>());
+
+  // response code handling
+  Handler.handleStatusCode(200u);
+  EXPECT_EQ(Handler.ResponseBuffer.Code, 200u);
+}
+
+TEST(HTTPClientTests, bufferedHTTPResponseHandlerZeroContentLengthTest) {
+  BufferedHTTPResponseHandler Handler;
+  EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("Content-Length: 0"),
+                       HasValue(17));
+  EXPECT_NE(Handler.ResponseBuffer.Body, nullptr);
+  EXPECT_EQ(Handler.ResponseBuffer.Body->getBufferSize(), 0u);
+
+  // all content should be rejected by the handler
+  EXPECT_THAT_EXPECTED(Handler.handleBodyChunk("non-empty body content", 0),
+                       Failed<llvm::StringError>());
+}
+
+TEST(HTTPClientTests, bufferedHTTPResponseHandlerMalformedContentLengthTest) {
+  // confirm getAsUnsignedInteger behaves as expected
+  // in particular, getAsUnsignedInteger returns false
+  // for valid inputs
+  unsigned long long ContentLength;
+  EXPECT_EQ(llvm::getAsUnsignedInteger("fff", 10, ContentLength), true);
+  EXPECT_EQ(llvm::getAsUnsignedInteger("", 10, ContentLength), true);
+  EXPECT_EQ(llvm::getAsUnsignedInteger("-1", 10, ContentLength), true);
+  EXPECT_EQ(llvm::getAsUnsignedInteger("0111", 10, ContentLength), false);
+  EXPECT_EQ(llvm::getAsUnsignedInteger("0", 10, ContentLength), false);
+  EXPECT_EQ(ContentLength, 0u);
+
+  // check several invalid content lengths are ignored
+  BufferedHTTPResponseHandler Handler;
+  EXPECT_EQ(Handler.ResponseBuffer.Body, nullptr);
+  EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("Content-Length: fff"),
+                       HasValue(19));
+  EXPECT_EQ(Handler.ResponseBuffer.Body, nullptr);
+  EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("Content-Length:    "),
+                       HasValue(19));
+  EXPECT_EQ(Handler.ResponseBuffer.Body, nullptr);
+  EXPECT_THAT_EXPECTED(
+      Handler.handleHeaderLine(StringRef("Content-Length: \0\0\0", 19)),
+      HasValue(19));
+  EXPECT_EQ(Handler.ResponseBuffer.Body, nullptr);
+  EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("Content-Length: -11"),
+                       HasValue(19));
+  EXPECT_EQ(Handler.ResponseBuffer.Body, nullptr);
+
+  // all content should be rejected by the handler because
+  // no valid content length has been received
+  EXPECT_THAT_EXPECTED(Handler.handleBodyChunk("non-empty body content", 0),
+                       Failed<llvm::StringError>());
+}
+
+class HTTPResponseHandlerSimulator : public HTTPResponseHandler {
+public:
+  struct {
+    size_t BodyBytesTotal = 0;
+    size_t HeaderLinesTotal = 0;
+    unsigned StatusCode = 0;
+  } State;
+
+  Expected<size_t> handleHeaderLine(StringRef HeaderLine) {
+    State.HeaderLinesTotal++;
+    return HeaderLine.size();
+  };
+
+  Expected<size_t> handleBodyChunk(StringRef BodyChunk, size_t Offset) {
+    State.BodyBytesTotal += BodyChunk.size();
+    return BodyChunk.size();
+  };
+
+  void handleStatusCode(unsigned Code) { State.StatusCode = Code; };
+};
+
+TEST(HTTPClientTests, simulatedHTTPResponseHandlerTest) {
+  HTTPResponseHandlerSimulator Handler;
+  EXPECT_EQ(Handler.State.HeaderLinesTotal, 0u);
+  EXPECT_EQ(Handler.State.BodyBytesTotal, 0u);
+  EXPECT_EQ(Handler.State.StatusCode, 0u);
+  Handler.handleStatusCode(200);
+  EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("Content-Length: 0"),
+                       HasValue(17));
+  EXPECT_THAT_EXPECTED(Handler.handleHeaderLine("Another-Header: foo"),
+                       HasValue(19));
+  EXPECT_THAT_EXPECTED(Handler.handleBodyChunk("1234567", 0), HasValue(7));
+  EXPECT_EQ(Handler.State.HeaderLinesTotal, 2u);
+  EXPECT_EQ(Handler.State.BodyBytesTotal, 7u);
+  EXPECT_EQ(Handler.State.StatusCode, 200u);
+}