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

pvillard 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 4d7c1b5a68 NIFI-14379 Fixed frontend HTTP redirect for paths without 
trailing slash
4d7c1b5a68 is described below

commit 4d7c1b5a68d157112835bd5056b2c5e665aba8ae
Author: exceptionfactory <[email protected]>
AuthorDate: Tue Mar 18 20:19:08 2025 -0500

    NIFI-14379 Fixed frontend HTTP redirect for paths without trailing slash
    
    - Added Redirect Pattern Rule for frontend path without trailing slash
    - Updated default Redirect Pattern Rule to use frontend path with trailing 
slash
    
    Signed-off-by: Pierre Villard <[email protected]>
    
    This closes #9811.
---
 .../nifi/web/server/StandardServerProvider.java    | 17 ++++++++---
 .../web/server/StandardServerProviderTest.java     | 34 +++++++++++++++++++++-
 2 files changed, 46 insertions(+), 5 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 492ea6071e..9d7d50fb66 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
@@ -51,6 +51,8 @@ class StandardServerProvider implements ServerProvider {
 
     private static final String FRONTEND_CONTEXT_PATH = "/nifi";
 
+    private static final String FRONTEND_CONTEXT_PATH_DIRECTORY = "/nifi/";
+
     private final SSLContext sslContext;
 
     StandardServerProvider(final SSLContext sslContext) {
@@ -69,10 +71,7 @@ class StandardServerProvider implements ServerProvider {
         final Handler standardHandler = getStandardHandler();
         server.setHandler(standardHandler);
 
-        final RewriteHandler defaultRewriteHandler = new RewriteHandler();
-        final List<String> allowedContextPaths = 
properties.getAllowedContextPathsAsList();
-        final RedirectPatternRule redirectDefault = new 
ContextPathRedirectPatternRule(ALL_PATHS_PATTERN, FRONTEND_CONTEXT_PATH, 
allowedContextPaths);
-        defaultRewriteHandler.addRule(redirectDefault);
+        final RewriteHandler defaultRewriteHandler = 
getDefaultRewriteHandler(properties);
         server.setDefaultHandler(defaultRewriteHandler);
 
         final String requestLogFormat = 
properties.getProperty(NiFiProperties.WEB_REQUEST_LOG_FORMAT);
@@ -83,6 +82,16 @@ class StandardServerProvider implements ServerProvider {
         return server;
     }
 
+    private RewriteHandler getDefaultRewriteHandler(final NiFiProperties 
properties) {
+        final RewriteHandler defaultRewriteHandler = new RewriteHandler();
+        final List<String> allowedContextPaths = 
properties.getAllowedContextPathsAsList();
+        final RedirectPatternRule redirectFrontend = new 
ContextPathRedirectPatternRule(FRONTEND_CONTEXT_PATH, 
FRONTEND_CONTEXT_PATH_DIRECTORY, allowedContextPaths);
+        defaultRewriteHandler.addRule(redirectFrontend);
+        final RedirectPatternRule redirectDefault = new 
ContextPathRedirectPatternRule(ALL_PATHS_PATTERN, 
FRONTEND_CONTEXT_PATH_DIRECTORY, allowedContextPaths);
+        defaultRewriteHandler.addRule(redirectDefault);
+        return defaultRewriteHandler;
+    }
+
     private void addConnectors(final Server server, final NiFiProperties 
properties, final SSLContext sslContext) {
         final FrameworkServerConnectorFactory serverConnectorFactory = new 
FrameworkServerConnectorFactory(server, properties);
         if (properties.isHTTPSConfigured()) {
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 f0d9b193cb..7087b15592 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
@@ -22,6 +22,7 @@ import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
 import org.apache.nifi.security.ssl.StandardSslContextBuilder;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.server.handler.HeaderWriterHandler;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.rewrite.handler.RewriteHandler;
 import org.eclipse.jetty.server.Connector;
@@ -47,6 +48,7 @@ import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.time.Duration;
 import java.util.List;
+import java.util.Optional;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
@@ -84,6 +86,10 @@ class StandardServerProviderTest {
 
     private static final String ALLOW_RESTRICTED_HEADERS_PROPERTY = 
"jdk.httpclient.allowRestrictedHeaders";
 
+    private static final String FRONTEND_PATH = "/nifi";
+
+    private static final String FRONTEND_PATH_TRAILING_SLASH = "/nifi/";
+
     private static SSLContext sslContext;
 
     @BeforeAll
@@ -195,12 +201,37 @@ class StandardServerProviderTest {
         ) {
             final URI localhostUri = 
UriComponentsBuilder.fromUri(serverUri).host(LOCALHOST_NAME).build().toUri();
 
+            assertFrontendRedirectRequestsCompleted(httpClient, localhostUri);
             assertRedirectRequestsCompleted(httpClient, localhostUri);
             assertBadRequestsCompleted(httpClient, localhostUri);
             assertMisdirectedRequestsCompleted(httpClient, localhostUri);
         }
     }
 
+    void assertFrontendRedirectRequestsCompleted(final HttpClient httpClient, 
final URI localhostUri) throws IOException, InterruptedException {
+        final HttpRequest localhostRequest = 
HttpRequest.newBuilder(localhostUri)
+                .version(HttpClient.Version.HTTP_2)
+                .build();
+        final HttpResponse<Void> localhostResponse = 
assertResponseStatusCode(httpClient, localhostRequest, 
HttpStatus.MOVED_TEMPORARILY_302);
+        final Optional<String> localhostLocationFound = 
localhostResponse.headers().firstValue(HttpHeader.LOCATION.lowerCaseName());
+        assertTrue(localhostLocationFound.isPresent());
+
+        final String localhostLocation = localhostLocationFound.get();
+        assertEquals(FRONTEND_PATH_TRAILING_SLASH, localhostLocation);
+
+        final URI frontendPathUri = 
UriComponentsBuilder.fromUri(localhostUri).path(FRONTEND_PATH).build().toUri();
+
+        final HttpRequest frontendPathRequest = 
HttpRequest.newBuilder(frontendPathUri)
+                .version(HttpClient.Version.HTTP_2)
+                .build();
+        final HttpResponse<Void> frontendPathResponse = 
assertResponseStatusCode(httpClient, frontendPathRequest, 
HttpStatus.MOVED_TEMPORARILY_302);
+        final Optional<String> frontendPathLocationFound = 
frontendPathResponse.headers().firstValue(HttpHeader.LOCATION.lowerCaseName());
+        assertTrue(frontendPathLocationFound.isPresent());
+
+        final String frontendPathLocation = frontendPathLocationFound.get();
+        assertEquals(FRONTEND_PATH_TRAILING_SLASH, frontendPathLocation);
+    }
+
     void assertRedirectRequestsCompleted(final HttpClient httpClient, final 
URI localhostUri) throws IOException, InterruptedException {
         final HttpRequest localhostRequest = 
HttpRequest.newBuilder(localhostUri)
                 .version(HttpClient.Version.HTTP_2)
@@ -236,9 +267,10 @@ class StandardServerProviderTest {
         assertResponseStatusCode(httpClient, localhostPortRequest, 
HttpStatus.MISDIRECTED_REQUEST_421);
     }
 
-    void assertResponseStatusCode(final HttpClient httpClient, final 
HttpRequest request, final int statusCodeExpected) throws IOException, 
InterruptedException {
+    HttpResponse<Void> assertResponseStatusCode(final HttpClient httpClient, 
final HttpRequest request, final int statusCodeExpected) throws IOException, 
InterruptedException {
         final HttpResponse<Void> response = httpClient.send(request, 
HttpResponse.BodyHandlers.discarding());
         assertEquals(statusCodeExpected, response.statusCode());
+        return response;
     }
 
     void assertHttpConnectorFound(final Server server) {

Reply via email to