【问题标题】:Tomcat 8 Disable Chunked Encoding FilterTomcat 8 禁用分块编码过滤器
【发布时间】:2018-11-14 06:20:46
【问题描述】:

我们有一个 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 实际影响,当我查看标头时,我仍在使用分块传输编码没有固定的内容长度。 (我也尝试设置这样的自定义标头,但也没有看到它附加在我的响应中。我假设以基本形式进入过滤器的响应是某种形式的只读。)

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

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

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

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

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

【问题讨论】:

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


    【解决方案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);
        }
    }
    

    现在我唯一剩下的问题是,有没有更聪明的方法来获取我的文件的缓冲区数据,还是我每次都必须从磁盘重新读取文件?我假设在那里的某个地方,我的文件可能已经加载到内存中,可以使用了。在这里,我第二次将其加载到内存中,我想这不是很高效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-29
      • 2019-01-30
      • 2016-11-04
      相关资源
      最近更新 更多