Bug 1628068 p2 - Implement viaduct gecko backend. r=tcsc,dragana,froydnj
authorEdouard Oger <eoger@fastmail.com>
Tue, 12 May 2020 21:36:19 +0000
changeset 529487 c9ca86d98171f6853176bb78e65829ab5f4badea
parent 529486 e1d5a153eaf70ba02ed2b1d707bd58b028fa3b01
child 529488 972fcba11cb6e6ef865aa40cb881753d2059a4c2
push id115735
push usereoger@mozilla.com
push dateTue, 12 May 2020 21:38:12 +0000
treeherderautoland@c9ca86d98171 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcsc, dragana, froydnj
bugs1628068
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1628068 p2 - Implement viaduct gecko backend. r=tcsc,dragana,froydnj Differential Revision: https://phabricator.services.mozilla.com/D70257
toolkit/components/moz.build
toolkit/components/protobuf/regenerate_cpp_files.sh
toolkit/components/viaduct/Viaduct.cpp
toolkit/components/viaduct/Viaduct.h
toolkit/components/viaduct/ViaductRequest.cpp
toolkit/components/viaduct/ViaductRequest.h
toolkit/components/viaduct/components.conf
toolkit/components/viaduct/fetch_msg_types.pb.cc
toolkit/components/viaduct/fetch_msg_types.pb.h
toolkit/components/viaduct/fetch_msg_types.proto
toolkit/components/viaduct/moz.build
toolkit/components/viaduct/mozIViaduct.idl
tools/rewriting/ThirdPartyPaths.txt
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -73,16 +73,17 @@ DIRS += [
     'telemetry',
     'thumbnails',
     'timermanager',
     'tooltiptext',
     'typeaheadfind',
     'utils',
     'url-classifier',
     'urlformatter',
+    'viaduct',
     'viewconfig',
     'viewsource',
     'windowcreator',
     'windowwatcher',
     'workerloader',
     'xulstore',
 ]
 
--- a/toolkit/components/protobuf/regenerate_cpp_files.sh
+++ b/toolkit/components/protobuf/regenerate_cpp_files.sh
@@ -21,8 +21,10 @@ regenerate() {
 
 cd $(dirname $0)
 cd ../../.. # Top level.
 
 regenerate gfx/layers/protobuf/ LayerScopePacket.proto
 regenerate devtools/shared/heapsnapshot/ CoreDump.proto
 regenerate toolkit/components/reputationservice/chromium/chrome/common/safe_browsing/ csd.proto
 regenerate toolkit/components/url-classifier/chromium/ safebrowsing.proto
+command cp third_party/rust/viaduct/src/fetch_msg_types.proto toolkit/components/viaduct/fetch_msg_types.proto
+regenerate toolkit/components/viaduct/ fetch_msg_types.proto
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/Viaduct.cpp
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Viaduct.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/ViaductRequest.h"
+
+namespace mozilla {
+
+namespace {
+
+extern "C" {
+uint8_t viaduct_initialize(
+    ViaductByteBuffer (*fetch_callback)(ViaductByteBuffer));
+}
+
+}  // namespace
+
+static StaticRefPtr<Viaduct> gViaduct;
+
+// static
+already_AddRefed<Viaduct> Viaduct::GetSingleton() {
+  if (gViaduct) {
+    return do_AddRef(gViaduct);
+  }
+
+  gViaduct = new Viaduct();
+  ClearOnShutdown(&gViaduct);
+  return do_AddRef(gViaduct);
+}
+
+NS_IMETHODIMP
+Viaduct::EnsureInitialized() {
+  if (mInitialized.compareExchange(false, true)) {
+    viaduct_initialize(ViaductCallback);
+  }
+  return NS_OK;
+}
+
+// static
+ViaductByteBuffer Viaduct::ViaductCallback(ViaductByteBuffer buffer) {
+  MOZ_ASSERT(!NS_IsMainThread(), "Background thread only!");
+  RefPtr<ViaductRequest> request = new ViaductRequest();
+  return request->MakeRequest(buffer);
+}
+
+NS_IMPL_ISUPPORTS(Viaduct, mozIViaduct)
+
+};  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/Viaduct.h
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Viaduct_h
+#define mozilla_Viaduct_h
+
+#include "mozIViaduct.h"
+#include "mozilla/Atomics.h"
+
+/**
+ * Viaduct is a way for Application Services Rust components
+ * (https://github.com/mozilla/application-services) to make network requests
+ * using a trusted stack (gecko).
+ *
+ * The way it works is roughly as follows:
+ * - First we register a callback using `viaduct_initialize`
+ * (Viaduct::Initialize). This callback is stored on the Rust side
+ * in a static variable, therefore Initialize() must be called only once.
+ *
+ * - When the Rust code needs to make a network request, our callback
+ * (Viaduct::ViaductCallback) will be called with a protocol buffer describing
+ * the request to make on their behalf. Note 1: The callback MUST be called from
+ * a background thread as it is blocking. Note 2: It is our side responsibility
+ * to call `viaduct_destroy_bytebuffer` on the buffer.
+ *
+ * - We set a semaphore to make the background thread wait while we make the
+ * request on the main thread using nsIChannel. (ViaductRequest::MakeRequest)
+ *
+ * - Once a response is received, we allocate a bytebuffer to store the
+ * response using `viaduct_alloc_bytebuffer` and unlock the semaphore.
+ * (ViaductRequest::OnStopRequest)
+ *
+ * - The background thread is unlocked, and the callback returns the response to
+ * the Rust caller. (Viaduct::ViaductCallback)
+ *
+ * - The Rust caller will free the response buffer we allocated earlier.
+ *
+ * Reference:
+ * https://github.com/mozilla/application-services/blob/master/components/viaduct/README.md
+ */
+
+namespace mozilla {
+
+namespace {
+
+// A mapping of the ByteBuffer repr(C) Rust struct.
+typedef struct ViaductByteBuffer {
+  int64_t len;
+  uint8_t* data;
+} ViaductByteBuffer;
+
+}  // namespace
+
+class Viaduct final : public mozIViaduct {
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_MOZIVIADUCT
+
+  Viaduct() : mInitialized(false) {}
+  static already_AddRefed<Viaduct> GetSingleton();
+
+ private:
+  static ViaductByteBuffer ViaductCallback(ViaductByteBuffer buffer);
+  Atomic<bool> mInitialized;
+
+  ~Viaduct() = default;
+};
+
+};  // namespace mozilla
+
+#endif  // mozilla_Viaduct_h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/ViaductRequest.cpp
@@ -0,0 +1,314 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ViaductRequest.h"
+
+#include "mozilla/ErrorNames.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+
+#include "nsContentUtils.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpHeaderVisitor.h"
+#include "nsIInputStream.h"
+#include "nsIUploadChannel2.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "nsString.h"
+#include "nsStringStream.h"
+
+namespace mozilla {
+
+namespace {
+
+extern "C" {
+ViaductByteBuffer viaduct_alloc_bytebuffer(int32_t);
+void viaduct_destroy_bytebuffer(ViaductByteBuffer);
+}
+
+}  // namespace
+
+class HeaderVisitor final : public nsIHttpHeaderVisitor {
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIHTTPHEADERVISITOR
+
+  explicit HeaderVisitor(
+      google::protobuf::Map<std::string, std::string>* aHeaders)
+      : mHeaders(aHeaders) {}
+
+ private:
+  google::protobuf::Map<std::string, std::string>* mHeaders;
+  ~HeaderVisitor() = default;
+};
+
+NS_IMETHODIMP
+HeaderVisitor::VisitHeader(const nsACString& aHeader,
+                           const nsACString& aValue) {
+  (*mHeaders)[aHeader.BeginReading()] = aValue.BeginReading();
+  return NS_OK;
+}
+NS_IMPL_ISUPPORTS(HeaderVisitor, nsIHttpHeaderVisitor)
+
+nsCString ConvertMethod(
+    appservices::httpconfig::protobuf::Request_Method method);
+
+///////////////////////////////////////////////////////////////////////////////
+// ViaductRequest implementation
+
+ViaductByteBuffer ViaductRequest::MakeRequest(ViaductByteBuffer reqBuf) {
+  MOZ_ASSERT(!NS_IsMainThread(), "Background thread only!");
+  auto clearBuf = MakeScopeExit([&] { viaduct_destroy_bytebuffer(reqBuf); });
+  // We keep the protobuf parsing/serializing in the background thread.
+  appservices::httpconfig::protobuf::Request request;
+  if (!request.ParseFromArray(static_cast<const void*>(reqBuf.data),
+                              reqBuf.len)) {
+    // We still need to return something!
+    return ViaductByteBuffer{.len = 0, .data = nullptr};
+  }
+  MonitorAutoLock lock(mMonitor);
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+      "ViaductRequest::LaunchRequest", [this, &request]() {
+        nsresult rv = LaunchRequest(request);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          // Something went very very wrong, but we still have to unblock
+          // the calling thread.
+          NotifyMonitor();
+        }
+      }));
+  while (!mDone) {
+    mMonitor.Wait();
+  }
+  ViaductByteBuffer respBuf =
+      viaduct_alloc_bytebuffer(mResponse.ByteSizeLong());
+  if (!mResponse.SerializeToArray(respBuf.data, respBuf.len)) {
+    viaduct_destroy_bytebuffer(respBuf);
+    return ViaductByteBuffer{.len = 0, .data = nullptr};
+  }
+  return respBuf;
+}
+
+nsresult ViaductRequest::LaunchRequest(
+    appservices::httpconfig::protobuf::Request& request) {
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), request.url().c_str());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsSecurityFlags secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL |
+                             nsILoadInfo::SEC_COOKIES_OMIT;
+  uint32_t loadFlags = 0;
+
+  if (!request.use_caches()) {
+    loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
+  }
+
+  if (!request.follow_redirects()) {
+    secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
+  }
+
+  rv = NS_NewChannel(getter_AddRefs(mChannel), uri,
+                     nsContentUtils::GetSystemPrincipal(), secFlags,
+                     nsIContentPolicy::TYPE_OTHER,
+                     nullptr,  // nsICookieJarSettings
+                     nullptr,  // aPerformanceStorage
+                     nullptr,  // aLoadGroup
+                     nullptr, loadFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
+  nsCString method = ConvertMethod(request.method());
+  rv = httpChannel->SetRequestMethod(method);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (auto& header : request.headers()) {
+    rv = httpChannel->SetRequestHeader(
+        nsDependentCString(header.first.c_str(), header.first.size()),
+        nsDependentCString(header.second.c_str(), header.second.size()),
+        false /* merge */);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Body
+  if (request.has_body()) {
+    const std::string& body = request.body();
+    nsCOMPtr<nsIStringInputStream> stream(
+        do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
+    rv = stream->SetData(body.data(), body.size());
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(mChannel);
+    uploadChannel->ExplicitSetUploadStream(stream, VoidCString(), -1, method,
+                                           false /* aStreamHasHeaders */);
+  }
+
+  MOZ_TRY_VAR(
+      mConnectTimeoutTimer,
+      NS_NewTimerWithCallback(this, request.connect_timeout_secs() * 1000,
+                              nsITimer::TYPE_ONE_SHOT));
+  MOZ_TRY_VAR(mReadTimeoutTimer,
+              NS_NewTimerWithCallback(this, request.read_timeout_secs() * 1000,
+                                      nsITimer::TYPE_ONE_SHOT));
+
+  rv = httpChannel->AsyncOpen(this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return rv;
+}
+
+nsCString ConvertMethod(
+    appservices::httpconfig::protobuf::Request_Method method) {
+  using appservices::httpconfig::protobuf::Request_Method;
+  switch (method) {
+    case Request_Method::Request_Method_GET:
+      return NS_LITERAL_CSTRING("GET");
+    case Request_Method::Request_Method_HEAD:
+      return NS_LITERAL_CSTRING("HEAD");
+    case Request_Method::Request_Method_POST:
+      return NS_LITERAL_CSTRING("POST");
+    case Request_Method::Request_Method_PUT:
+      return NS_LITERAL_CSTRING("PUT");
+    case Request_Method::Request_Method_DELETE:
+      return NS_LITERAL_CSTRING("DELETE");
+    case Request_Method::Request_Method_CONNECT:
+      return NS_LITERAL_CSTRING("CONNECT");
+    case Request_Method::Request_Method_OPTIONS:
+      return NS_LITERAL_CSTRING("OPTIONS");
+    case Request_Method::Request_Method_TRACE:
+      return NS_LITERAL_CSTRING("TRACE");
+  }
+  return NS_LITERAL_CSTRING("UNKNOWN");
+}
+
+void ViaductRequest::ClearTimers() {
+  if (mConnectTimeoutTimer) {
+    mConnectTimeoutTimer->Cancel();
+    mConnectTimeoutTimer = nullptr;
+  }
+  if (mReadTimeoutTimer) {
+    mReadTimeoutTimer->Cancel();
+    mReadTimeoutTimer = nullptr;
+  }
+}
+
+void ViaductRequest::NotifyMonitor() {
+  mDone = true;
+  mMonitor.Notify();
+}
+
+ViaductRequest::~ViaductRequest() {
+  ClearTimers();
+  if (mChannel) {
+    mChannel->Cancel(NS_ERROR_ABORT);
+    mChannel = nullptr;
+  }
+  NotifyMonitor();
+}
+
+NS_IMPL_ISUPPORTS(ViaductRequest, nsIStreamListener, nsITimerCallback,
+                  nsIChannelEventSink)
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIStreamListener implementation
+
+NS_IMETHODIMP
+ViaductRequest::OnStartRequest(nsIRequest* aRequest) {
+  if (mConnectTimeoutTimer) {
+    mConnectTimeoutTimer->Cancel();
+    mConnectTimeoutTimer = nullptr;
+  }
+  return NS_OK;
+}
+
+static nsresult AssignResponseToBuffer(nsIInputStream* aIn, void* aClosure,
+                                       const char* aFromRawSegment,
+                                       uint32_t aToOffset, uint32_t aCount,
+                                       uint32_t* aWriteCount) {
+  nsCString* buf = static_cast<nsCString*>(aClosure);
+  buf->Append(aFromRawSegment, aCount);
+  *aWriteCount = aCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ViaductRequest::OnDataAvailable(nsIRequest* aRequest,
+                                nsIInputStream* aInputStream, uint64_t aOffset,
+                                uint32_t aCount) {
+  nsresult rv;
+  uint32_t readCount;
+  rv = aInputStream->ReadSegments(AssignResponseToBuffer, &mBodyBuffer, aCount,
+                                  &readCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ViaductRequest::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
+  ClearTimers();
+  auto defer = MakeScopeExit([&] {
+    mChannel = nullptr;
+    NotifyMonitor();
+  });
+
+  if (NS_FAILED(aStatusCode)) {
+    nsCString errorName;
+    GetErrorName(aStatusCode, errorName);
+    nsPrintfCString msg("Request error: %s", errorName.get());
+    mResponse.set_exception_message(msg.BeginReading());
+  } else {
+    nsresult rv;
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
+    // Early return is OK because MakeScopeExit will call Notify()
+    // and unblock the original calling thread.
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint32_t httpStatus;
+    rv = httpChannel->GetResponseStatus(&httpStatus);
+    NS_ENSURE_SUCCESS(rv, rv);
+    mResponse.set_status(httpStatus);
+
+    nsCOMPtr<nsIURI> uri;
+    httpChannel->GetURI(getter_AddRefs(uri));
+    nsAutoCString uriStr;
+    uri->GetSpec(uriStr);
+    mResponse.set_url(uriStr.BeginReading());
+
+    auto* headers = mResponse.mutable_headers();
+    nsCOMPtr<nsIHttpHeaderVisitor> visitor = new HeaderVisitor(headers);
+    rv = httpChannel->VisitResponseHeaders(visitor);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mResponse.set_body(mBodyBuffer.BeginReading());
+  }
+
+  return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIChannelEventSink implementation
+
+NS_IMETHODIMP
+ViaductRequest::AsyncOnChannelRedirect(
+    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t flags,
+    nsIAsyncVerifyRedirectCallback* callback) {
+  mChannel = aNewChannel;
+  callback->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsITimerCallback implementation
+
+NS_IMETHODIMP
+ViaductRequest::Notify(nsITimer* timer) {
+  ClearTimers();
+  // Cancelling the channel will trigger OnStopRequest.
+  if (mChannel) {
+    mChannel->Cancel(NS_ERROR_ABORT);
+    mChannel = nullptr;
+  }
+  return NS_OK;
+}
+
+};  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/ViaductRequest.h
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ViaductRequest_h
+#define mozilla_ViaductRequest_h
+
+#include "mozilla/Monitor.h"
+#include "mozilla/fetch_msg_types.pb.h"
+#include "mozilla/Viaduct.h"
+
+#include "nsCOMPtr.h"
+#include "nsIChannel.h"
+#include "nsIChannelEventSink.h"
+#include "nsIStreamListener.h"
+#include "nsITimer.h"
+
+namespace mozilla {
+
+class ViaductRequest final : public nsIStreamListener,
+                             public nsITimerCallback,
+                             public nsIChannelEventSink {
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSICHANNELEVENTSINK
+
+  ViaductRequest()
+      : mDone(false), mChannel(nullptr), mMonitor("ViaductRequest") {}
+  ViaductByteBuffer MakeRequest(ViaductByteBuffer reqBuf);
+
+ private:
+  nsresult LaunchRequest(appservices::httpconfig::protobuf::Request&);
+  void ClearTimers();
+  void NotifyMonitor();
+  bool mDone;
+  nsCOMPtr<nsIChannel> mChannel;
+  nsCString mBodyBuffer;
+  nsCOMPtr<nsITimer> mConnectTimeoutTimer;
+  nsCOMPtr<nsITimer> mReadTimeoutTimer;
+  appservices::httpconfig::protobuf::Response mResponse;
+  Monitor mMonitor;
+  ~ViaductRequest();
+};
+
+};  // namespace mozilla
+
+#endif  // mozilla_ViaductRequest_h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/components.conf
@@ -0,0 +1,14 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Classes = [
+    {
+        'cid': '{1e668408-40aa-4185-991c-12671e2471dc}',
+        'contract_ids': ['@mozilla.org/toolkit/viaduct;1'],
+        'singleton': True,
+        'type': 'mozilla::Viaduct',
+        'headers': ['mozilla/Viaduct.h'],
+        'constructor': 'mozilla::Viaduct::GetSingleton',
+    }
+]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/fetch_msg_types.pb.cc
@@ -0,0 +1,1083 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: fetch_msg_types.proto
+
+#include "fetch_msg_types.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+extern PROTOBUF_INTERNAL_EXPORT_fetch_5fmsg_5ftypes_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Request_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_fetch_5fmsg_5ftypes_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Response_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto;
+namespace mozilla {
+namespace appservices {
+namespace httpconfig {
+namespace protobuf {
+class Request_HeadersEntry_DoNotUseDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Request_HeadersEntry_DoNotUse> _instance;
+} _Request_HeadersEntry_DoNotUse_default_instance_;
+class RequestDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Request> _instance;
+} _Request_default_instance_;
+class Response_HeadersEntry_DoNotUseDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Response_HeadersEntry_DoNotUse> _instance;
+} _Response_HeadersEntry_DoNotUse_default_instance_;
+class ResponseDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Response> _instance;
+} _Response_default_instance_;
+}  // namespace protobuf
+}  // namespace httpconfig
+}  // namespace appservices
+}  // namespace mozilla
+static void InitDefaultsscc_info_Request_fetch_5fmsg_5ftypes_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::mozilla::appservices::httpconfig::protobuf::_Request_default_instance_;
+    new (ptr) ::mozilla::appservices::httpconfig::protobuf::Request();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::mozilla::appservices::httpconfig::protobuf::Request::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_Request_fetch_5fmsg_5ftypes_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, 0, InitDefaultsscc_info_Request_fetch_5fmsg_5ftypes_2eproto}, {
+      &scc_info_Request_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto.base,}};
+
+static void InitDefaultsscc_info_Request_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::mozilla::appservices::httpconfig::protobuf::_Request_HeadersEntry_DoNotUse_default_instance_;
+    new (ptr) ::mozilla::appservices::httpconfig::protobuf::Request_HeadersEntry_DoNotUse();
+  }
+  ::mozilla::appservices::httpconfig::protobuf::Request_HeadersEntry_DoNotUse::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Request_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Request_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto}, {}};
+
+static void InitDefaultsscc_info_Response_fetch_5fmsg_5ftypes_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::mozilla::appservices::httpconfig::protobuf::_Response_default_instance_;
+    new (ptr) ::mozilla::appservices::httpconfig::protobuf::Response();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::mozilla::appservices::httpconfig::protobuf::Response::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_Response_fetch_5fmsg_5ftypes_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, 0, InitDefaultsscc_info_Response_fetch_5fmsg_5ftypes_2eproto}, {
+      &scc_info_Response_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto.base,}};
+
+static void InitDefaultsscc_info_Response_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::mozilla::appservices::httpconfig::protobuf::_Response_HeadersEntry_DoNotUse_default_instance_;
+    new (ptr) ::mozilla::appservices::httpconfig::protobuf::Response_HeadersEntry_DoNotUse();
+  }
+  ::mozilla::appservices::httpconfig::protobuf::Response_HeadersEntry_DoNotUse::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Response_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Response_HeadersEntry_DoNotUse_fetch_5fmsg_5ftypes_2eproto}, {}};
+
+namespace mozilla {
+namespace appservices {
+namespace httpconfig {
+namespace protobuf {
+bool Request_Method_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+      return true;
+    default:
+      return false;
+  }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> Request_Method_strings[8] = {};
+
+static const char Request_Method_names[] =
+  "CONNECT"
+  "DELETE"
+  "GET"
+  "HEAD"
+  "OPTIONS"
+  "POST"
+  "PUT"
+  "TRACE";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry Request_Method_entries[] = {
+  { {Request_Method_names + 0, 7}, 5 },
+  { {Request_Method_names + 7, 6}, 4 },
+  { {Request_Method_names + 13, 3}, 0 },
+  { {Request_Method_names + 16, 4}, 1 },
+  { {Request_Method_names + 20, 7}, 6 },
+  { {Request_Method_names + 27, 4}, 2 },
+  { {Request_Method_names + 31, 3}, 3 },
+  { {Request_Method_names + 34, 5}, 7 },
+};
+
+static const int Request_Method_entries_by_number[] = {
+  2, // 0 -> GET
+  3, // 1 -> HEAD
+  5, // 2 -> POST
+  6, // 3 -> PUT
+  1, // 4 -> DELETE
+  0, // 5 -> CONNECT
+  4, // 6 -> OPTIONS
+  7, // 7 -> TRACE
+};
+
+const std::string& Request_Method_Name(
+    Request_Method value) {
+  static const bool dummy =
+      ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+          Request_Method_entries,
+          Request_Method_entries_by_number,
+          8, Request_Method_strings);
+  (void) dummy;
+  int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+      Request_Method_entries,
+      Request_Method_entries_by_number,
+      8, value);
+  return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+                     Request_Method_strings[idx].get();
+}
+bool Request_Method_Parse(
+    const std::string& name, Request_Method* value) {
+  int int_value;
+  bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+      Request_Method_entries, 8, name, &int_value);
+  if (success) {
+    *value = static_cast<Request_Method>(int_value);
+  }
+  return success;
+}
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+constexpr Request_Method Request::GET;
+constexpr Request_Method Request::HEAD;
+constexpr Request_Method Request::POST;
+constexpr Request_Method Request::PUT;
+constexpr Request_Method Request::DELETE;
+constexpr Request_Method Request::CONNECT;
+constexpr Request_Method Request::OPTIONS;
+constexpr Request_Method Request::TRACE;
+constexpr Request_Method Request::Method_MIN;
+constexpr Request_Method Request::Method_MAX;
+constexpr int Request::Method_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+
+// ===================================================================
+
+Request_HeadersEntry_DoNotUse::Request_HeadersEntry_DoNotUse() {}
+Request_HeadersEntry_DoNotUse::Request_HeadersEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena)
+    : SuperType(arena) {}
+void Request_HeadersEntry_DoNotUse::MergeFrom(const Request_HeadersEntry_DoNotUse& other) {
+  MergeFromInternal(other);
+}
+
+// ===================================================================
+
+void Request::InitAsDefaultInstance() {
+}
+class Request::_Internal {
+ public:
+  using HasBits = decltype(std::declval<Request>()._has_bits_);
+  static void set_has_method(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static void set_has_url(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_body(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_follow_redirects(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static void set_has_use_caches(HasBits* has_bits) {
+    (*has_bits)[0] |= 16u;
+  }
+  static void set_has_connect_timeout_secs(HasBits* has_bits) {
+    (*has_bits)[0] |= 32u;
+  }
+  static void set_has_read_timeout_secs(HasBits* has_bits) {
+    (*has_bits)[0] |= 64u;
+  }
+};
+
+Request::Request()
+  : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:mozilla.appservices.httpconfig.protobuf.Request)
+}
+Request::Request(const Request& from)
+  : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+      _internal_metadata_(nullptr),
+      _has_bits_(from._has_bits_) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  headers_.MergeFrom(from.headers_);
+  url_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (from._internal_has_url()) {
+    url_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.url_);
+  }
+  body_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (from._internal_has_body()) {
+    body_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.body_);
+  }
+  ::memcpy(&method_, &from.method_,
+    static_cast<size_t>(reinterpret_cast<char*>(&read_timeout_secs_) -
+    reinterpret_cast<char*>(&method_)) + sizeof(read_timeout_secs_));
+  // @@protoc_insertion_point(copy_constructor:mozilla.appservices.httpconfig.protobuf.Request)
+}
+
+void Request::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Request_fetch_5fmsg_5ftypes_2eproto.base);
+  url_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  body_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  ::memset(&method_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&read_timeout_secs_) -
+      reinterpret_cast<char*>(&method_)) + sizeof(read_timeout_secs_));
+}
+
+Request::~Request() {
+  // @@protoc_insertion_point(destructor:mozilla.appservices.httpconfig.protobuf.Request)
+  SharedDtor();
+}
+
+void Request::SharedDtor() {
+  url_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  body_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+void Request::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const Request& Request::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Request_fetch_5fmsg_5ftypes_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void Request::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.appservices.httpconfig.protobuf.Request)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  headers_.Clear();
+  cached_has_bits = _has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      url_.ClearNonDefaultToEmptyNoArena();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      body_.ClearNonDefaultToEmptyNoArena();
+    }
+  }
+  if (cached_has_bits & 0x0000007cu) {
+    ::memset(&method_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&read_timeout_secs_) -
+        reinterpret_cast<char*>(&method_)) + sizeof(read_timeout_secs_));
+  }
+  _has_bits_.Clear();
+  _internal_metadata_.Clear();
+}
+
+const char* Request::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // required .mozilla.appservices.httpconfig.protobuf.Request.Method method = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) {
+          ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+          if (PROTOBUF_PREDICT_TRUE(::mozilla::appservices::httpconfig::protobuf::Request_Method_IsValid(val))) {
+            _internal_set_method(static_cast<::mozilla::appservices::httpconfig::protobuf::Request_Method>(val));
+          } else {
+            ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+          }
+        } else goto handle_unusual;
+        continue;
+      // required string url = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) {
+          auto str = _internal_mutable_url();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // optional bytes body = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) {
+          auto str = _internal_mutable_body();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // map<string, string> headers = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(&headers_, ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
+        } else goto handle_unusual;
+        continue;
+      // required bool follow_redirects = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) {
+          _Internal::set_has_follow_redirects(&has_bits);
+          follow_redirects_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // required bool use_caches = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 48)) {
+          _Internal::set_has_use_caches(&has_bits);
+          use_caches_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // required int32 connect_timeout_secs = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 56)) {
+          _Internal::set_has_connect_timeout_secs(&has_bits);
+          connect_timeout_secs_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // required int32 read_timeout_secs = 8;
+      case 8:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 64)) {
+          _Internal::set_has_read_timeout_secs(&has_bits);
+          read_timeout_secs_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  _has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Request::_InternalSerialize(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:mozilla.appservices.httpconfig.protobuf.Request)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _has_bits_[0];
+  // required .mozilla.appservices.httpconfig.protobuf.Request.Method method = 1;
+  if (cached_has_bits & 0x00000004u) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray(
+      1, this->_internal_method(), target);
+  }
+
+  // required string url = 2;
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_url(), target);
+  }
+
+  // optional bytes body = 3;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->WriteBytesMaybeAliased(
+        3, this->_internal_body(), target);
+  }
+
+  // map<string, string> headers = 4;
+  if (!this->_internal_headers().empty()) {
+    typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_pointer
+        ConstPtr;
+    typedef ConstPtr SortItem;
+    typedef ::PROTOBUF_NAMESPACE_ID::internal::CompareByDerefFirst<SortItem> Less;
+    struct Utf8Check {
+      static void Check(ConstPtr p) {
+      }
+    };
+
+    if (stream->IsSerializationDeterministic() &&
+        this->_internal_headers().size() > 1) {
+      ::std::unique_ptr<SortItem[]> items(
+          new SortItem[this->_internal_headers().size()]);
+      typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::size_type size_type;
+      size_type n = 0;
+      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_iterator
+          it = this->_internal_headers().begin();
+          it != this->_internal_headers().end(); ++it, ++n) {
+        items[static_cast<ptrdiff_t>(n)] = SortItem(&*it);
+      }
+      ::std::sort(&items[0], &items[static_cast<ptrdiff_t>(n)], Less());
+      for (size_type i = 0; i < n; i++) {
+        target = Request_HeadersEntry_DoNotUse::Funcs::InternalSerialize(4, items[static_cast<ptrdiff_t>(i)]->first, items[static_cast<ptrdiff_t>(i)]->second, target, stream);
+        Utf8Check::Check(&(*items[static_cast<ptrdiff_t>(i)]));
+      }
+    } else {
+      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_iterator
+          it = this->_internal_headers().begin();
+          it != this->_internal_headers().end(); ++it) {
+        target = Request_HeadersEntry_DoNotUse::Funcs::InternalSerialize(4, it->first, it->second, target, stream);
+        Utf8Check::Check(&(*it));
+      }
+    }
+  }
+
+  // required bool follow_redirects = 5;
+  if (cached_has_bits & 0x00000008u) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(5, this->_internal_follow_redirects(), target);
+  }
+
+  // required bool use_caches = 6;
+  if (cached_has_bits & 0x00000010u) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(6, this->_internal_use_caches(), target);
+  }
+
+  // required int32 connect_timeout_secs = 7;
+  if (cached_has_bits & 0x00000020u) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(7, this->_internal_connect_timeout_secs(), target);
+  }
+
+  // required int32 read_timeout_secs = 8;
+  if (cached_has_bits & 0x00000040u) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(8, this->_internal_read_timeout_secs(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+        static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:mozilla.appservices.httpconfig.protobuf.Request)
+  return target;
+}
+
+size_t Request::RequiredFieldsByteSizeFallback() const {
+// @@protoc_insertion_point(required_fields_byte_size_fallback_start:mozilla.appservices.httpconfig.protobuf.Request)
+  size_t total_size = 0;
+
+  if (_internal_has_url()) {
+    // required string url = 2;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_url());
+  }
+
+  if (_internal_has_method()) {
+    // required .mozilla.appservices.httpconfig.protobuf.Request.Method method = 1;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_method());
+  }
+
+  if (_internal_has_follow_redirects()) {
+    // required bool follow_redirects = 5;
+    total_size += 1 + 1;
+  }
+
+  if (_internal_has_use_caches()) {
+    // required bool use_caches = 6;
+    total_size += 1 + 1;
+  }
+
+  if (_internal_has_connect_timeout_secs()) {
+    // required int32 connect_timeout_secs = 7;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+        this->_internal_connect_timeout_secs());
+  }
+
+  if (_internal_has_read_timeout_secs()) {
+    // required int32 read_timeout_secs = 8;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+        this->_internal_read_timeout_secs());
+  }
+
+  return total_size;
+}
+size_t Request::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.appservices.httpconfig.protobuf.Request)
+  size_t total_size = 0;
+
+  if (((_has_bits_[0] & 0x0000007d) ^ 0x0000007d) == 0) {  // All required fields are present.
+    // required string url = 2;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_url());
+
+    // required .mozilla.appservices.httpconfig.protobuf.Request.Method method = 1;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_method());
+
+    // required bool follow_redirects = 5;
+    total_size += 1 + 1;
+
+    // required bool use_caches = 6;
+    total_size += 1 + 1;
+
+    // required int32 connect_timeout_secs = 7;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+        this->_internal_connect_timeout_secs());
+
+    // required int32 read_timeout_secs = 8;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+        this->_internal_read_timeout_secs());
+
+  } else {
+    total_size += RequiredFieldsByteSizeFallback();
+  }
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // map<string, string> headers = 4;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->_internal_headers_size());
+  for (::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_iterator
+      it = this->_internal_headers().begin();
+      it != this->_internal_headers().end(); ++it) {
+    total_size += Request_HeadersEntry_DoNotUse::Funcs::ByteSizeLong(it->first, it->second);
+  }
+
+  // optional bytes body = 3;
+  cached_has_bits = _has_bits_[0];
+  if (cached_has_bits & 0x00000002u) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+        this->_internal_body());
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    total_size += _internal_metadata_.unknown_fields().size();
+  }
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void Request::CheckTypeAndMergeFrom(
+    const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+  MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const Request*>(
+      &from));
+}
+
+void Request::MergeFrom(const Request& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:mozilla.appservices.httpconfig.protobuf.Request)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  headers_.MergeFrom(from.headers_);
+  cached_has_bits = from._has_bits_[0];
+  if (cached_has_bits & 0x0000007fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _has_bits_[0] |= 0x00000001u;
+      url_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.url_);
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _has_bits_[0] |= 0x00000002u;
+      body_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.body_);
+    }
+    if (cached_has_bits & 0x00000004u) {
+      method_ = from.method_;
+    }
+    if (cached_has_bits & 0x00000008u) {
+      follow_redirects_ = from.follow_redirects_;
+    }
+    if (cached_has_bits & 0x00000010u) {
+      use_caches_ = from.use_caches_;
+    }
+    if (cached_has_bits & 0x00000020u) {
+      connect_timeout_secs_ = from.connect_timeout_secs_;
+    }
+    if (cached_has_bits & 0x00000040u) {
+      read_timeout_secs_ = from.read_timeout_secs_;
+    }
+    _has_bits_[0] |= cached_has_bits;
+  }
+}
+
+void Request::CopyFrom(const Request& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.appservices.httpconfig.protobuf.Request)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Request::IsInitialized() const {
+  if ((_has_bits_[0] & 0x0000007d) != 0x0000007d) return false;
+  return true;
+}
+
+void Request::InternalSwap(Request* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  swap(_has_bits_[0], other->_has_bits_[0]);
+  headers_.Swap(&other->headers_);
+  url_.Swap(&other->url_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  body_.Swap(&other->body_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  swap(method_, other->method_);
+  swap(follow_redirects_, other->follow_redirects_);
+  swap(use_caches_, other->use_caches_);
+  swap(connect_timeout_secs_, other->connect_timeout_secs_);
+  swap(read_timeout_secs_, other->read_timeout_secs_);
+}
+
+std::string Request::GetTypeName() const {
+  return "mozilla.appservices.httpconfig.protobuf.Request";
+}
+
+
+// ===================================================================
+
+Response_HeadersEntry_DoNotUse::Response_HeadersEntry_DoNotUse() {}
+Response_HeadersEntry_DoNotUse::Response_HeadersEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena)
+    : SuperType(arena) {}
+void Response_HeadersEntry_DoNotUse::MergeFrom(const Response_HeadersEntry_DoNotUse& other) {
+  MergeFromInternal(other);
+}
+
+// ===================================================================
+
+void Response::InitAsDefaultInstance() {
+}
+class Response::_Internal {
+ public:
+  using HasBits = decltype(std::declval<Response>()._has_bits_);
+  static void set_has_exception_message(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_url(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_status(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static void set_has_body(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+};
+
+Response::Response()
+  : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:mozilla.appservices.httpconfig.protobuf.Response)
+}
+Response::Response(const Response& from)
+  : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+      _internal_metadata_(nullptr),
+      _has_bits_(from._has_bits_) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  headers_.MergeFrom(from.headers_);
+  exception_message_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (from._internal_has_exception_message()) {
+    exception_message_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.exception_message_);
+  }
+  url_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (from._internal_has_url()) {
+    url_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.url_);
+  }
+  body_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (from._internal_has_body()) {
+    body_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.body_);
+  }
+  status_ = from.status_;
+  // @@protoc_insertion_point(copy_constructor:mozilla.appservices.httpconfig.protobuf.Response)
+}
+
+void Response::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Response_fetch_5fmsg_5ftypes_2eproto.base);
+  exception_message_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  url_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  body_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  status_ = 0;
+}
+
+Response::~Response() {
+  // @@protoc_insertion_point(destructor:mozilla.appservices.httpconfig.protobuf.Response)
+  SharedDtor();
+}
+
+void Response::SharedDtor() {
+  exception_message_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  url_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  body_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+void Response::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const Response& Response::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Response_fetch_5fmsg_5ftypes_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void Response::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.appservices.httpconfig.protobuf.Response)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  headers_.Clear();
+  cached_has_bits = _has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    if (cached_has_bits & 0x00000001u) {
+      exception_message_.ClearNonDefaultToEmptyNoArena();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      url_.ClearNonDefaultToEmptyNoArena();
+    }
+    if (cached_has_bits & 0x00000004u) {
+      body_.ClearNonDefaultToEmptyNoArena();
+    }
+  }
+  status_ = 0;
+  _has_bits_.Clear();
+  _internal_metadata_.Clear();
+}
+
+const char* Response::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // optional string exception_message = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+          auto str = _internal_mutable_exception_message();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // optional string url = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) {
+          auto str = _internal_mutable_url();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // optional int32 status = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) {
+          _Internal::set_has_status(&has_bits);
+          status_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // optional bytes body = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) {
+          auto str = _internal_mutable_body();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // map<string, string> headers = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(&headers_, ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<42>(ptr));
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  _has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Response::_InternalSerialize(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:mozilla.appservices.httpconfig.protobuf.Response)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _has_bits_[0];
+  // optional string exception_message = 1;
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_exception_message(), target);
+  }
+
+  // optional string url = 2;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_url(), target);
+  }
+
+  // optional int32 status = 3;
+  if (cached_has_bits & 0x00000008u) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(3, this->_internal_status(), target);
+  }
+
+  // optional bytes body = 4;
+  if (cached_has_bits & 0x00000004u) {
+    target = stream->WriteBytesMaybeAliased(
+        4, this->_internal_body(), target);
+  }
+
+  // map<string, string> headers = 5;
+  if (!this->_internal_headers().empty()) {
+    typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_pointer
+        ConstPtr;
+    typedef ConstPtr SortItem;
+    typedef ::PROTOBUF_NAMESPACE_ID::internal::CompareByDerefFirst<SortItem> Less;
+    struct Utf8Check {
+      static void Check(ConstPtr p) {
+      }
+    };
+
+    if (stream->IsSerializationDeterministic() &&
+        this->_internal_headers().size() > 1) {
+      ::std::unique_ptr<SortItem[]> items(
+          new SortItem[this->_internal_headers().size()]);
+      typedef ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::size_type size_type;
+      size_type n = 0;
+      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_iterator
+          it = this->_internal_headers().begin();
+          it != this->_internal_headers().end(); ++it, ++n) {
+        items[static_cast<ptrdiff_t>(n)] = SortItem(&*it);
+      }
+      ::std::sort(&items[0], &items[static_cast<ptrdiff_t>(n)], Less());
+      for (size_type i = 0; i < n; i++) {
+        target = Response_HeadersEntry_DoNotUse::Funcs::InternalSerialize(5, items[static_cast<ptrdiff_t>(i)]->first, items[static_cast<ptrdiff_t>(i)]->second, target, stream);
+        Utf8Check::Check(&(*items[static_cast<ptrdiff_t>(i)]));
+      }
+    } else {
+      for (::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_iterator
+          it = this->_internal_headers().begin();
+          it != this->_internal_headers().end(); ++it) {
+        target = Response_HeadersEntry_DoNotUse::Funcs::InternalSerialize(5, it->first, it->second, target, stream);
+        Utf8Check::Check(&(*it));
+      }
+    }
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+        static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:mozilla.appservices.httpconfig.protobuf.Response)
+  return target;
+}
+
+size_t Response::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.appservices.httpconfig.protobuf.Response)
+  size_t total_size = 0;
+
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // map<string, string> headers = 5;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->_internal_headers_size());
+  for (::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >::const_iterator
+      it = this->_internal_headers().begin();
+      it != this->_internal_headers().end(); ++it) {
+    total_size += Response_HeadersEntry_DoNotUse::Funcs::ByteSizeLong(it->first, it->second);
+  }
+
+  cached_has_bits = _has_bits_[0];
+  if (cached_has_bits & 0x0000000fu) {
+    // optional string exception_message = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_exception_message());
+    }
+
+    // optional string url = 2;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_url());
+    }
+
+    // optional bytes body = 4;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+          this->_internal_body());
+    }
+
+    // optional int32 status = 3;
+    if (cached_has_bits & 0x00000008u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+          this->_internal_status());
+    }
+
+  }
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    total_size += _internal_metadata_.unknown_fields().size();
+  }
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void Response::CheckTypeAndMergeFrom(
+    const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+  MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const Response*>(
+      &from));
+}
+
+void Response::MergeFrom(const Response& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:mozilla.appservices.httpconfig.protobuf.Response)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  headers_.MergeFrom(from.headers_);
+  cached_has_bits = from._has_bits_[0];
+  if (cached_has_bits & 0x0000000fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _has_bits_[0] |= 0x00000001u;
+      exception_message_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.exception_message_);
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _has_bits_[0] |= 0x00000002u;
+      url_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.url_);
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _has_bits_[0] |= 0x00000004u;
+      body_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.body_);
+    }
+    if (cached_has_bits & 0x00000008u) {
+      status_ = from.status_;
+    }
+    _has_bits_[0] |= cached_has_bits;
+  }
+}
+
+void Response::CopyFrom(const Response& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.appservices.httpconfig.protobuf.Response)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Response::IsInitialized() const {
+  return true;
+}
+
+void Response::InternalSwap(Response* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  swap(_has_bits_[0], other->_has_bits_[0]);
+  headers_.Swap(&other->headers_);
+  exception_message_.Swap(&other->exception_message_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  url_.Swap(&other->url_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  body_.Swap(&other->body_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  swap(status_, other->status_);
+}
+
+std::string Response::GetTypeName() const {
+  return "mozilla.appservices.httpconfig.protobuf.Response";
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+}  // namespace protobuf
+}  // namespace httpconfig
+}  // namespace appservices
+}  // namespace mozilla
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::mozilla::appservices::httpconfig::protobuf::Request_HeadersEntry_DoNotUse* Arena::CreateMaybeMessage< ::mozilla::appservices::httpconfig::protobuf::Request_HeadersEntry_DoNotUse >(Arena* arena) {
+  return Arena::CreateInternal< ::mozilla::appservices::httpconfig::protobuf::Request_HeadersEntry_DoNotUse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::appservices::httpconfig::protobuf::Request* Arena::CreateMaybeMessage< ::mozilla::appservices::httpconfig::protobuf::Request >(Arena* arena) {
+  return Arena::CreateInternal< ::mozilla::appservices::httpconfig::protobuf::Request >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::appservices::httpconfig::protobuf::Response_HeadersEntry_DoNotUse* Arena::CreateMaybeMessage< ::mozilla::appservices::httpconfig::protobuf::Response_HeadersEntry_DoNotUse >(Arena* arena) {
+  return Arena::CreateInternal< ::mozilla::appservices::httpconfig::protobuf::Response_HeadersEntry_DoNotUse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::appservices::httpconfig::protobuf::Response* Arena::CreateMaybeMessage< ::mozilla::appservices::httpconfig::protobuf::Response >(Arena* arena) {
+  return Arena::CreateInternal< ::mozilla::appservices::httpconfig::protobuf::Response >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/fetch_msg_types.pb.h
@@ -0,0 +1,1294 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: fetch_msg_types.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_fetch_5fmsg_5ftypes_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_fetch_5fmsg_5ftypes_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3011000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3011004 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_table_driven.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/inlined_string_field.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/map.h>  // IWYU pragma: export
+#include <google/protobuf/map_entry_lite.h>
+#include <google/protobuf/map_field_lite.h>
+#include <google/protobuf/generated_enum_util.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_fetch_5fmsg_5ftypes_2eproto
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_fetch_5fmsg_5ftypes_2eproto {
+  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[4]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[];
+  static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[];
+  static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[];
+};
+namespace mozilla {
+namespace appservices {
+namespace httpconfig {
+namespace protobuf {
+class Request;
+class RequestDefaultTypeInternal;
+extern RequestDefaultTypeInternal _Request_default_instance_;
+class Request_HeadersEntry_DoNotUse;
+class Request_HeadersEntry_DoNotUseDefaultTypeInternal;
+extern Request_HeadersEntry_DoNotUseDefaultTypeInternal _Request_HeadersEntry_DoNotUse_default_instance_;
+class Response;
+class ResponseDefaultTypeInternal;
+extern ResponseDefaultTypeInternal _Response_default_instance_;
+class Response_HeadersEntry_DoNotUse;
+class Response_HeadersEntry_DoNotUseDefaultTypeInternal;
+extern Response_HeadersEntry_DoNotUseDefaultTypeInternal _Response_HeadersEntry_DoNotUse_default_instance_;
+}  // namespace protobuf
+}  // namespace httpconfig
+}  // namespace appservices
+}  // namespace mozilla
+PROTOBUF_NAMESPACE_OPEN
+template<> ::mozilla::appservices::httpconfig::protobuf::Request* Arena::CreateMaybeMessage<::mozilla::appservices::httpconfig::protobuf::Request>(Arena*);
+template<> ::mozilla::appservices::httpconfig::protobuf::Request_HeadersEntry_DoNotUse* Arena::CreateMaybeMessage<::mozilla::appservices::httpconfig::protobuf::Request_HeadersEntry_DoNotUse>(Arena*);
+template<> ::mozilla::appservices::httpconfig::protobuf::Response* Arena::CreateMaybeMessage<::mozilla::appservices::httpconfig::protobuf::Response>(Arena*);
+template<> ::mozilla::appservices::httpconfig::protobuf::Response_HeadersEntry_DoNotUse* Arena::CreateMaybeMessage<::mozilla::appservices::httpconfig::protobuf::Response_HeadersEntry_DoNotUse>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+namespace mozilla {
+namespace appservices {
+namespace httpconfig {
+namespace protobuf {
+
+enum Request_Method : int {
+  Request_Method_GET = 0,
+  Request_Method_HEAD = 1,
+  Request_Method_POST = 2,
+  Request_Method_PUT = 3,
+  Request_Method_DELETE = 4,
+  Request_Method_CONNECT = 5,
+  Request_Method_OPTIONS = 6,
+  Request_Method_TRACE = 7
+};
+bool Request_Method_IsValid(int value);
+constexpr Request_Method Request_Method_Method_MIN = Request_Method_GET;
+constexpr Request_Method Request_Method_Method_MAX = Request_Method_TRACE;
+constexpr int Request_Method_Method_ARRAYSIZE = Request_Method_Method_MAX + 1;
+
+const std::string& Request_Method_Name(Request_Method value);
+template<typename T>
+inline const std::string& Request_Method_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, Request_Method>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function Request_Method_Name.");
+  return Request_Method_Name(static_cast<Request_Method>(enum_t_value));
+}
+bool Request_Method_Parse(
+    const std::string& name, Request_Method* value);
+// ===================================================================
+
+class Request_HeadersEntry_DoNotUse : public ::PROTOBUF_NAMESPACE_ID::internal::MapEntryLite<Request_HeadersEntry_DoNotUse, 
+    std::string, std::string,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    0 > {
+public:
+  typedef ::PROTOBUF_NAMESPACE_ID::internal::MapEntryLite<Request_HeadersEntry_DoNotUse, 
+    std::string, std::string,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    0 > SuperType;
+  Request_HeadersEntry_DoNotUse();
+  explicit Request_HeadersEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena);
+  void MergeFrom(const Request_HeadersEntry_DoNotUse& other);
+  static const Request_HeadersEntry_DoNotUse* internal_default_instance() { return reinterpret_cast<const Request_HeadersEntry_DoNotUse*>(&_Request_HeadersEntry_DoNotUse_default_instance_); }
+  static bool ValidateKey(void*) { return true; }
+  static bool ValidateValue(void*) { return true; }
+};
+
+// -------------------------------------------------------------------
+
+class Request :
+    public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.appservices.httpconfig.protobuf.Request) */ {
+ public:
+  Request();
+  virtual ~Request();
+
+  Request(const Request& from);
+  Request(Request&& from) noexcept
+    : Request() {
+    *this = ::std::move(from);
+  }
+
+  inline Request& operator=(const Request& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Request& operator=(Request&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const std::string& unknown_fields() const {
+    return _internal_metadata_.unknown_fields();
+  }
+  inline std::string* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields();
+  }
+
+  static const Request& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const Request* internal_default_instance() {
+    return reinterpret_cast<const Request*>(
+               &_Request_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(Request& a, Request& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Request* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline Request* New() const final {
+    return CreateMaybeMessage<Request>(nullptr);
+  }
+
+  Request* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<Request>(arena);
+  }
+  void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+    final;
+  void CopyFrom(const Request& from);
+  void MergeFrom(const Request& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  void DiscardUnknownFields();
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const;
+  void InternalSwap(Request* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "mozilla.appservices.httpconfig.protobuf.Request";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  std::string GetTypeName() const final;
+
+  // nested types ----------------------------------------------------
+
+
+  typedef Request_Method Method;
+  static constexpr Method GET =
+    Request_Method_GET;
+  static constexpr Method HEAD =
+    Request_Method_HEAD;
+  static constexpr Method POST =
+    Request_Method_POST;
+  static constexpr Method PUT =
+    Request_Method_PUT;
+  static constexpr Method DELETE =
+    Request_Method_DELETE;
+  static constexpr Method CONNECT =
+    Request_Method_CONNECT;
+  static constexpr Method OPTIONS =
+    Request_Method_OPTIONS;
+  static constexpr Method TRACE =
+    Request_Method_TRACE;
+  static inline bool Method_IsValid(int value) {
+    return Request_Method_IsValid(value);
+  }
+  static constexpr Method Method_MIN =
+    Request_Method_Method_MIN;
+  static constexpr Method Method_MAX =
+    Request_Method_Method_MAX;
+  static constexpr int Method_ARRAYSIZE =
+    Request_Method_Method_ARRAYSIZE;
+  template<typename T>
+  static inline const std::string& Method_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, Method>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function Method_Name.");
+    return Request_Method_Name(enum_t_value);
+  }
+  static inline bool Method_Parse(const std::string& name,
+      Method* value) {
+    return Request_Method_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kHeadersFieldNumber = 4,
+    kUrlFieldNumber = 2,
+    kBodyFieldNumber = 3,
+    kMethodFieldNumber = 1,
+    kFollowRedirectsFieldNumber = 5,
+    kUseCachesFieldNumber = 6,
+    kConnectTimeoutSecsFieldNumber = 7,
+    kReadTimeoutSecsFieldNumber = 8,
+  };
+  // map<string, string> headers = 4;
+  int headers_size() const;
+  private:
+  int _internal_headers_size() const;
+  public:
+  void clear_headers();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+      _internal_headers() const;
+  ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+      _internal_mutable_headers();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+      headers() const;
+  ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+      mutable_headers();
+
+  // required string url = 2;
+  bool has_url() const;
+  private:
+  bool _internal_has_url() const;
+  public:
+  void clear_url();
+  const std::string& url() const;
+  void set_url(const std::string& value);
+  void set_url(std::string&& value);
+  void set_url(const char* value);
+  void set_url(const char* value, size_t size);
+  std::string* mutable_url();
+  std::string* release_url();
+  void set_allocated_url(std::string* url);
+  private:
+  const std::string& _internal_url() const;
+  void _internal_set_url(const std::string& value);
+  std::string* _internal_mutable_url();
+  public:
+
+  // optional bytes body = 3;
+  bool has_body() const;
+  private:
+  bool _internal_has_body() const;
+  public:
+  void clear_body();
+  const std::string& body() const;
+  void set_body(const std::string& value);
+  void set_body(std::string&& value);
+  void set_body(const char* value);
+  void set_body(const void* value, size_t size);
+  std::string* mutable_body();
+  std::string* release_body();
+  void set_allocated_body(std::string* body);
+  private:
+  const std::string& _internal_body() const;
+  void _internal_set_body(const std::string& value);
+  std::string* _internal_mutable_body();
+  public:
+
+  // required .mozilla.appservices.httpconfig.protobuf.Request.Method method = 1;
+  bool has_method() const;
+  private:
+  bool _internal_has_method() const;
+  public:
+  void clear_method();
+  ::mozilla::appservices::httpconfig::protobuf::Request_Method method() const;
+  void set_method(::mozilla::appservices::httpconfig::protobuf::Request_Method value);
+  private:
+  ::mozilla::appservices::httpconfig::protobuf::Request_Method _internal_method() const;
+  void _internal_set_method(::mozilla::appservices::httpconfig::protobuf::Request_Method value);
+  public:
+
+  // required bool follow_redirects = 5;
+  bool has_follow_redirects() const;
+  private:
+  bool _internal_has_follow_redirects() const;
+  public:
+  void clear_follow_redirects();
+  bool follow_redirects() const;
+  void set_follow_redirects(bool value);
+  private:
+  bool _internal_follow_redirects() const;
+  void _internal_set_follow_redirects(bool value);
+  public:
+
+  // required bool use_caches = 6;
+  bool has_use_caches() const;
+  private:
+  bool _internal_has_use_caches() const;
+  public:
+  void clear_use_caches();
+  bool use_caches() const;
+  void set_use_caches(bool value);
+  private:
+  bool _internal_use_caches() const;
+  void _internal_set_use_caches(bool value);
+  public:
+
+  // required int32 connect_timeout_secs = 7;
+  bool has_connect_timeout_secs() const;
+  private:
+  bool _internal_has_connect_timeout_secs() const;
+  public:
+  void clear_connect_timeout_secs();
+  ::PROTOBUF_NAMESPACE_ID::int32 connect_timeout_secs() const;
+  void set_connect_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::int32 _internal_connect_timeout_secs() const;
+  void _internal_set_connect_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value);
+  public:
+
+  // required int32 read_timeout_secs = 8;
+  bool has_read_timeout_secs() const;
+  private:
+  bool _internal_has_read_timeout_secs() const;
+  public:
+  void clear_read_timeout_secs();
+  ::PROTOBUF_NAMESPACE_ID::int32 read_timeout_secs() const;
+  void set_read_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::int32 _internal_read_timeout_secs() const;
+  void _internal_set_read_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:mozilla.appservices.httpconfig.protobuf.Request)
+ private:
+  class _Internal;
+
+  // helper for ByteSizeLong()
+  size_t RequiredFieldsByteSizeFallback() const;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  ::PROTOBUF_NAMESPACE_ID::internal::MapFieldLite<
+      Request_HeadersEntry_DoNotUse,
+      std::string, std::string,
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+      0 > headers_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr url_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr body_;
+  int method_;
+  bool follow_redirects_;
+  bool use_caches_;
+  ::PROTOBUF_NAMESPACE_ID::int32 connect_timeout_secs_;
+  ::PROTOBUF_NAMESPACE_ID::int32 read_timeout_secs_;
+  friend struct ::TableStruct_fetch_5fmsg_5ftypes_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Response_HeadersEntry_DoNotUse : public ::PROTOBUF_NAMESPACE_ID::internal::MapEntryLite<Response_HeadersEntry_DoNotUse, 
+    std::string, std::string,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    0 > {
+public:
+  typedef ::PROTOBUF_NAMESPACE_ID::internal::MapEntryLite<Response_HeadersEntry_DoNotUse, 
+    std::string, std::string,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    0 > SuperType;
+  Response_HeadersEntry_DoNotUse();
+  explicit Response_HeadersEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena);
+  void MergeFrom(const Response_HeadersEntry_DoNotUse& other);
+  static const Response_HeadersEntry_DoNotUse* internal_default_instance() { return reinterpret_cast<const Response_HeadersEntry_DoNotUse*>(&_Response_HeadersEntry_DoNotUse_default_instance_); }
+  static bool ValidateKey(void*) { return true; }
+  static bool ValidateValue(void*) { return true; }
+};
+
+// -------------------------------------------------------------------
+
+class Response :
+    public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.appservices.httpconfig.protobuf.Response) */ {
+ public:
+  Response();
+  virtual ~Response();
+
+  Response(const Response& from);
+  Response(Response&& from) noexcept
+    : Response() {
+    *this = ::std::move(from);
+  }
+
+  inline Response& operator=(const Response& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Response& operator=(Response&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const std::string& unknown_fields() const {
+    return _internal_metadata_.unknown_fields();
+  }
+  inline std::string* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields();
+  }
+
+  static const Response& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const Response* internal_default_instance() {
+    return reinterpret_cast<const Response*>(
+               &_Response_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    3;
+
+  friend void swap(Response& a, Response& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Response* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline Response* New() const final {
+    return CreateMaybeMessage<Response>(nullptr);
+  }
+
+  Response* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<Response>(arena);
+  }
+  void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+    final;
+  void CopyFrom(const Response& from);
+  void MergeFrom(const Response& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  void DiscardUnknownFields();
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const;
+  void InternalSwap(Response* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "mozilla.appservices.httpconfig.protobuf.Response";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  std::string GetTypeName() const final;
+
+  // nested types ----------------------------------------------------
+
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kHeadersFieldNumber = 5,
+    kExceptionMessageFieldNumber = 1,
+    kUrlFieldNumber = 2,
+    kBodyFieldNumber = 4,
+    kStatusFieldNumber = 3,
+  };
+  // map<string, string> headers = 5;
+  int headers_size() const;
+  private:
+  int _internal_headers_size() const;
+  public:
+  void clear_headers();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+      _internal_headers() const;
+  ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+      _internal_mutable_headers();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+      headers() const;
+  ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+      mutable_headers();
+
+  // optional string exception_message = 1;
+  bool has_exception_message() const;
+  private:
+  bool _internal_has_exception_message() const;
+  public:
+  void clear_exception_message();
+  const std::string& exception_message() const;
+  void set_exception_message(const std::string& value);
+  void set_exception_message(std::string&& value);
+  void set_exception_message(const char* value);
+  void set_exception_message(const char* value, size_t size);
+  std::string* mutable_exception_message();
+  std::string* release_exception_message();
+  void set_allocated_exception_message(std::string* exception_message);
+  private:
+  const std::string& _internal_exception_message() const;
+  void _internal_set_exception_message(const std::string& value);
+  std::string* _internal_mutable_exception_message();
+  public:
+
+  // optional string url = 2;
+  bool has_url() const;
+  private:
+  bool _internal_has_url() const;
+  public:
+  void clear_url();
+  const std::string& url() const;
+  void set_url(const std::string& value);
+  void set_url(std::string&& value);
+  void set_url(const char* value);
+  void set_url(const char* value, size_t size);
+  std::string* mutable_url();
+  std::string* release_url();
+  void set_allocated_url(std::string* url);
+  private:
+  const std::string& _internal_url() const;
+  void _internal_set_url(const std::string& value);
+  std::string* _internal_mutable_url();
+  public:
+
+  // optional bytes body = 4;
+  bool has_body() const;
+  private:
+  bool _internal_has_body() const;
+  public:
+  void clear_body();
+  const std::string& body() const;
+  void set_body(const std::string& value);
+  void set_body(std::string&& value);
+  void set_body(const char* value);
+  void set_body(const void* value, size_t size);
+  std::string* mutable_body();
+  std::string* release_body();
+  void set_allocated_body(std::string* body);
+  private:
+  const std::string& _internal_body() const;
+  void _internal_set_body(const std::string& value);
+  std::string* _internal_mutable_body();
+  public:
+
+  // optional int32 status = 3;
+  bool has_status() const;
+  private:
+  bool _internal_has_status() const;
+  public:
+  void clear_status();
+  ::PROTOBUF_NAMESPACE_ID::int32 status() const;
+  void set_status(::PROTOBUF_NAMESPACE_ID::int32 value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::int32 _internal_status() const;
+  void _internal_set_status(::PROTOBUF_NAMESPACE_ID::int32 value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:mozilla.appservices.httpconfig.protobuf.Response)
+ private:
+  class _Internal;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  ::PROTOBUF_NAMESPACE_ID::internal::MapFieldLite<
+      Response_HeadersEntry_DoNotUse,
+      std::string, std::string,
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+      0 > headers_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr exception_message_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr url_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr body_;
+  ::PROTOBUF_NAMESPACE_ID::int32 status_;
+  friend struct ::TableStruct_fetch_5fmsg_5ftypes_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// Request
+
+// required .mozilla.appservices.httpconfig.protobuf.Request.Method method = 1;
+inline bool Request::_internal_has_method() const {
+  bool value = (_has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool Request::has_method() const {
+  return _internal_has_method();
+}
+inline void Request::clear_method() {
+  method_ = 0;
+  _has_bits_[0] &= ~0x00000004u;
+}
+inline ::mozilla::appservices::httpconfig::protobuf::Request_Method Request::_internal_method() const {
+  return static_cast< ::mozilla::appservices::httpconfig::protobuf::Request_Method >(method_);
+}
+inline ::mozilla::appservices::httpconfig::protobuf::Request_Method Request::method() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Request.method)
+  return _internal_method();
+}
+inline void Request::_internal_set_method(::mozilla::appservices::httpconfig::protobuf::Request_Method value) {
+  assert(::mozilla::appservices::httpconfig::protobuf::Request_Method_IsValid(value));
+  _has_bits_[0] |= 0x00000004u;
+  method_ = value;
+}
+inline void Request::set_method(::mozilla::appservices::httpconfig::protobuf::Request_Method value) {
+  _internal_set_method(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Request.method)
+}
+
+// required string url = 2;
+inline bool Request::_internal_has_url() const {
+  bool value = (_has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool Request::has_url() const {
+  return _internal_has_url();
+}
+inline void Request::clear_url() {
+  url_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  _has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& Request::url() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Request.url)
+  return _internal_url();
+}
+inline void Request::set_url(const std::string& value) {
+  _internal_set_url(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Request.url)
+}
+inline std::string* Request::mutable_url() {
+  // @@protoc_insertion_point(field_mutable:mozilla.appservices.httpconfig.protobuf.Request.url)
+  return _internal_mutable_url();
+}
+inline const std::string& Request::_internal_url() const {
+  return url_.GetNoArena();
+}
+inline void Request::_internal_set_url(const std::string& value) {
+  _has_bits_[0] |= 0x00000001u;
+  url_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Request::set_url(std::string&& value) {
+  _has_bits_[0] |= 0x00000001u;
+  url_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:mozilla.appservices.httpconfig.protobuf.Request.url)
+}
+inline void Request::set_url(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _has_bits_[0] |= 0x00000001u;
+  url_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:mozilla.appservices.httpconfig.protobuf.Request.url)
+}
+inline void Request::set_url(const char* value, size_t size) {
+  _has_bits_[0] |= 0x00000001u;
+  url_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:mozilla.appservices.httpconfig.protobuf.Request.url)
+}
+inline std::string* Request::_internal_mutable_url() {
+  _has_bits_[0] |= 0x00000001u;
+  return url_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Request::release_url() {
+  // @@protoc_insertion_point(field_release:mozilla.appservices.httpconfig.protobuf.Request.url)
+  if (!_internal_has_url()) {
+    return nullptr;
+  }
+  _has_bits_[0] &= ~0x00000001u;
+  return url_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Request::set_allocated_url(std::string* url) {
+  if (url != nullptr) {
+    _has_bits_[0] |= 0x00000001u;
+  } else {
+    _has_bits_[0] &= ~0x00000001u;
+  }
+  url_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), url);
+  // @@protoc_insertion_point(field_set_allocated:mozilla.appservices.httpconfig.protobuf.Request.url)
+}
+
+// optional bytes body = 3;
+inline bool Request::_internal_has_body() const {
+  bool value = (_has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool Request::has_body() const {
+  return _internal_has_body();
+}
+inline void Request::clear_body() {
+  body_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  _has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& Request::body() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Request.body)
+  return _internal_body();
+}
+inline void Request::set_body(const std::string& value) {
+  _internal_set_body(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Request.body)
+}
+inline std::string* Request::mutable_body() {
+  // @@protoc_insertion_point(field_mutable:mozilla.appservices.httpconfig.protobuf.Request.body)
+  return _internal_mutable_body();
+}
+inline const std::string& Request::_internal_body() const {
+  return body_.GetNoArena();
+}
+inline void Request::_internal_set_body(const std::string& value) {
+  _has_bits_[0] |= 0x00000002u;
+  body_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Request::set_body(std::string&& value) {
+  _has_bits_[0] |= 0x00000002u;
+  body_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:mozilla.appservices.httpconfig.protobuf.Request.body)
+}
+inline void Request::set_body(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _has_bits_[0] |= 0x00000002u;
+  body_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:mozilla.appservices.httpconfig.protobuf.Request.body)
+}
+inline void Request::set_body(const void* value, size_t size) {
+  _has_bits_[0] |= 0x00000002u;
+  body_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:mozilla.appservices.httpconfig.protobuf.Request.body)
+}
+inline std::string* Request::_internal_mutable_body() {
+  _has_bits_[0] |= 0x00000002u;
+  return body_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Request::release_body() {
+  // @@protoc_insertion_point(field_release:mozilla.appservices.httpconfig.protobuf.Request.body)
+  if (!_internal_has_body()) {
+    return nullptr;
+  }
+  _has_bits_[0] &= ~0x00000002u;
+  return body_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Request::set_allocated_body(std::string* body) {
+  if (body != nullptr) {
+    _has_bits_[0] |= 0x00000002u;
+  } else {
+    _has_bits_[0] &= ~0x00000002u;
+  }
+  body_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), body);
+  // @@protoc_insertion_point(field_set_allocated:mozilla.appservices.httpconfig.protobuf.Request.body)
+}
+
+// map<string, string> headers = 4;
+inline int Request::_internal_headers_size() const {
+  return headers_.size();
+}
+inline int Request::headers_size() const {
+  return _internal_headers_size();
+}
+inline void Request::clear_headers() {
+  headers_.Clear();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+Request::_internal_headers() const {
+  return headers_.GetMap();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+Request::headers() const {
+  // @@protoc_insertion_point(field_map:mozilla.appservices.httpconfig.protobuf.Request.headers)
+  return _internal_headers();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+Request::_internal_mutable_headers() {
+  return headers_.MutableMap();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+Request::mutable_headers() {
+  // @@protoc_insertion_point(field_mutable_map:mozilla.appservices.httpconfig.protobuf.Request.headers)
+  return _internal_mutable_headers();
+}
+
+// required bool follow_redirects = 5;
+inline bool Request::_internal_has_follow_redirects() const {
+  bool value = (_has_bits_[0] & 0x00000008u) != 0;
+  return value;
+}
+inline bool Request::has_follow_redirects() const {
+  return _internal_has_follow_redirects();
+}
+inline void Request::clear_follow_redirects() {
+  follow_redirects_ = false;
+  _has_bits_[0] &= ~0x00000008u;
+}
+inline bool Request::_internal_follow_redirects() const {
+  return follow_redirects_;
+}
+inline bool Request::follow_redirects() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Request.follow_redirects)
+  return _internal_follow_redirects();
+}
+inline void Request::_internal_set_follow_redirects(bool value) {
+  _has_bits_[0] |= 0x00000008u;
+  follow_redirects_ = value;
+}
+inline void Request::set_follow_redirects(bool value) {
+  _internal_set_follow_redirects(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Request.follow_redirects)
+}
+
+// required bool use_caches = 6;
+inline bool Request::_internal_has_use_caches() const {
+  bool value = (_has_bits_[0] & 0x00000010u) != 0;
+  return value;
+}
+inline bool Request::has_use_caches() const {
+  return _internal_has_use_caches();
+}
+inline void Request::clear_use_caches() {
+  use_caches_ = false;
+  _has_bits_[0] &= ~0x00000010u;
+}
+inline bool Request::_internal_use_caches() const {
+  return use_caches_;
+}
+inline bool Request::use_caches() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Request.use_caches)
+  return _internal_use_caches();
+}
+inline void Request::_internal_set_use_caches(bool value) {
+  _has_bits_[0] |= 0x00000010u;
+  use_caches_ = value;
+}
+inline void Request::set_use_caches(bool value) {
+  _internal_set_use_caches(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Request.use_caches)
+}
+
+// required int32 connect_timeout_secs = 7;
+inline bool Request::_internal_has_connect_timeout_secs() const {
+  bool value = (_has_bits_[0] & 0x00000020u) != 0;
+  return value;
+}
+inline bool Request::has_connect_timeout_secs() const {
+  return _internal_has_connect_timeout_secs();
+}
+inline void Request::clear_connect_timeout_secs() {
+  connect_timeout_secs_ = 0;
+  _has_bits_[0] &= ~0x00000020u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Request::_internal_connect_timeout_secs() const {
+  return connect_timeout_secs_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Request::connect_timeout_secs() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Request.connect_timeout_secs)
+  return _internal_connect_timeout_secs();
+}
+inline void Request::_internal_set_connect_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  _has_bits_[0] |= 0x00000020u;
+  connect_timeout_secs_ = value;
+}
+inline void Request::set_connect_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  _internal_set_connect_timeout_secs(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Request.connect_timeout_secs)
+}
+
+// required int32 read_timeout_secs = 8;
+inline bool Request::_internal_has_read_timeout_secs() const {
+  bool value = (_has_bits_[0] & 0x00000040u) != 0;
+  return value;
+}
+inline bool Request::has_read_timeout_secs() const {
+  return _internal_has_read_timeout_secs();
+}
+inline void Request::clear_read_timeout_secs() {
+  read_timeout_secs_ = 0;
+  _has_bits_[0] &= ~0x00000040u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Request::_internal_read_timeout_secs() const {
+  return read_timeout_secs_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Request::read_timeout_secs() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Request.read_timeout_secs)
+  return _internal_read_timeout_secs();
+}
+inline void Request::_internal_set_read_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  _has_bits_[0] |= 0x00000040u;
+  read_timeout_secs_ = value;
+}
+inline void Request::set_read_timeout_secs(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  _internal_set_read_timeout_secs(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Request.read_timeout_secs)
+}
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// Response
+
+// optional string exception_message = 1;
+inline bool Response::_internal_has_exception_message() const {
+  bool value = (_has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool Response::has_exception_message() const {
+  return _internal_has_exception_message();
+}
+inline void Response::clear_exception_message() {
+  exception_message_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  _has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& Response::exception_message() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+  return _internal_exception_message();
+}
+inline void Response::set_exception_message(const std::string& value) {
+  _internal_set_exception_message(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+}
+inline std::string* Response::mutable_exception_message() {
+  // @@protoc_insertion_point(field_mutable:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+  return _internal_mutable_exception_message();
+}
+inline const std::string& Response::_internal_exception_message() const {
+  return exception_message_.GetNoArena();
+}
+inline void Response::_internal_set_exception_message(const std::string& value) {
+  _has_bits_[0] |= 0x00000001u;
+  exception_message_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Response::set_exception_message(std::string&& value) {
+  _has_bits_[0] |= 0x00000001u;
+  exception_message_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+}
+inline void Response::set_exception_message(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _has_bits_[0] |= 0x00000001u;
+  exception_message_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+}
+inline void Response::set_exception_message(const char* value, size_t size) {
+  _has_bits_[0] |= 0x00000001u;
+  exception_message_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+}
+inline std::string* Response::_internal_mutable_exception_message() {
+  _has_bits_[0] |= 0x00000001u;
+  return exception_message_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Response::release_exception_message() {
+  // @@protoc_insertion_point(field_release:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+  if (!_internal_has_exception_message()) {
+    return nullptr;
+  }
+  _has_bits_[0] &= ~0x00000001u;
+  return exception_message_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Response::set_allocated_exception_message(std::string* exception_message) {
+  if (exception_message != nullptr) {
+    _has_bits_[0] |= 0x00000001u;
+  } else {
+    _has_bits_[0] &= ~0x00000001u;
+  }
+  exception_message_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), exception_message);
+  // @@protoc_insertion_point(field_set_allocated:mozilla.appservices.httpconfig.protobuf.Response.exception_message)
+}
+
+// optional string url = 2;
+inline bool Response::_internal_has_url() const {
+  bool value = (_has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool Response::has_url() const {
+  return _internal_has_url();
+}
+inline void Response::clear_url() {
+  url_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  _has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& Response::url() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Response.url)
+  return _internal_url();
+}
+inline void Response::set_url(const std::string& value) {
+  _internal_set_url(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Response.url)
+}
+inline std::string* Response::mutable_url() {
+  // @@protoc_insertion_point(field_mutable:mozilla.appservices.httpconfig.protobuf.Response.url)
+  return _internal_mutable_url();
+}
+inline const std::string& Response::_internal_url() const {
+  return url_.GetNoArena();
+}
+inline void Response::_internal_set_url(const std::string& value) {
+  _has_bits_[0] |= 0x00000002u;
+  url_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Response::set_url(std::string&& value) {
+  _has_bits_[0] |= 0x00000002u;
+  url_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:mozilla.appservices.httpconfig.protobuf.Response.url)
+}
+inline void Response::set_url(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _has_bits_[0] |= 0x00000002u;
+  url_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:mozilla.appservices.httpconfig.protobuf.Response.url)
+}
+inline void Response::set_url(const char* value, size_t size) {
+  _has_bits_[0] |= 0x00000002u;
+  url_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:mozilla.appservices.httpconfig.protobuf.Response.url)
+}
+inline std::string* Response::_internal_mutable_url() {
+  _has_bits_[0] |= 0x00000002u;
+  return url_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Response::release_url() {
+  // @@protoc_insertion_point(field_release:mozilla.appservices.httpconfig.protobuf.Response.url)
+  if (!_internal_has_url()) {
+    return nullptr;
+  }
+  _has_bits_[0] &= ~0x00000002u;
+  return url_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Response::set_allocated_url(std::string* url) {
+  if (url != nullptr) {
+    _has_bits_[0] |= 0x00000002u;
+  } else {
+    _has_bits_[0] &= ~0x00000002u;
+  }
+  url_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), url);
+  // @@protoc_insertion_point(field_set_allocated:mozilla.appservices.httpconfig.protobuf.Response.url)
+}
+
+// optional int32 status = 3;
+inline bool Response::_internal_has_status() const {
+  bool value = (_has_bits_[0] & 0x00000008u) != 0;
+  return value;
+}
+inline bool Response::has_status() const {
+  return _internal_has_status();
+}
+inline void Response::clear_status() {
+  status_ = 0;
+  _has_bits_[0] &= ~0x00000008u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Response::_internal_status() const {
+  return status_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Response::status() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Response.status)
+  return _internal_status();
+}
+inline void Response::_internal_set_status(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  _has_bits_[0] |= 0x00000008u;
+  status_ = value;
+}
+inline void Response::set_status(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  _internal_set_status(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Response.status)
+}
+
+// optional bytes body = 4;
+inline bool Response::_internal_has_body() const {
+  bool value = (_has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool Response::has_body() const {
+  return _internal_has_body();
+}
+inline void Response::clear_body() {
+  body_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  _has_bits_[0] &= ~0x00000004u;
+}
+inline const std::string& Response::body() const {
+  // @@protoc_insertion_point(field_get:mozilla.appservices.httpconfig.protobuf.Response.body)
+  return _internal_body();
+}
+inline void Response::set_body(const std::string& value) {
+  _internal_set_body(value);
+  // @@protoc_insertion_point(field_set:mozilla.appservices.httpconfig.protobuf.Response.body)
+}
+inline std::string* Response::mutable_body() {
+  // @@protoc_insertion_point(field_mutable:mozilla.appservices.httpconfig.protobuf.Response.body)
+  return _internal_mutable_body();
+}
+inline const std::string& Response::_internal_body() const {
+  return body_.GetNoArena();
+}
+inline void Response::_internal_set_body(const std::string& value) {
+  _has_bits_[0] |= 0x00000004u;
+  body_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Response::set_body(std::string&& value) {
+  _has_bits_[0] |= 0x00000004u;
+  body_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:mozilla.appservices.httpconfig.protobuf.Response.body)
+}
+inline void Response::set_body(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _has_bits_[0] |= 0x00000004u;
+  body_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:mozilla.appservices.httpconfig.protobuf.Response.body)
+}
+inline void Response::set_body(const void* value, size_t size) {
+  _has_bits_[0] |= 0x00000004u;
+  body_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:mozilla.appservices.httpconfig.protobuf.Response.body)
+}
+inline std::string* Response::_internal_mutable_body() {
+  _has_bits_[0] |= 0x00000004u;
+  return body_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Response::release_body() {
+  // @@protoc_insertion_point(field_release:mozilla.appservices.httpconfig.protobuf.Response.body)
+  if (!_internal_has_body()) {
+    return nullptr;
+  }
+  _has_bits_[0] &= ~0x00000004u;
+  return body_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Response::set_allocated_body(std::string* body) {
+  if (body != nullptr) {
+    _has_bits_[0] |= 0x00000004u;
+  } else {
+    _has_bits_[0] &= ~0x00000004u;
+  }
+  body_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), body);
+  // @@protoc_insertion_point(field_set_allocated:mozilla.appservices.httpconfig.protobuf.Response.body)
+}
+
+// map<string, string> headers = 5;
+inline int Response::_internal_headers_size() const {
+  return headers_.size();
+}
+inline int Response::headers_size() const {
+  return _internal_headers_size();
+}
+inline void Response::clear_headers() {
+  headers_.Clear();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+Response::_internal_headers() const {
+  return headers_.GetMap();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >&
+Response::headers() const {
+  // @@protoc_insertion_point(field_map:mozilla.appservices.httpconfig.protobuf.Response.headers)
+  return _internal_headers();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+Response::_internal_mutable_headers() {
+  return headers_.MutableMap();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string >*
+Response::mutable_headers() {
+  // @@protoc_insertion_point(field_mutable_map:mozilla.appservices.httpconfig.protobuf.Response.headers)
+  return _internal_mutable_headers();
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace protobuf
+}  // namespace httpconfig
+}  // namespace appservices
+}  // namespace mozilla
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::mozilla::appservices::httpconfig::protobuf::Request_Method> : ::std::true_type {};
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_fetch_5fmsg_5ftypes_2eproto
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/fetch_msg_types.proto
@@ -0,0 +1,41 @@
+syntax = "proto2";
+
+// Note: this file name must be unique due to how the iOS megazord works :(
+
+package mozilla.appservices.httpconfig.protobuf;
+
+option java_package = "mozilla.appservices.httpconfig";
+option java_outer_classname = "MsgTypes";
+option swift_prefix = "MsgTypes_";
+option optimize_for = LITE_RUNTIME;
+
+message Request {
+    enum Method {
+        GET = 0;
+        HEAD = 1;
+        POST = 2;
+        PUT = 3;
+        DELETE = 4;
+        CONNECT = 5;
+        OPTIONS = 6;
+        TRACE = 7;
+    }
+    required Method method = 1;
+    required string url = 2;
+    optional bytes body = 3;
+    map<string, string> headers = 4;
+    required bool follow_redirects = 5;
+    required bool use_caches = 6;
+    required int32 connect_timeout_secs = 7;
+    required int32 read_timeout_secs = 8;
+}
+
+message Response {
+    // If this is present, nothing else is.
+    optional string exception_message = 1;
+    optional string url = 2;
+    optional int32 status = 3;
+    optional bytes body = 4;
+    map<string, string> headers = 5;
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/moz.build
@@ -0,0 +1,32 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_MODULE = 'viaduct'
+
+XPIDL_SOURCES += [
+    'mozIViaduct.idl',
+]
+
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
+UNIFIED_SOURCES += [
+    'fetch_msg_types.pb.cc',
+    'Viaduct.cpp',
+    'ViaductRequest.cpp',
+]
+
+EXPORTS.mozilla += [
+    'fetch_msg_types.pb.h',
+    'Viaduct.h',
+    'ViaductRequest.h',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
+DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viaduct/mozIViaduct.idl
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[uuid(f8f11a45-ff04-4d8b-80e0-9ce14824538e), scriptable, builtinclass]
+interface mozIViaduct: nsISupports
+{
+  void EnsureInitialized();
+};
--- a/tools/rewriting/ThirdPartyPaths.txt
+++ b/tools/rewriting/ThirdPartyPaths.txt
@@ -163,15 +163,17 @@ testing/xpcshell/node-ip/
 testing/xpcshell/node-http2/
 third_party/
 toolkit/components/certviewer/content/vendor/
 toolkit/components/jsoncpp/
 toolkit/components/normandy/vendor/
 toolkit/components/protobuf/
 toolkit/components/url-classifier/chromium/
 toolkit/components/utils/mozjexl.js
+toolkit/components/viaduct/fetch_msg_types.pb.cc
+toolkit/components/viaduct/fetch_msg_types.pb.h
 toolkit/crashreporter/breakpad-client/
 toolkit/crashreporter/google-breakpad/
 tools/fuzzing/libfuzzer/
 tools/profiler/core/vtune/
 xpcom/build/mach_override.c
 xpcom/build/mach_override.h
 xpcom/io/crc32c.c