Tomcat 8 Disable Chunked Encoding Filter - PullRequest
0 голосов
/ 04 июня 2018

У нас есть VPN, который мы иногда используем для удаленного подключения к сайтам только для того, чтобы обнаружить, что tomcat, использующий фрагментированную кодировку, часто использует обрезанные файлы JavaScript.В попытке исправить это я хотел создать фильтр Tomcat для файлов JS, который отправляет их за один раз.

Прочитав статьи здесь и здесь IЯ попытался сделать это самостоятельно и попытался реализовать его следующим образом.

Я подключил фильтр к своему контексту с помощью пользовательской карты фильтров

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, несмотря на все мои усилия по установке фиксированной длины содержимого на мою оболочку и первоначальный ответ.Теперь я могу поместить свой заголовок в свой контент, чтобы знать, что он проходит через мой фильтр.Кажется, по какой-то причине он все еще просто закодирован.

Может кто-нибудь сказать мне, что я делаю неправильно, или, если для начала можно использовать фильтры, чтобы отключить чанкованное кодирование (я думаю, что я видел вещи, предполагающие, что они могут из поисков Google, но правда в том, что я простоне знаю)Я с радостью приветствую любые советы по этому вопросу, у меня больше нет идей, чтобы попробовать, и я, конечно, не знаю, что я делаю.

1 Ответ

0 голосов
/ 05 июня 2018

Таким образом, мне действительно удалось заставить это работать, вернувшись к моему первоначальному подходу, полностью отказавшись от своей обертки и переместив filterChain.doFilter в конец моего блока кода.Я не совсем уверен, почему это работает по сравнению с тем, что я делал, потому что, честно говоря, должен признаться, я не знаю, что на самом деле делает 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);
    }
}

Теперь у меня остается только один вопрос: есть ли более разумный способ получить данные буфера для моего файла, или мне нужно перечитатьфайл с диска каждый раз?Я предполагаю, что где-то там мой файл, вероятно, уже загружен в память, готовую к обработке.Здесь я загружаю его в память во второй раз, и я думаю, что это не очень эффективно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...