This is an automated email from the ASF dual-hosted git repository.
joewitt 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 5f72313003 NIFI-14115 Set Standard HTTP Headers for Framework
Responses This closes #9598
5f72313003 is described below
commit 5f72313003fe8b0142657892ebd94bfee2a053d4
Author: exceptionfactory <[email protected]>
AuthorDate: Thu Dec 26 22:17:21 2024 -0600
NIFI-14115 Set Standard HTTP Headers for Framework Responses
This closes #9598
- Added HeaderWriterHandler implementing Jetty Handler methods for setting
standard HTTP response headers
- Removed HeaderWriterFilter approach for writing standard HTTP headers
- Refactored Jetty Server instantiation to StandardServerProvider for
streamlined configuration and testing
- Simplified Server start test method to avoid timing issues
- Removed unnecessary Timeout annotation
Signed-off-by: Joseph Witt <[email protected]>
---
.../org/apache/nifi/web/server/JettyServer.java | 109 ++++------------
.../org/apache/nifi/web/server/ServerProvider.java | 33 +++++
.../nifi/web/server/StandardServerProvider.java | 138 +++++++++++++++++++++
.../filter/StandardRequestFilterProvider.java | 27 ----
.../web/server/handler/HeaderWriterHandler.java | 64 ++++++++++
.../web/server/StandardServerProviderTest.java | 131 +++++++++++++++++++
.../filter/RestApiRequestFilterProviderTest.java | 2 -
.../filter/StandardRequestFilterProviderTest.java | 4 +-
8 files changed, 388 insertions(+), 120 deletions(-)
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 27f17894b6..21e21e434a 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -22,11 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
-import java.io.UncheckedIOException;
import java.lang.reflect.InvocationTargetException;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
@@ -36,7 +32,6 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
@@ -103,14 +98,11 @@ import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.ContentAccess;
import org.apache.nifi.web.NiFiWebConfigurationContext;
import org.apache.nifi.web.UiExtensionType;
-import org.apache.nifi.web.server.connector.FrameworkServerConnectorFactory;
import org.apache.nifi.web.server.filter.FilterParameter;
import org.apache.nifi.web.server.filter.LogoutCompleteRedirectFilter;
import org.apache.nifi.web.server.filter.RequestFilterProvider;
import org.apache.nifi.web.server.filter.RestApiRequestFilterProvider;
import org.apache.nifi.web.server.filter.StandardRequestFilterProvider;
-import org.apache.nifi.web.server.log.RequestLogProvider;
-import org.apache.nifi.web.server.log.StandardRequestLogProvider;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
@@ -121,7 +113,6 @@ import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration;
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
@@ -130,7 +121,6 @@ import
org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee10.servlet.FilterHolder;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
@@ -222,42 +212,29 @@ public class JettyServer implements NiFiServer,
ExtensionUiLoader {
public void init() {
clearWorkingDirectory();
- final QueuedThreadPool threadPool = new
QueuedThreadPool(props.getWebThreads());
- threadPool.setName("NiFi Web Server");
- this.server = new Server(threadPool);
- final FrameworkSslContextProvider sslContextProvider = new
FrameworkSslContextProvider(props);
- this.sslContext = sslContextProvider.loadSslContext().orElse(null);
-
- configureConnectors(server);
-
- final ContextHandlerCollection handlerCollection = new
ContextHandlerCollection();
- final Handler standardHandler = getStandardHandler(handlerCollection);
- server.setHandler(standardHandler);
-
- final RewriteHandler defaultRewriteHandler = new RewriteHandler();
- final RedirectPatternRule redirectDefault = new
RedirectPatternRule("/*", "/nifi");
- defaultRewriteHandler.addRule(redirectDefault);
- server.setDefaultHandler(defaultRewriteHandler);
-
- deploymentManager.setContexts(handlerCollection);
- server.addBean(deploymentManager);
-
- final String requestLogFormat =
props.getProperty(NiFiProperties.WEB_REQUEST_LOG_FORMAT);
- final RequestLogProvider requestLogProvider = new
StandardRequestLogProvider(requestLogFormat);
- final RequestLog requestLog = requestLogProvider.getRequestLog();
- server.setRequestLog(requestLog);
- }
- private Handler getStandardHandler(final ContextHandlerCollection
handlerCollection) {
- // Only restrict the host header if running in HTTPS mode
- if (props.isHTTPSConfigured()) {
- final HostHeaderHandler hostHeaderHandler = new
HostHeaderHandler(props);
- handlerCollection.addHandler(hostHeaderHandler);
- }
+ try {
+ final FrameworkSslContextProvider sslContextProvider = new
FrameworkSslContextProvider(props);
+ sslContext = sslContextProvider.loadSslContext().orElse(null);
- final Handler warHandlers = loadInitialWars(bundles);
- handlerCollection.addHandler(warHandlers);
- return handlerCollection;
+ final ServerProvider serverProvider = new
StandardServerProvider(sslContext);
+ server = serverProvider.getServer(props);
+
+ final Handler serverHandler = server.getHandler();
+ if (serverHandler instanceof Handler.Collection
serverHandlerCollection) {
+ final ContextHandlerCollection contextHandlerCollection = new
ContextHandlerCollection();
+ final Handler warHandlers = loadInitialWars(bundles);
+ contextHandlerCollection.addHandler(warHandlers);
+ deploymentManager.setContexts(contextHandlerCollection);
+ server.addBean(deploymentManager);
+
+ serverHandlerCollection.addHandler(contextHandlerCollection);
+ } else {
+ throw new IllegalStateException("Server Handler not
Handler.Collection: Server Provider configuration failed");
+ }
+ } catch (final Throwable e) {
+ startUpFailure(e);
+ }
}
private void clearWorkingDirectory() {
@@ -771,50 +748,6 @@ public class JettyServer implements NiFiServer,
ExtensionUiLoader {
return webApiDocsDir;
}
- private void configureConnectors(final Server server) {
- try {
- final FrameworkServerConnectorFactory serverConnectorFactory = new
FrameworkServerConnectorFactory(server, props);
- if (props.isHTTPSConfigured()) {
- serverConnectorFactory.setSslContext(sslContext);
- }
-
- final Map<String, String> interfaces = props.isHTTPSConfigured() ?
props.getHttpsNetworkInterfaces() : props.getHttpNetworkInterfaces();
- final Set<String> interfaceNames =
interfaces.values().stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
- // Add Server Connectors based on configured Network Interface
Names
- if (interfaceNames.isEmpty()) {
- final ServerConnector serverConnector =
serverConnectorFactory.getServerConnector();
- final String host = props.isHTTPSConfigured() ?
props.getProperty(NiFiProperties.WEB_HTTPS_HOST) :
props.getProperty(NiFiProperties.WEB_HTTP_HOST);
- if (StringUtils.isNotBlank(host)) {
- serverConnector.setHost(host);
- }
- server.addConnector(serverConnector);
- } else {
- interfaceNames.stream()
- // Map interface name properties to Network Interfaces
- .map(interfaceName -> {
- try {
- return
NetworkInterface.getByName(interfaceName);
- } catch (final SocketException e) {
- throw new
UncheckedIOException(String.format("Network Interface [%s] not found",
interfaceName), e);
- }
- })
- // Map Network Interfaces to host addresses
- .filter(Objects::nonNull)
- .flatMap(networkInterface ->
Collections.list(networkInterface.getInetAddresses()).stream())
- .map(InetAddress::getHostAddress)
- // Map host addresses to Server Connectors
- .map(host -> {
- final ServerConnector serverConnector =
serverConnectorFactory.getServerConnector();
- serverConnector.setHost(host);
- return serverConnector;
- })
- .forEach(server::addConnector);
- }
- } catch (final Throwable e) {
- startUpFailure(e);
- }
- }
-
protected List<URI> getApplicationUrls() {
return Arrays.stream(server.getConnectors())
.map(connector -> (ServerConnector) connector)
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/ServerProvider.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/ServerProvider.java
new file mode 100644
index 0000000000..3c8cf1e01f
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/ServerProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.server;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.eclipse.jetty.server.Server;
+
+/**
+ * Abstraction for configuring Server instances based on application properties
+ */
+interface ServerProvider {
+ /**
+ * Get Server configured using application properties
+ *
+ * @param properties Application properties
+ * @return Server
+ */
+ Server getServer(NiFiProperties properties);
+}
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
new file mode 100644
index 0000000000..5fb29c001a
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/StandardServerProvider.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.server;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.server.connector.FrameworkServerConnectorFactory;
+import org.apache.nifi.web.server.handler.HeaderWriterHandler;
+import org.apache.nifi.web.server.log.RequestLogProvider;
+import org.apache.nifi.web.server.log.StandardRequestLogProvider;
+import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
+import org.eclipse.jetty.rewrite.handler.RewriteHandler;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+import javax.net.ssl.SSLContext;
+import java.io.UncheckedIOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Standard implementation of Server Provider with default Handlers
+ */
+class StandardServerProvider implements ServerProvider {
+ private static final String ALL_PATHS_PATTERN = "/*";
+
+ private static final String FRONTEND_CONTEXT_PATH = "/nifi";
+
+ private final SSLContext sslContext;
+
+ StandardServerProvider(final SSLContext sslContext) {
+ this.sslContext = sslContext;
+ }
+
+ @Override
+ public Server getServer(final NiFiProperties properties) {
+ Objects.requireNonNull(properties, "Properties required");
+
+ final QueuedThreadPool threadPool = new
QueuedThreadPool(properties.getWebThreads());
+ threadPool.setName("NiFi Web Server");
+ final Server server = new Server(threadPool);
+ addConnectors(server, properties, sslContext);
+
+ final Handler standardHandler = getStandardHandler(properties);
+ server.setHandler(standardHandler);
+
+ final RewriteHandler defaultRewriteHandler = new RewriteHandler();
+ final RedirectPatternRule redirectDefault = new
RedirectPatternRule(ALL_PATHS_PATTERN, FRONTEND_CONTEXT_PATH);
+ defaultRewriteHandler.addRule(redirectDefault);
+ server.setDefaultHandler(defaultRewriteHandler);
+
+ final String requestLogFormat =
properties.getProperty(NiFiProperties.WEB_REQUEST_LOG_FORMAT);
+ final RequestLogProvider requestLogProvider = new
StandardRequestLogProvider(requestLogFormat);
+ final RequestLog requestLog = requestLogProvider.getRequestLog();
+ server.setRequestLog(requestLog);
+
+ return server;
+ }
+
+ private void addConnectors(final Server server, final NiFiProperties
properties, final SSLContext sslContext) {
+ final FrameworkServerConnectorFactory serverConnectorFactory = new
FrameworkServerConnectorFactory(server, properties);
+ if (properties.isHTTPSConfigured()) {
+ serverConnectorFactory.setSslContext(sslContext);
+ }
+
+ final Map<String, String> interfaces = properties.isHTTPSConfigured()
? properties.getHttpsNetworkInterfaces() :
properties.getHttpNetworkInterfaces();
+ final Set<String> interfaceNames =
interfaces.values().stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
+ // Add Server Connectors based on configured Network Interface Names
+ if (interfaceNames.isEmpty()) {
+ final ServerConnector serverConnector =
serverConnectorFactory.getServerConnector();
+ final String host = properties.isHTTPSConfigured() ?
properties.getProperty(NiFiProperties.WEB_HTTPS_HOST) :
properties.getProperty(NiFiProperties.WEB_HTTP_HOST);
+ if (StringUtils.isNotBlank(host)) {
+ serverConnector.setHost(host);
+ }
+ server.addConnector(serverConnector);
+ } else {
+ interfaceNames.stream()
+ // Map interface name properties to Network Interfaces
+ .map(interfaceName -> {
+ try {
+ return NetworkInterface.getByName(interfaceName);
+ } catch (final SocketException e) {
+ throw new
UncheckedIOException(String.format("Network Interface [%s] not found",
interfaceName), e);
+ }
+ })
+ // Map Network Interfaces to host addresses
+ .filter(Objects::nonNull)
+ .flatMap(networkInterface ->
Collections.list(networkInterface.getInetAddresses()).stream())
+ .map(InetAddress::getHostAddress)
+ // Map host addresses to Server Connectors
+ .map(host -> {
+ final ServerConnector serverConnector =
serverConnectorFactory.getServerConnector();
+ serverConnector.setHost(host);
+ return serverConnector;
+ })
+ .forEach(server::addConnector);
+ }
+ }
+
+ private Handler getStandardHandler(final NiFiProperties properties) {
+ // Standard Handler supporting an ordered sequence of Handlers invoked
until completion
+ final Handler.Collection standardHandler = new Handler.Sequence();
+
+ // Set Handler for standard response headers
+ standardHandler.addHandler(new HeaderWriterHandler());
+
+ // Validate Host Header when running with HTTPS enabled
+ if (properties.isHTTPSConfigured()) {
+ final HostHeaderHandler hostHeaderHandler = new
HostHeaderHandler(properties);
+ standardHandler.addHandler(hostHeaderHandler);
+ }
+
+ return standardHandler;
+ }
+}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/StandardRequestFilterProvider.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/StandardRequestFilterProvider.java
index 471d2b3194..364773734c 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/StandardRequestFilterProvider.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/StandardRequestFilterProvider.java
@@ -24,17 +24,9 @@ import
org.apache.nifi.web.security.requests.ContentLengthFilter;
import org.apache.nifi.web.server.log.RequestAuthenticationFilter;
import org.eclipse.jetty.ee10.servlet.FilterHolder;
import org.eclipse.jetty.ee10.servlets.DoSFilter;
-import org.springframework.security.web.header.HeaderWriter;
-import org.springframework.security.web.header.HeaderWriterFilter;
-import
org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
-import org.springframework.security.web.header.writers.HstsHeaderWriter;
-import
org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter;
-import
org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
-import
org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import jakarta.servlet.Filter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@@ -45,8 +37,6 @@ import java.util.concurrent.TimeUnit;
public class StandardRequestFilterProvider implements RequestFilterProvider {
private static final int MAX_CONTENT_SIZE_DISABLED = 0;
- private static final String STANDARD_CONTENT_POLICY = "frame-ancestors
'self'";
-
/**
* Get Filters using provided NiFi Properties
*
@@ -63,8 +53,6 @@ public class StandardRequestFilterProvider implements
RequestFilterProvider {
filters.add(getFilterHolder(RequestAuthenticationFilter.class));
}
- filters.add(getHeaderWriterFilter());
-
final int maxContentSize = getMaxContentSize(properties);
if (maxContentSize > MAX_CONTENT_SIZE_DISABLED) {
final FilterHolder contentLengthFilter =
getContentLengthFilter(maxContentSize);
@@ -93,21 +81,6 @@ public class StandardRequestFilterProvider implements
RequestFilterProvider {
return filter;
}
- private FilterHolder getHeaderWriterFilter() {
- final List<HeaderWriter> headerWriters = Arrays.asList(
- new ContentSecurityPolicyHeaderWriter(STANDARD_CONTENT_POLICY),
- new HstsHeaderWriter(),
- new XContentTypeOptionsHeaderWriter(),
- new
XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN),
- new XXssProtectionHeaderWriter()
- );
-
- final HeaderWriterFilter headerWriterFilter = new
HeaderWriterFilter(headerWriters);
- final FilterHolder filterHolder = new FilterHolder(headerWriterFilter);
- filterHolder.setName(HeaderWriterFilter.class.getSimpleName());
- return filterHolder;
- }
-
private FilterHolder getFilterHolder(final Class<? extends Filter>
filterClass) {
final FilterHolder filter = new FilterHolder(filterClass);
filter.setName(filterClass.getSimpleName());
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/handler/HeaderWriterHandler.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/handler/HeaderWriterHandler.java
new file mode 100644
index 0000000000..e2db859c54
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/handler/HeaderWriterHandler.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.server.handler;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Handler;
+
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
+
+/**
+ * HTTP Response Header Writer Handler applies standard headers to HTTP
responses
+ */
+public class HeaderWriterHandler extends Handler.Wrapper {
+ private static final String CONTENT_SECURITY_POLICY_HEADER =
"Content-Security-Policy";
+ private static final String CONTENT_SECURITY_POLICY = "frame-ancestors
'self'";
+
+ private static final String FRAME_OPTIONS_HEADER = "X-Frame-Options";
+ private static final String FRAME_OPTIONS = "SAMEORIGIN";
+
+ private static final String STRICT_TRANSPORT_SECURITY_HEADER =
"Strict-Transport-Security";
+ private static final String STRICT_TRANSPORT_SECURITY = "max-age=31540000";
+
+ private static final String XSS_PROTECTION_HEADER = "X-XSS-Protection";
+ private static final String XSS_PROTECTION = "1; mode=block";
+
+ /**
+ * Handle requests and set HTTP response headers
+ *
+ * @param request Jetty Request
+ * @param response Jetty Response
+ * @param callback Jetty Callback
+ * @return Handled status
+ * @throws Exception Thrown on failures from subsequent handlers
+ */
+ @Override
+ public boolean handle(final Request request, final Response response,
final Callback callback) throws Exception {
+ final HttpFields.Mutable responseHeaders = response.getHeaders();
+ responseHeaders.put(CONTENT_SECURITY_POLICY_HEADER,
CONTENT_SECURITY_POLICY);
+ responseHeaders.put(FRAME_OPTIONS_HEADER, FRAME_OPTIONS);
+ responseHeaders.put(XSS_PROTECTION_HEADER, XSS_PROTECTION);
+
+ if (request.isSecure()) {
+ responseHeaders.put(STRICT_TRANSPORT_SECURITY_HEADER,
STRICT_TRANSPORT_SECURITY);
+ }
+
+ return super.handle(request, response, callback);
+ }
+}
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
new file mode 100644
index 0000000000..093fb0ffae
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/StandardServerProviderTest.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.server;
+
+import org.apache.nifi.jetty.configuration.connector.ApplicationLayerProtocol;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.server.handler.HeaderWriterHandler;
+import org.eclipse.jetty.rewrite.handler.RewriteHandler;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Server;
+import org.junit.jupiter.api.Test;
+
+import javax.net.ssl.SSLContext;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class StandardServerProviderTest {
+
+ private static final String RANDOM_PORT = "0";
+
+ private static final String SSL_PROTOCOL = "ssl";
+
+ @Test
+ void testGetServer() {
+ final Properties applicationProperties = new Properties();
+ applicationProperties.setProperty(NiFiProperties.WEB_HTTP_PORT,
RANDOM_PORT);
+ final NiFiProperties properties =
NiFiProperties.createBasicNiFiProperties(null, applicationProperties);
+
+ final StandardServerProvider provider = new
StandardServerProvider(null);
+
+ final Server server = provider.getServer(properties);
+
+ assertStandardConfigurationFound(server);
+ assertHttpConnectorFound(server);
+ }
+
+ @Test
+ void testGetServerHttps() throws NoSuchAlgorithmException {
+ final Properties applicationProperties = new Properties();
+ applicationProperties.setProperty(NiFiProperties.WEB_HTTPS_PORT,
RANDOM_PORT);
+ final NiFiProperties properties =
NiFiProperties.createBasicNiFiProperties(null, applicationProperties);
+
+ final SSLContext sslContext = SSLContext.getDefault();
+ final StandardServerProvider provider = new
StandardServerProvider(sslContext);
+
+ final Server server = provider.getServer(properties);
+
+ assertStandardConfigurationFound(server);
+ assertHttpsConnectorFound(server);
+ }
+
+ @Test
+ void testGetServerStart() throws Exception {
+ final Properties applicationProperties = new Properties();
+ applicationProperties.setProperty(NiFiProperties.WEB_HTTP_PORT,
RANDOM_PORT);
+ final NiFiProperties properties =
NiFiProperties.createBasicNiFiProperties(null, applicationProperties);
+
+ final StandardServerProvider provider = new
StandardServerProvider(null);
+
+ final Server server = provider.getServer(properties);
+
+ assertStandardConfigurationFound(server);
+ assertHttpConnectorFound(server);
+
+ try {
+ server.start();
+
+ assertFalse(server.isFailed());
+ } finally {
+ server.stop();
+ }
+ }
+
+ void assertHttpConnectorFound(final Server server) {
+ final Connector[] connectors = server.getConnectors();
+ assertNotNull(connectors);
+ final Connector connector = connectors[0];
+ final List<String> protocols = connector.getProtocols();
+ assertEquals(ApplicationLayerProtocol.HTTP_1_1.getProtocol(),
protocols.getFirst());
+ }
+
+ void assertHttpsConnectorFound(final Server server) {
+ final Connector[] connectors = server.getConnectors();
+ assertNotNull(connectors);
+ final Connector connector = connectors[0];
+ final List<String> protocols = connector.getProtocols();
+ assertEquals(SSL_PROTOCOL, protocols.getFirst());
+ }
+
+ void assertStandardConfigurationFound(final Server server) {
+ assertNotNull(server);
+ assertHandlersFound(server);
+
+ final RequestLog requestLog = server.getRequestLog();
+ assertNotNull(requestLog);
+ }
+
+ void assertHandlersFound(final Server server) {
+ final Handler serverHandler = server.getHandler();
+ assertInstanceOf(Handler.Collection.class, serverHandler);
+
+ Handler defaultHandler = server.getDefaultHandler();
+ assertInstanceOf(RewriteHandler.class, defaultHandler);
+
+ final Handler.Collection handlerCollection = (Handler.Collection)
serverHandler;
+ final HeaderWriterHandler headerWriterHandler =
handlerCollection.getDescendant(HeaderWriterHandler.class);
+ assertNotNull(headerWriterHandler);
+ }
+}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProviderTest.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProviderTest.java
index bf7059532a..cc05b119f5 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProviderTest.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProviderTest.java
@@ -21,7 +21,6 @@ import org.eclipse.jetty.ee10.servlet.FilterHolder;
import org.eclipse.jetty.ee10.servlets.DoSFilter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.springframework.security.web.header.HeaderWriterFilter;
import jakarta.servlet.Filter;
import java.util.Collections;
@@ -56,7 +55,6 @@ public class RestApiRequestFilterProviderTest {
assertNotNull(filters);
assertFalse(filters.isEmpty());
- assertFilterClassFound(filters, HeaderWriterFilter.class);
assertFilterClassFound(filters, DataTransferExcludedDoSFilter.class);
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/StandardRequestFilterProviderTest.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/StandardRequestFilterProviderTest.java
index a0dc2b8e90..51380af793 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/StandardRequestFilterProviderTest.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/StandardRequestFilterProviderTest.java
@@ -22,7 +22,6 @@ import
org.apache.nifi.web.server.log.RequestAuthenticationFilter;
import org.eclipse.jetty.ee10.servlet.FilterHolder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.springframework.security.web.header.HeaderWriterFilter;
import jakarta.servlet.Filter;
import java.util.Collections;
@@ -81,7 +80,7 @@ public class StandardRequestFilterProviderTest {
assertFilterClassFound(filters, RequestAuthenticationFilter.class);
- final FilterHolder firstFilterHolder = filters.get(0);
+ final FilterHolder firstFilterHolder = filters.getFirst();
final Class<? extends Filter> firstFilterClass =
firstFilterHolder.getHeldClass();
assertEquals(RequestAuthenticationFilter.class, firstFilterClass);
}
@@ -90,7 +89,6 @@ public class StandardRequestFilterProviderTest {
assertNotNull(filters);
assertFalse(filters.isEmpty());
- assertFilterClassFound(filters, HeaderWriterFilter.class);
assertFilterClassFound(filters, DataTransferExcludedDoSFilter.class);
}