mirror of https://github.com/apache/nifi.git
NIFI-5146 Only support HTTP or HTTPS operation for NiFi API/UI
- Added logic to check for simultaneous configuration of HTTP and HTTPS connectors in JettyServer. - Added test logging resources. Added unit tests. - Refactored shared functionality to generic method which accepts lambdas. Fixed unit test with logging side effects. - Added note about exclusive HTTP/HTTPS behavior to Admin Guide. Fixed typos. This closes #2683. Signed-off-by: Kevin Doran <kdoran@apache.org>
This commit is contained in:
parent
92b4a3208f
commit
7a4990e7fe
|
@ -148,7 +148,7 @@ should run on. If it is desired that the HTTPS interface be accessible from all
|
|||
admins to configure the application to run only on specific network interfaces, `nifi.web.http.network.interface*` or `nifi.web.https.network.interface*`
|
||||
properties can be specified.
|
||||
|
||||
NOTE: It is important when enabling HTTPS that the `nifi.web.http.port` property be unset.
|
||||
NOTE: It is important when enabling HTTPS that the `nifi.web.http.port` property be unset. NiFi only supports running on HTTP *or* HTTPS, not both simultaneously.
|
||||
|
||||
Similar to `nifi.security.needClientAuth`, the web server can be configured to require certificate based client authentication for users accessing
|
||||
the User Interface. In order to do this it must be configured to not support username/password authentication using <<ldap_login_identity_provider>> or <<kerberos_login_identity_provider>>. Either of these options
|
||||
|
@ -1967,8 +1967,8 @@ they must be set the same on every instance in the cluster.
|
|||
|
||||
For each Node, the minimum properties to configure are as follows:
|
||||
|
||||
* Under the _Web Properties_ section, set either the http or https port that you want the Node to run on.
|
||||
Also, consider whether you need to set the http or https host property.
|
||||
* Under the _Web Properties_ section, set either the HTTP or HTTPS port that you want the Node to run on.
|
||||
Also, consider whether you need to set the HTTP or HTTPS host property. All nodes in the cluster should use the same protocol setting.
|
||||
* Under the _State Management section_, set the `nifi.state.management.provider.cluster` property
|
||||
to the identifier of the Cluster State Provider. Ensure that the Cluster State Provider has been
|
||||
configured in the _state-management.xml_ file. See <<state_providers>> for more information.
|
||||
|
@ -2164,7 +2164,7 @@ In order to secure the communications, we need to ensure that both the client an
|
|||
NiFi ZooKeeper client and embedded ZooKeeper server to use Kerberos are provided below.
|
||||
|
||||
If Kerberos is not already setup in your environment, you can find information on installing and setting up a Kerberos Server at
|
||||
link:https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Managing_Smart_Cards/Configuring_a_Kerberos_5_Server.html[https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Managing_Smart_Cards/Configuring_a_Kerberos_5_Server.html^]. This guide assumes that Kerberos already has been installed in the environment in which NiFi is running.
|
||||
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Managing_Smart_Cards/Configuring_a_Kerberos_5_Server.html[Red Hat Customer Portal: Configuring a Kerberos 5 Server]. This guide assumes that Kerberos already has been installed in the environment in which NiFi is running.
|
||||
|
||||
Note, the following procedures for kerberizing an Embedded ZooKeeper server in your NiFi Node and kerberizing a ZooKeeper NiFi client will require that
|
||||
Kerberos client libraries be installed. This is accomplished in Fedora-based Linux distributions via:
|
||||
|
@ -2652,7 +2652,7 @@ documentation of the proxy for guidance for your deployment environment and use
|
|||
|
||||
** By default, if NiFi is running securely it will only accept HTTP requests with a Host header matching the host[:port] that it is bound to. If NiFi is to accept requests directed to a different
|
||||
host[:port] the expected values need to be configured. This may be required when running behind a proxy or in a containerized environment. This is configured in a comma
|
||||
separated list in _nifi.properties_ using the `nifi.web.proxy.host` property (e.g. localhost:18443, proxyhost:443). IPv6 addressed are accepted. Please refer to
|
||||
separated list in _nifi.properties_ using the `nifi.web.proxy.host` property (e.g. localhost:18443, proxyhost:443). IPv6 addresses are accepted. Please refer to
|
||||
RFC 5952 Sections link:https://tools.ietf.org/html/rfc5952#section-4[4] and link:https://tools.ietf.org/html/rfc5952#section-6[6] for additional details.
|
||||
|
||||
** NiFi will only accept HTTP requests with a X-ProxyContextPath or X-Forwarded-Context header if the value is whitelisted in the `nifi.web.proxy.context.path` property in
|
||||
|
|
|
@ -22,7 +22,27 @@
|
|||
</parent>
|
||||
<artifactId>nifi-jetty</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>2.4.13</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-api</artifactId>
|
||||
|
@ -168,6 +188,12 @@
|
|||
<artifactId>nifi-framework-cluster</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.stefanbirkner</groupId>
|
||||
<artifactId>system-rules</artifactId>
|
||||
<version>1.16.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
|
|
@ -173,6 +173,14 @@ public class JettyServer implements NiFiServer {
|
|||
server.setHandler(allHandlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates this object but does not perform any configuration. Used for unit testing.
|
||||
*/
|
||||
JettyServer(Server server, NiFiProperties properties) {
|
||||
this.server = server;
|
||||
this.props = properties;
|
||||
}
|
||||
|
||||
private Handler loadWars(final Set<Bundle> bundles) {
|
||||
|
||||
// load WARs
|
||||
|
@ -601,106 +609,143 @@ public class JettyServer implements NiFiServer {
|
|||
httpConfiguration.setRequestHeaderSize(headerSize);
|
||||
httpConfiguration.setResponseHeaderSize(headerSize);
|
||||
|
||||
if (props.getPort() != null) {
|
||||
final Integer port = props.getPort();
|
||||
if (port < 0 || (int) Math.pow(2, 16) <= port) {
|
||||
throw new ServerConfigurationException("Invalid HTTP port: " + port);
|
||||
}
|
||||
|
||||
logger.info("Configuring Jetty for HTTP on port: " + port);
|
||||
|
||||
final List<Connector> serverConnectors = Lists.newArrayList();
|
||||
|
||||
final Map<String, String> httpNetworkInterfaces = props.getHttpNetworkInterfaces();
|
||||
if (httpNetworkInterfaces.isEmpty() || httpNetworkInterfaces.values().stream().filter(value -> !Strings.isNullOrEmpty(value)).collect(Collectors.toList()).isEmpty()) {
|
||||
// create the connector
|
||||
final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
|
||||
// set host and port
|
||||
if (StringUtils.isNotBlank(props.getProperty(NiFiProperties.WEB_HTTP_HOST))) {
|
||||
http.setHost(props.getProperty(NiFiProperties.WEB_HTTP_HOST));
|
||||
}
|
||||
http.setPort(port);
|
||||
serverConnectors.add(http);
|
||||
} else {
|
||||
// add connectors for all IPs from http network interfaces
|
||||
serverConnectors.addAll(Lists.newArrayList(httpNetworkInterfaces.values().stream().map(ifaceName -> {
|
||||
NetworkInterface iface = null;
|
||||
try {
|
||||
iface = NetworkInterface.getByName(ifaceName);
|
||||
} catch (SocketException e) {
|
||||
logger.error("Unable to get network interface by name {}", ifaceName, e);
|
||||
}
|
||||
if (iface == null) {
|
||||
logger.warn("Unable to find network interface named {}", ifaceName);
|
||||
}
|
||||
return iface;
|
||||
}).filter(Objects::nonNull).flatMap(iface -> Collections.list(iface.getInetAddresses()).stream())
|
||||
.map(inetAddress -> {
|
||||
// create the connector
|
||||
final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
|
||||
// set host and port
|
||||
http.setHost(inetAddress.getHostAddress());
|
||||
http.setPort(port);
|
||||
return http;
|
||||
}).collect(Collectors.toList())));
|
||||
}
|
||||
// add all connectors
|
||||
serverConnectors.forEach(server::addConnector);
|
||||
// Check if both HTTP and HTTPS connectors are configured and fail if both are configured
|
||||
if (bothHttpAndHttpsConnectorsConfigured(props)) {
|
||||
logger.error("NiFi only supports one mode of HTTP or HTTPS operation, not both simultaneously. " +
|
||||
"Check the nifi.properties file and ensure that either the HTTP hostname and port or the HTTPS hostname and port are empty");
|
||||
startUpFailure(new IllegalStateException("Only one of the HTTP and HTTPS connectors can be configured at one time"));
|
||||
}
|
||||
|
||||
if (props.getSslPort() != null) {
|
||||
final Integer port = props.getSslPort();
|
||||
if (port < 0 || (int) Math.pow(2, 16) <= port) {
|
||||
throw new ServerConfigurationException("Invalid HTTPs port: " + port);
|
||||
}
|
||||
|
||||
logger.info("Configuring Jetty for HTTPs on port: " + port);
|
||||
|
||||
final List<Connector> serverConnectors = Lists.newArrayList();
|
||||
|
||||
final Map<String, String> httpsNetworkInterfaces = props.getHttpsNetworkInterfaces();
|
||||
if (httpsNetworkInterfaces.isEmpty() || httpsNetworkInterfaces.values().stream().filter(value -> !Strings.isNullOrEmpty(value)).collect(Collectors.toList()).isEmpty()) {
|
||||
final ServerConnector https = createUnconfiguredSslServerConnector(server, httpConfiguration);
|
||||
|
||||
// set host and port
|
||||
if (StringUtils.isNotBlank(props.getProperty(NiFiProperties.WEB_HTTPS_HOST))) {
|
||||
https.setHost(props.getProperty(NiFiProperties.WEB_HTTPS_HOST));
|
||||
}
|
||||
https.setPort(port);
|
||||
serverConnectors.add(https);
|
||||
} else {
|
||||
// add connectors for all IPs from https network interfaces
|
||||
serverConnectors.addAll(Lists.newArrayList(httpsNetworkInterfaces.values().stream().map(ifaceName -> {
|
||||
NetworkInterface iface = null;
|
||||
try {
|
||||
iface = NetworkInterface.getByName(ifaceName);
|
||||
} catch (SocketException e) {
|
||||
logger.error("Unable to get network interface by name {}", ifaceName, e);
|
||||
}
|
||||
if (iface == null) {
|
||||
logger.warn("Unable to find network interface named {}", ifaceName);
|
||||
}
|
||||
return iface;
|
||||
}).filter(Objects::nonNull).flatMap(iface -> Collections.list(iface.getInetAddresses()).stream())
|
||||
.map(inetAddress -> {
|
||||
final ServerConnector https = createUnconfiguredSslServerConnector(server, httpConfiguration);
|
||||
|
||||
// set host and port
|
||||
https.setHost(inetAddress.getHostAddress());
|
||||
https.setPort(port);
|
||||
return https;
|
||||
}).collect(Collectors.toList())));
|
||||
}
|
||||
// add all connectors
|
||||
serverConnectors.forEach(server::addConnector);
|
||||
configureHttpsConnector(server, httpConfiguration);
|
||||
} else if (props.getPort() != null) {
|
||||
configureHttpConnector(server, httpConfiguration);
|
||||
} else {
|
||||
logger.error("Neither the HTTP nor HTTPS connector was configured in nifi.properties");
|
||||
startUpFailure(new IllegalStateException("Must configure HTTP or HTTPS connector"));
|
||||
}
|
||||
}
|
||||
|
||||
private ServerConnector createUnconfiguredSslServerConnector(Server server, HttpConfiguration httpConfiguration) {
|
||||
/**
|
||||
* Configures an HTTPS connector and adds it to the server.
|
||||
*
|
||||
* @param server the Jetty server instance
|
||||
* @param httpConfiguration the configuration object for the HTTPS protocol settings
|
||||
*/
|
||||
private void configureHttpsConnector(Server server, HttpConfiguration httpConfiguration) {
|
||||
String hostname = props.getProperty(NiFiProperties.WEB_HTTPS_HOST);
|
||||
final Integer port = props.getSslPort();
|
||||
String connectorLabel = "HTTPS";
|
||||
final Map<String, String> httpsNetworkInterfaces = props.getHttpsNetworkInterfaces();
|
||||
ServerConnectorCreator<Server, HttpConfiguration, ServerConnector> scc = (s, c) -> createUnconfiguredSslServerConnector(s, c, port);
|
||||
|
||||
configureGenericConnector(server, httpConfiguration, hostname, port, connectorLabel, httpsNetworkInterfaces, scc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an HTTP connector and adds it to the server.
|
||||
*
|
||||
* @param server the Jetty server instance
|
||||
* @param httpConfiguration the configuration object for the HTTP protocol settings
|
||||
*/
|
||||
private void configureHttpConnector(Server server, HttpConfiguration httpConfiguration) {
|
||||
String hostname = props.getProperty(NiFiProperties.WEB_HTTP_HOST);
|
||||
final Integer port = props.getPort();
|
||||
String connectorLabel = "HTTP";
|
||||
final Map<String, String> httpNetworkInterfaces = props.getHttpNetworkInterfaces();
|
||||
ServerConnectorCreator<Server, HttpConfiguration, ServerConnector> scc = (s, c) -> new ServerConnector(s, new HttpConnectionFactory(c));
|
||||
|
||||
configureGenericConnector(server, httpConfiguration, hostname, port, connectorLabel, httpNetworkInterfaces, scc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an HTTP(S) connector for the server given the provided parameters. The functionality between HTTP and HTTPS connectors is largely similar.
|
||||
* Here the common behavior has been extracted into a shared method and the respective calling methods obtain the right values and a lambda function for the differing behavior.
|
||||
*
|
||||
* @param server the Jetty server instance
|
||||
* @param configuration the HTTP/HTTPS configuration instance
|
||||
* @param hostname the hostname from the nifi.properties file
|
||||
* @param port the port to expose
|
||||
* @param connectorLabel used for log output (e.g. "HTTP" or "HTTPS")
|
||||
* @param networkInterfaces the map of network interfaces from nifi.properties
|
||||
* @param serverConnectorCreator a function which accepts a {@code Server} and {@code HttpConnection} instance and returns a {@code ServerConnector}
|
||||
*/
|
||||
private void configureGenericConnector(Server server, HttpConfiguration configuration, String hostname, Integer port, String connectorLabel, Map<String, String> networkInterfaces,
|
||||
ServerConnectorCreator<Server, HttpConfiguration, ServerConnector> serverConnectorCreator) {
|
||||
if (port < 0 || (int) Math.pow(2, 16) <= port) {
|
||||
throw new ServerConfigurationException("Invalid " + connectorLabel + " port: " + port);
|
||||
}
|
||||
|
||||
logger.info("Configuring Jetty for " + connectorLabel + " on port: " + port);
|
||||
|
||||
final List<Connector> serverConnectors = Lists.newArrayList();
|
||||
|
||||
// If the interfaces collection is empty or each element is empty
|
||||
if (networkInterfaces.isEmpty() || networkInterfaces.values().stream().filter(value -> !Strings.isNullOrEmpty(value)).collect(Collectors.toList()).isEmpty()) {
|
||||
final ServerConnector serverConnector = serverConnectorCreator.create(server, configuration);
|
||||
|
||||
// Set host and port
|
||||
if (StringUtils.isNotBlank(hostname)) {
|
||||
serverConnector.setHost(hostname);
|
||||
}
|
||||
serverConnector.setPort(port);
|
||||
serverConnectors.add(serverConnector);
|
||||
} else {
|
||||
// Add connectors for all IPs from network interfaces
|
||||
serverConnectors.addAll(Lists.newArrayList(networkInterfaces.values().stream().map(ifaceName -> {
|
||||
NetworkInterface iface = null;
|
||||
try {
|
||||
iface = NetworkInterface.getByName(ifaceName);
|
||||
} catch (SocketException e) {
|
||||
logger.error("Unable to get network interface by name {}", ifaceName, e);
|
||||
}
|
||||
if (iface == null) {
|
||||
logger.warn("Unable to find network interface named {}", ifaceName);
|
||||
}
|
||||
return iface;
|
||||
}).filter(Objects::nonNull).flatMap(iface -> Collections.list(iface.getInetAddresses()).stream())
|
||||
.map(inetAddress -> {
|
||||
final ServerConnector serverConnector = serverConnectorCreator.create(server, configuration);
|
||||
|
||||
// Set host and port
|
||||
serverConnector.setHost(inetAddress.getHostAddress());
|
||||
serverConnector.setPort(port);
|
||||
return serverConnector;
|
||||
}).collect(Collectors.toList())));
|
||||
}
|
||||
// Add all connectors
|
||||
serverConnectors.forEach(server::addConnector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are configured properties for both HTTP and HTTPS connectors (specifically port because the hostname can be left blank in the HTTP connector).
|
||||
* Prints a warning log message with the relevant properties.
|
||||
*
|
||||
* @param props the NiFiProperties
|
||||
* @return true if both ports are present
|
||||
*/
|
||||
static boolean bothHttpAndHttpsConnectorsConfigured(NiFiProperties props) {
|
||||
Integer httpPort = props.getPort();
|
||||
String httpHostname = props.getProperty(NiFiProperties.WEB_HTTP_HOST);
|
||||
|
||||
Integer httpsPort = props.getSslPort();
|
||||
String httpsHostname = props.getProperty(NiFiProperties.WEB_HTTPS_HOST);
|
||||
|
||||
if (httpPort != null && httpsPort != null) {
|
||||
logger.warn("Both the HTTP and HTTPS connectors are configured in nifi.properties. Only one of these connectors should be configured. See the NiFi Admin Guide for more details");
|
||||
logger.warn("HTTP connector: http://" + httpHostname + ":" + httpPort);
|
||||
logger.warn("HTTPS connector: https://" + httpsHostname + ":" + httpsPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private ServerConnector createUnconfiguredSslServerConnector(Server server, HttpConfiguration httpConfiguration, int port) {
|
||||
// add some secure config
|
||||
final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration);
|
||||
httpsConfiguration.setSecureScheme("https");
|
||||
httpsConfiguration.setSecurePort(props.getSslPort());
|
||||
httpsConfiguration.setSecurePort(port);
|
||||
httpsConfiguration.addCustomizer(new SecureRequestCustomizer());
|
||||
|
||||
// build the connector
|
||||
|
@ -990,3 +1035,8 @@ public class JettyServer implements NiFiServer {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ServerConnectorCreator<Server, HttpConfiguration, ServerConnector> {
|
||||
ServerConnector create(Server server, HttpConfiguration httpConfiguration);
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* 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.log4j.AppenderSkeleton
|
||||
import org.apache.log4j.spi.LoggingEvent
|
||||
import org.apache.nifi.bundle.Bundle
|
||||
import org.apache.nifi.properties.StandardNiFiProperties
|
||||
import org.apache.nifi.util.NiFiProperties
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.eclipse.jetty.server.Connector
|
||||
import org.eclipse.jetty.server.HttpConfiguration
|
||||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.server.ServerConnector
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.contrib.java.lang.system.Assertion
|
||||
import org.junit.contrib.java.lang.system.ExpectedSystemExit
|
||||
import org.junit.contrib.java.lang.system.SystemErrRule
|
||||
import org.junit.contrib.java.lang.system.SystemOutRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import java.security.Security
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
class JettyServerGroovyTest extends GroovyTestCase {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JettyServerGroovyTest.class)
|
||||
|
||||
@Rule
|
||||
public final ExpectedSystemExit exit = ExpectedSystemExit.none()
|
||||
|
||||
@Rule
|
||||
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog()
|
||||
|
||||
@Rule
|
||||
public final SystemErrRule systemErrRule = new SystemErrRule().enableLog()
|
||||
|
||||
@BeforeClass
|
||||
static void setUpOnce() throws Exception {
|
||||
Security.addProvider(new BouncyCastleProvider())
|
||||
|
||||
logger.metaClass.methodMissing = { String name, args ->
|
||||
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
|
||||
}
|
||||
|
||||
TestAppender.reset()
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
static void tearDownOnce() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Before
|
||||
void setUp() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
void tearDown() throws Exception {
|
||||
TestAppender.reset()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldDetectHttpAndHttpsConfigurationsBothPresent() {
|
||||
// Arrange
|
||||
Map badProps = [
|
||||
(NiFiProperties.WEB_HTTP_HOST) : "localhost",
|
||||
(NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
|
||||
(NiFiProperties.WEB_THREADS) : NiFiProperties.DEFAULT_WEB_THREADS
|
||||
]
|
||||
NiFiProperties mockProps = [
|
||||
getPort : { -> 8080 },
|
||||
getSslPort : { -> 8443 },
|
||||
getProperty: { String prop ->
|
||||
String value = badProps[prop] ?: "no_value"
|
||||
logger.mock("getProperty(${prop}) -> ${value}")
|
||||
value
|
||||
},
|
||||
] as StandardNiFiProperties
|
||||
|
||||
// Act
|
||||
boolean bothConfigsPresent = JettyServer.bothHttpAndHttpsConnectorsConfigured(mockProps)
|
||||
logger.info("Both configs present: ${bothConfigsPresent}")
|
||||
def log = TestAppender.getLogLines()
|
||||
|
||||
// Assert
|
||||
assert bothConfigsPresent
|
||||
assert !log.isEmpty()
|
||||
assert log.first() =~ "Both the HTTP and HTTPS connectors are configured in nifi.properties. Only one of these connectors should be configured. See the NiFi Admin Guide for more details"
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDetectHttpAndHttpsConfigurationsShouldAllowEither() {
|
||||
// Arrange
|
||||
Map httpMap = [
|
||||
(NiFiProperties.WEB_HTTP_HOST) : "localhost",
|
||||
(NiFiProperties.WEB_HTTPS_HOST): null,
|
||||
]
|
||||
NiFiProperties httpProps = [
|
||||
getPort : { -> 8080 },
|
||||
getSslPort : { -> null },
|
||||
getProperty: { String prop ->
|
||||
String value = httpMap[prop] ?: "no_value"
|
||||
logger.mock("getProperty(${prop}) -> ${value}")
|
||||
value
|
||||
},
|
||||
] as StandardNiFiProperties
|
||||
|
||||
Map httpsMap = [
|
||||
(NiFiProperties.WEB_HTTP_HOST) : null,
|
||||
(NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
|
||||
]
|
||||
NiFiProperties httpsProps = [
|
||||
getPort : { -> null },
|
||||
getSslPort : { -> 8443 },
|
||||
getProperty: { String prop ->
|
||||
String value = httpsMap[prop] ?: "no_value"
|
||||
logger.mock("getProperty(${prop}) -> ${value}")
|
||||
value
|
||||
},
|
||||
] as StandardNiFiProperties
|
||||
|
||||
// Act
|
||||
boolean bothConfigsPresentForHttp = JettyServer.bothHttpAndHttpsConnectorsConfigured(httpProps)
|
||||
logger.info("Both configs present for HTTP properties: ${bothConfigsPresentForHttp}")
|
||||
|
||||
boolean bothConfigsPresentForHttps = JettyServer.bothHttpAndHttpsConnectorsConfigured(httpsProps)
|
||||
logger.info("Both configs present for HTTPS properties: ${bothConfigsPresentForHttps}")
|
||||
def log = TestAppender.getLogLines()
|
||||
|
||||
// Assert
|
||||
assert !bothConfigsPresentForHttp
|
||||
assert !bothConfigsPresentForHttps
|
||||
|
||||
// Verifies that the warning was not logged
|
||||
assert log.size() == 2
|
||||
assert log.first() == "Both configs present for HTTP properties: false"
|
||||
assert log.last() == "Both configs present for HTTPS properties: false"
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldFailToStartWithHttpAndHttpsConfigurationsBothPresent() {
|
||||
// Arrange
|
||||
Map badProps = [
|
||||
(NiFiProperties.WEB_HTTP_HOST) : "localhost",
|
||||
(NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
|
||||
]
|
||||
NiFiProperties mockProps = [
|
||||
getPort : { -> 8080 },
|
||||
getSslPort : { -> 8443 },
|
||||
getProperty: { String prop ->
|
||||
String value = badProps[prop] ?: "no_value"
|
||||
logger.mock("getProperty(${prop}) -> ${value}")
|
||||
value
|
||||
},
|
||||
getWebThreads: { -> NiFiProperties.DEFAULT_WEB_THREADS },
|
||||
getWebMaxHeaderSize: { -> NiFiProperties.DEFAULT_WEB_MAX_HEADER_SIZE },
|
||||
isHTTPSConfigured: { -> true }
|
||||
] as StandardNiFiProperties
|
||||
|
||||
// The web server should fail to start and exit Java
|
||||
exit.expectSystemExitWithStatus(1)
|
||||
exit.checkAssertionAfterwards(new Assertion() {
|
||||
void checkAssertion() {
|
||||
final String standardErr = systemErrRule.getLog()
|
||||
List<String> errLines = standardErr.split("\n")
|
||||
|
||||
assert errLines.any { it =~ "Failed to start web server: "}
|
||||
assert errLines.any { it =~ "Shutting down..."}
|
||||
}
|
||||
})
|
||||
|
||||
// Act
|
||||
JettyServer jettyServer = new JettyServer(mockProps, [] as Set<Bundle>)
|
||||
|
||||
// Assert
|
||||
|
||||
// Assertions defined above
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldConfigureHTTPSConnector() {
|
||||
// Arrange
|
||||
NiFiProperties httpsProps = new StandardNiFiProperties(rawProperties: new Properties([
|
||||
// (NiFiProperties.WEB_HTTP_PORT): null,
|
||||
// (NiFiProperties.WEB_HTTP_HOST): null,
|
||||
(NiFiProperties.WEB_HTTPS_PORT): "8443",
|
||||
(NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
|
||||
]))
|
||||
|
||||
Server internalServer = new Server()
|
||||
JettyServer jetty = new JettyServer(internalServer, httpsProps)
|
||||
|
||||
// Act
|
||||
jetty.configureHttpsConnector(internalServer, new HttpConfiguration())
|
||||
List<Connector> connectors = Arrays.asList(internalServer.connectors)
|
||||
|
||||
// Assert
|
||||
assert connectors.size() == 1
|
||||
ServerConnector connector = connectors.first() as ServerConnector
|
||||
assert connector.host == "secure.host.com"
|
||||
assert connector.port == 8443
|
||||
}
|
||||
}
|
||||
|
||||
class TestAppender extends AppenderSkeleton {
|
||||
static final List<LoggingEvent> events = new ArrayList<>()
|
||||
|
||||
@Override
|
||||
protected void append(LoggingEvent e) {
|
||||
synchronized (events) {
|
||||
events.add(e)
|
||||
}
|
||||
}
|
||||
|
||||
static void reset() {
|
||||
synchronized (events) {
|
||||
events.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean requiresLayout() {
|
||||
return false
|
||||
}
|
||||
|
||||
static List<String> getLogLines() {
|
||||
synchronized (events) {
|
||||
events.collect { LoggingEvent le -> le.getRenderedMessage() }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
log4j.rootLogger=DEBUG,console,test
|
||||
|
||||
log4j.appender.console=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.console.Target=System.err
|
||||
log4j.appender.console.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n
|
||||
|
||||
log4j.appender.test=org.apache.nifi.web.server.TestAppender
|
||||
log4j.appender.test.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.test.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n
|
Loading…
Reference in New Issue