Get Config for a request from the GitilesAccess

Allow for per-repo configuration by reading config values on each
request rather than in servlet constructors. Reading a single config
value is cheap enough, but parsing configs from the on-disk format is
potentially expensive; leave this optimization up to the
GitilesAccess implementation.

Currently, in DefaultAccess, we just pass in the global config, no
per-repo config. This is simple expediency because Config objects
don't trivially support merging multiple configs except by
"inheritance".

Because of the way we were caching per-config maps of extension to
ArchiveFormat, this requires some more substantial refactoring to
that class and ArchiveServlet.

Change-Id: Ia1fcf97ecc71571537402629f49e8a65128a105e
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveFormat.java b/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveFormat.java
index 16fdf50..4ca5d3e 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveFormat.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveFormat.java
@@ -14,7 +14,10 @@
 
 package com.google.gitiles;
 
-import com.google.common.collect.Maps;
+import com.google.common.base.Enums;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 
 import org.eclipse.jgit.api.ArchiveCommand;
 import org.eclipse.jgit.archive.TarFormat;
@@ -22,11 +25,6 @@
 import org.eclipse.jgit.archive.TgzFormat;
 import org.eclipse.jgit.archive.TxzFormat;
 import org.eclipse.jgit.lib.Config;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collections;
-import java.util.Map;
 
 enum ArchiveFormat {
   TGZ("application/x-gzip", new TgzFormat()),
@@ -36,7 +34,16 @@
   // Zip is not supported because it may be interpreted by a Java plugin as a
   // valid JAR file, whose code would have access to cookies on the domain.
 
-  static final Logger log = LoggerFactory.getLogger(ArchiveFormat.class);
+  private static final ImmutableMap<String, ArchiveFormat> BY_EXT;
+  static {
+    ImmutableMap.Builder<String, ArchiveFormat> byExt = ImmutableMap.builder();
+    for (ArchiveFormat format : ArchiveFormat.values()) {
+      for (String ext : format.getSuffixes()) {
+        byExt.put(ext.toLowerCase(), format);
+      }
+    }
+    BY_EXT = byExt.build();
+  }
 
   private final ArchiveCommand.Format<?> format;
   private final String mimeType;
@@ -64,28 +71,34 @@
   }
 
   static ArchiveFormat getDefault(Config cfg) {
-    return byExtension(cfg).values().iterator().next();
+    for (String allowed : cfg.getStringList("archive", null, "format")) {
+      Optional<ArchiveFormat> result =
+          Enums.getIfPresent(ArchiveFormat.class, allowed.toUpperCase());
+      if (result.isPresent()) {
+        return result.get();
+      }
+    }
+    return TGZ;
   }
 
-  static Map<String, ArchiveFormat> byExtension(Config cfg) {
+  static ImmutableSet<String> allExtensions() {
+    return BY_EXT.keySet();
+  }
+
+  static Optional<ArchiveFormat> byExtension(String ext, Config cfg) {
+    ArchiveFormat format = BY_EXT.get(ext);
+    if (format == null) {
+      return Optional.absent();
+    }
     String[] formats = cfg.getStringList("archive", null, "format");
     if (formats.length == 0) {
-      formats = new String[values().length];
-      for (int i = 0; i < values().length; i++) {
-        formats[i] = values()[i].name();
+      return Optional.of(format);
+    }
+    for (String allowed : formats) {
+      if (format.name().equals(allowed.toUpperCase())) {
+        return Optional.of(format);
       }
     }
-    Map<String, ArchiveFormat> exts = Maps.newLinkedHashMap();
-    for (String name : formats) {
-      try {
-        ArchiveFormat format = valueOf(name.toUpperCase());
-        for (String ext : format.getSuffixes()) {
-          exts.put(ext, format);
-        }
-      } catch (IllegalArgumentException e) {
-        log.warn("Invalid archive.format {}", name);
-      }
-    }
-    return Collections.unmodifiableMap(exts);
+    return Optional.absent();
   }
 }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveServlet.java
index defaadb..1fd5009 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveServlet.java
@@ -14,14 +14,16 @@
 
 package com.google.gitiles;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
 import static javax.servlet.http.HttpServletResponse.SC_OK;
 
+import com.google.common.base.Optional;
+
 import org.eclipse.jgit.api.ArchiveCommand;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.http.server.ServletUtils;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
@@ -30,7 +32,6 @@
 import org.eclipse.jgit.treewalk.TreeWalk;
 
 import java.io.IOException;
-import java.util.Map;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -39,11 +40,11 @@
 public class ArchiveServlet extends BaseServlet {
   private static final long serialVersionUID = 1L;
 
-  private final Map<String, ArchiveFormat> byExt;
+  private final GitilesAccess.Factory accessFactory;
 
-  public ArchiveServlet(Config cfg) {
-    super(cfg, null);
-    byExt = ArchiveFormat.byExtension(cfg);
+  public ArchiveServlet(GitilesAccess.Factory accessFactory) {
+    super(null);
+    this.accessFactory = checkNotNull(accessFactory, "accessFactory");
   }
 
   @Override
@@ -59,14 +60,19 @@
       return;
     }
 
-    ArchiveFormat format = byExt.get(view.getExtension());
+    Optional<ArchiveFormat> format = ArchiveFormat.byExtension(
+        view.getExtension(), accessFactory.forRequest(req).getConfig());
+    if (!format.isPresent()) {
+      res.setStatus(SC_NOT_FOUND);
+      return;
+    }
     String filename = getFilename(view, rev, view.getExtension());
-    setDownloadHeaders(res, filename, format.getMimeType());
+    setDownloadHeaders(res, filename, format.get().getMimeType());
     res.setStatus(SC_OK);
 
     try {
       new ArchiveCommand(repo)
-          .setFormat(format.name())
+          .setFormat(format.get().name())
           .setTree(treeId)
           .setOutputStream(res.getOutputStream())
           .call();
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java
index 2969987..3c42386 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java
@@ -14,7 +14,6 @@
 
 package com.google.gitiles;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.gitiles.FormatType.DEFAULT;
 import static com.google.gitiles.FormatType.HTML;
 import static com.google.gitiles.FormatType.JSON;
@@ -30,7 +29,6 @@
 import com.google.gson.FieldNamingPolicy;
 import com.google.gson.GsonBuilder;
 
-import org.eclipse.jgit.lib.Config;
 import org.joda.time.Instant;
 
 import java.io.IOException;
@@ -56,7 +54,7 @@
   }
 
   public static BaseServlet notFoundServlet() {
-    return new BaseServlet(new Config(), null) {
+    return new BaseServlet(null) {
       private static final long serialVersionUID = 1L;
       @Override
       public void service(HttpServletRequest req, HttpServletResponse res) {
@@ -73,6 +71,10 @@
     }
   }
 
+  protected static ArchiveFormat getArchiveFormat(GitilesAccess access) throws IOException {
+    return ArchiveFormat.getDefault(access.getConfig());
+  }
+
   /**
    * Put a value into a request's Soy data map.
    *
@@ -166,11 +168,9 @@
   }
 
   protected final Renderer renderer;
-  protected final ArchiveFormat archiveFormat;
 
-  protected BaseServlet(Config cfg, Renderer renderer) {
+  protected BaseServlet(Renderer renderer) {
     this.renderer = renderer;
-    this.archiveFormat = ArchiveFormat.getDefault(checkNotNull(cfg, "cfg"));
   }
 
   /**
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/BlameServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/BlameServlet.java
index 6892b25..9278747 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/BlameServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/BlameServlet.java
@@ -24,7 +24,6 @@
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.http.server.ServletUtils;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
@@ -48,8 +47,8 @@
 
   private final BlameCache cache;
 
-  public BlameServlet(Config cfg, Renderer renderer, BlameCache cache) {
-    super(cfg, renderer);
+  public BlameServlet(Renderer renderer, BlameCache cache) {
+    super(renderer);
     this.cache = checkNotNull(cache, "cache");
   }
 
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/DefaultAccess.java b/gitiles-servlet/src/main/java/com/google/gitiles/DefaultAccess.java
index a89d6cb..0373375 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/DefaultAccess.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/DefaultAccess.java
@@ -24,6 +24,7 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.http.server.ServletUtils;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
@@ -64,12 +65,14 @@
     private final File basePath;
     private final String canonicalBasePath;
     private final String baseGitUrl;
+    private final Config baseConfig;
     private final FileResolver<HttpServletRequest> resolver;
 
-    Factory(File basePath, String baseGitUrl, FileResolver<HttpServletRequest> resolver)
-        throws IOException {
+    Factory(File basePath, String baseGitUrl, Config baseConfig,
+        FileResolver<HttpServletRequest> resolver) throws IOException {
       this.basePath = checkNotNull(basePath, "basePath");
       this.baseGitUrl = checkNotNull(baseGitUrl, "baseGitUrl");
+      this.baseConfig = checkNotNull(baseConfig, "baseConfig");
       this.resolver = checkNotNull(resolver, "resolver");
       this.canonicalBasePath = basePath.getCanonicalPath();
     }
@@ -81,21 +84,23 @@
 
     protected DefaultAccess newAccess(File basePath, String canonicalBasePath, String baseGitUrl,
         FileResolver<HttpServletRequest> resolver, HttpServletRequest req) {
-      return new DefaultAccess(basePath, canonicalBasePath, baseGitUrl, resolver, req);
+      return new DefaultAccess(basePath, canonicalBasePath, baseGitUrl, baseConfig, resolver, req);
     }
   }
 
   protected final File basePath;
   protected final String canonicalBasePath;
   protected final String baseGitUrl;
+  protected final Config baseConfig;
   protected final FileResolver<HttpServletRequest> resolver;
   protected final HttpServletRequest req;
 
   protected DefaultAccess(File basePath, String canonicalBasePath, String baseGitUrl,
-      FileResolver<HttpServletRequest> resolver, HttpServletRequest req) {
+      Config baseConfig, FileResolver<HttpServletRequest> resolver, HttpServletRequest req) {
     this.basePath = checkNotNull(basePath, "basePath");
     this.canonicalBasePath = checkNotNull(canonicalBasePath, "canonicalBasePath");
     this.baseGitUrl = checkNotNull(baseGitUrl, "baseGitUrl");
+    this.baseConfig = checkNotNull(baseConfig, "baseConfig");
     this.resolver = checkNotNull(resolver, "resolver");
     this.req = checkNotNull(req, "req");
   }
@@ -128,6 +133,11 @@
     return buildDescription(ServletUtils.getRepository(req), Collections.<String> emptySet());
   }
 
+  @Override
+  public Config getConfig() {
+    return baseConfig;
+  }
+
   private String getRepositoryName(Repository repo) {
     String path = getRelativePath(repo);
     if (repo.isBare() && path.endsWith(".git")) {
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/DescribeServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/DescribeServlet.java
index 96e3cf9..7e9afb6 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/DescribeServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/DescribeServlet.java
@@ -27,7 +27,6 @@
 import org.eclipse.jgit.errors.AmbiguousObjectException;
 import org.eclipse.jgit.errors.RevisionSyntaxException;
 import org.eclipse.jgit.http.server.ServletUtils;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
@@ -54,8 +53,8 @@
         && (values.get(0).equals("") || values.get(0).equals("1"));
   }
 
-  protected DescribeServlet(Config cfg) {
-    super(cfg, null);
+  protected DescribeServlet() {
+    super(null);
   }
 
   @Override
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/DiffServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/DiffServlet.java
index 02d81af..04e8391 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/DiffServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/DiffServlet.java
@@ -23,7 +23,6 @@
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.http.server.ServletUtils;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -48,10 +47,13 @@
   private static final long serialVersionUID = 1L;
   private static final String PLACEHOLDER = "id=\"DIFF_OUTPUT_BLOCK\"";
 
+  private final GitilesAccess.Factory accessFactory;
   private final Linkifier linkifier;
 
-  public DiffServlet(Config cfg, Renderer renderer, Linkifier linkifier) {
-    super(cfg, renderer);
+  public DiffServlet(GitilesAccess.Factory accessFactory, Renderer renderer,
+      Linkifier linkifier) {
+    super(renderer);
+    this.accessFactory = checkNotNull(accessFactory, "accessFactory");
     this.linkifier = checkNotNull(linkifier, "linkifier");
   }
 
@@ -85,7 +87,7 @@
         GitDateFormatter df = new GitDateFormatter(Format.DEFAULT);
         data.put("commit", new CommitSoyData()
             .setLinkifier(linkifier)
-            .setArchiveFormat(archiveFormat)
+            .setArchiveFormat(getArchiveFormat(accessFactory.forRequest(req)))
             .toSoyData(req, walk.parseCommit(view.getRevision().getId()), df));
       }
       if (!data.containsKey("repositoryName") && (view.getRepositoryName() != null)) {
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesAccess.java b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesAccess.java
index 648d709..978baa3 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesAccess.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesAccess.java
@@ -14,6 +14,7 @@
 
 package com.google.gitiles;
 
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 
@@ -65,4 +66,10 @@
    *         the repository.
    */
   public RepositoryDescription getRepositoryDescription() throws IOException;
+
+  /**
+   * @return configuration to apply to the host/repository for this request.
+   * @throws IOException an error occurred reading the configuration.
+   */
+  public Config getConfig() throws IOException;
 }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java
index 1bdcc0b..048d844 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java
@@ -230,25 +230,25 @@
     checkNotInitialized();
     switch (view) {
       case HOST_INDEX:
-        return new HostIndexServlet(config, renderer, urls, accessFactory);
+        return new HostIndexServlet(renderer, urls, accessFactory);
       case REPOSITORY_INDEX:
-        return new RepositoryIndexServlet(config, renderer, accessFactory, timeCache);
+        return new RepositoryIndexServlet(renderer, accessFactory, timeCache);
       case REFS:
-        return new RefServlet(config, renderer, timeCache);
+        return new RefServlet(renderer, timeCache);
       case REVISION:
-        return new RevisionServlet(config, renderer, linkifier());
+        return new RevisionServlet(accessFactory, renderer, linkifier());
       case PATH:
-        return new PathServlet(config, renderer, urls);
+        return new PathServlet(accessFactory, renderer, urls);
       case DIFF:
-        return new DiffServlet(config, renderer, linkifier());
+        return new DiffServlet(accessFactory, renderer, linkifier());
       case LOG:
-        return new LogServlet(config, renderer, linkifier());
+        return new LogServlet(renderer, linkifier());
       case DESCRIBE:
-        return new DescribeServlet(config);
+        return new DescribeServlet();
       case ARCHIVE:
-        return new ArchiveServlet(config);
+        return new ArchiveServlet(accessFactory);
       case BLAME:
-        return new BlameServlet(config, renderer, blameCache);
+        return new BlameServlet(renderer, blameCache);
       default:
         throw new IllegalArgumentException("Invalid view type: " + view);
     }
@@ -363,6 +363,7 @@
         accessFactory = new DefaultAccess.Factory(
             new File(basePath),
             getBaseGitUrl(config),
+            config,
             fileResolver);
         } catch (IOException e) {
           throw new ServletException(e);
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/HostIndexServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/HostIndexServlet.java
index e713c7b..db563ff 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/HostIndexServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/HostIndexServlet.java
@@ -28,7 +28,6 @@
 import com.google.template.soy.data.SoyMapData;
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.transport.ServiceMayNotContinueException;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
@@ -52,9 +51,9 @@
   protected final GitilesUrls urls;
   private final GitilesAccess.Factory accessFactory;
 
-  public HostIndexServlet(Config cfg, Renderer renderer, GitilesUrls urls,
+  public HostIndexServlet(Renderer renderer, GitilesUrls urls,
       GitilesAccess.Factory accessFactory) {
-    super(cfg, renderer);
+    super(renderer);
     this.urls = checkNotNull(urls, "urls");
     this.accessFactory = checkNotNull(accessFactory, "accessFactory");
   }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/LogServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/LogServlet.java
index 86bd5a7..aa6bd34 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/LogServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/LogServlet.java
@@ -32,7 +32,6 @@
 import org.eclipse.jgit.errors.RevWalkException;
 import org.eclipse.jgit.http.server.ServletUtils;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
@@ -70,8 +69,8 @@
 
   private final Linkifier linkifier;
 
-  public LogServlet(Config cfg, Renderer renderer, Linkifier linkifier) {
-    super(cfg, renderer);
+  public LogServlet(Renderer renderer, Linkifier linkifier) {
+    super(renderer);
     this.linkifier = checkNotNull(linkifier, "linkifier");
   }
 
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java
index 0dbf793..8723722 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java
@@ -35,7 +35,6 @@
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StopWalkException;
 import org.eclipse.jgit.http.server.ServletUtils;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
@@ -105,10 +104,12 @@
     }
   }
 
+  private final GitilesAccess.Factory accessFactory;
   private final GitilesUrls urls;
 
-  public PathServlet(Config cfg, Renderer renderer, GitilesUrls urls) {
-    super(cfg, renderer);
+  public PathServlet(GitilesAccess.Factory accessFactory, Renderer renderer, GitilesUrls urls) {
+    super(renderer);
+    this.accessFactory = checkNotNull(accessFactory, "accessFactory");
     this.urls = checkNotNull(urls, "urls");
   }
 
@@ -294,7 +295,7 @@
         "breadcrumbs", view.getBreadcrumbs(hasSingleTree),
         "type", FileType.TREE.toString(),
         "data", new TreeSoyData(rw, view)
-            .setArchiveFormat(archiveFormat)
+            .setArchiveFormat(getArchiveFormat(accessFactory.forRequest(req)))
             .toSoyData(id, tw)));
   }
 
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java
index e499266..8d259a8 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java
@@ -26,7 +26,6 @@
 
 import org.eclipse.jgit.http.server.ServletUtils;
 import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefComparator;
@@ -50,8 +49,8 @@
 
   private final TimeCache timeCache;
 
-  protected RefServlet(Config cfg, Renderer renderer, TimeCache timeCache) {
-    super(cfg, renderer);
+  protected RefServlet(Renderer renderer, TimeCache timeCache) {
+    super(renderer);
     this.timeCache = checkNotNull(timeCache, "timeCache");
   }
 
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
index 3fa73b6..ec67621 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
@@ -22,7 +22,6 @@
 import com.google.common.collect.Maps;
 
 import org.eclipse.jgit.http.server.ServletUtils;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
@@ -49,9 +48,9 @@
   private final GitilesAccess.Factory accessFactory;
   private final TimeCache timeCache;
 
-  public RepositoryIndexServlet(Config cfg, Renderer renderer, GitilesAccess.Factory accessFactory,
+  public RepositoryIndexServlet(Renderer renderer, GitilesAccess.Factory accessFactory,
       TimeCache timeCache) {
-    super(cfg, renderer);
+    super(renderer);
     this.accessFactory = checkNotNull(accessFactory, "accessFactory");
     this.timeCache = checkNotNull(timeCache, "timeCache");
   }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java
index 1e0b5b2..b433432 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java
@@ -28,7 +28,6 @@
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.http.server.ServletUtils;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
@@ -53,10 +52,13 @@
   private static final long serialVersionUID = 1L;
   private static final Logger log = LoggerFactory.getLogger(RevisionServlet.class);
 
+  private final GitilesAccess.Factory accessFactory;
   private final Linkifier linkifier;
 
-  public RevisionServlet(Config cfg, Renderer renderer, Linkifier linkifier) {
-    super(cfg, renderer);
+  public RevisionServlet(GitilesAccess.Factory accessFactory, Renderer renderer,
+      Linkifier linkifier) {
+    super(renderer);
+    this.accessFactory = accessFactory;
     this.linkifier = checkNotNull(linkifier, "linkifier");
   }
 
@@ -82,7 +84,7 @@
                   "data", new CommitSoyData()
                       .setLinkifier(linkifier)
                       .setRevWalk(walk)
-                      .setArchiveFormat(archiveFormat)
+                      .setArchiveFormat(getArchiveFormat(accessFactory.forRequest(req)))
                       .toSoyData(req, (RevCommit) obj, KeySet.DETAIL_DIFF_TREE, df)));
               break;
             case OBJ_TREE:
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java b/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java
index aece87b..deea6ae 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java
@@ -20,7 +20,6 @@
 import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
 
 import com.google.common.base.Strings;
-import com.google.common.collect.Sets;
 
 import org.eclipse.jgit.http.server.ServletUtils;
 import org.eclipse.jgit.http.server.glue.WrappedRequest;
@@ -30,7 +29,6 @@
 
 import java.io.IOException;
 import java.util.Map;
-import java.util.Set;
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
@@ -85,14 +83,12 @@
   private final GitilesUrls urls;
   private final GitilesAccess.Factory accessFactory;
   private final VisibilityCache visibilityCache;
-  private final Set<String> archiveExts;
 
   public ViewFilter(Config cfg, GitilesAccess.Factory accessFactory,
       GitilesUrls urls, VisibilityCache visibilityCache) {
     this.urls = checkNotNull(urls, "urls");
     this.accessFactory = checkNotNull(accessFactory, "accessFactory");
     this.visibilityCache = checkNotNull(visibilityCache, "visibilityCache");
-    this.archiveExts = Sets.newHashSet(ArchiveFormat.byExtension(cfg).keySet());
   }
 
   @Override
@@ -162,7 +158,7 @@
   private GitilesView.Builder parseArchiveCommand(
       HttpServletRequest req, String repoName, String path) throws IOException {
     String ext = null;
-    for (String e : archiveExts) {
+    for (String e : ArchiveFormat.allExtensions()) {
       if (path.endsWith(e)) {
         path = path.substring(0, path.length() - e.length());
         ext = e;
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/RepositoryIndexServletTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/RepositoryIndexServletTest.java
index 876390b..1273a36 100644
--- a/gitiles-servlet/src/test/java/com/google/gitiles/RepositoryIndexServletTest.java
+++ b/gitiles-servlet/src/test/java/com/google/gitiles/RepositoryIndexServletTest.java
@@ -25,7 +25,6 @@
 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.Config;
 
 import java.io.IOException;
 import java.util.Map;
@@ -42,7 +41,6 @@
     repo = new TestRepository<DfsRepository>(
         new InMemoryRepository(new DfsRepositoryDescription("test")));
     servlet = new RepositoryIndexServlet(
-        new Config(),
         new DefaultRenderer(),
         new TestGitilesAccess(repo.getRepository()),
         new TimeCache());
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java
index a511d40..50877ba 100644
--- a/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java
+++ b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java
@@ -16,15 +16,16 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import com.google.common.collect.ImmutableMap;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
+import org.eclipse.jgit.lib.Config;
+
 import java.util.Map;
 import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
-import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
-
-import com.google.common.collect.ImmutableMap;
-
 /** Gitiles access for testing. */
 public class TestGitilesAccess implements GitilesAccess.Factory {
   private final DfsRepository repo;
@@ -65,6 +66,11 @@
         d.cloneUrl = TestGitilesUrls.URLS.getBaseGitUrl(req) + "/" + d.name;
         return d;
       }
+
+      @Override
+      public Config getConfig() {
+        return new Config();
+      }
     };
   }
 }