Add tests for PathServlet text rendering
Fix a bug where we were setting the content type to text/plain even
though we meant not to.
Change-Id: I440b57aac035a4986846d2992d8089cf72557624
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 cf45af7..7990267 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/BaseServlet.java
@@ -23,6 +23,7 @@
import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.common.base.Charsets;
+import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.HttpHeaders;
@@ -234,6 +235,19 @@
}
/**
+ * @see #startRenderText(HttpServletRequest, HttpServletResponse)
+ * @param req in-progress request.
+ * @param res in-progress response.
+ * @param contentType contentType to set.
+ * @return the response's writer.
+ */
+ protected PrintWriter startRenderText(HttpServletRequest req, HttpServletResponse res,
+ String contentType) throws IOException {
+ setApiHeaders(res, contentType);
+ return res.getWriter();
+ }
+
+ /**
* Prepare the response to render plain text.
* <p>
* Unlike
@@ -287,14 +301,20 @@
setNotCacheable(res);
}
- protected void setApiHeaders(HttpServletResponse res, FormatType type) {
- res.setContentType(type.getMimeType());
+ protected void setApiHeaders(HttpServletResponse res, String contentType) {
+ if (!Strings.isNullOrEmpty(contentType)) {
+ res.setContentType(contentType);
+ }
res.setCharacterEncoding(Charsets.UTF_8.name());
res.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment");
res.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
setCacheHeaders(res);
}
+ protected void setApiHeaders(HttpServletResponse res, FormatType type) {
+ setApiHeaders(res, type.getMimeType());
+ }
+
protected void setDownloadHeaders(HttpServletResponse res, String filename, String contentType) {
res.setContentType(contentType);
res.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
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 d946f2b..e48ea15 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java
@@ -55,6 +55,7 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@@ -68,6 +69,8 @@
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(PathServlet.class);
+ static final String MODE_HEADER = "X-Gitiles-Path-Mode";
+
/**
* Submodule URLs where we know there is a web page if the user visits the
* repository URL verbatim in a web browser.
@@ -204,8 +207,9 @@
// under the assumption that any hint we can give to a browser that
// this is base64 data might cause it to try to decode it and render
// as HTML, which would be bad.
- res.setHeader("X-Gitiles-Path-Mode", String.format("%06o", tw.getRawMode(0)));
- try (OutputStream out = BaseEncoding.base64().encodingStream(startRenderText(req, res))) {
+ PrintWriter writer = startRenderText(req, res, null);
+ res.setHeader(MODE_HEADER, String.format("%06o", tw.getRawMode(0)));
+ try (OutputStream out = BaseEncoding.base64().encodingStream(writer)) {
rw.getObjectReader().open(tw.getObjectId(0)).copyTo(out);
}
break;
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java
index 6fc0bc4..a6d29b6 100644
--- a/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java
+++ b/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java
@@ -14,9 +14,13 @@
package com.google.gitiles;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import com.google.common.collect.ImmutableList;
+import com.google.common.io.BaseEncoding;
+import com.google.common.net.HttpHeaders;
import com.google.template.soy.data.SoyListData;
import com.google.template.soy.data.restricted.StringData;
@@ -153,6 +157,52 @@
assertEquals("https://gerrit.googlesource.com/gitiles", linkData.get("httpUrl"));
}
+ @Test
+ public void blobText() throws Exception {
+ repo.branch("master").commit().add("foo", "contents").create();
+ String text = buildText("/repo/+/master/foo?format=TEXT", "100644");
+ assertEquals("contents", new String(BaseEncoding.base64().decode(text), UTF_8));
+ }
+
+ @Test
+ public void symlinkText() throws Exception {
+ final RevBlob link = repo.blob("foo");
+ repo.branch("master").commit()
+ .edit(new PathEdit("baz") {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.SYMLINK);
+ ent.setObjectId(link);
+ }
+ }).create();
+ String text = buildText("/repo/+/master/baz?format=TEXT", "120000");
+ assertEquals("foo", new String(BaseEncoding.base64().decode(text), UTF_8));
+ }
+
+ @Test
+ public void nonBlobText() throws Exception {
+ String gitmodules = "[submodule \"gitiles\"]\n"
+ + " path = gitiles\n"
+ + " url = https://gerrit.googlesource.com/gitiles\n";
+ final String gitilesSha = "2b2f34bba3c2be7e2506ce6b1f040949da350cf9";
+ repo.branch("master").commit()
+ .add("foo/bar", "contents")
+ .add(".gitmodules", gitmodules)
+ .edit(new PathEdit("gitiles") {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.GITLINK);
+ ent.setObjectId(ObjectId.fromString(gitilesSha));
+ }
+ }).create();
+
+ assertNotFound("/repo/+/master/nonexistent?format=TEXT");
+ assertNotFound("/repo/+/master/?format=TEXT");
+ assertNotFound("/repo/+/master/foo?format=TEXT");
+ assertNotFound("/repo/+/master/foo/?format=TEXT");
+ assertNotFound("/repo/+/master/gitiles?format=TEXT");
+ }
+
private Map<String, ?> getBlobData(Map<String, ?> data) {
return ((Map<String, Map<String, ?>>) data).get("data");
}
@@ -169,6 +219,17 @@
return res;
}
+ private void assertNotFound(String pathAndQuery) throws Exception {
+ assertEquals(404, service(pathAndQuery).getResponse().getStatus());
+ }
+
+ private String buildText(String pathAndQuery, String expectedMode) throws Exception {
+ TestViewFilter.Result res = service(pathAndQuery);
+ assertNull(res.getResponse().getHeader(HttpHeaders.CONTENT_TYPE));
+ assertEquals(expectedMode, res.getResponse().getHeader(PathServlet.MODE_HEADER));
+ return res.getResponse().getActualBodyString();
+ }
+
private Map<String, ?> buildData(String pathAndQuery) throws Exception {
// Render the page through Soy to ensure templates are valid, then return
// the Soy data for introspection.