Skip to content

Commit

Permalink
Delegate compression to servlet container (#557)
Browse files Browse the repository at this point in the history
  • Loading branch information
basil authored Jun 14, 2024
1 parent b6d94a3 commit d39f3ee
Show file tree
Hide file tree
Showing 16 changed files with 80 additions and 565 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* [Getting Started](docs/getting-started.adoc)
* Developer Reference
* [URL binding reference](docs/reference.adoc)
* [HTTP compression](docs/compression.adoc)
* [Internationalization support](docs/i18n.adoc)
* [Javadoc](https://javadoc.jenkins.io/component/stapler/)
* [Stapler jelly tag library reference and XML Schema](docs/jelly-taglib-ref.adoc)
46 changes: 3 additions & 43 deletions core/src/main/java/org/kohsuke/stapler/ResponseImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,13 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import net.sf.json.JsonConfig;
import org.apache.commons.io.IOUtils;
import org.kohsuke.stapler.compression.CompressionFilter;
import org.kohsuke.stapler.compression.FilterServletOutputStream;
import org.kohsuke.stapler.export.DataWriter;
import org.kohsuke.stapler.export.ExportConfig;
import org.kohsuke.stapler.export.Flavor;
Expand Down Expand Up @@ -293,7 +290,7 @@ public void serveExposedBean(StaplerRequest req, Object exposedBean, ExportConfi
String pad = null;
Flavor flavor = config.getFlavor();
setContentType(flavor.contentType);
Writer w = getCompressedWriter(req);
Writer w = getWriter();

if (flavor == Flavor.JSON
|| flavor == Flavor.JSONP) { // for compatibility reasons, accept JSON for JSONP as well.
Expand Down Expand Up @@ -347,51 +344,14 @@ private void writeOne(TreePruner pruner, DataWriter dw, Object item) throws IOEx
p.writeTo(item, pruner, dw);
}

private boolean acceptsGzip(HttpServletRequest req) {
String acceptEncoding = req.getHeader("Accept-Encoding");
return acceptEncoding != null && acceptEncoding.contains("gzip");
}

@Override
public OutputStream getCompressedOutputStream(HttpServletRequest req) throws IOException {
if (mode != null) { // we already made the call and created OutputStream/Writer
return getOutputStream();
}

if (!acceptsGzip(req)) {
return getOutputStream(); // compression not applicable here
}

if (CompressionFilter.activate(req)) {
return getOutputStream(); // CompressionFilter will set up compression. no need to do anything
}

// CompressionFilter not available, so do it on our own.
// see CompressionFilter for why this is not desirable
setHeader("Content-Encoding", "gzip");
return recordOutput(
new FilterServletOutputStream(new GZIPOutputStream(super.getOutputStream()), super.getOutputStream()));
return getOutputStream();
}

@Override
public Writer getCompressedWriter(HttpServletRequest req) throws IOException {
if (mode != null) {
return getWriter();
}

if (!acceptsGzip(req)) {
return getWriter(); // compression not available
}

if (CompressionFilter.activate(req)) {
return getWriter(); // CompressionFilter will set up compression. no need to do anything
}

// CompressionFilter not available, so do it on our own.
// see CompressionFilter for why this is not desirable
setHeader("Content-Encoding", "gzip");
return recordOutput(new PrintWriter(
new OutputStreamWriter(new GZIPOutputStream(super.getOutputStream()), getCharacterEncoding())));
return getWriter();
}

@Override
Expand Down
37 changes: 4 additions & 33 deletions core/src/main/java/org/kohsuke/stapler/Stapler.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -611,21 +610,6 @@ boolean serveStaticResource(
// a comprehensive discussion on this topic
rsp.setHeader("X-Content-Type-Options", "nosniff");

int idx = fileName.lastIndexOf('.');
String ext = fileName.substring(idx + 1);

OutputStream out = null;
if (mimeType.startsWith("text/") || TEXT_FILES.contains(ext)) {
// Need to duplicate this logic from ResponseImpl.getCompressedOutputStream,
// since we want to set content length if we are not using encoding.
String acceptEncoding = req.getHeader("Accept-Encoding");
if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
// with gzip compression, Content-Length header needs to indicate the # of bytes after compression,
// so we can't compute it upfront.
out = rsp.getCompressedOutputStream(req);
}
}

// somewhat limited implementation of the partial GET
String range = req.getHeader("Range");
if (range != null
Expand Down Expand Up @@ -664,18 +648,11 @@ boolean serveStaticResource(
}
}

if (out == null) {
if (contentLength != -1) {
rsp.setHeader("Content-Length", Long.toString(contentLength));
}
out = rsp.getOutputStream();
}

byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
if (contentLength != -1) {
rsp.setHeader("Content-Length", Long.toString(contentLength));
}
OutputStream out = rsp.getOutputStream();
in.transferTo(out);
out.close();
return true;
} finally {
Expand Down Expand Up @@ -1109,12 +1086,6 @@ public static Stapler getCurrent() {

private static final Logger LOGGER = Logger.getLogger(Stapler.class.getName());

/**
* Extensions that look like text files.
*/
private static final Set<String> TEXT_FILES = new HashSet<>(
Arrays.asList("css", "js", "html", "txt", "java", "htm", "c", "cpp", "h", "rb", "pl", "py", "xml", "json"));

/**
* Get raw servlet path (decoded in TokenList).
*/
Expand Down
14 changes: 4 additions & 10 deletions core/src/main/java/org/kohsuke/stapler/StaplerResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,21 +221,15 @@ default void serveExposedBean(StaplerRequest req, Object exposedBean, ExportConf
}

/**
* Works like {@link #getOutputStream()} but tries to send the response
* with gzip compression if the client supports it.
*
* <p>
* This method is useful for sending out a large text content.
*
* @param req
* Used to determine whether the client supports compression
* @deprecated use {@link #getOutputStream}
*/
@Deprecated
OutputStream getCompressedOutputStream(HttpServletRequest req) throws IOException;

/**
* Works like {@link #getCompressedOutputStream(HttpServletRequest)} but this
* method is for {@link #getWriter()}.
* @deprecated use {@link #getWriter}
*/
@Deprecated
Writer getCompressedWriter(HttpServletRequest req) throws IOException;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.kohsuke.stapler;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UncaughtExceptionFilter implements Filter {
private ServletContext context;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
context = filterConfig.getServletContext();
}

@Override
public void doFilter(ServletRequest req, ServletResponse rsp, FilterChain filterChain)
throws IOException, ServletException {
try {
filterChain.doFilter(req, rsp);
} catch (IOException | Error | RuntimeException | ServletException e) {
if (DISABLED) {
throw e;
}
reportException(e, (HttpServletRequest) req, (HttpServletResponse) rsp);
}
}

private void reportException(Throwable e, HttpServletRequest req, HttpServletResponse rsp)
throws IOException, ServletException {
getUncaughtExceptionHandler(context).reportException(e, context, req, rsp);
}

@Override
public void destroy() {}

public static void setUncaughtExceptionHandler(ServletContext context, UncaughtExceptionHandler handler) {
context.setAttribute(UncaughtExceptionHandler.class.getName(), handler);
}

public static UncaughtExceptionHandler getUncaughtExceptionHandler(ServletContext context) {
UncaughtExceptionHandler h =
(UncaughtExceptionHandler) context.getAttribute(UncaughtExceptionHandler.class.getName());
if (h == null) {
h = UncaughtExceptionHandler.DEFAULT;
}
return h;
}

/**
* Disables the effect of {@link #setUncaughtExceptionHandler}, letting all errors be rethrown.
*/
@SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console")
public static boolean DISABLED = Boolean.getBoolean(UncaughtExceptionFilter.class.getName() + ".disabled");
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.kohsuke.stapler.compression;
package org.kohsuke.stapler;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
Expand All @@ -10,13 +10,9 @@
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.kohsuke.stapler.Stapler;

/**
* Handles an exception caught by {@link CompressionFilter}.
*
* See {@link CompressionFilter} javadoc for why this exception needs to be handled
* by us and can't just be handled by the servlet container like it does all others.
* Handles an exception caught by {@link UncaughtExceptionFilter}.
*
* @author Kohsuke Kawaguchi
*/
Expand Down

This file was deleted.

Loading

0 comments on commit d39f3ee

Please sign in to comment.