Tomcat 8禁用分块编码过滤器

时间:2018-06-04 20:54:45

标签: java tomcat servlet-filters chunked-encoding embedded-tomcat-8

我们有一个VPN,我们有时会远程连接到网站,但却发现tomcat似乎经常使用的分块编码经常会被大量的JavaScript文件截断。为了解决这个问题,我想为JS文件构建一个Tomcat过滤器,一次性发送它们。

通过阅读文章herehere,我试图自己动手,并尝试按如下方式实施。

我使用自定义过滤器映射

将过滤器连接到我的上下文中
new CustomFilterMap(
    new String[]{"*.js"},
    new String[]{"REQUEST"}
)

这部分实际上运行良好,我的过滤器似乎应用于JS文件。现在,我没有得到很好的工作的部分是实际的过滤器。

public class NoChunkEncodedJSFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        val unversionedServletPath = request.getRequestURI().substring(request.getRequestURI().indexOf("/js"));
        val requestFilePath = getServletContext().getRealPath(unversionedServletPath);

        if (requestFilePath != null) {
            File requestedFile = new File(requestFilePath);
            if (requestedFile.exists()) {
                val fileSize = Math.toIntExact(requestedFile.length());

                val out = response.getWriter();
                val wrappedResponse = new NonEncodedResponse(response, fileSize);
                filterChain.doFilter(request, wrappedResponse);

                out.write(wrappedResponse.toString(), "UTF-8");
                out.close();
                return;
            }
        }
        filterChain.doFilter(request, response);
    }
}

使用我的NonEncodedResponse:

public class NonEncodedResponse extends HttpServletResponseWrapper {
    private ByteArrayOutputStream baos;

    public String toString() {
        try {
            return baos.toString("UTF-8");
        } catch (UnsupportedEncodingException unsuportedEncodingException) {
            return baos.toString();
        }
    }

    /**
     * Constructs a response adaptor wrapping the given response.
     *
     * @param response The response to be wrapped
     * @throws IllegalArgumentException if the response is null
     */
    public NonEncodedResponse(HttpServletResponse response, Integer fileSize) {
        super(response);

        this.setContentLength(fileSize);
        this.setBufferSize(fileSize);
        super.setContentLength(fileSize);
        super.setBufferSize(fileSize);

        baos = new ByteArrayOutputStream(fileSize);
    }

    @Override
    public PrintWriter getWriter(){
        return new PrintWriter(baos);
    }
}

现在,我最初尝试不包装响应,只是调用response.setBufferSize(fileSize);response.setContentLength(fileSize);,但这似乎对我的输出有0个实际效果,当我看到我仍在使用的标题时没有固定Content-Length的分块传输编码。 (我也试过像这样设置一个自定义标题,也没有看到它附加在我的响应中。我假设进入过滤器的响应是基本形式是某种形式的只读。)

我还尝试使用我的包装器并通过直接从文件中读取和发送字节来绕过其输出流

val fileContents = Files.readAllBytes(Paths.get(requestFilePath));
out.write(new String(fileContents));

因为看起来即使我尝试修复内容长度失败了,我仍然只是在尝试调试时看到部分文件在我的浏览器网络选项卡中被发送。

总而言之,现在我有它提供这样的文件(可能不是理想的),甚至更不理想它仍然说Transfer-Encoding: chunked尽管我努力将固定的Content-Length放在我的包装和原始响应上。我现在可以在我的内容上添加自定义标题,因此我知道它正在运行我的过滤器。似乎它仍然只是由于某种原因编码块。

有人可以告诉我我做错了什么,或者过滤器甚至可以用来开始禁用分块编码(我相信我看到的东西暗示他们可以从谷歌搜索,但事实是我只是不知道)。我很高兴地欢迎任何和所有关于这个问题的建议我不再有想法尝试,我当然不知道我在做什么。

1 个答案:

答案 0 :(得分:1)

所以我实际上通过恢复原始方法并完全抛弃我的包装并将filterChain.doFilter移动到我的代码块的 end 来设法实现这一点。我真的不得不承认我不知道filterChain.doFilter实际上做了什么,我不确定为什么这比我做的更有效。这是我最终的结果,让事情有效。

public class NoChunkEncodedJSFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        val requestFilePath = this.getRealFilePath(request.getRequestURI());
        File requestedFile = new File(requestFilePath);

        if (requestedFile.exists()) {
            val fileSize = Math.toIntExact(requestedFile.length());
            val fileContents = Files.readAllBytes(Paths.get(requestFilePath));

            response.reset();
            response.setHeader("Content-Length", String.valueOf(fileSize));
            response.setContentLength(fileSize);

            ServletOutputStream sos = response.getOutputStream();
            sos.write(fileContents);
            sos.close();
        }

        filterChain.doFilter(request, response);
    }

    private String getRealFilePath(String requestURI) {
        val unversionedServletPath = requestURI.substring(requestURI.indexOf("/js"));
        return getServletContext().getRealPath(unversionedServletPath);
    }
}

现在我唯一剩下的问题是,是否有更智能的方法来获取我的文件的缓冲区数据,还是每次都必须从磁盘重新读取文件?我假设在那里的某个地方,我的文件可能已经被加载到准备服务的内存中。在这里,我第二次将它加载到内存中,我想这并不是非常高效。