Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ProtocolSslContextSpec generic #3150

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ ext {
}
nettyIoUringVersion = '0.0.25.Final'
nettyQuicVersion = '0.0.62.Final'
nettyHttp3Version = '0.0.28.Final'

// Testing
brotli4jVersion = '1.16.0'
Expand Down
5 changes: 4 additions & 1 deletion reactor-netty-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,10 @@ task japicmp(type: JapicmpTask) {
// Deprecated methods are removed
'reactor.netty.tcp.SslProvider$SslContextSpec#sslContext(io.netty.handler.ssl.SslContextBuilder)',
'reactor.netty.tcp.SslProvider#getDefaultConfigurationType()',
'reactor.netty.tcp.SslProvider#updateDefaultConfiguration(reactor.netty.tcp.SslProvider, reactor.netty.tcp.SslProvider$DefaultConfigurationType)'
'reactor.netty.tcp.SslProvider#updateDefaultConfiguration(reactor.netty.tcp.SslProvider, reactor.netty.tcp.SslProvider$DefaultConfigurationType)',

// New method is added
'reactor.netty.tcp.SslProvider$SslContextSpec#sslContext(reactor.netty.tcp.SslProvider$GenericSslContextSpec)'
]

classExcludes = [
Expand Down
54 changes: 42 additions & 12 deletions reactor-netty-core/src/main/java/reactor/netty/tcp/SslProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ public interface Builder {

public interface SslContextSpec {

/**
* SslContext builder that provides, specific for the protocol, default configuration
* e.g. {@link DefaultSslContextSpec}, {@link TcpSslContextSpec} etc.
* The default configuration is applied before any other custom configuration.
*
* @param spec SslContext builder that provides, specific for the protocol, default configuration
* @return {@literal this}
* @since 1.2.0
*/
Builder sslContext(GenericSslContextSpec<?> spec);

violetagg marked this conversation as resolved.
Show resolved Hide resolved
/**
* SslContext builder that provides, specific for the protocol, default configuration
* e.g. {@link DefaultSslContextSpec}, {@link TcpSslContextSpec} etc.
Expand All @@ -246,20 +257,21 @@ public interface SslContextSpec {
}

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

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

/**
* Create a new {@link SslContext} instance with the configured settings.
Expand All @@ -270,6 +282,18 @@ public interface ProtocolSslContextSpec {
SslContext sslContext() throws SSLException;
}

/**
* 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 extends GenericSslContextSpec<SslContextBuilder> {

@Override
ProtocolSslContextSpec configure(Consumer<SslContextBuilder> sslCtxBuilder);
}

final SslContext sslContext;
final long handshakeTimeoutMillis;
final long closeNotifyFlushTimeoutMillis;
Expand All @@ -282,9 +306,9 @@ public interface ProtocolSslContextSpec {

SslProvider(SslProvider.Build builder) {
if (builder.sslContext == null) {
if (builder.protocolSslContextSpec != null) {
if (builder.genericSslContextSpec != null) {
try {
this.sslContext = builder.protocolSslContextSpec.sslContext();
this.sslContext = builder.genericSslContextSpec.sslContext();
}
catch (SSLException e) {
throw Exceptions.propagate(e);
Expand Down Expand Up @@ -457,7 +481,7 @@ static final class Build implements SslContextSpec, Builder {
ReactorNetty.SSL_HANDSHAKE_TIMEOUT,
"10000"));

ProtocolSslContextSpec protocolSslContextSpec;
GenericSslContextSpec<?> genericSslContextSpec;
SslContext sslContext;
Consumer<? super SslHandler> handlerConfigurator;
long handshakeTimeoutMillis = DEFAULT_SSL_HANDSHAKE_TIMEOUT;
Expand All @@ -469,9 +493,15 @@ static final class Build implements SslContextSpec, Builder {

// SslContextSpec

@Override
public Builder sslContext(GenericSslContextSpec<?> genericSslContextSpec) {
this.genericSslContextSpec = genericSslContextSpec;
return this;
}

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

Expand Down Expand Up @@ -597,7 +627,7 @@ public boolean equals(Object o) {
Objects.equals(handlerConfigurator, build.handlerConfigurator) &&
Objects.equals(serverNames, build.serverNames) &&
confPerDomainName.equals(build.confPerDomainName) &&
Objects.equals(protocolSslContextSpec, build.protocolSslContextSpec);
Objects.equals(genericSslContextSpec, build.genericSslContextSpec);
}

@Override
Expand All @@ -610,7 +640,7 @@ public int hashCode() {
result = 31 * result + Long.hashCode(closeNotifyReadTimeoutMillis);
result = 31 * result + Objects.hashCode(serverNames);
result = 31 * result + Objects.hashCode(confPerDomainName);
result = 31 * result + Objects.hashCode(protocolSslContextSpec);
result = 31 * result + Objects.hashCode(genericSslContextSpec);
return result;
}

Expand Down
2 changes: 2 additions & 0 deletions reactor-netty-http/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ ext {
"io.netty.channel.kqueue;resolution:=optional;version=\"[4.1,5)\"",
"io.netty.handler.codec.haproxy;resolution:=optional;version=\"[4.1,5)\"",
"io.netty.incubator.channel.uring;resolution:=optional",
"io.netty.incubator.codec.http3;resolution:=optional",
"io.micrometer.*;resolution:=optional",
"*"
].join(","),
Expand Down Expand Up @@ -81,6 +82,7 @@ dependencies {
api "io.netty:netty-resolver-dns-native-macos:$nettyVersion"
}
compileOnly "io.netty:netty-codec-haproxy:$nettyVersion"
compileOnly "io.netty.incubator:netty-incubator-codec-http3:$nettyHttp3Version"
//transport resolution: typical build forces epoll but not kqueue transitively
//on the other hand, if we want to make transport-specific tests, we'll make all
// native optional at compile time and add correct native/nio to testRuntime
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2024 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.http;

import io.netty.handler.ssl.SslContext;
import io.netty.incubator.codec.quic.QuicSslContextBuilder;
import reactor.netty.tcp.SslProvider;
import reactor.util.annotation.Nullable;

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

import static io.netty.incubator.codec.http3.Http3.supportedApplicationProtocols;

/**
* SslContext builder that provides default configuration specific to HTTP/3 as follows:
* <ul>
* <li>Supported application protocols</li>
* </ul>
* <p>The default configuration is applied prior any other custom configuration.</p>
*
* @author Violeta Georgieva
* @since 1.2.0
* @see io.netty.incubator.codec.http3.Http3#supportedApplicationProtocols()
*/
public final class Http3SslContextSpec implements SslProvider.GenericSslContextSpec<QuicSslContextBuilder> {

/**
* Creates a builder for new client-side {@link SslContext}.
*
* @see QuicSslContextBuilder#forClient()
*/
public static Http3SslContextSpec forClient() {
return new Http3SslContextSpec(QuicSslContextBuilder.forClient());
}

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

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see QuicSslContextBuilder#forServer(KeyManager, String)
*/
public static Http3SslContextSpec forServer(KeyManager keyManager, @Nullable String keyPassword) {
return new Http3SslContextSpec(QuicSslContextBuilder.forServer(keyManager, keyPassword));
}

/**
* Creates a builder for new server-side {@link SslContext}.
*
* @see QuicSslContextBuilder#forServer(KeyManagerFactory, String)
*/
public static Http3SslContextSpec forServer(KeyManagerFactory keyManagerFactory, @Nullable String password) {
return new Http3SslContextSpec(QuicSslContextBuilder.forServer(keyManagerFactory, password));
}

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

@Override
public Http3SslContextSpec configure(Consumer<QuicSslContextBuilder> sslCtxBuilder) {
Objects.requireNonNull(sslCtxBuilder, "sslCtxBuilder");
sslCtxBuilder.accept(sslContextBuilder);
return this;
}

@Override
public SslContext sslContext() throws SSLException {
return sslContextBuilder.build();
}

final QuicSslContextBuilder sslContextBuilder;

Http3SslContextSpec(QuicSslContextBuilder sslContextBuilder) {
this.sslContextBuilder = sslContextBuilder;
configure(DEFAULT_CONFIGURATOR);
}

static final Consumer<QuicSslContextBuilder> DEFAULT_CONFIGURATOR =
sslCtxBuilder -> sslCtxBuilder.applicationProtocols(supportedApplicationProtocols());
}