mirror of https://github.com/apache/nifi.git
NIFI-3869 Added HTTP/2 support to ListenHTTP and HandleHttpRequest
Signed-off-by: Nathan Gough <thenatog@gmail.com> This closes #6048.
This commit is contained in:
parent
38b51b0dde
commit
e0976f42d3
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.apache.nifi</groupId>
|
||||||
|
<artifactId>nifi-commons</artifactId>
|
||||||
|
<version>1.17.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>nifi-jetty-configuration</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.http2</groupId>
|
||||||
|
<artifactId>http2-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-alpn-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.jetty.configuration.connector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application Layer Protocols supported for Server Connectors
|
||||||
|
*/
|
||||||
|
public enum ApplicationLayerProtocol {
|
||||||
|
HTTP_1_1("http/1.1"),
|
||||||
|
|
||||||
|
H2("h2");
|
||||||
|
|
||||||
|
private String protocol;
|
||||||
|
|
||||||
|
ApplicationLayerProtocol(final String protocol) {
|
||||||
|
this.protocol = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProtocol() {
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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.jetty.configuration.connector;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jetty Server Connector Factory
|
||||||
|
*/
|
||||||
|
public interface ServerConnectorFactory {
|
||||||
|
/**
|
||||||
|
* Get Server Connector
|
||||||
|
*
|
||||||
|
* @return Configured Server Connector
|
||||||
|
*/
|
||||||
|
ServerConnector getServerConnector();
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* 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.jetty.configuration.connector;
|
||||||
|
|
||||||
|
import org.apache.nifi.jetty.configuration.connector.alpn.ALPNServerConnectionFactory;
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.http2.HTTP2Cipher;
|
||||||
|
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.ConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard implementation of Server Connector Factory supporting HTTP/2 and HTTP/1.1 with TLS or simple HTTP/1.1
|
||||||
|
*/
|
||||||
|
public class StandardServerConnectorFactory implements ServerConnectorFactory {
|
||||||
|
private static final boolean SEND_SERVER_VERSION = false;
|
||||||
|
|
||||||
|
private static final String[] INCLUDE_ALL_SECURITY_PROTOCOLS = new String[0];
|
||||||
|
|
||||||
|
private static final Set<ApplicationLayerProtocol> DEFAULT_APPLICATION_LAYER_PROTOCOLS = Collections.singleton(ApplicationLayerProtocol.HTTP_1_1);
|
||||||
|
|
||||||
|
private final Server server;
|
||||||
|
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
private Set<ApplicationLayerProtocol> applicationLayerProtocols = DEFAULT_APPLICATION_LAYER_PROTOCOLS;
|
||||||
|
|
||||||
|
private SSLContext sslContext;
|
||||||
|
|
||||||
|
private boolean needClientAuth;
|
||||||
|
|
||||||
|
private boolean wantClientAuth;
|
||||||
|
|
||||||
|
private String[] includeSecurityProtocols = INCLUDE_ALL_SECURITY_PROTOCOLS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard Server Connector Factory Constructor with required properties
|
||||||
|
*
|
||||||
|
* @param server Jetty Server
|
||||||
|
* @param port Secure Port Number
|
||||||
|
*/
|
||||||
|
public StandardServerConnectorFactory(
|
||||||
|
final Server server,
|
||||||
|
final int port
|
||||||
|
) {
|
||||||
|
this.server = Objects.requireNonNull(server, "Server required");
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Server Connector configured with HTTP/2 and ALPN as well as fallback to HTTP/1.1 with TLS
|
||||||
|
*
|
||||||
|
* @return Secure Server Connector
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ServerConnector getServerConnector() {
|
||||||
|
final HttpConfiguration httpConfiguration = getHttpConfiguration();
|
||||||
|
final HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(httpConfiguration);
|
||||||
|
|
||||||
|
final ServerConnector serverConnector;
|
||||||
|
if (sslContext == null) {
|
||||||
|
serverConnector = new ServerConnector(server, httpConnectionFactory);
|
||||||
|
} else {
|
||||||
|
final List<ConnectionFactory> connectionFactories = new ArrayList<>();
|
||||||
|
if (applicationLayerProtocols.contains(ApplicationLayerProtocol.H2)) {
|
||||||
|
final ALPNServerConnectionFactory alpnServerConnectionFactory = new ALPNServerConnectionFactory();
|
||||||
|
final HTTP2ServerConnectionFactory http2ServerConnectionFactory = new HTTP2ServerConnectionFactory(httpConfiguration);
|
||||||
|
|
||||||
|
connectionFactories.add(alpnServerConnectionFactory);
|
||||||
|
connectionFactories.add(http2ServerConnectionFactory);
|
||||||
|
}
|
||||||
|
// Add HTTP/1.1 Connection Factory after HTTP/2
|
||||||
|
if (applicationLayerProtocols.contains(ApplicationLayerProtocol.HTTP_1_1)) {
|
||||||
|
connectionFactories.add(httpConnectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SslConnectionFactory must be first and must indicate the next protocol
|
||||||
|
final String nextProtocol = connectionFactories.get(0).getProtocol();
|
||||||
|
final SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(getSslContextFactory(), nextProtocol);
|
||||||
|
connectionFactories.add(0, sslConnectionFactory);
|
||||||
|
|
||||||
|
final ConnectionFactory[] factories = connectionFactories.toArray(new ConnectionFactory[0]);
|
||||||
|
serverConnector = new ServerConnector(server, factories);
|
||||||
|
}
|
||||||
|
|
||||||
|
serverConnector.setPort(port);
|
||||||
|
return serverConnector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set SSL Context enables TLS communication
|
||||||
|
*
|
||||||
|
* @param sslContext SSL Context
|
||||||
|
*/
|
||||||
|
public void setSslContext(final SSLContext sslContext) {
|
||||||
|
this.sslContext = sslContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Need Client Authentication requires clients to provide certificates for mutual TLS
|
||||||
|
*
|
||||||
|
* @param needClientAuth Need Client Authentication status
|
||||||
|
*/
|
||||||
|
public void setNeedClientAuth(final boolean needClientAuth) {
|
||||||
|
this.needClientAuth = needClientAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Want Client Authentication requests clients to provide certificates for mutual TLS but does not require certificates
|
||||||
|
*
|
||||||
|
* @param wantClientAuth Want Client Authentication status
|
||||||
|
*/
|
||||||
|
public void setWantClientAuth(final boolean wantClientAuth) {
|
||||||
|
this.wantClientAuth = wantClientAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Include Security Protocols limits enabled TLS Protocols to the values provided
|
||||||
|
*
|
||||||
|
* @param includeSecurityProtocols Security Protocols with null or empty enabling all standard TLS protocol versions
|
||||||
|
*/
|
||||||
|
public void setIncludeSecurityProtocols(final String[] includeSecurityProtocols) {
|
||||||
|
this.includeSecurityProtocols = includeSecurityProtocols == null ? INCLUDE_ALL_SECURITY_PROTOCOLS : includeSecurityProtocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Application Layer Protocols applicable when TLS is enabled
|
||||||
|
*
|
||||||
|
* @param applicationLayerProtocols Protocols requires at one Application Layer Protocol
|
||||||
|
*/
|
||||||
|
public void setApplicationLayerProtocols(final Set<ApplicationLayerProtocol> applicationLayerProtocols) {
|
||||||
|
if (Objects.requireNonNull(applicationLayerProtocols, "Application Layer Protocols required").isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Application Layer Protocols not specified");
|
||||||
|
}
|
||||||
|
this.applicationLayerProtocols = applicationLayerProtocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpConfiguration getHttpConfiguration() {
|
||||||
|
final HttpConfiguration httpConfiguration = new HttpConfiguration();
|
||||||
|
|
||||||
|
if (sslContext != null) {
|
||||||
|
httpConfiguration.setSecurePort(port);
|
||||||
|
httpConfiguration.setSecureScheme(HttpScheme.HTTPS.asString());
|
||||||
|
httpConfiguration.setSendServerVersion(SEND_SERVER_VERSION);
|
||||||
|
|
||||||
|
final SecureRequestCustomizer secureRequestCustomizer = new SecureRequestCustomizer();
|
||||||
|
httpConfiguration.addCustomizer(secureRequestCustomizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SslContextFactory.Server getSslContextFactory() {
|
||||||
|
final SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||||
|
sslContextFactory.setSslContext(sslContext);
|
||||||
|
sslContextFactory.setNeedClientAuth(needClientAuth);
|
||||||
|
sslContextFactory.setWantClientAuth(wantClientAuth);
|
||||||
|
sslContextFactory.setIncludeProtocols(includeSecurityProtocols);
|
||||||
|
|
||||||
|
if (applicationLayerProtocols.contains(ApplicationLayerProtocol.H2)) {
|
||||||
|
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sslContextFactory;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.jetty.configuration.connector.alpn;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.alpn.server.ALPNServerConnection;
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALPN Server Connection Factory with standard ALPN Processor implementation
|
||||||
|
*/
|
||||||
|
public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory {
|
||||||
|
private static final String ALPN_PROTOCOL = "alpn";
|
||||||
|
|
||||||
|
private final ALPNProcessor.Server processor;
|
||||||
|
|
||||||
|
public ALPNServerConnectionFactory() {
|
||||||
|
super(ALPN_PROTOCOL);
|
||||||
|
processor = new StandardALPNProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new Server Connection and configure the connection using ALPN Processor
|
||||||
|
*
|
||||||
|
* @param connector Connector for the Connection
|
||||||
|
* @param endPoint End Point for the Connection
|
||||||
|
* @param sslEngine SSL Engine for the Connection
|
||||||
|
* @param protocols Application Protocols
|
||||||
|
* @param defaultProtocol Default Application Protocol
|
||||||
|
* @return ALPN Server Connection
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected AbstractConnection newServerConnection(
|
||||||
|
final Connector connector,
|
||||||
|
final EndPoint endPoint,
|
||||||
|
final SSLEngine sslEngine,
|
||||||
|
final List<String> protocols,
|
||||||
|
final String defaultProtocol
|
||||||
|
) {
|
||||||
|
final ALPNServerConnection connection = new ALPNServerConnection(connector, endPoint, sslEngine, protocols, defaultProtocol);
|
||||||
|
processor.configure(sslEngine, connection);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* 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.jetty.configuration.connector.alpn;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.alpn.server.ALPNServerConnection;
|
||||||
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||||
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
|
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard ALPN Processor supporting JDK 1.8.0-251 and higher based on Jetty JDK9ServerALPNProcessor
|
||||||
|
*/
|
||||||
|
public class StandardALPNProcessor implements ALPNProcessor.Server, SslHandshakeListener {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(StandardALPNProcessor.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies to SSL Engine instances regardless of implementation
|
||||||
|
*
|
||||||
|
* @param sslEngine SSL Engine to be evaluated
|
||||||
|
* @return Applicable Status
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean appliesTo(final SSLEngine sslEngine) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure ALPN negotiation for Connection
|
||||||
|
*
|
||||||
|
* @param sslEngine SSL Engine to be configured
|
||||||
|
* @param connection Connection to be configured
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configure(final SSLEngine sslEngine, final Connection connection) {
|
||||||
|
logger.debug("Configuring Connection Remote Address [{}]", connection.getEndPoint().getRemoteAddress());
|
||||||
|
final ALPNServerConnection serverConnection = (ALPNServerConnection) connection;
|
||||||
|
final ProtocolSelector protocolSelector = new ProtocolSelector(serverConnection);
|
||||||
|
sslEngine.setHandshakeApplicationProtocolSelector(protocolSelector);
|
||||||
|
|
||||||
|
final SslConnection.DecryptedEndPoint endPoint = (SslConnection.DecryptedEndPoint) serverConnection.getEndPoint();
|
||||||
|
endPoint.getSslConnection().addHandshakeListener(protocolSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ProtocolSelector implements BiFunction<SSLEngine, List<String>, String>, SslHandshakeListener {
|
||||||
|
private final ALPNServerConnection serverConnection;
|
||||||
|
|
||||||
|
private ProtocolSelector(final ALPNServerConnection connection) {
|
||||||
|
serverConnection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select supported Application Layer Protocol based on requested protocols
|
||||||
|
*
|
||||||
|
* @param sslEngine SSL Engine
|
||||||
|
* @param protocols Protocols requested
|
||||||
|
* @return Protocol selected or null when no supported protocol found
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String apply(final SSLEngine sslEngine, final List<String> protocols) {
|
||||||
|
String protocol = null;
|
||||||
|
try {
|
||||||
|
serverConnection.select(protocols);
|
||||||
|
protocol = serverConnection.getProtocol();
|
||||||
|
logger.debug("Connection Remote Address [{}] Application Layer Protocol [{}] selected", serverConnection.getEndPoint().getRemoteAddress(), protocol);
|
||||||
|
} catch (final Throwable e) {
|
||||||
|
logger.debug("Connection Remote Address [{}] Application Layer Protocols {} not supported", serverConnection.getEndPoint().getRemoteAddress(), protocols);
|
||||||
|
}
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for successful handshake checks for selected Application Layer Protocol
|
||||||
|
*
|
||||||
|
* @param event Event is not used
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void handshakeSucceeded(final Event event) {
|
||||||
|
final InetSocketAddress remoteAddress = serverConnection.getEndPoint().getRemoteAddress();
|
||||||
|
final SSLSession session = event.getSSLEngine().getSession();
|
||||||
|
logger.debug("Connection Remote Address [{}] Handshake Succeeded [{}] Cipher Suite [{}]", remoteAddress, session.getProtocol(), session.getCipherSuite());
|
||||||
|
|
||||||
|
final String protocol = serverConnection.getProtocol();
|
||||||
|
if (protocol == null) {
|
||||||
|
logger.debug("Connection Remote Address [{}] Application Layer Protocol not supported", remoteAddress);
|
||||||
|
serverConnection.unsupported();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for failed handshake logs status
|
||||||
|
*
|
||||||
|
* @param event Event is not used
|
||||||
|
* @param failure Failure cause to be logged
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void handshakeFailed(final Event event, final Throwable failure) {
|
||||||
|
logger.warn("Connection Remote Address [{}] Handshake Failed", serverConnection.getEndPoint().getRemoteAddress(), failure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* 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.jetty.configuration.connector;
|
||||||
|
|
||||||
|
import org.apache.nifi.jetty.configuration.connector.alpn.ALPNServerConnectionFactory;
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class StandardServerConnectorFactoryTest {
|
||||||
|
private static final int HTTP_PORT = 8080;
|
||||||
|
|
||||||
|
private static final int HTTPS_PORT = 8443;
|
||||||
|
|
||||||
|
private static final String[] INCLUDE_PROTOCOLS = new String[]{ "TLSv1.2" };
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetServerConnector() {
|
||||||
|
final Server server = new Server();
|
||||||
|
final StandardServerConnectorFactory factory = new StandardServerConnectorFactory(server, HTTP_PORT);
|
||||||
|
|
||||||
|
final ServerConnector serverConnector = factory.getServerConnector();
|
||||||
|
|
||||||
|
assertHttpConnectionFactoryFound(serverConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetServerConnectorSecured() throws NoSuchAlgorithmException {
|
||||||
|
final StandardServerConnectorFactory factory = createSecuredStandardServerConnectorFactory();
|
||||||
|
|
||||||
|
final ServerConnector serverConnector = factory.getServerConnector();
|
||||||
|
|
||||||
|
assertHttpConnectionFactoryFound(serverConnector);
|
||||||
|
final SslConnectionFactory sslConnectionFactory = assertSslConnectionFactoryFound(serverConnector);
|
||||||
|
|
||||||
|
final HttpConnectionFactory httpConnectionFactory = assertHttpConnectionFactoryFound(serverConnector);
|
||||||
|
assertHttpConnectionFactorySecured(httpConnectionFactory);
|
||||||
|
|
||||||
|
final SslContextFactory.Server sslContextFactory = (SslContextFactory.Server) sslConnectionFactory.getSslContextFactory();
|
||||||
|
assertFalse(sslContextFactory.getNeedClientAuth());
|
||||||
|
assertFalse(sslContextFactory.getWantClientAuth());
|
||||||
|
assertNotNull(sslContextFactory.getIncludeProtocols());
|
||||||
|
|
||||||
|
final HTTP2ServerConnectionFactory http2ConnectionFactory = serverConnector.getConnectionFactory(HTTP2ServerConnectionFactory.class);
|
||||||
|
assertNull(http2ConnectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetServerConnectorSecuredNeedClientAuthentication() throws NoSuchAlgorithmException {
|
||||||
|
final StandardServerConnectorFactory factory = createSecuredStandardServerConnectorFactory();
|
||||||
|
factory.setNeedClientAuth(true);
|
||||||
|
factory.setIncludeSecurityProtocols(INCLUDE_PROTOCOLS);
|
||||||
|
|
||||||
|
final ServerConnector serverConnector = factory.getServerConnector();
|
||||||
|
|
||||||
|
assertHttpConnectionFactoryFound(serverConnector);
|
||||||
|
final SslConnectionFactory sslConnectionFactory = assertSslConnectionFactoryFound(serverConnector);
|
||||||
|
|
||||||
|
final HttpConnectionFactory httpConnectionFactory = assertHttpConnectionFactoryFound(serverConnector);
|
||||||
|
assertHttpConnectionFactorySecured(httpConnectionFactory);
|
||||||
|
|
||||||
|
final SslContextFactory.Server sslContextFactory = (SslContextFactory.Server) sslConnectionFactory.getSslContextFactory();
|
||||||
|
assertTrue(sslContextFactory.getNeedClientAuth());
|
||||||
|
assertArrayEquals(INCLUDE_PROTOCOLS, sslContextFactory.getIncludeProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetServerConnectorSecuredHttp2AndHttp1() throws NoSuchAlgorithmException {
|
||||||
|
final StandardServerConnectorFactory factory = createSecuredStandardServerConnectorFactory();
|
||||||
|
factory.setApplicationLayerProtocols(new LinkedHashSet<>(Arrays.asList(ApplicationLayerProtocol.H2, ApplicationLayerProtocol.HTTP_1_1)));
|
||||||
|
|
||||||
|
final ServerConnector serverConnector = factory.getServerConnector();
|
||||||
|
|
||||||
|
final HttpConnectionFactory httpConnectionFactory = assertHttpConnectionFactoryFound(serverConnector);
|
||||||
|
assertHttpConnectionFactorySecured(httpConnectionFactory);
|
||||||
|
|
||||||
|
final SslConnectionFactory sslConnectionFactory = assertSslConnectionFactoryFound(serverConnector);
|
||||||
|
final SslContextFactory.Server sslContextFactory = (SslContextFactory.Server) sslConnectionFactory.getSslContextFactory();
|
||||||
|
assertFalse(sslContextFactory.getNeedClientAuth());
|
||||||
|
|
||||||
|
assertHttp2ConnectionFactoriesFound(serverConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetServerConnectorSecuredHttp2() throws NoSuchAlgorithmException {
|
||||||
|
final StandardServerConnectorFactory factory = createSecuredStandardServerConnectorFactory();
|
||||||
|
factory.setApplicationLayerProtocols(Collections.singleton(ApplicationLayerProtocol.H2));
|
||||||
|
|
||||||
|
final ServerConnector serverConnector = factory.getServerConnector();
|
||||||
|
|
||||||
|
final HttpConnectionFactory connectionFactory = serverConnector.getConnectionFactory(HttpConnectionFactory.class);
|
||||||
|
assertNull(connectionFactory);
|
||||||
|
|
||||||
|
final SslConnectionFactory sslConnectionFactory = assertSslConnectionFactoryFound(serverConnector);
|
||||||
|
final SslContextFactory.Server sslContextFactory = (SslContextFactory.Server) sslConnectionFactory.getSslContextFactory();
|
||||||
|
assertFalse(sslContextFactory.getNeedClientAuth());
|
||||||
|
|
||||||
|
assertHttp2ConnectionFactoriesFound(serverConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StandardServerConnectorFactory createSecuredStandardServerConnectorFactory() throws NoSuchAlgorithmException {
|
||||||
|
final Server server = new Server();
|
||||||
|
final StandardServerConnectorFactory factory = new StandardServerConnectorFactory(server, HTTPS_PORT);
|
||||||
|
final SSLContext sslContext = SSLContext.getDefault();
|
||||||
|
factory.setSslContext(sslContext);
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpConnectionFactory assertHttpConnectionFactoryFound(final ServerConnector serverConnector) {
|
||||||
|
assertNotNull(serverConnector);
|
||||||
|
final HttpConnectionFactory connectionFactory = serverConnector.getConnectionFactory(HttpConnectionFactory.class);
|
||||||
|
assertNotNull(connectionFactory);
|
||||||
|
return connectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertHttp2ConnectionFactoriesFound(final ServerConnector serverConnector) {
|
||||||
|
final HTTP2ServerConnectionFactory http2ConnectionFactory = serverConnector.getConnectionFactory(HTTP2ServerConnectionFactory.class);
|
||||||
|
assertNotNull(http2ConnectionFactory);
|
||||||
|
|
||||||
|
final ALPNServerConnectionFactory alpnServerConnectionFactory = serverConnector.getConnectionFactory(ALPNServerConnectionFactory.class);
|
||||||
|
assertNotNull(alpnServerConnectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SslConnectionFactory assertSslConnectionFactoryFound(final ServerConnector serverConnector) {
|
||||||
|
final SslConnectionFactory sslConnectionFactory = serverConnector.getConnectionFactory(SslConnectionFactory.class);
|
||||||
|
assertNotNull(sslConnectionFactory);
|
||||||
|
return sslConnectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertHttpConnectionFactorySecured(final HttpConnectionFactory httpConnectionFactory) {
|
||||||
|
final HttpConfiguration configuration = httpConnectionFactory.getHttpConfiguration();
|
||||||
|
assertEquals(HTTPS_PORT, configuration.getSecurePort());
|
||||||
|
assertEquals(HttpScheme.HTTPS.asString(), configuration.getSecureScheme());
|
||||||
|
final SecureRequestCustomizer secureRequestCustomizer = configuration.getCustomizer(SecureRequestCustomizer.class);
|
||||||
|
assertNotNull(secureRequestCustomizer);
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@
|
||||||
<module>nifi-flow-encryptor</module>
|
<module>nifi-flow-encryptor</module>
|
||||||
<module>nifi-hl7-query-language</module>
|
<module>nifi-hl7-query-language</module>
|
||||||
<module>nifi-json-utils</module>
|
<module>nifi-json-utils</module>
|
||||||
|
<module>nifi-jetty-configuration</module>
|
||||||
<module>nifi-logging-utils</module>
|
<module>nifi-logging-utils</module>
|
||||||
<module>nifi-metrics</module>
|
<module>nifi-metrics</module>
|
||||||
<module>nifi-parameter</module>
|
<module>nifi-parameter</module>
|
||||||
|
|
|
@ -78,5 +78,15 @@
|
||||||
<artifactId>apache-jstl</artifactId>
|
<artifactId>apache-jstl</artifactId>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-alpn-server</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.http2</groupId>
|
||||||
|
<artifactId>http2-server</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -92,6 +92,11 @@
|
||||||
<artifactId>nifi-flowfile-packager</artifactId>
|
<artifactId>nifi-flowfile-packager</artifactId>
|
||||||
<version>1.17.0-SNAPSHOT</version>
|
<version>1.17.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.nifi</groupId>
|
||||||
|
<artifactId>nifi-jetty-configuration</artifactId>
|
||||||
|
<version>1.17.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-distributed-cache-client-service-api</artifactId>
|
<artifactId>nifi-distributed-cache-client-service-api</artifactId>
|
||||||
|
@ -180,6 +185,14 @@
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-servlet</artifactId>
|
<artifactId>jetty-servlet</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-alpn-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.http2</groupId>
|
||||||
|
<artifactId>http2-server</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.apache.nifi.components.PropertyDescriptor;
|
||||||
import org.apache.nifi.expression.ExpressionLanguageScope;
|
import org.apache.nifi.expression.ExpressionLanguageScope;
|
||||||
import org.apache.nifi.flowfile.FlowFile;
|
import org.apache.nifi.flowfile.FlowFile;
|
||||||
import org.apache.nifi.http.HttpContextMap;
|
import org.apache.nifi.http.HttpContextMap;
|
||||||
|
import org.apache.nifi.jetty.configuration.connector.StandardServerConnectorFactory;
|
||||||
import org.apache.nifi.processor.AbstractProcessor;
|
import org.apache.nifi.processor.AbstractProcessor;
|
||||||
import org.apache.nifi.processor.DataUnit;
|
import org.apache.nifi.processor.DataUnit;
|
||||||
import org.apache.nifi.processor.ProcessContext;
|
import org.apache.nifi.processor.ProcessContext;
|
||||||
|
@ -40,21 +41,17 @@ import org.apache.nifi.processor.ProcessSession;
|
||||||
import org.apache.nifi.processor.Relationship;
|
import org.apache.nifi.processor.Relationship;
|
||||||
import org.apache.nifi.processor.exception.ProcessException;
|
import org.apache.nifi.processor.exception.ProcessException;
|
||||||
import org.apache.nifi.processor.util.StandardValidators;
|
import org.apache.nifi.processor.util.StandardValidators;
|
||||||
|
import org.apache.nifi.processors.standard.http.HttpProtocolStrategy;
|
||||||
import org.apache.nifi.processors.standard.util.HTTPUtils;
|
import org.apache.nifi.processors.standard.util.HTTPUtils;
|
||||||
import org.apache.nifi.scheduling.ExecutionNode;
|
import org.apache.nifi.scheduling.ExecutionNode;
|
||||||
import org.apache.nifi.ssl.RestrictedSSLContextService;
|
import org.apache.nifi.ssl.RestrictedSSLContextService;
|
||||||
import org.apache.nifi.ssl.SSLContextService;
|
import org.apache.nifi.ssl.SSLContextService;
|
||||||
import org.apache.nifi.stream.io.StreamUtils;
|
import org.apache.nifi.stream.io.StreamUtils;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
|
||||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
|
@ -187,6 +184,14 @@ public class HandleHttpRequest extends AbstractProcessor {
|
||||||
.required(false)
|
.required(false)
|
||||||
.identifiesControllerService(RestrictedSSLContextService.class)
|
.identifiesControllerService(RestrictedSSLContextService.class)
|
||||||
.build();
|
.build();
|
||||||
|
public static final PropertyDescriptor HTTP_PROTOCOL_STRATEGY = new PropertyDescriptor.Builder()
|
||||||
|
.name("HTTP Protocols")
|
||||||
|
.description("HTTP Protocols supported for Application Layer Protocol Negotiation with TLS")
|
||||||
|
.required(true)
|
||||||
|
.allowableValues(HttpProtocolStrategy.class)
|
||||||
|
.defaultValue(HttpProtocolStrategy.HTTP_1_1.getValue())
|
||||||
|
.dependsOn(SSL_CONTEXT)
|
||||||
|
.build();
|
||||||
public static final PropertyDescriptor URL_CHARACTER_SET = new PropertyDescriptor.Builder()
|
public static final PropertyDescriptor URL_CHARACTER_SET = new PropertyDescriptor.Builder()
|
||||||
.name("Default URL Character Set")
|
.name("Default URL Character Set")
|
||||||
.description("The character set to use for decoding URL parameters if the HTTP Request does not supply one")
|
.description("The character set to use for decoding URL parameters if the HTTP Request does not supply one")
|
||||||
|
@ -303,6 +308,7 @@ public class HandleHttpRequest extends AbstractProcessor {
|
||||||
descriptors.add(PORT);
|
descriptors.add(PORT);
|
||||||
descriptors.add(HOSTNAME);
|
descriptors.add(HOSTNAME);
|
||||||
descriptors.add(SSL_CONTEXT);
|
descriptors.add(SSL_CONTEXT);
|
||||||
|
descriptors.add(HTTP_PROTOCOL_STRATEGY);
|
||||||
descriptors.add(HTTP_CONTEXT_MAP);
|
descriptors.add(HTTP_CONTEXT_MAP);
|
||||||
descriptors.add(PATH_REGEX);
|
descriptors.add(PATH_REGEX);
|
||||||
descriptors.add(URL_CHARACTER_SET);
|
descriptors.add(URL_CHARACTER_SET);
|
||||||
|
@ -356,61 +362,24 @@ public class HandleHttpRequest extends AbstractProcessor {
|
||||||
final long requestTimeout = httpContextMap.getRequestTimeout(TimeUnit.MILLISECONDS);
|
final long requestTimeout = httpContextMap.getRequestTimeout(TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
final String clientAuthValue = context.getProperty(CLIENT_AUTH).getValue();
|
final String clientAuthValue = context.getProperty(CLIENT_AUTH).getValue();
|
||||||
final boolean need;
|
final Server server = new Server();
|
||||||
final boolean want;
|
|
||||||
if (CLIENT_NEED.getValue().equals(clientAuthValue)) {
|
final StandardServerConnectorFactory serverConnectorFactory = new StandardServerConnectorFactory(server, port);
|
||||||
need = true;
|
final boolean needClientAuth = CLIENT_NEED.getValue().equals(clientAuthValue);
|
||||||
want = false;
|
serverConnectorFactory.setNeedClientAuth(needClientAuth);
|
||||||
} else if (CLIENT_WANT.getValue().equals(clientAuthValue)) {
|
final boolean wantClientAuth = CLIENT_WANT.getValue().equals(clientAuthValue);
|
||||||
need = false;
|
serverConnectorFactory.setNeedClientAuth(wantClientAuth);
|
||||||
want = true;
|
final SSLContext sslContext = sslService == null ? null : sslService.createContext();
|
||||||
} else {
|
serverConnectorFactory.setSslContext(sslContext);
|
||||||
need = false;
|
final HttpProtocolStrategy httpProtocolStrategy = HttpProtocolStrategy.valueOf(context.getProperty(HTTP_PROTOCOL_STRATEGY).getValue());
|
||||||
want = false;
|
serverConnectorFactory.setApplicationLayerProtocols(httpProtocolStrategy.getApplicationLayerProtocols());
|
||||||
}
|
|
||||||
|
final ServerConnector serverConnector = serverConnectorFactory.getServerConnector();
|
||||||
final SslContextFactory sslFactory = (sslService == null) ? null : createSslFactory(sslService, need, want);
|
serverConnector.setIdleTimeout(Math.max(serverConnector.getIdleTimeout(), requestTimeout));
|
||||||
final Server server = new Server(port);
|
if (StringUtils.isNotBlank(host)) {
|
||||||
|
serverConnector.setHost(host);
|
||||||
// create the http configuration
|
|
||||||
final HttpConfiguration httpConfiguration = new HttpConfiguration();
|
|
||||||
if (sslFactory == null) {
|
|
||||||
// create the connector
|
|
||||||
final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
|
|
||||||
|
|
||||||
// set host and port
|
|
||||||
if (StringUtils.isNotBlank(host)) {
|
|
||||||
http.setHost(host);
|
|
||||||
}
|
|
||||||
http.setPort(port);
|
|
||||||
|
|
||||||
// If request timeout is longer than default Idle Timeout, then increase Idle Timeout as well.
|
|
||||||
http.setIdleTimeout(Math.max(http.getIdleTimeout(), requestTimeout));
|
|
||||||
|
|
||||||
// add this connector
|
|
||||||
server.setConnectors(new Connector[]{http});
|
|
||||||
} else {
|
|
||||||
// add some secure config
|
|
||||||
final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration);
|
|
||||||
httpsConfiguration.setSecureScheme("https");
|
|
||||||
httpsConfiguration.setSecurePort(port);
|
|
||||||
httpsConfiguration.addCustomizer(new SecureRequestCustomizer());
|
|
||||||
|
|
||||||
// build the connector
|
|
||||||
final ServerConnector https = new ServerConnector(server, new SslConnectionFactory(sslFactory, "http/1.1"), new HttpConnectionFactory(httpsConfiguration));
|
|
||||||
|
|
||||||
// set host and port
|
|
||||||
if (StringUtils.isNotBlank(host)) {
|
|
||||||
https.setHost(host);
|
|
||||||
}
|
|
||||||
https.setPort(port);
|
|
||||||
|
|
||||||
// If request timeout is longer than default Idle Timeout, then increase Idle Timeout as well.
|
|
||||||
https.setIdleTimeout(Math.max(https.getIdleTimeout(), requestTimeout));
|
|
||||||
|
|
||||||
// add this connector
|
|
||||||
server.setConnectors(new Connector[]{https});
|
|
||||||
}
|
}
|
||||||
|
server.addConnector(serverConnector);
|
||||||
|
|
||||||
final Set<String> allowedMethods = new HashSet<>();
|
final Set<String> allowedMethods = new HashSet<>();
|
||||||
if (context.getProperty(ALLOW_GET).asBoolean()) {
|
if (context.getProperty(ALLOW_GET).asBoolean()) {
|
||||||
|
@ -522,18 +491,6 @@ public class HandleHttpRequest extends AbstractProcessor {
|
||||||
return containerQueue.size();
|
return containerQueue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SslContextFactory createSslFactory(final SSLContextService sslContextService, final boolean needClientAuth, final boolean wantClientAuth) {
|
|
||||||
final SslContextFactory.Server sslFactory = new SslContextFactory.Server();
|
|
||||||
|
|
||||||
sslFactory.setNeedClientAuth(needClientAuth);
|
|
||||||
sslFactory.setWantClientAuth(wantClientAuth);
|
|
||||||
|
|
||||||
final SSLContext sslContext = sslContextService.createContext();
|
|
||||||
sslFactory.setSslContext(sslContext);
|
|
||||||
|
|
||||||
return sslFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnUnscheduled
|
@OnUnscheduled
|
||||||
public void shutdown() throws Exception {
|
public void shutdown() throws Exception {
|
||||||
ready = false;
|
ready = false;
|
||||||
|
|
|
@ -188,7 +188,6 @@ public class HandleHttpResponse extends AbstractProcessor {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session.exportTo(flowFile, response.getOutputStream());
|
session.exportTo(flowFile, response.getOutputStream());
|
||||||
response.flushBuffer();
|
|
||||||
} catch (final ProcessException e) {
|
} catch (final ProcessException e) {
|
||||||
getLogger().error("Failed to respond to HTTP request for {} due to {}", new Object[]{flowFile, e});
|
getLogger().error("Failed to respond to HTTP request for {} due to {}", new Object[]{flowFile, e});
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.nifi.components.ValidationContext;
|
||||||
import org.apache.nifi.components.ValidationResult;
|
import org.apache.nifi.components.ValidationResult;
|
||||||
import org.apache.nifi.expression.ExpressionLanguageScope;
|
import org.apache.nifi.expression.ExpressionLanguageScope;
|
||||||
import org.apache.nifi.flowfile.FlowFile;
|
import org.apache.nifi.flowfile.FlowFile;
|
||||||
|
import org.apache.nifi.jetty.configuration.connector.StandardServerConnectorFactory;
|
||||||
import org.apache.nifi.processor.AbstractSessionFactoryProcessor;
|
import org.apache.nifi.processor.AbstractSessionFactoryProcessor;
|
||||||
import org.apache.nifi.processor.DataUnit;
|
import org.apache.nifi.processor.DataUnit;
|
||||||
import org.apache.nifi.processor.ProcessContext;
|
import org.apache.nifi.processor.ProcessContext;
|
||||||
|
@ -39,26 +40,21 @@ import org.apache.nifi.processor.ProcessSessionFactory;
|
||||||
import org.apache.nifi.processor.Relationship;
|
import org.apache.nifi.processor.Relationship;
|
||||||
import org.apache.nifi.processor.exception.ProcessException;
|
import org.apache.nifi.processor.exception.ProcessException;
|
||||||
import org.apache.nifi.processor.util.StandardValidators;
|
import org.apache.nifi.processor.util.StandardValidators;
|
||||||
|
import org.apache.nifi.processors.standard.http.HttpProtocolStrategy;
|
||||||
import org.apache.nifi.processors.standard.servlets.ContentAcknowledgmentServlet;
|
import org.apache.nifi.processors.standard.servlets.ContentAcknowledgmentServlet;
|
||||||
import org.apache.nifi.processors.standard.servlets.HealthCheckServlet;
|
import org.apache.nifi.processors.standard.servlets.HealthCheckServlet;
|
||||||
import org.apache.nifi.processors.standard.servlets.ListenHTTPServlet;
|
import org.apache.nifi.processors.standard.servlets.ListenHTTPServlet;
|
||||||
import org.apache.nifi.scheduling.ExecutionNode;
|
import org.apache.nifi.scheduling.ExecutionNode;
|
||||||
import org.apache.nifi.security.util.ClientAuth;
|
import org.apache.nifi.security.util.ClientAuth;
|
||||||
import org.apache.nifi.security.util.TlsConfiguration;
|
|
||||||
import org.apache.nifi.serialization.RecordReaderFactory;
|
import org.apache.nifi.serialization.RecordReaderFactory;
|
||||||
import org.apache.nifi.serialization.RecordSetWriterFactory;
|
import org.apache.nifi.serialization.RecordSetWriterFactory;
|
||||||
import org.apache.nifi.ssl.RestrictedSSLContextService;
|
import org.apache.nifi.ssl.RestrictedSSLContextService;
|
||||||
import org.apache.nifi.ssl.SSLContextService;
|
import org.apache.nifi.ssl.SSLContextService;
|
||||||
import org.apache.nifi.stream.io.LeakyBucketStreamThrottler;
|
import org.apache.nifi.stream.io.LeakyBucketStreamThrottler;
|
||||||
import org.apache.nifi.stream.io.StreamThrottler;
|
import org.apache.nifi.stream.io.StreamThrottler;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
|
||||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
|
||||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
@ -191,10 +187,18 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
||||||
.build();
|
.build();
|
||||||
public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
|
public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
|
||||||
.name("SSL Context Service")
|
.name("SSL Context Service")
|
||||||
.description("The Controller Service to use in order to obtain an SSL Context")
|
.description("SSL Context Service enables support for HTTPS")
|
||||||
.required(false)
|
.required(false)
|
||||||
.identifiesControllerService(RestrictedSSLContextService.class)
|
.identifiesControllerService(RestrictedSSLContextService.class)
|
||||||
.build();
|
.build();
|
||||||
|
public static final PropertyDescriptor HTTP_PROTOCOL_STRATEGY = new PropertyDescriptor.Builder()
|
||||||
|
.name("HTTP Protocols")
|
||||||
|
.description("HTTP Protocols supported for Application Layer Protocol Negotiation with TLS")
|
||||||
|
.required(true)
|
||||||
|
.allowableValues(HttpProtocolStrategy.class)
|
||||||
|
.defaultValue(HttpProtocolStrategy.HTTP_1_1.getValue())
|
||||||
|
.dependsOn(SSL_CONTEXT_SERVICE)
|
||||||
|
.build();
|
||||||
public static final PropertyDescriptor HEADERS_AS_ATTRIBUTES_REGEX = new PropertyDescriptor.Builder()
|
public static final PropertyDescriptor HEADERS_AS_ATTRIBUTES_REGEX = new PropertyDescriptor.Builder()
|
||||||
.name("HTTP Headers to receive as Attributes (Regex)")
|
.name("HTTP Headers to receive as Attributes (Regex)")
|
||||||
.description("Specifies the Regular Expression that determines the names of HTTP Headers that should be passed along as FlowFile attributes")
|
.description("Specifies the Regular Expression that determines the names of HTTP Headers that should be passed along as FlowFile attributes")
|
||||||
|
@ -276,6 +280,7 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
||||||
HEALTH_CHECK_PORT,
|
HEALTH_CHECK_PORT,
|
||||||
MAX_DATA_RATE,
|
MAX_DATA_RATE,
|
||||||
SSL_CONTEXT_SERVICE,
|
SSL_CONTEXT_SERVICE,
|
||||||
|
HTTP_PROTOCOL_STRATEGY,
|
||||||
CLIENT_AUTHENTICATION,
|
CLIENT_AUTHENTICATION,
|
||||||
AUTHORIZED_DN_PATTERN,
|
AUTHORIZED_DN_PATTERN,
|
||||||
AUTHORIZED_ISSUER_DN_PATTERN,
|
AUTHORIZED_ISSUER_DN_PATTERN,
|
||||||
|
@ -396,7 +401,6 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
||||||
int maxThreadPoolSize = context.getProperty(MAX_THREAD_POOL_SIZE).asInteger();
|
int maxThreadPoolSize = context.getProperty(MAX_THREAD_POOL_SIZE).asInteger();
|
||||||
throttlerRef.set(streamThrottler);
|
throttlerRef.set(streamThrottler);
|
||||||
|
|
||||||
final boolean sslRequired = sslContextService != null;
|
|
||||||
final PropertyValue clientAuthenticationProperty = context.getProperty(CLIENT_AUTHENTICATION);
|
final PropertyValue clientAuthenticationProperty = context.getProperty(CLIENT_AUTHENTICATION);
|
||||||
final ClientAuthentication clientAuthentication = getClientAuthentication(sslContextService, clientAuthenticationProperty);
|
final ClientAuthentication clientAuthentication = getClientAuthentication(sslContextService, clientAuthenticationProperty);
|
||||||
|
|
||||||
|
@ -409,12 +413,13 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
||||||
|
|
||||||
// get the configured port
|
// get the configured port
|
||||||
final int port = context.getProperty(PORT).evaluateAttributeExpressions().asInteger();
|
final int port = context.getProperty(PORT).evaluateAttributeExpressions().asInteger();
|
||||||
|
final HttpProtocolStrategy httpProtocolStrategy = HttpProtocolStrategy.valueOf(context.getProperty(HTTP_PROTOCOL_STRATEGY).getValue());
|
||||||
final ServerConnector connector = createServerConnector(server,
|
final ServerConnector connector = createServerConnector(server,
|
||||||
port,
|
port,
|
||||||
sslContextService,
|
sslContextService,
|
||||||
sslRequired,
|
clientAuthentication,
|
||||||
clientAuthentication);
|
httpProtocolStrategy
|
||||||
|
);
|
||||||
server.addConnector(connector);
|
server.addConnector(connector);
|
||||||
|
|
||||||
// Add a separate connector for the health check port (if specified)
|
// Add a separate connector for the health check port (if specified)
|
||||||
|
@ -423,12 +428,14 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
||||||
final ServerConnector healthCheckConnector = createServerConnector(server,
|
final ServerConnector healthCheckConnector = createServerConnector(server,
|
||||||
healthCheckPort,
|
healthCheckPort,
|
||||||
sslContextService,
|
sslContextService,
|
||||||
sslRequired,
|
ClientAuthentication.NONE,
|
||||||
ClientAuthentication.NONE);
|
httpProtocolStrategy
|
||||||
|
);
|
||||||
server.addConnector(healthCheckConnector);
|
server.addConnector(healthCheckConnector);
|
||||||
}
|
}
|
||||||
|
|
||||||
final ServletContextHandler contextHandler = new ServletContextHandler(server, "/", true, sslRequired);
|
final boolean securityEnabled = sslContextService != null;
|
||||||
|
final ServletContextHandler contextHandler = new ServletContextHandler(server, "/", true, securityEnabled);
|
||||||
for (final Class<? extends Servlet> cls : getServerClasses()) {
|
for (final Class<? extends Servlet> cls : getServerClasses()) {
|
||||||
final Path path = cls.getAnnotation(Path.class);
|
final Path path = cls.getAnnotation(Path.class);
|
||||||
// Note: servlets must have a path annotation - this will NPE otherwise
|
// Note: servlets must have a path annotation - this will NPE otherwise
|
||||||
|
@ -488,41 +495,24 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
||||||
private ServerConnector createServerConnector(final Server server,
|
private ServerConnector createServerConnector(final Server server,
|
||||||
final int port,
|
final int port,
|
||||||
final SSLContextService sslContextService,
|
final SSLContextService sslContextService,
|
||||||
final boolean sslRequired,
|
final ClientAuthentication clientAuthentication,
|
||||||
final ClientAuthentication clientAuthentication) {
|
final HttpProtocolStrategy httpProtocolStrategy
|
||||||
final ServerConnector connector;
|
) {
|
||||||
final HttpConfiguration httpConfiguration = new HttpConfiguration();
|
final StandardServerConnectorFactory serverConnectorFactory = new StandardServerConnectorFactory(server, port);
|
||||||
if (sslRequired) {
|
final SSLContext sslContext = sslContextService == null ? null : sslContextService.createContext();
|
||||||
httpConfiguration.setSecureScheme("https");
|
serverConnectorFactory.setSslContext(sslContext);
|
||||||
httpConfiguration.setSecurePort(port);
|
|
||||||
httpConfiguration.addCustomizer(new SecureRequestCustomizer());
|
|
||||||
|
|
||||||
final SslContextFactory contextFactory = createSslContextFactory(sslContextService, clientAuthentication);
|
final String[] enabledProtocols = sslContextService == null ? new String[0] : sslContextService.createTlsConfiguration().getEnabledProtocols();
|
||||||
|
serverConnectorFactory.setIncludeSecurityProtocols(enabledProtocols);
|
||||||
|
|
||||||
connector = new ServerConnector(server, new SslConnectionFactory(contextFactory, "http/1.1"), new HttpConnectionFactory(httpConfiguration));
|
if (ClientAuthentication.REQUIRED == clientAuthentication) {
|
||||||
} else {
|
serverConnectorFactory.setNeedClientAuth(true);
|
||||||
connector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
|
} else if (ClientAuthentication.WANT == clientAuthentication) {
|
||||||
|
serverConnectorFactory.setWantClientAuth(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
connector.setPort(port);
|
serverConnectorFactory.setApplicationLayerProtocols(httpProtocolStrategy.getApplicationLayerProtocols());
|
||||||
return connector;
|
return serverConnectorFactory.getServerConnector();
|
||||||
}
|
|
||||||
|
|
||||||
private SslContextFactory createSslContextFactory(final SSLContextService sslContextService, final ClientAuthentication clientAuthentication) {
|
|
||||||
final SslContextFactory.Server contextFactory = new SslContextFactory.Server();
|
|
||||||
final SSLContext sslContext = sslContextService.createContext();
|
|
||||||
contextFactory.setSslContext(sslContext);
|
|
||||||
|
|
||||||
final TlsConfiguration tlsConfiguration = sslContextService.createTlsConfiguration();
|
|
||||||
contextFactory.setIncludeProtocols(tlsConfiguration.getEnabledProtocols());
|
|
||||||
|
|
||||||
if (ClientAuthentication.REQUIRED.equals(clientAuthentication)) {
|
|
||||||
contextFactory.setNeedClientAuth(true);
|
|
||||||
} else if (ClientAuthentication.WANT.equals(clientAuthentication)) {
|
|
||||||
contextFactory.setWantClientAuth(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return contextFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnScheduled
|
@OnScheduled
|
||||||
|
@ -572,7 +562,7 @@ public class ListenHTTP extends AbstractSessionFactoryProcessor {
|
||||||
for (final String id : findOldFlowFileIds(context)) {
|
for (final String id : findOldFlowFileIds(context)) {
|
||||||
final FlowFileEntryTimeWrapper wrapper = flowFileMap.remove(id);
|
final FlowFileEntryTimeWrapper wrapper = flowFileMap.remove(id);
|
||||||
if (wrapper != null) {
|
if (wrapper != null) {
|
||||||
getLogger().warn("failed to received acknowledgment for HOLD with ID {} sent by {}; rolling back session", new Object[] {id, wrapper.getClientIP()});
|
getLogger().warn("failed to received acknowledgment for HOLD with ID {} sent by {}; rolling back session", id, wrapper.getClientIP());
|
||||||
wrapper.session.rollback();
|
wrapper.session.rollback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.processors.standard.http;
|
||||||
|
|
||||||
|
import org.apache.nifi.components.DescribedValue;
|
||||||
|
import org.apache.nifi.jetty.configuration.connector.ApplicationLayerProtocol;
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP protocol configuration strategy
|
||||||
|
*/
|
||||||
|
public enum HttpProtocolStrategy implements DescribedValue {
|
||||||
|
HTTP_1_1("http/1.1", "HTTP/1.1", singleton(ApplicationLayerProtocol.HTTP_1_1)),
|
||||||
|
|
||||||
|
H2_HTTP_1_1("h2 http/1.1", "HTTP/2 and HTTP/1.1 negotiated based on requested protocols", new LinkedHashSet<>(asList(ApplicationLayerProtocol.HTTP_1_1, ApplicationLayerProtocol.H2))),
|
||||||
|
|
||||||
|
H2("h2", "HTTP/2", singleton(ApplicationLayerProtocol.H2));
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private final Set<ApplicationLayerProtocol> applicationLayerProtocols;
|
||||||
|
|
||||||
|
HttpProtocolStrategy(final String displayName, final String description, final Set<ApplicationLayerProtocol> applicationLayerProtocols) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.description = description;
|
||||||
|
this.applicationLayerProtocols = applicationLayerProtocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<ApplicationLayerProtocol> getApplicationLayerProtocols() {
|
||||||
|
return applicationLayerProtocols;
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.apache.nifi.processor.ProcessContext;
|
import org.apache.nifi.processor.ProcessContext;
|
||||||
import org.apache.nifi.processor.ProcessSessionFactory;
|
import org.apache.nifi.processor.ProcessSessionFactory;
|
||||||
|
import org.apache.nifi.processors.standard.http.HttpProtocolStrategy;
|
||||||
import org.apache.nifi.remote.io.socket.NetworkUtils;
|
import org.apache.nifi.remote.io.socket.NetworkUtils;
|
||||||
import org.apache.nifi.reporting.InitializationException;
|
import org.apache.nifi.reporting.InitializationException;
|
||||||
import org.apache.nifi.security.util.SslContextFactory;
|
import org.apache.nifi.security.util.SslContextFactory;
|
||||||
|
@ -99,7 +100,6 @@ public class TestListenHTTP {
|
||||||
private static final Duration CLIENT_CALL_TIMEOUT = Duration.ofSeconds(10);
|
private static final Duration CLIENT_CALL_TIMEOUT = Duration.ofSeconds(10);
|
||||||
public static final String LOCALHOST_DN = "CN=localhost";
|
public static final String LOCALHOST_DN = "CN=localhost";
|
||||||
|
|
||||||
private static TlsConfiguration tlsConfiguration;
|
|
||||||
private static TlsConfiguration serverConfiguration;
|
private static TlsConfiguration serverConfiguration;
|
||||||
private static TlsConfiguration serverTls_1_3_Configuration;
|
private static TlsConfiguration serverTls_1_3_Configuration;
|
||||||
private static TlsConfiguration serverNoTruststoreConfiguration;
|
private static TlsConfiguration serverNoTruststoreConfiguration;
|
||||||
|
@ -117,7 +117,7 @@ public class TestListenHTTP {
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setUpSuite() throws GeneralSecurityException {
|
public static void setUpSuite() throws GeneralSecurityException {
|
||||||
// generate new keystore and truststore
|
// generate new keystore and truststore
|
||||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||||
|
|
||||||
serverConfiguration = new StandardTlsConfiguration(
|
serverConfiguration = new StandardTlsConfiguration(
|
||||||
tlsConfiguration.getKeystorePath(),
|
tlsConfiguration.getKeystorePath(),
|
||||||
|
@ -223,23 +223,25 @@ public class TestListenHTTP {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSecurePOSTRequestsReceivedWithoutEL() throws Exception {
|
public void testSecurePOSTRequestsReceivedWithoutELHttp2AndHttp1() throws Exception {
|
||||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||||
|
|
||||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||||
|
runner.setProperty(ListenHTTP.HTTP_PROTOCOL_STRATEGY, HttpProtocolStrategy.H2_HTTP_1_1.getValue());
|
||||||
runner.assertValid();
|
runner.assertValid();
|
||||||
|
|
||||||
testPOSTRequestsReceived(HttpServletResponse.SC_OK, true, false);
|
testPOSTRequestsReceived(HttpServletResponse.SC_OK, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSecurePOSTRequestsReturnCodeReceivedWithoutEL() throws Exception {
|
public void testSecurePOSTRequestsReturnCodeReceivedWithoutELHttp2() throws Exception {
|
||||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||||
|
|
||||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||||
runner.setProperty(ListenHTTP.RETURN_CODE, Integer.toString(HttpServletResponse.SC_NO_CONTENT));
|
runner.setProperty(ListenHTTP.RETURN_CODE, Integer.toString(HttpServletResponse.SC_NO_CONTENT));
|
||||||
|
runner.setProperty(ListenHTTP.HTTP_PROTOCOL_STRATEGY, HttpProtocolStrategy.H2.getValue());
|
||||||
runner.assertValid();
|
runner.assertValid();
|
||||||
|
|
||||||
testPOSTRequestsReceived(HttpServletResponse.SC_NO_CONTENT, true, false);
|
testPOSTRequestsReceived(HttpServletResponse.SC_NO_CONTENT, true, false);
|
||||||
|
|
13
pom.xml
13
pom.xml
|
@ -420,6 +420,19 @@
|
||||||
<version>${jetty.version}</version>
|
<version>${jetty.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-alpn-server</artifactId>
|
||||||
|
<version>${jetty.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.http2</groupId>
|
||||||
|
<artifactId>http2-server</artifactId>
|
||||||
|
<version>${jetty.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-alpn-client</artifactId>
|
<artifactId>jetty-alpn-client</artifactId>
|
||||||
|
|
Loading…
Reference in New Issue