Skip to content

Commit

Permalink
Make ProtocolSslContextSpec generic (#3150)
Browse files Browse the repository at this point in the history
- QuicSslContextBuilder does not extend SslContextBuilder.
- Add Http3SslContextSpec, similar to Http2SslContextSpec and Http11SslContextSpec.

Related to #1531
  • Loading branch information
violetagg authored Apr 16, 2024
1 parent 7af191c commit 6d0c24d
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 13 deletions.
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);

/**
* 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());
}

0 comments on commit 6d0c24d

Please sign in to comment.