From da36f528ac0f6066051b4745424072cfd38b52e3 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 14 Aug 2026 18:21:44 -0700 Subject: [PATCH 1/5] Fix stale Content-Encoding header in ProxyServlet after HttpClient bump HttpClient5 5.6+ stopped stripping Content-Encoding/Content-Length after auto-decompressing responses, so ProxyServlet forwarded a decompressed body under a stale compressed-encoding header. Disable auto-decompression so bytes and headers always stay consistent. --- api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java index 996586df030..feab358b6b4 100644 --- a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -299,6 +299,9 @@ protected CloseableHttpClient createHttpClient() { return HttpClientBuilder.create() .useSystemProperties() .setDefaultRequestConfig(config) + // httpclient5 5.6+ leaves stale Content-Encoding/Content-Length headers after auto-decompressing a response + // (5.5.x stripped them), so copyResponseHeaders() would forward a mismatched header/body pair to the client + .disableContentCompression() .build(); } From 49f42618138df2495da11b5cb3200a0c9fd8976f Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 17 Aug 2026 12:12:54 -0700 Subject: [PATCH 2/5] Code review feedback. --- .../org/mitre/dsmiley/httpproxy/ProxyServlet.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java index feab358b6b4..bb07f96861b 100644 --- a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -296,13 +296,14 @@ protected CloseableHttpClient createHttpClient() { .setProtocolUpgradeEnabled(allowProtocolUpgrade()) // LKS override .build(); - return HttpClientBuilder.create() - .useSystemProperties() - .setDefaultRequestConfig(config) - // httpclient5 5.6+ leaves stale Content-Encoding/Content-Length headers after auto-decompressing a response - // (5.5.x stripped them), so copyResponseHeaders() would forward a mismatched header/body pair to the client - .disableContentCompression() - .build(); + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create() + .useSystemProperties() + .setDefaultRequestConfig(config); + + if (!doHandleCompression) + httpClientBuilder.disableContentCompression(); + + return httpClientBuilder.build(); } // LKS override From 930667f5936225f0b2feae7b20259b1a81d65b2a Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 17 Aug 2026 13:19:14 -0700 Subject: [PATCH 3/5] Remove headers if necessary --- api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java index bb07f96861b..54cda3f024b 100644 --- a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -489,6 +489,9 @@ protected void copyRequestHeader(HttpServletRequest servletRequest, HttpRequest // control the Accept-Encoding header, not the client if (doHandleCompression && headerName.equals(HttpHeaders.ACCEPT_ENCODING)) return; + // In Apache HttpClient <5.6.4, these headers were automatically removed. Now we need to remove them manually. + if (doHandleCompression && (headerName.equals(HttpHeaders.CONTENT_ENCODING) || headerName.equals(HttpHeaders.CONTENT_MD5))) + return; @SuppressWarnings("unchecked") Enumeration headers = servletRequest.getHeaders(headerName); From 27add45d2a7daa9f248ac47481cc8b1b11abcb9c Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 17 Aug 2026 13:36:45 -0700 Subject: [PATCH 4/5] Remove headers in the correct place --- api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java index 54cda3f024b..ee085d35775 100644 --- a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -489,9 +489,6 @@ protected void copyRequestHeader(HttpServletRequest servletRequest, HttpRequest // control the Accept-Encoding header, not the client if (doHandleCompression && headerName.equals(HttpHeaders.ACCEPT_ENCODING)) return; - // In Apache HttpClient <5.6.4, these headers were automatically removed. Now we need to remove them manually. - if (doHandleCompression && (headerName.equals(HttpHeaders.CONTENT_ENCODING) || headerName.equals(HttpHeaders.CONTENT_MD5))) - return; @SuppressWarnings("unchecked") Enumeration headers = servletRequest.getHeaders(headerName); @@ -553,6 +550,13 @@ protected void copyResponseHeader(HttpServletRequest servletRequest, String headerName = header.getName(); if (hopByHopHeaders.containsHeader(headerName)) return; + // In Apache HttpClient <5.6.4, these headers were automatically removed after the client transparently + // decompressed the entity. Now we need to remove them manually, or the stale values reach the browser + // alongside the already-decompressed body. + if (doHandleCompression && (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_ENCODING) || + headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || + headerName.equalsIgnoreCase(HttpHeaders.CONTENT_MD5))) + return; String headerValue = header.getValue(); if (headerName.equalsIgnoreCase(SM.SET_COOKIE) || headerName.equalsIgnoreCase(SM.SET_COOKIE2)) { From 0f1bc949a22d016773b123820ada69b760a16348 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 17 Aug 2026 13:45:33 -0700 Subject: [PATCH 5/5] Move header customization to LabKeyProxyServlet --- .../dsmiley/httpproxy/LabKeyProxyServlet.java | 20 +++++++++++++++++++ .../mitre/dsmiley/httpproxy/ProxyServlet.java | 7 ------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/api/src/org/mitre/dsmiley/httpproxy/LabKeyProxyServlet.java b/api/src/org/mitre/dsmiley/httpproxy/LabKeyProxyServlet.java index 9d41c8a5d91..334265e563e 100644 --- a/api/src/org/mitre/dsmiley/httpproxy/LabKeyProxyServlet.java +++ b/api/src/org/mitre/dsmiley/httpproxy/LabKeyProxyServlet.java @@ -1,6 +1,8 @@ package org.mitre.dsmiley.httpproxy; import org.apache.commons.lang3.StringUtils; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpHeaders; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.labkey.api.util.PageFlowUtil; @@ -8,6 +10,7 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.net.HttpCookie; import java.util.Objects; @@ -84,6 +87,23 @@ protected boolean skipXForwardedProto() return true; } + @Override + protected void copyResponseHeader(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Header header) + { + // In Apache HttpClient <5.6.4, these headers were automatically removed after the client transparently + // decompressed the entity. Now we need to remove them manually, or the stale values reach the browser + // alongside the already-decompressed body. + if (doHandleCompression) + { + String headerName = header.getName(); + if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_ENCODING) || + headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || + headerName.equalsIgnoreCase(HttpHeaders.CONTENT_MD5)) + return; + } + super.copyResponseHeader(servletRequest, servletResponse, header); + } + @Override protected String getCookieNamePrefix(String cName) { diff --git a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java index ee085d35775..bb07f96861b 100644 --- a/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/api/src/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -550,13 +550,6 @@ protected void copyResponseHeader(HttpServletRequest servletRequest, String headerName = header.getName(); if (hopByHopHeaders.containsHeader(headerName)) return; - // In Apache HttpClient <5.6.4, these headers were automatically removed after the client transparently - // decompressed the entity. Now we need to remove them manually, or the stale values reach the browser - // alongside the already-decompressed body. - if (doHandleCompression && (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_ENCODING) || - headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || - headerName.equalsIgnoreCase(HttpHeaders.CONTENT_MD5))) - return; String headerValue = header.getValue(); if (headerName.equalsIgnoreCase(SM.SET_COOKIE) || headerName.equalsIgnoreCase(SM.SET_COOKIE2)) {