Wrap possible HTML plaintext in JSON

If the HTML appears like MSIE might guess it is HTML (such as if it
contains <) encode the response as a JSON object instead of as a
simple plain text string. This won't show up very often for clients,
and protects MSIE users stuck on ancient versions (pre MSIE 8).

Change-Id: I1fc32fda4093b6ad2f8f492f3457db5adaa906b4
This commit is contained in:
Shawn O. Pearce
2012-11-26 21:00:22 -08:00
parent 327048b612
commit f74b5d0ef9
2 changed files with 86 additions and 41 deletions

View File

@@ -28,6 +28,7 @@ import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
@@ -63,6 +64,7 @@ import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
@@ -93,6 +95,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
@@ -388,9 +391,10 @@ public class RestApiServlet extends HttpServlet {
return c.newInstance();
}
private static void replyJson(HttpServletRequest req,
private static void replyJson(@Nullable HttpServletRequest req,
HttpServletResponse res,
Multimap<String, String> config, Object result)
Multimap<String, String> config,
Object result)
throws IOException {
final TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE);
buf.write(JSON_MAGIC);
@@ -421,7 +425,7 @@ public class RestApiServlet extends HttpServlet {
FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES;
private static Gson newGson(Multimap<String, String> config,
HttpServletRequest req) {
@Nullable HttpServletRequest req) {
GsonBuilder gb = OutputFormat.JSON_COMPACT.newGsonBuilder()
.setFieldNamingPolicy(NAMING);
@@ -432,11 +436,12 @@ public class RestApiServlet extends HttpServlet {
}
private static void enablePrettyPrint(GsonBuilder gb,
Multimap<String, String> config, HttpServletRequest req) {
Multimap<String, String> config,
@Nullable HttpServletRequest req) {
String pp = Iterables.getFirst(config.get("pp"), null);
if (pp == null) {
pp = Iterables.getFirst(config.get("prettyPrint"), null);
if (pp == null) {
if (pp == null && req != null) {
pp = acceptsJson(req) ? "0" : "1";
}
}
@@ -484,8 +489,10 @@ public class RestApiServlet extends HttpServlet {
}
}
private static void replyBinaryResult(HttpServletRequest req,
HttpServletResponse res, BinaryResult bin) throws IOException {
private static void replyBinaryResult(
@Nullable HttpServletRequest req,
HttpServletResponse res,
BinaryResult bin) throws IOException {
try {
res.setContentType(bin.getContentType());
OutputStream dst = res.getOutputStream();
@@ -651,11 +658,22 @@ public class RestApiServlet extends HttpServlet {
static void replyText(@Nullable HttpServletRequest req,
HttpServletResponse res, String text) throws IOException {
if (!text.endsWith("\n")) {
text += "\n";
if (isMaybeHTML(text)) {
JsonObject obj = new JsonObject();
obj.addProperty("message", text);
replyJson(req, res, ImmutableMultimap.of("pp", "0"), obj);
} else {
if (!text.endsWith("\n")) {
text += "\n";
}
replyBinaryResult(req, res,
BinaryResult.create(text).setContentType("text/plain"));
}
replyBinaryResult(req, res,
BinaryResult.create(text).setContentType("text/plain"));
}
private static final Pattern IS_HTML = Pattern.compile("[<&]");
private static boolean isMaybeHTML(String text) {
return IS_HTML.matcher(text).find();
}
private static boolean acceptsJson(HttpServletRequest req) {