This is an automated email from the ASF dual-hosted git repository.

pvillard31 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new d47787ab2de NIFI-16170 Improved Content-Encoding handling for REST API 
(#11509)
d47787ab2de is described below

commit d47787ab2debc151ed05ee97f143c3743080d470
Author: David Handermann <[email protected]>
AuthorDate: Wed Aug 5 09:32:14 2026 -0500

    NIFI-16170 Improved Content-Encoding handling for REST API (#11509)
    
    - Added consistent handling of multiple Content-Encoding headers
    - Excluded all paths from decompression
---
 .../nifi/web/server/StandardServerProvider.java    |  5 +-
 .../handler/UnsupportedContentEncodingHandler.java | 32 +++++++--
 .../web/server/StandardServerProviderTest.java     | 77 ++++++++++++++--------
 3 files changed, 79 insertions(+), 35 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/StandardServerProvider.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/StandardServerProvider.java
index ad5a02be511..8db7d1b5dd1 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/StandardServerProvider.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/StandardServerProvider.java
@@ -27,6 +27,7 @@ import 
org.apache.nifi.web.server.log.StandardRequestLogProvider;
 import org.eclipse.jetty.compression.gzip.GzipCompression;
 import org.eclipse.jetty.compression.server.CompressionConfig;
 import org.eclipse.jetty.compression.server.CompressionHandler;
+import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
 import org.eclipse.jetty.rewrite.handler.RewriteHandler;
 import org.eclipse.jetty.server.Handler;
@@ -161,8 +162,10 @@ class StandardServerProvider implements ServerProvider {
 
         final CompressionConfig compressionConfig = CompressionConfig.builder()
                 .defaults()
+                // Override default inclusion of decompression for POST method
+                .decompressExcludeMethod(HttpMethod.POST.asString())
                 // Disable decompression of requests
-                .decompressExcludeEncoding(gzipCompression.getEncodingName())
+                .decompressExcludePath(ALL_PATHS_PATTERN)
                 .build();
         compressionHandler.putConfiguration(ROOT_PATH, compressionConfig);
 
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/handler/UnsupportedContentEncodingHandler.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/handler/UnsupportedContentEncodingHandler.java
index d3668665384..82644ef12e0 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/handler/UnsupportedContentEncodingHandler.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/handler/UnsupportedContentEncodingHandler.java
@@ -23,6 +23,8 @@ import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.util.Callback;
 
+import java.util.List;
+
 /**
  * Handler that rejects requests declaring an unsupported Content-Encoding
  */
@@ -34,14 +36,32 @@ public class UnsupportedContentEncodingHandler extends 
Handler.Abstract {
 
     @Override
     public boolean handle(final Request request, final Response response, 
final Callback callback) {
-        final String contentEncoding = 
request.getHeaders().get(HttpHeader.CONTENT_ENCODING);
+        // Handle one or more Content-Encoding request headers
+        final List<String> contentEncodings = 
request.getHeaders().getValuesList(HttpHeader.CONTENT_ENCODING);
+
+        final boolean handled;
+        if (isContentEncodingUnsupported(contentEncodings)) {
+            Response.writeError(request, response, callback, 
HttpStatus.UNSUPPORTED_MEDIA_TYPE_415, UNSUPPORTED_MESSAGE);
+            handled = true;
+        } else {
+            handled = false;
+        }
+
+        return handled;
+    }
+
+    private boolean isContentEncodingUnsupported(final List<String> 
contentEncodings) {
+        // Empty Content-Encoding header is allowed
+        boolean unsupported = false;
 
-        // A request without a Content-Encoding, or one declaring only the 
identity encoding, is passed to later Handlers
-        if (contentEncoding == null || contentEncoding.isBlank() || 
IDENTITY_ENCODING.equalsIgnoreCase(contentEncoding.trim())) {
-            return false;
+        for (final String contentEncoding : contentEncodings) {
+            // Content-Encoding with a value of identity is allowed indicating 
no encoding
+            if (!IDENTITY_ENCODING.equalsIgnoreCase(contentEncoding)) {
+                unsupported = true;
+                break;
+            }
         }
 
-        Response.writeError(request, response, callback, 
HttpStatus.UNSUPPORTED_MEDIA_TYPE_415, UNSUPPORTED_MESSAGE);
-        return true;
+        return unsupported;
     }
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/StandardServerProviderTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/StandardServerProviderTest.java
index f7a79dcfcae..343466b47b7 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/StandardServerProviderTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/StandardServerProviderTest.java
@@ -108,6 +108,11 @@ class StandardServerProviderTest {
 
     private static final String FRONTEND_PATH_TRAILING_SLASH = "/nifi/";
 
+    private static final String ALL_PATHS = "/*";
+    private static final String CONTENT_ENCODING_HEADER = "Content-Encoding";
+    private static final String GZIP_CONTENT_ENCODING = "gzip";
+    private static final String IDENTITY_CONTENT_ENCODING = "identity";
+
     private static final List<String> STANDARD_RESPONSE_HEADERS = List.of(
             "Content-Security-Policy",
             "Strict-Transport-Security",
@@ -177,36 +182,14 @@ class StandardServerProviderTest {
         assertTrue(compressMethods.contains(HttpMethod.GET.asString()));
         assertTrue(compressMethods.contains(HttpMethod.POST.asString()));
 
-        final UnsupportedContentEncodingHandler 
unsupportedContentEncodingHandler = 
handlerCollection.getDescendant(UnsupportedContentEncodingHandler.class);
-        assertNotNull(unsupportedContentEncodingHandler);
-    }
-
-    @Timeout(15)
-    @Test
-    void testGetServerRejectsCompressedRequestBody() throws Exception {
-        final Properties applicationProperties = new Properties();
-        applicationProperties.setProperty(NiFiProperties.WEB_HTTP_PORT, 
RANDOM_PORT);
-        final NiFiProperties properties = 
NiFiProperties.createBasicNiFiProperties((String) null, applicationProperties);
-
-        final StandardServerProvider provider = new 
StandardServerProvider(null);
+        
assertFalse(compressionConfig.isDecompressMethodSupported(HttpMethod.POST.asString()));
+        
assertFalse(compressionConfig.isDecompressMethodSupported(HttpMethod.PUT.asString()));
 
-        final Server server = provider.getServer(properties);
+        final Set<String> decompressExcludePaths = 
compressionConfig.getDecompressExcludePaths();
+        assertTrue(decompressExcludePaths.contains(ALL_PATHS));
 
-        try {
-            startServer(server);
-            final URI localhostUri = 
UriComponentsBuilder.fromUri(server.getURI()).host(LOCALHOST_NAME).build().toUri();
-
-            try (HttpClient httpClient = 
HttpClient.newBuilder().connectTimeout(TIMEOUT).build()) {
-                final HttpRequest compressedRequest = 
HttpRequest.newBuilder(localhostUri)
-                        .version(HttpClient.Version.HTTP_1_1)
-                        .header(HttpHeader.CONTENT_ENCODING.asString(), "gzip")
-                        .POST(HttpRequest.BodyPublishers.ofByteArray(new 
byte[]{1, 2, 3}))
-                        .build();
-                assertResponseStatusCode(httpClient, compressedRequest, 
HttpStatus.UNSUPPORTED_MEDIA_TYPE_415);
-            }
-        } finally {
-            server.stop();
-        }
+        final UnsupportedContentEncodingHandler 
unsupportedContentEncodingHandler = 
handlerCollection.getDescendant(UnsupportedContentEncodingHandler.class);
+        assertNotNull(unsupportedContentEncodingHandler);
     }
 
     @Test
@@ -317,6 +300,7 @@ class StandardServerProviderTest {
             assertFrontendRedirectRequestsCompleted(httpClient, localhostUri);
             assertBadRequestsCompleted(httpClient, localhostUri);
             assertMisdirectedRequestsCompleted(httpClient, localhostUri);
+            assertContentEncodingRequestsCompleted(httpClient, localhostUri);
 
             assertReplicatedRequestCompleted(httpClient, localhostUri, 
HttpStatus.MISDIRECTED_REQUEST_421);
             assertForwardedToCoordinatorRequestCompleted(httpClient, 
localhostUri, HttpStatus.MISDIRECTED_REQUEST_421);
@@ -335,6 +319,7 @@ class StandardServerProviderTest {
             assertRedirectRequestsCompleted(httpClient, localhostUri);
             assertBadRequestsCompleted(httpClient, localhostUri);
             assertMisdirectedRequestsCompleted(httpClient, localhostUri);
+            assertContentEncodingRequestsCompleted(httpClient, localhostUri);
 
             assertReplicatedRequestCompleted(httpClient, localhostUri, 
HttpStatus.MOVED_TEMPORARILY_302);
             assertForwardedToCoordinatorRequestCompleted(httpClient, 
localhostUri, HttpStatus.MOVED_TEMPORARILY_302);
@@ -436,6 +421,42 @@ class StandardServerProviderTest {
         assertResponseStatusCode(httpClient, localhostAddressRequest, 
HttpStatus.BAD_REQUEST_400);
     }
 
+    void assertContentEncodingRequestsCompleted(final HttpClient httpClient, 
final URI localhostUri) throws IOException, InterruptedException {
+        final HttpRequest identityContentEncodingRequest = 
HttpRequest.newBuilder(localhostUri)
+                .POST(HttpRequest.BodyPublishers.noBody())
+                .header(CONTENT_ENCODING_HEADER, IDENTITY_CONTENT_ENCODING)
+                .version(HttpClient.Version.HTTP_1_1)
+                .build();
+        assertResponseStatusCode(httpClient, identityContentEncodingRequest, 
HttpStatus.MOVED_TEMPORARILY_302);
+
+        final HttpRequest gzipContentEncodingRequest = 
HttpRequest.newBuilder(localhostUri)
+                .POST(HttpRequest.BodyPublishers.noBody())
+                .header(CONTENT_ENCODING_HEADER, GZIP_CONTENT_ENCODING)
+                .version(HttpClient.Version.HTTP_1_1)
+                .build();
+        assertResponseStatusCode(httpClient, gzipContentEncodingRequest, 
HttpStatus.UNSUPPORTED_MEDIA_TYPE_415);
+
+        final HttpRequest identityGzipContentEncodingRequest = 
HttpRequest.newBuilder(localhostUri)
+                .POST(HttpRequest.BodyPublishers.noBody())
+                .headers(
+                        CONTENT_ENCODING_HEADER, IDENTITY_CONTENT_ENCODING,
+                        CONTENT_ENCODING_HEADER, GZIP_CONTENT_ENCODING
+                )
+                .version(HttpClient.Version.HTTP_2)
+                .build();
+        assertResponseStatusCode(httpClient, 
identityGzipContentEncodingRequest, HttpStatus.UNSUPPORTED_MEDIA_TYPE_415);
+
+        final HttpRequest identityGzipUppercasedContentEncodingRequest = 
HttpRequest.newBuilder(localhostUri)
+                .POST(HttpRequest.BodyPublishers.noBody())
+                .headers(
+                        CONTENT_ENCODING_HEADER, 
IDENTITY_CONTENT_ENCODING.toUpperCase(),
+                        CONTENT_ENCODING_HEADER, 
GZIP_CONTENT_ENCODING.toUpperCase()
+                )
+                .version(HttpClient.Version.HTTP_2)
+                .build();
+        assertResponseStatusCode(httpClient, 
identityGzipUppercasedContentEncodingRequest, 
HttpStatus.UNSUPPORTED_MEDIA_TYPE_415);
+    }
+
     void assertMisdirectedRequestsCompleted(final HttpClient httpClient, final 
URI localhostUri) throws IOException, InterruptedException {
         final HttpRequest localhostPortRequest = 
HttpRequest.newBuilder(localhostUri)
                 .version(HttpClient.Version.HTTP_1_1)

Reply via email to