1c0f09389SShawn O. Pearce /* 25c5f7c6bSMatthias Sohn * Copyright (C) 2010, Google Inc. and others 3c0f09389SShawn O. Pearce * 45c5f7c6bSMatthias Sohn * This program and the accompanying materials are made available under the 55c5f7c6bSMatthias Sohn * terms of the Eclipse Distribution License v. 1.0 which is available at 65c5f7c6bSMatthias Sohn * https://www.eclipse.org/org/documents/edl-v10.php. 7c0f09389SShawn O. Pearce * 85c5f7c6bSMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 9c0f09389SShawn O. Pearce */ 10c0f09389SShawn O. Pearce 11c0f09389SShawn O. Pearce package org.eclipse.jgit.http.server; 12c0f09389SShawn O. Pearce 13c0f09389SShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.acceptsGzipEncoding; 14c0f09389SShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP; 15c0f09389SShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING; 16c0f09389SShawn O. Pearce 17c0f09389SShawn O. Pearce import java.io.IOException; 18c0f09389SShawn O. Pearce import java.io.OutputStream; 19c0f09389SShawn O. Pearce import java.util.zip.GZIPOutputStream; 20c0f09389SShawn O. Pearce 21c0f09389SShawn O. Pearce import javax.servlet.http.HttpServletRequest; 22c0f09389SShawn O. Pearce import javax.servlet.http.HttpServletResponse; 23c0f09389SShawn O. Pearce 24c0f09389SShawn O. Pearce import org.eclipse.jgit.util.TemporaryBuffer; 25c0f09389SShawn O. Pearce 26c0f09389SShawn O. Pearce /** 27c0f09389SShawn O. Pearce * Buffers a response, trying to gzip it if the user agent supports that. 28c0f09389SShawn O. Pearce * <p> 29c0f09389SShawn O. Pearce * If the response overflows the buffer, gzip is skipped and the response is 30c0f09389SShawn O. Pearce * streamed to the client as its produced, most likely using HTTP/1.1 chunked 31c0f09389SShawn O. Pearce * encoding. This is useful for servlets that produce mixed-mode content, where 32c0f09389SShawn O. Pearce * smaller payloads are primarily pure text that compresses well, while much 33c0f09389SShawn O. Pearce * larger payloads are heavily compressed binary data. {@link UploadPackServlet} 34c0f09389SShawn O. Pearce * is one such servlet. 35c0f09389SShawn O. Pearce */ 36c0f09389SShawn O. Pearce class SmartOutputStream extends TemporaryBuffer { 37c0f09389SShawn O. Pearce private static final int LIMIT = 32 * 1024; 38c0f09389SShawn O. Pearce 39c0f09389SShawn O. Pearce private final HttpServletRequest req; 40c0f09389SShawn O. Pearce private final HttpServletResponse rsp; 41f93a6a72SShawn O. Pearce private boolean compressStream; 42c0f09389SShawn O. Pearce private boolean startedOutput; 43c0f09389SShawn O. Pearce SmartOutputStream(final HttpServletRequest req, final HttpServletResponse rsp, boolean compressStream)44c0f09389SShawn O. Pearce SmartOutputStream(final HttpServletRequest req, 45f93a6a72SShawn O. Pearce final HttpServletResponse rsp, 46f93a6a72SShawn O. Pearce boolean compressStream) { 47c0f09389SShawn O. Pearce super(LIMIT); 48c0f09389SShawn O. Pearce this.req = req; 49c0f09389SShawn O. Pearce this.rsp = rsp; 50f93a6a72SShawn O. Pearce this.compressStream = compressStream; 51c0f09389SShawn O. Pearce } 52c0f09389SShawn O. Pearce 533b00041cSMatthias Sohn /** {@inheritDoc} */ 54c0f09389SShawn O. Pearce @Override overflow()55c0f09389SShawn O. Pearce protected OutputStream overflow() throws IOException { 56c0f09389SShawn O. Pearce startedOutput = true; 57f93a6a72SShawn O. Pearce 58f93a6a72SShawn O. Pearce OutputStream out = rsp.getOutputStream(); 59f93a6a72SShawn O. Pearce if (compressStream && acceptsGzipEncoding(req)) { 60f93a6a72SShawn O. Pearce rsp.setHeader(HDR_CONTENT_ENCODING, ENCODING_GZIP); 61f93a6a72SShawn O. Pearce out = new GZIPOutputStream(out); 62f93a6a72SShawn O. Pearce } 63f93a6a72SShawn O. Pearce return out; 64c0f09389SShawn O. Pearce } 65c0f09389SShawn O. Pearce 663b00041cSMatthias Sohn /** {@inheritDoc} */ 677ac182f4SDavid Pursehouse @Override close()68c0f09389SShawn O. Pearce public void close() throws IOException { 69c0f09389SShawn O. Pearce super.close(); 70c0f09389SShawn O. Pearce 71c0f09389SShawn O. Pearce if (!startedOutput) { 72c0f09389SShawn O. Pearce // If output hasn't started yet, the entire thing fit into our 73c0f09389SShawn O. Pearce // buffer. Try to use a proper Content-Length header, and also 74c0f09389SShawn O. Pearce // deflate the response with gzip if it will be smaller. 75*4209a0f8SMatthias Sohn if (256 < this.length() && acceptsGzipEncoding(req)) { 76c0f09389SShawn O. Pearce TemporaryBuffer gzbuf = new TemporaryBuffer.Heap(LIMIT); 77c0f09389SShawn O. Pearce try { 785c70be00SDavid Pursehouse try (GZIPOutputStream gzip = new GZIPOutputStream(gzbuf)) { 79*4209a0f8SMatthias Sohn this.writeTo(gzip, null); 800d61707fSShawn O. Pearce } 81*4209a0f8SMatthias Sohn if (gzbuf.length() < this.length()) { 82c0f09389SShawn O. Pearce rsp.setHeader(HDR_CONTENT_ENCODING, ENCODING_GZIP); 83*4209a0f8SMatthias Sohn writeResponse(gzbuf); 84*4209a0f8SMatthias Sohn return; 85c0f09389SShawn O. Pearce } 86c0f09389SShawn O. Pearce } catch (IOException err) { 87c0f09389SShawn O. Pearce // Most likely caused by overflowing the buffer, meaning 88c0f09389SShawn O. Pearce // its larger if it were compressed. Discard compressed 89c0f09389SShawn O. Pearce // copy and use the original. 90c0f09389SShawn O. Pearce } 91c0f09389SShawn O. Pearce } 92*4209a0f8SMatthias Sohn writeResponse(this); 93*4209a0f8SMatthias Sohn } 94*4209a0f8SMatthias Sohn } 95c0f09389SShawn O. Pearce writeResponse(TemporaryBuffer out)96*4209a0f8SMatthias Sohn private void writeResponse(TemporaryBuffer out) throws IOException { 97c0f09389SShawn O. Pearce // The Content-Length cannot overflow when cast to an int, our 98c0f09389SShawn O. Pearce // hardcoded LIMIT constant above assures us we wouldn't store 99c0f09389SShawn O. Pearce // more than 2 GiB of content in memory. 100c0f09389SShawn O. Pearce rsp.setContentLength((int) out.length()); 1015c70be00SDavid Pursehouse try (OutputStream os = rsp.getOutputStream()) { 102c0f09389SShawn O. Pearce out.writeTo(os, null); 103c0f09389SShawn O. Pearce os.flush(); 104c0f09389SShawn O. Pearce } 105c0f09389SShawn O. Pearce } 106c0f09389SShawn O. Pearce } 107