Skip to content

Commit

Permalink
Expose an API for applying the SslContext's default configuration pri…
Browse files Browse the repository at this point in the history
…or any other configuration.

SslProvider.SslContextSpec#sslContext(SslProvider.ProtocolSslContextSpec)
provides, specific for the protocol, default configuration (DefaultSslContextSpec,
TcpSslContextSpec, Http11SslContextSpec, Http2SslContextSpec).
As opposed to SslProvider.SslContextSpec#sslContext(SslContextBuilder),
the default configuration is applied before any other custom configuration.

Fixes #1543
  • Loading branch information
violetagg committed Mar 31, 2021
1 parent 22a49de commit 555d330
Show file tree
Hide file tree
Showing 37 changed files with 1,134 additions and 304 deletions.
2 changes: 1 addition & 1 deletion docs/asciidoc/http-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ To customize the default settings, you can configure `HttpClient` as follows:
[source,java,indent=0]
.{examplesdir}/security/custom/Application.java
----
include::{examplesdir}/security/custom/Application.java[lines=18..47]
include::{examplesdir}/security/custom/Application.java[lines=18..45]
----
<1> Configures the SSL handshake timeout to 30 seconds.
<2> Configures the SSL `close_notify` flush timeout to 10 seconds.
Expand Down
2 changes: 1 addition & 1 deletion docs/asciidoc/tcp-server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ The following example uses `SslContextBuilder`:
[source,java,indent=0]
.{examplesdir}/security/Application.java
----
include::{examplesdir}/security/Application.java[lines=18..39]
include::{examplesdir}/security/Application.java[lines=18..40]
----
====

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2011-Present VMware, Inc. or its affiliates, All Rights Reserved.
*
* Licensed 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
*
* https://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 reactor.netty.tcp;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;

import javax.net.ssl.SSLException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
* SslContext builder that provides, specific for protocol, default configuration.
*
* @author Violeta Georgieva
* @since 1.0.6
*/
public abstract class AbstractProtocolSslContextSpec<T extends AbstractProtocolSslContextSpec<T>>
implements SslProvider.ProtocolSslContextSpec, Supplier<T> {

final SslContextBuilder sslContextBuilder;

protected AbstractProtocolSslContextSpec(SslContextBuilder sslContextBuilder) {
this.sslContextBuilder = sslContextBuilder;
configure(defaultConfiguration());
}

protected abstract Consumer<SslContextBuilder> defaultConfiguration();

@Override
public T configure(Consumer<SslContextBuilder> sslCtxBuilder) {
Objects.requireNonNull(sslCtxBuilder, "sslCtxBuilder");
sslCtxBuilder.accept(sslContextBuilder);
return get();
}

@Override
public SslContext sslContext() throws SSLException {
return sslContextBuilder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2011-Present VMware, Inc. or its affiliates, All Rights Reserved.
*
* Licensed 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
*
* https://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 reactor.netty.tcp;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import java.io.File;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.function.Consumer;

/**
* SslContext builder that does not provide any default configuration.
*
* @author Violeta Georgieva
* @since 1.0.6
*/
public final class DefaultSslContextSpec extends AbstractProtocolSslContextSpec<DefaultSslContextSpec> {

/**
* Creates a builder for new client-side {@link SslContext}.
*
* @return {@literal this}
*/
public static DefaultSslContextSpec forClient() {
return new DefaultSslContextSpec(SslContextBuilder.forClient());
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(File, File)
*/
public static DefaultSslContextSpec forServer(File keyCertChainFile, File keyFile) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(keyCertChainFile, keyFile));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(File, File, String)
*/
public static DefaultSslContextSpec forServer(File keyCertChainFile, File keyFile, String keyPassword) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(keyCertChainFile, keyFile, keyPassword));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(InputStream, InputStream)
*/
public static DefaultSslContextSpec forServer(InputStream keyCertChainInputStream, InputStream keyInputStream) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(keyCertChainInputStream, keyInputStream));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(InputStream, InputStream, String)
*/
public static DefaultSslContextSpec forServer(
InputStream keyCertChainInputStream, InputStream keyInputStream, String keyPassword) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(keyCertChainInputStream, keyInputStream, keyPassword));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(KeyManager)
*/
public static DefaultSslContextSpec forServer(KeyManager keyManager) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(keyManager));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(KeyManagerFactory)
*/
public static DefaultSslContextSpec forServer(KeyManagerFactory keyManagerFactory) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(keyManagerFactory));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(PrivateKey, Iterable)
*/
public static DefaultSslContextSpec forServer(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(key, keyCertChain));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(PrivateKey, String, Iterable)
*/
public static DefaultSslContextSpec forServer(
PrivateKey key, String keyPassword, Iterable<? extends X509Certificate> keyCertChain) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(key, keyPassword, keyCertChain));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(PrivateKey, String, X509Certificate...)
*/
public static DefaultSslContextSpec forServer(PrivateKey key, String keyPassword, X509Certificate... keyCertChain) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(key, keyPassword, keyCertChain));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see SslContextBuilder#forServer(PrivateKey, X509Certificate...)
*/
public static DefaultSslContextSpec forServer(PrivateKey key, X509Certificate... keyCertChain) {
return new DefaultSslContextSpec(SslContextBuilder.forServer(key, keyCertChain));
}

DefaultSslContextSpec(SslContextBuilder sslContextBuilder) {
super(sslContextBuilder);
}

@Override
public DefaultSslContextSpec get() {
return this;
}

@Override
protected Consumer<SslContextBuilder> defaultConfiguration() {
return DEFAULT_CONFIGURATOR;
}

static final Consumer<SslContextBuilder> DEFAULT_CONFIGURATOR = sslCtxBuilder -> {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ public interface Builder {

public interface SslContextSpec {

/**
* SslContext builder that provides, specific for the protocol, default configuration
* e.g. {@link DefaultSslContextSpec}, {@link TcpSslContextSpec} etc.
* As opposed to {@link #sslContext(SslContextBuilder)}, the default configuration is applied before
* any other custom configuration.
*
* @param spec SslContext builder that provides, specific for the protocol, default configuration
* @return a new {@link SslContext}
*/
Builder sslContext(ProtocolSslContextSpec spec);

/**
* The SslContext to set when configuring SSL
*
Expand All @@ -233,12 +244,16 @@ public interface SslContextSpec {
Builder sslContext(SslContext sslContext);

/**
* The SslContextBuilder for building a new {@link SslContext}.
* The SslContextBuilder for building a new {@link SslContext}. The default configuration is applied after
* the custom configuration.
*
* @return {@literal this}
* @deprecated as of 1.0.6. Prefer {@link #sslContext(ProtocolSslContextSpec)}, where the default
* configuration is applied before any other custom configuration.
* This method will be removed in version 1.2.0.
*/
@Deprecated
DefaultConfigurationSpec sslContext(SslContextBuilder sslCtxBuilder);

}

/**
Expand Down Expand Up @@ -277,6 +292,31 @@ public interface DefaultConfigurationSpec {
Builder defaultConfiguration(DefaultConfigurationType type);
}

/**
* SslContext builder that provides, specific for the protocol, default configuration.
* The default configuration is applied prior any other custom configuration.
*
* @since 1.0.6
*/
public interface ProtocolSslContextSpec {

/**
* Configures the underlying {@link SslContextBuilder}.
*
* @param sslCtxBuilder a callback for configuring the underlying {@link SslContextBuilder}
* @return {@code this}
*/
ProtocolSslContextSpec configure(Consumer<SslContextBuilder> sslCtxBuilder);

/**
* Create a new {@link SslContext} instance with the configured settings.
*
* @return a new {@link SslContext} instance
* @throws SSLException thrown when {@link SslContext} instance cannot be created
*/
SslContext sslContext() throws SSLException;
}

final SslContext sslContext;
final SslContextBuilder sslContextBuilder;
final DefaultConfigurationType type;
Expand All @@ -302,6 +342,14 @@ public interface DefaultConfigurationSpec {
throw Exceptions.propagate(e);
}
}
else if (builder.protocolSslContextSpec != null) {
try {
this.sslContext = builder.protocolSslContextSpec.sslContext();
}
catch (SSLException e) {
throw Exceptions.propagate(e);
}
}
else {
throw new IllegalArgumentException("Neither SslContextBuilder nor SslContext is specified");
}
Expand Down Expand Up @@ -547,6 +595,7 @@ static final class Build implements SslContextSpec, DefaultConfigurationSpec, Bu
"10000"));

SslContextBuilder sslCtxBuilder;
ProtocolSslContextSpec protocolSslContextSpec;
DefaultConfigurationType type;
SslContext sslContext;
Consumer<? super SslHandler> handlerConfigurator;
Expand All @@ -558,9 +607,17 @@ static final class Build implements SslContextSpec, DefaultConfigurationSpec, Bu

// SslContextSpec

@Override
public Builder sslContext(ProtocolSslContextSpec protocolSslContextSpec) {
this.protocolSslContextSpec = protocolSslContextSpec;
this.type = DefaultConfigurationType.NONE;
return this;
}

@Override
public final Builder sslContext(SslContext sslContext) {
this.sslContext = Objects.requireNonNull(sslContext, "sslContext");
this.type = DefaultConfigurationType.NONE;
return this;
}

Expand Down Expand Up @@ -685,14 +742,15 @@ public boolean equals(Object o) {
Objects.equals(sslContext, build.sslContext) &&
Objects.equals(handlerConfigurator, build.handlerConfigurator) &&
Objects.equals(serverNames, build.serverNames) &&
confPerDomainName.equals(build.confPerDomainName);
confPerDomainName.equals(build.confPerDomainName) &&
Objects.equals(protocolSslContextSpec, build.protocolSslContextSpec);
}

@Override
public int hashCode() {
return Objects.hash(sslCtxBuilder, type, sslContext, handlerConfigurator,
handshakeTimeoutMillis, closeNotifyFlushTimeoutMillis, closeNotifyReadTimeoutMillis,
serverNames, confPerDomainName);
serverNames, confPerDomainName, protocolSslContextSpec);
}

void addInternal(String domainName, Consumer<? super SslProvider.SslContextSpec> sslProviderBuilder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package reactor.netty.tcp;

import io.netty.handler.ssl.SslContextBuilder;

/**
* Initializes the default {@link SslProvider} for the TCP client.
*
Expand All @@ -33,8 +31,7 @@ final class TcpClientSecure {
try {
sslProvider =
SslProvider.builder()
.sslContext(SslContextBuilder.forClient())
.defaultConfiguration(SslProvider.DefaultConfigurationType.TCP)
.sslContext(TcpSslContextSpec.forClient())
.build();
}
catch (Exception e) {
Expand Down
Loading

0 comments on commit 555d330

Please sign in to comment.