Cleanup handling of BinaryResult
Simplify the logic to stack BinaryResult instances on top of each other when applying the base64 and/or gzip transforms before writing to the HttpServletResponse. Change-Id: I1a477d5b9888cac981021149905a9aaa8f2f89ad
This commit is contained in:
		| @@ -535,7 +535,7 @@ public class RestApiServlet extends HttpServlet { | |||||||
|       Multimap<String, String> config, |       Multimap<String, String> config, | ||||||
|       Object result) |       Object result) | ||||||
|       throws IOException { |       throws IOException { | ||||||
|     final TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE); |     TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE); | ||||||
|     buf.write(JSON_MAGIC); |     buf.write(JSON_MAGIC); | ||||||
|     Writer w = new BufferedWriter(new OutputStreamWriter(buf, UTF_8)); |     Writer w = new BufferedWriter(new OutputStreamWriter(buf, UTF_8)); | ||||||
|     Gson gson = newGson(config, req); |     Gson gson = newGson(config, req); | ||||||
| @@ -546,18 +546,9 @@ public class RestApiServlet extends HttpServlet { | |||||||
|     } |     } | ||||||
|     w.write('\n'); |     w.write('\n'); | ||||||
|     w.flush(); |     w.flush(); | ||||||
|  |     replyBinaryResult(req, res, asBinaryResult(buf) | ||||||
|     replyBinaryResult(req, res, new BinaryResult() { |       .setContentType(JSON_TYPE) | ||||||
|       @Override |       .setCharacterEncoding(UTF_8.name())); | ||||||
|       public long getContentLength() { |  | ||||||
|         return buf.length(); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       @Override |  | ||||||
|       public void writeTo(OutputStream os) throws IOException { |  | ||||||
|         buf.writeTo(os, null); |  | ||||||
|       } |  | ||||||
|     }.setContentType(JSON_TYPE).setCharacterEncoding(UTF_8.name())); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private static Gson newGson(Multimap<String, String> config, |   private static Gson newGson(Multimap<String, String> config, | ||||||
| @@ -627,68 +618,78 @@ public class RestApiServlet extends HttpServlet { | |||||||
|       @Nullable HttpServletRequest req, |       @Nullable HttpServletRequest req, | ||||||
|       HttpServletResponse res, |       HttpServletResponse res, | ||||||
|       BinaryResult bin) throws IOException { |       BinaryResult bin) throws IOException { | ||||||
|  |     final BinaryResult appResult = bin; | ||||||
|     try { |     try { | ||||||
|  |       if (bin.isBase64()) { | ||||||
|  |         bin = stackBase64(res, bin); | ||||||
|  |       } | ||||||
|  |       if (bin.canGzip() && acceptsGzip(req)) { | ||||||
|  |         bin = stackGzip(res, bin); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       res.setContentType(bin.getContentType()); | ||||||
|  |       long len = bin.getContentLength(); | ||||||
|  |       if (0 <= len && len < Integer.MAX_VALUE) { | ||||||
|  |         res.setContentLength((int) len); | ||||||
|  |       } else if (0 <= len) { | ||||||
|  |         res.setHeader("Content-Length", Long.toString(len)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|       OutputStream dst = res.getOutputStream(); |       OutputStream dst = res.getOutputStream(); | ||||||
|       try { |       try { | ||||||
|         long len = bin.getContentLength(); |         bin.writeTo(dst); | ||||||
|         if (bin.isBase64() && 0 <= len && len <= (10 << 20)) { |  | ||||||
|           final TemporaryBuffer.Heap buf = base64(bin); |  | ||||||
|           len = buf.length(); |  | ||||||
|           base64(res, bin); |  | ||||||
|           bin = new BinaryResult() { |  | ||||||
|             @Override |  | ||||||
|             public void writeTo(OutputStream os) throws IOException { |  | ||||||
|               buf.writeTo(os, null); |  | ||||||
|             } |  | ||||||
|           }.setContentLength(len); |  | ||||||
|         } else if (bin.isBase64()) { |  | ||||||
|           len = -1; |  | ||||||
|           base64(res, bin); |  | ||||||
|           dst = BaseEncoding.base64().encodingStream( |  | ||||||
|               new OutputStreamWriter(dst, Charsets.ISO_8859_1)); |  | ||||||
|         } else { |  | ||||||
|           res.setContentType(bin.getContentType()); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         boolean gzip = bin.canGzip() && acceptsGzip(req); |  | ||||||
|         if (gzip && 256 <= len && len <= (10 << 20)) { |  | ||||||
|           TemporaryBuffer.Heap buf = compress(bin); |  | ||||||
|           if (buf.length() < len) { |  | ||||||
|             res.setContentLength((int) buf.length()); |  | ||||||
|             res.setHeader("Content-Encoding", "gzip"); |  | ||||||
|             buf.writeTo(dst, null); |  | ||||||
|           } else { |  | ||||||
|             replyUncompressed(res, dst, bin, len); |  | ||||||
|           } |  | ||||||
|         } else if (gzip) { |  | ||||||
|           res.setHeader("Content-Encoding", "gzip"); |  | ||||||
|           dst = new GZIPOutputStream(dst); |  | ||||||
|           bin.writeTo(dst); |  | ||||||
|         } else { |  | ||||||
|           replyUncompressed(res, dst, bin, len); |  | ||||||
|         } |  | ||||||
|       } finally { |       } finally { | ||||||
|         dst.close(); |         dst.close(); | ||||||
|       } |       } | ||||||
|     } finally { |     } finally { | ||||||
|       bin.close(); |       appResult.close(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private static void base64(HttpServletResponse res, BinaryResult bin) { |   private static BinaryResult stackBase64(HttpServletResponse res, | ||||||
|     res.setContentType("text/plain; charset=ISO-8859-1"); |       final BinaryResult src) throws IOException { | ||||||
|  |     BinaryResult b64; | ||||||
|  |     long len = src.getContentLength(); | ||||||
|  |     if (0 <= len && len <= (7 << 20)) { | ||||||
|  |       b64 = base64(src); | ||||||
|  |     } else { | ||||||
|  |       b64 = new BinaryResult() { | ||||||
|  |         @Override | ||||||
|  |         public void writeTo(OutputStream out) throws IOException { | ||||||
|  |           OutputStream e = BaseEncoding.base64().encodingStream( | ||||||
|  |               new OutputStreamWriter(out, Charsets.ISO_8859_1)); | ||||||
|  |           src.writeTo(e); | ||||||
|  |           e.flush(); | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|     res.setHeader("X-FYI-Content-Encoding", "base64"); |     res.setHeader("X-FYI-Content-Encoding", "base64"); | ||||||
|     res.setHeader("X-FYI-Content-Type", bin.getContentType()); |     res.setHeader("X-FYI-Content-Type", src.getContentType()); | ||||||
|  |     return b64.setContentType("text/plain").setCharacterEncoding("ISO-8859-1"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private static void replyUncompressed(HttpServletResponse res, |   private static BinaryResult stackGzip(HttpServletResponse res, | ||||||
|       OutputStream dst, BinaryResult bin, long len) throws IOException { |       final BinaryResult src) throws IOException { | ||||||
|     if (0 <= len && len < Integer.MAX_VALUE) { |     BinaryResult gz; | ||||||
|       res.setContentLength((int) len); |     long len = src.getContentLength(); | ||||||
|     } else if (0 <= len) { |     if (256 <= len && len <= (10 << 20)) { | ||||||
|       res.setHeader("Content-Length", Long.toString(len)); |       gz = compress(src); | ||||||
|  |       if (len <= gz.getContentLength()) { | ||||||
|  |         return src; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       gz = new BinaryResult() { | ||||||
|  |         @Override | ||||||
|  |         public void writeTo(OutputStream out) throws IOException { | ||||||
|  |           GZIPOutputStream gz = new GZIPOutputStream(out); | ||||||
|  |           src.writeTo(gz); | ||||||
|  |           gz.finish(); | ||||||
|  |           gz.flush(); | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
|     bin.writeTo(dst); |     res.setHeader("Content-Encoding", "gzip"); | ||||||
|  |     return gz.setContentType(src.getContentType()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private RestView<RestResource> view( |   private RestView<RestResource> view( | ||||||
| @@ -867,26 +868,33 @@ public class RestApiServlet extends HttpServlet { | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private static TemporaryBuffer.Heap base64(BinaryResult bin) |   private static BinaryResult base64(BinaryResult bin) | ||||||
|       throws IOException { |       throws IOException { | ||||||
|     int len = (int) bin.getContentLength(); |     int max = 4 * IntMath.divide((int) bin.getContentLength(), 3, CEILING); | ||||||
|     int max = 4 * IntMath.divide(len, 3, CEILING); |  | ||||||
|     TemporaryBuffer.Heap buf = heap(max); |     TemporaryBuffer.Heap buf = heap(max); | ||||||
|     OutputStream encoded = BaseEncoding.base64().encodingStream( |     OutputStream encoded = BaseEncoding.base64().encodingStream( | ||||||
|         new OutputStreamWriter(buf, Charsets.ISO_8859_1)); |         new OutputStreamWriter(buf, Charsets.ISO_8859_1)); | ||||||
|     bin.writeTo(encoded); |     bin.writeTo(encoded); | ||||||
|     encoded.close(); |     encoded.close(); | ||||||
|     return buf; |     return asBinaryResult(buf); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private static TemporaryBuffer.Heap compress(BinaryResult bin) |   private static BinaryResult compress(BinaryResult bin) | ||||||
|       throws IOException { |       throws IOException { | ||||||
|     TemporaryBuffer.Heap buf = heap(20 << 20); |     TemporaryBuffer.Heap buf = heap(20 << 20); | ||||||
|     GZIPOutputStream gz = new GZIPOutputStream(buf); |     GZIPOutputStream gz = new GZIPOutputStream(buf); | ||||||
|     bin.writeTo(gz); |     bin.writeTo(gz); | ||||||
|     gz.finish(); |     gz.close(); | ||||||
|     gz.flush(); |     return asBinaryResult(buf).setContentType(bin.getContentType()); | ||||||
|     return buf; |   } | ||||||
|  |  | ||||||
|  |   private static BinaryResult asBinaryResult(final TemporaryBuffer.Heap buf) { | ||||||
|  |     return new BinaryResult() { | ||||||
|  |       @Override | ||||||
|  |       public void writeTo(OutputStream os) throws IOException { | ||||||
|  |         buf.writeTo(os, null); | ||||||
|  |       } | ||||||
|  |     }.setContentLength(buf.length()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private static Heap heap(int max) { |   private static Heap heap(int max) { | ||||||
|   | |||||||
| @@ -64,7 +64,8 @@ public class GetContent implements RestReadView<FileResource> { | |||||||
|             public void writeTo(OutputStream os) throws IOException { |             public void writeTo(OutputStream os) throws IOException { | ||||||
|               object.copyTo(os); |               object.copyTo(os); | ||||||
|             } |             } | ||||||
|           }.setContentLength(object.getSize()).base64(); |           }.setContentLength(object.getSize()) | ||||||
|  |            .base64(); | ||||||
|         } finally { |         } finally { | ||||||
|           tw.release(); |           tw.release(); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -83,7 +83,8 @@ public class GarbageCollect implements RestModifyView<ProjectResource, Input> { | |||||||
|           writer.flush(); |           writer.flush(); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }.setContentType("text/plain; charset=UTF-8") |     }.setContentType("text/plain") | ||||||
|  |      .setCharacterEncoding(Charsets.UTF_8.name()) | ||||||
|      .disableGzip(); |      .disableGzip(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Shawn Pearce
					Shawn Pearce