Tomcat 8 Disable Chunked Encoding Filter
У нас есть VPN, который мы иногда используем для удаленного подключения к сайтам только для того, чтобы обнаружить, что tomcat, использующий фрагментированную кодировку, часто использует усеченные файлы JavaScript. В попытке исправить это я хотел создать фильтр Tomcat для файлов JS, который отправляет их за один раз.
Читая статьи здесь и здесь, я попытался сделать свой собственный снимок и попытался реализовать его следующим образом.
Я подключил фильтр к своему контексту с помощью пользовательской карты фильтров
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 ответ
Так что мне действительно удалось заставить это работать, вернувшись к моему первоначальному подходу и полностью отказавшись от своей обертки, и переместив 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);
}
}
Теперь у меня остается только один вопрос: есть ли более разумный способ получить данные буфера для моего файла, или мне нужно каждый раз перечитывать файл с диска? Я предполагаю, что где-то там мой файл, вероятно, уже загружен в память, готовую к обработке. Здесь я загружаю его в память во второй раз, и я думаю, что это не очень эффективно.