Cache URLs containing SHA hashes for 2 hours

+ The git content of these urls will never change, and when navigating
  through a tree or traversing blame history, the cost of each request
  adds up.
+ Add a stale-while-revalidate[1] of 1 week.

[1] https://www.mnot.net/blog/2014/06/01/chrome_and_stale-while-revalidate

Change-Id: I3af9782036aa238dcae7b543935d16c46212dd61
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 402674c..3f03455 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/ArchiveServlet.java
@@ -61,7 +61,7 @@
       return;
     }
     String filename = getFilename(view, rev, view.getExtension());
-    setDownloadHeaders(res, filename, format.get().getMimeType());
+    setDownloadHeaders(req, res, filename, format.get().getMimeType());
     res.setStatus(SC_OK);
 
     try {
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 6160a51..e2751e8 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java
@@ -231,7 +231,7 @@
       HttpServletRequest req, HttpServletResponse res, Map<String, ?> soyData) throws IOException {
     res.setContentType(FormatType.HTML.getMimeType());
     res.setCharacterEncoding(UTF_8.name());
-    setCacheHeaders(res);
+    setCacheHeaders(req, res);
 
     Map<String, Object> allData = getData(req);
 
@@ -323,7 +323,7 @@
       throws IOException {
     res.setStatus(statusCode);
     setApiHeaders(req, res, TEXT);
-    setCacheHeaders(res);
+    setCacheHeaders(req, res);
     try (Writer out = newWriter(req, res)) {
       out.write(message);
     }
@@ -338,7 +338,22 @@
     return access;
   }
 
-  protected void setCacheHeaders(HttpServletResponse res) {
+  protected void setCacheHeaders(HttpServletRequest req, HttpServletResponse res) {
+    if (Strings.nullToEmpty(req.getHeader(HttpHeaders.PRAGMA)).equalsIgnoreCase("no-cache") ||
+        Strings.nullToEmpty(req.getHeader(HttpHeaders.CACHE_CONTROL))
+            .equalsIgnoreCase("no-cache")) {
+      setNotCacheable(res);
+      return;
+    }
+
+    GitilesView view = ViewFilter.getView(req);
+    Revision rev = view.getRevision();
+    if (rev.nameIsId()) {
+      res.setHeader(HttpHeaders.CACHE_CONTROL,
+          "private, max-age=7200, stale-while-revalidate=604800");
+      return;
+    }
+
     setNotCacheable(res);
   }
 
@@ -366,7 +381,7 @@
     } else {
       res.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
     }
-    setCacheHeaders(res);
+    setCacheHeaders(req, res);
   }
 
   protected void setApiHeaders(HttpServletRequest req, HttpServletResponse res, FormatType type)
@@ -374,10 +389,11 @@
     setApiHeaders(req, res, type.getMimeType());
   }
 
-  protected void setDownloadHeaders(HttpServletResponse res, String filename, String contentType) {
+  protected void setDownloadHeaders(HttpServletRequest req, HttpServletResponse res,
+      String filename, String contentType) {
     res.setContentType(contentType);
     res.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
-    setCacheHeaders(res);
+    setCacheHeaders(req, res);
   }
 
   protected static Writer newWriter(OutputStream os, HttpServletResponse res) throws IOException {
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 273b0be..657edf8 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/DiffServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/DiffServlet.java
@@ -105,7 +105,7 @@
         data.put("breadcrumbs", view.getBreadcrumbs());
       }
 
-      setCacheHeaders(res);
+      setCacheHeaders(req, res);
       try (OutputStream out = startRenderStreamingHtml(req, res, "gitiles.diffDetail", data);
           DiffFormatter diff = new HtmlDiffFormatter(renderer, view, out)) {
         formatDiff(repo, oldTree, newTree, view.getPathPart(), diff);
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
index a55084f..0f83cce 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
@@ -155,14 +155,6 @@
     return h.hash().toString();
   }
 
-  @Override
-  protected void setCacheHeaders(HttpServletResponse res) {
-    long now = System.currentTimeMillis();
-    res.setDateHeader(HttpHeaders.EXPIRES, now);
-    res.setDateHeader(HttpHeaders.DATE, now);
-    res.setHeader(HttpHeaders.CACHE_CONTROL, "private, max-age=0, must-revalidate");
-  }
-
   private void showDoc(
       HttpServletRequest req,
       HttpServletResponse res,
@@ -196,7 +188,7 @@
     byte[] raw = page.getBytes(UTF_8);
     res.setContentType(FormatType.HTML.getMimeType());
     res.setCharacterEncoding(UTF_8.name());
-    setCacheHeaders(res);
+    setCacheHeaders(req, res);
     if (acceptsGzipEncoding(req)) {
       res.addHeader(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
       res.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip");