Allow passing extra HTTP headers to remote cache

Fixes GitHub Issue #8245
https://github.com/bazelbuild/bazel/issues/8245

Closes #8680.

PiperOrigin-RevId: 259326512
diff --git a/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreFactory.java b/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreFactory.java
index f7bed3c..b4cbadd 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreFactory.java
@@ -17,6 +17,7 @@
 import com.google.auth.Credentials;
 import com.google.common.base.Ascii;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.remote.blobstore.CombinedDiskHttpBlobStore;
 import com.google.devtools.build.lib.remote.blobstore.ConcurrentMapBlobStore;
 import com.google.devtools.build.lib.remote.blobstore.OnDiskBlobStore;
@@ -88,13 +89,18 @@
               uri,
               options.remoteTimeout,
               options.remoteMaxConnections,
+              ImmutableList.copyOf(options.remoteHeaders),
               creds);
         } else {
           throw new Exception("Remote cache proxy unsupported: " + options.remoteProxy);
         }
       } else {
         return HttpBlobStore.create(
-            uri, options.remoteTimeout, options.remoteMaxConnections, creds);
+            uri,
+            options.remoteTimeout,
+            options.remoteMaxConnections,
+            ImmutableList.copyOf(options.remoteHeaders),
+            creds);
       }
     } catch (Exception e) {
       throw new RuntimeException(e);
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandler.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandler.java
index 67d2d71..6b061cb 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandler.java
@@ -15,6 +15,7 @@
 
 import com.google.auth.Credentials;
 import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
 import com.google.common.io.BaseEncoding;
 import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
 import io.netty.channel.ChannelHandlerContext;
@@ -30,6 +31,7 @@
 import java.nio.channels.ClosedChannelException;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 /** Common functionality shared by concrete classes. */
 abstract class AbstractHttpHandler<T extends HttpObject> extends SimpleChannelInboundHandler<T>
@@ -39,9 +41,12 @@
       "bazel/" + BlazeVersionInfo.instance().getVersion();
 
   private final Credentials credentials;
+  private final ImmutableList<Entry<String, String>> extraHttpHeaders;
 
-  public AbstractHttpHandler(Credentials credentials) {
+  public AbstractHttpHandler(
+      Credentials credentials, ImmutableList<Entry<String, String>> extraHttpHeaders) {
     this.credentials = credentials;
+    this.extraHttpHeaders = extraHttpHeaders;
   }
 
   protected ChannelPromise userPromise;
@@ -54,8 +59,8 @@
     userPromise = null;
   }
 
-  @SuppressWarnings("FutureReturnValueIgnored") 
-  protected void succeedAndResetUserPromise() {  
+  @SuppressWarnings("FutureReturnValueIgnored")
+  protected void succeedAndResetUserPromise() {
     userPromise.setSuccess();
     userPromise = null;
   }
@@ -82,6 +87,12 @@
     }
   }
 
+  protected void addExtraRemoteHeaders(HttpRequest request) {
+    for (Map.Entry<String, String> header : extraHttpHeaders) {
+      request.headers().add(header.getKey(), header.getValue());
+    }
+  }
+
   protected void addUserAgentHeader(HttpRequest request) {
     request.headers().set(HttpHeaderNames.USER_AGENT, USER_AGENT_VALUE);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java
index 09c0979..a68d424 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java
@@ -15,6 +15,7 @@
 
 
 import com.google.auth.Credentials;
+import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 import com.google.devtools.build.lib.remote.blobstore.SimpleBlobStore;
@@ -65,6 +66,7 @@
 import java.net.SocketAddress;
 import java.net.URI;
 import java.util.List;
+import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -107,6 +109,7 @@
   private final ChannelPool channelPool;
   private final URI uri;
   private final int timeoutSeconds;
+  private final ImmutableList<Entry<String, String>> extraHttpHeaders;
   private final boolean useTls;
 
   private final Object closeLock = new Object();
@@ -123,7 +126,11 @@
   private long lastRefreshTime;
 
   public static HttpBlobStore create(
-      URI uri, int timeoutSeconds, int remoteMaxConnections, @Nullable final Credentials creds)
+      URI uri,
+      int timeoutSeconds,
+      int remoteMaxConnections,
+      ImmutableList<Entry<String, String>> extraHttpHeaders,
+      @Nullable final Credentials creds)
       throws Exception {
     return new HttpBlobStore(
         NioEventLoopGroup::new,
@@ -131,6 +138,7 @@
         uri,
         timeoutSeconds,
         remoteMaxConnections,
+        extraHttpHeaders,
         creds,
         null);
   }
@@ -140,6 +148,7 @@
       URI uri,
       int timeoutSeconds,
       int remoteMaxConnections,
+      ImmutableList<Entry<String, String>> extraHttpHeaders,
       @Nullable final Credentials creds)
       throws Exception {
 
@@ -150,6 +159,7 @@
           uri,
           timeoutSeconds,
           remoteMaxConnections,
+          extraHttpHeaders,
           creds,
           domainSocketAddress);
     } else if (Epoll.isAvailable()) {
@@ -159,6 +169,7 @@
           uri,
           timeoutSeconds,
           remoteMaxConnections,
+          extraHttpHeaders,
           creds,
           domainSocketAddress);
     } else {
@@ -172,6 +183,7 @@
       URI uri,
       int timeoutSeconds,
       int remoteMaxConnections,
+      ImmutableList<Entry<String, String>> extraHttpHeaders,
       @Nullable final Credentials creds,
       @Nullable SocketAddress socketAddress)
       throws Exception {
@@ -237,6 +249,7 @@
     }
     this.creds = creds;
     this.timeoutSeconds = timeoutSeconds;
+    this.extraHttpHeaders = extraHttpHeaders;
   }
 
   @SuppressWarnings("FutureReturnValueIgnored")
@@ -271,7 +284,7 @@
                 p.addLast(new HttpRequestEncoder());
                 p.addLast(new ChunkedWriteHandler());
                 synchronized (credentialsLock) {
-                  p.addLast(new HttpUploadHandler(creds));
+                  p.addLast(new HttpUploadHandler(creds, extraHttpHeaders));
                 }
 
                 if (!ch.eventLoop().inEventLoop()) {
@@ -343,7 +356,7 @@
                     new IdleTimeoutHandler(timeoutSeconds, ReadTimeoutException.INSTANCE));
                 p.addLast(new HttpClientCodec());
                 synchronized (credentialsLock) {
-                  p.addLast(new HttpDownloadHandler(creds));
+                  p.addLast(new HttpDownloadHandler(creds, extraHttpHeaders));
                 }
 
                 if (!ch.eventLoop().inEventLoop()) {
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandler.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandler.java
index 44597f1..ffcad78 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandler.java
@@ -16,6 +16,7 @@
 import static com.google.common.base.Preconditions.checkState;
 
 import com.google.auth.Credentials;
+import com.google.common.collect.ImmutableList;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPromise;
@@ -36,6 +37,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map.Entry;
 
 /** ChannelHandler for downloads. */
 final class HttpDownloadHandler extends AbstractHttpHandler<HttpObject> {
@@ -50,8 +52,9 @@
   /** the path header in the http request */
   private String path;
 
-  public HttpDownloadHandler(Credentials credentials) {
-    super(credentials);
+  public HttpDownloadHandler(
+      Credentials credentials, ImmutableList<Entry<String, String>> extraHttpHeaders) {
+    super(credentials, extraHttpHeaders);
   }
 
   @Override
@@ -136,6 +139,7 @@
     path = constructPath(cmd.uri(), cmd.hash(), cmd.casDownload());
     HttpRequest request = buildRequest(path, constructHost(cmd.uri()));
     addCredentialHeaders(request, cmd.uri());
+    addExtraRemoteHeaders(request);
     addUserAgentHeader(request);
     ctx.writeAndFlush(request)
         .addListener(
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandler.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandler.java
index 78f761f..c686b3b 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandler.java
@@ -16,6 +16,7 @@
 import static com.google.common.base.Preconditions.checkState;
 
 import com.google.auth.Credentials;
+import com.google.common.collect.ImmutableList;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPromise;
 import io.netty.handler.codec.http.DefaultHttpRequest;
@@ -32,6 +33,7 @@
 import io.netty.handler.timeout.WriteTimeoutException;
 import io.netty.util.internal.StringUtil;
 import java.io.IOException;
+import java.util.Map.Entry;
 
 /** ChannelHandler for uploads. */
 final class HttpUploadHandler extends AbstractHttpHandler<FullHttpResponse> {
@@ -41,8 +43,9 @@
   /** the size of the data being uploaded in bytes */
   private long contentLength;
 
-  public HttpUploadHandler(Credentials credentials) {
-    super(credentials);
+  public HttpUploadHandler(
+      Credentials credentials, ImmutableList<Entry<String, String>> extraHttpHeaders) {
+    super(credentials, extraHttpHeaders);
   }
 
   @SuppressWarnings("FutureReturnValueIgnored")
@@ -92,6 +95,7 @@
     contentLength = cmd.contentLength();
     HttpRequest request = buildRequest(path, constructHost(cmd.uri()), contentLength);
     addCredentialHeaders(request, cmd.uri());
+    addExtraRemoteHeaders(request);
     addUserAgentHeader(request);
     HttpChunkedInput body = buildBody(cmd);
     ctx.writeAndFlush(request)
diff --git a/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java b/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java
index f414bc3..a7bf4c8 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java
@@ -17,12 +17,15 @@
 import com.google.common.base.Strings;
 import com.google.devtools.build.lib.util.OptionsUtils;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.common.options.Converters;
 import com.google.devtools.common.options.EnumConverter;
 import com.google.devtools.common.options.Option;
 import com.google.devtools.common.options.OptionDocumentationCategory;
 import com.google.devtools.common.options.OptionEffectTag;
 import com.google.devtools.common.options.OptionMetadataTag;
 import com.google.devtools.common.options.OptionsBase;
+import java.util.List;
+import java.util.Map.Entry;
 
 /** Options for remote execution and distributed caching. */
 public final class RemoteOptions extends OptionsBase {
@@ -70,6 +73,20 @@
   public String remoteCache;
 
   @Option(
+      name = "remote_header",
+      converter = Converters.AssignmentConverter.class,
+      defaultValue = "",
+      documentationCategory = OptionDocumentationCategory.REMOTE,
+      effectTags = {OptionEffectTag.UNKNOWN},
+      help =
+          "Specify a HTTP header that will be included in requests: --remote_header=Name=Value. "
+              + "Multiple headers can be passed by specifying the flag multiple times. Multiple "
+              + "values for the same name will be converted to a comma-separated list. This flag"
+              + "is currently only implemented for the HTTP protocol.",
+      allowMultiple = true)
+  public List<Entry<String, String>> remoteHeaders;
+
+  @Option(
       name = "remote_timeout",
       defaultValue = "60",
       documentationCategory = OptionDocumentationCategory.REMOTE,
diff --git a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandlerTest.java b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandlerTest.java
index 542a5f6..67a4349 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandlerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/AbstractHttpHandlerTest.java
@@ -16,12 +16,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
 import io.netty.channel.ChannelPromise;
 import io.netty.channel.embedded.EmbeddedChannel;
 import io.netty.handler.codec.http.HttpHeaderNames;
 import io.netty.handler.codec.http.HttpRequest;
 import java.io.ByteArrayOutputStream;
 import java.net.URI;
+import java.util.Arrays;
+import java.util.Map.Entry;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -34,7 +38,7 @@
   @Test
   public void basicAuthShouldWork() throws Exception {
     URI uri = new URI("http://user:password@does.not.exist/foo");
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     ByteArrayOutputStream out = Mockito.spy(new ByteArrayOutputStream());
     DownloadCommand cmd = new DownloadCommand(uri, true, "abcdef", new ByteArrayOutputStream());
     ChannelPromise writePromise = ch.newPromise();
@@ -48,7 +52,7 @@
   @Test
   public void basicAuthShouldNotEnabled() throws Exception {
     URI uri = new URI("http://does.not.exist/foo");
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     ByteArrayOutputStream out = Mockito.spy(new ByteArrayOutputStream());
     DownloadCommand cmd = new DownloadCommand(uri, true, "abcdef", new ByteArrayOutputStream());
     ChannelPromise writePromise = ch.newPromise();
@@ -61,7 +65,7 @@
   @Test
   public void hostDoesntIncludePortHttp() throws Exception {
     URI uri = new URI("http://does.not.exist/foo");
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     ByteArrayOutputStream out = Mockito.spy(new ByteArrayOutputStream());
     DownloadCommand cmd = new DownloadCommand(uri, true, "abcdef", new ByteArrayOutputStream());
     ChannelPromise writePromise = ch.newPromise();
@@ -74,7 +78,7 @@
   @Test
   public void hostDoesntIncludePortHttps() throws Exception {
     URI uri = new URI("https://does.not.exist/foo");
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     ByteArrayOutputStream out = Mockito.spy(new ByteArrayOutputStream());
     DownloadCommand cmd = new DownloadCommand(uri, true, "abcdef", new ByteArrayOutputStream());
     ChannelPromise writePromise = ch.newPromise();
@@ -87,7 +91,7 @@
   @Test
   public void hostDoesIncludePort() throws Exception {
     URI uri = new URI("http://does.not.exist:8080/foo");
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     ByteArrayOutputStream out = Mockito.spy(new ByteArrayOutputStream());
     DownloadCommand cmd = new DownloadCommand(uri, true, "abcdef", new ByteArrayOutputStream());
     ChannelPromise writePromise = ch.newPromise();
@@ -100,7 +104,8 @@
   @Test
   public void headersDoIncludeUserAgent() throws Exception {
     URI uri = new URI("http://does.not.exist:8080/foo");
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(/* credentials= */ null));
+    EmbeddedChannel ch =
+        new EmbeddedChannel(new HttpDownloadHandler(/* credentials= */ null, ImmutableList.of()));
     DownloadCommand cmd =
         new DownloadCommand(
             uri, /* casDownload= */ true, /* hash= */ "abcdef", new ByteArrayOutputStream());
@@ -110,4 +115,43 @@
     HttpRequest request = ch.readOutbound();
     assertThat(request.headers().get(HttpHeaderNames.USER_AGENT)).isEqualTo("bazel/");
   }
+
+  @Test
+  public void extraHeadersAreIncluded() throws Exception {
+    URI uri = new URI("http://does.not.exist:8080/foo");
+    ImmutableList<Entry<String, String>> remoteHeaders =
+        ImmutableList.of(
+            Maps.immutableEntry("key1", "value1"), Maps.immutableEntry("key2", "value2"));
+
+    EmbeddedChannel ch =
+        new EmbeddedChannel(new HttpDownloadHandler(/* credentials= */ null, remoteHeaders));
+    DownloadCommand cmd =
+        new DownloadCommand(
+            uri, /* casDownload= */ true, /* hash= */ "abcdef", new ByteArrayOutputStream());
+    ChannelPromise writePromise = ch.newPromise();
+    ch.writeOneOutbound(cmd, writePromise);
+
+    HttpRequest request = ch.readOutbound();
+    assertThat(request.headers().get("key1")).isEqualTo("value1");
+    assertThat(request.headers().get("key2")).isEqualTo("value2");
+  }
+
+  @Test
+  public void multipleExtraHeadersAreSupported() throws Exception {
+    URI uri = new URI("http://does.not.exist:8080/foo");
+    ImmutableList<Entry<String, String>> remoteHeaders =
+        ImmutableList.of(
+            Maps.immutableEntry("key", "value1"), Maps.immutableEntry("key", "value2"));
+
+    EmbeddedChannel ch =
+        new EmbeddedChannel(new HttpDownloadHandler(/* credentials= */ null, remoteHeaders));
+    DownloadCommand cmd =
+        new DownloadCommand(
+            uri, /* casDownload= */ true, /* hash= */ "abcdef", new ByteArrayOutputStream());
+    ChannelPromise writePromise = ch.newPromise();
+    ch.writeOneOutbound(cmd, writePromise);
+
+    HttpRequest request = ch.readOutbound();
+    assertThat(request.headers().getAll("key")).isEqualTo(Arrays.asList("value1", "value2"));
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStoreTest.java b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStoreTest.java
index c45548e..370d5ff 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStoreTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStoreTest.java
@@ -29,6 +29,7 @@
 import com.google.api.client.util.Preconditions;
 import com.google.auth.Credentials;
 import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.Channel;
@@ -245,11 +246,17 @@
       DomainSocketAddress domainSocketAddress = (DomainSocketAddress) socketAddress;
       URI uri = new URI("http://localhost");
       return HttpBlobStore.create(
-          domainSocketAddress, uri, timeoutSeconds, /* remoteMaxConnections= */ 0, creds);
+          domainSocketAddress,
+          uri,
+          timeoutSeconds,
+          /* remoteMaxConnections= */ 0,
+          ImmutableList.of(),
+          creds);
     } else if (socketAddress instanceof InetSocketAddress) {
       InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
       URI uri = new URI("http://localhost:" + inetSocketAddress.getPort());
-      return HttpBlobStore.create(uri, timeoutSeconds, /* remoteMaxConnections= */ 0, creds);
+      return HttpBlobStore.create(
+          uri, timeoutSeconds, /* remoteMaxConnections= */ 0, ImmutableList.of(), creds);
     } else {
       throw new IllegalStateException(
           "unsupported socket address class " + socketAddress.getClass());
diff --git a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandlerTest.java b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandlerTest.java
index 4cd3a9ac..74ff960 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandlerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpDownloadHandlerTest.java
@@ -17,6 +17,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.net.HttpHeaders;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufUtil;
@@ -54,7 +55,7 @@
    */
   @Test
   public void downloadShouldWork() throws IOException {
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     downloadShouldWork(true, ch);
     downloadShouldWork(false, ch);
   }
@@ -93,7 +94,7 @@
   /** Test that the handler correctly supports http error codes i.e. 404 (NOT FOUND). */
   @Test
   public void httpErrorsAreSupported() throws IOException {
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     ByteArrayOutputStream out = Mockito.spy(new ByteArrayOutputStream());
     DownloadCommand cmd = new DownloadCommand(CACHE_URI, true, "abcdef", out);
     ChannelPromise writePromise = ch.newPromise();
@@ -123,7 +124,7 @@
    */
   @Test
   public void httpErrorsWithContentAreSupported() throws IOException {
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpDownloadHandler(null, ImmutableList.of()));
     ByteArrayOutputStream out = Mockito.spy(new ByteArrayOutputStream());
     DownloadCommand cmd = new DownloadCommand(CACHE_URI, true, "abcdef", out);
     ChannelPromise writePromise = ch.newPromise();
diff --git a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandlerTest.java b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandlerTest.java
index f3af676..9124faf 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandlerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/blobstore/http/HttpUploadHandlerTest.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.net.HttpHeaders;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
@@ -49,7 +50,7 @@
    */
   @Test
   public void uploadsShouldWork() throws Exception {
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null, ImmutableList.of()));
     HttpResponseStatus[] statuses = new HttpResponseStatus[] {HttpResponseStatus.OK,
         HttpResponseStatus.CREATED, HttpResponseStatus.ACCEPTED, HttpResponseStatus.NO_CONTENT};
 
@@ -86,7 +87,7 @@
   /** Test that the handler correctly supports http error codes i.e. 404 (NOT FOUND). */
   @Test
   public void httpErrorsAreSupported() {
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null, ImmutableList.of()));
     ByteArrayInputStream data = new ByteArrayInputStream(new byte[] {1, 2, 3, 4, 5});
     ChannelPromise writePromise = ch.newPromise();
     ch.writeOneOutbound(new UploadCommand(CACHE_URI, true, "abcdef", data, 5), writePromise);
@@ -115,7 +116,7 @@
    */
   @Test
   public void httpErrorsWithContentAreSupported() {
-    EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null));
+    EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null, ImmutableList.of()));
     ByteArrayInputStream data = new ByteArrayInputStream(new byte[] {1, 2, 3, 4, 5});
     ChannelPromise writePromise = ch.newPromise();
     ch.writeOneOutbound(new UploadCommand(CACHE_URI, true, "abcdef", data, 5), writePromise);