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

feat: Add experimental S2A integration in client libraries grpc transport #3326

Merged
merged 59 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
aebd139
bump grpc versions.
rmehta19 Oct 29, 2024
b456935
bump auth to local SNAPSHOT.
rmehta19 Oct 29, 2024
3510643
implementation.
rmehta19 Nov 1, 2024
e799526
tests.
rmehta19 Nov 1, 2024
70ae8d0
use endpoint override.
rmehta19 Nov 1, 2024
7ef7313
fix typo
rmehta19 Nov 1, 2024
6d98115
cleanup.
rmehta19 Nov 4, 2024
250fecc
Merge branch 'main' into grpc-channel-using-s2a
rmehta19 Nov 4, 2024
8469a1a
resolve merge conflict with PR#1560
rmehta19 Nov 4, 2024
e880fbe
remove unnecessary dependency.
rmehta19 Nov 4, 2024
28adb31
add mtlsEndpoint to ClientSettings.
rmehta19 Nov 4, 2024
b3e2e06
add logs.
rmehta19 Nov 4, 2024
90892ef
remove ignore rule.
rmehta19 Nov 4, 2024
33b710c
separate into single condition if statements.
rmehta19 Nov 4, 2024
b320cc2
add logic to resolve endpointOverride / customEndpoint in EndpointCon…
rmehta19 Nov 4, 2024
035e17e
add javadocs.
rmehta19 Nov 4, 2024
759c3df
mtlsendpoint changes.
rmehta19 Nov 5, 2024
c096cb7
isS2AEnabled.
rmehta19 Nov 5, 2024
dc4b61e
expose endpointContext.
rmehta19 Nov 5, 2024
d4fcc71
Merge branch 'main' into grpc-channel-using-s2a
rmehta19 Nov 5, 2024
393190a
remove needsEndpointContext.
rmehta19 Nov 6, 2024
30a37c2
reduce nesting.
rmehta19 Nov 6, 2024
c3b93a0
check Strings null or empty.
rmehta19 Nov 6, 2024
18a4cf2
remove unecessary check.
rmehta19 Nov 6, 2024
0897730
set endpointContext in grpc transport provider tests.
rmehta19 Nov 6, 2024
9901656
package private.
rmehta19 Nov 6, 2024
257c515
set System::getenv as envProvider.
rmehta19 Nov 6, 2024
e4565f4
add javadoc.
rmehta19 Nov 6, 2024
33bd7a6
no getters for endpointcontext.
rmehta19 Nov 6, 2024
f9eef5b
nits + package private.
rmehta19 Nov 6, 2024
a7af12e
update javadocs + add mtlsServiceAddress.
rmehta19 Nov 6, 2024
eb70225
add javadocs.
rmehta19 Nov 6, 2024
2a50985
update readme.
rmehta19 Nov 6, 2024
943683d
private + readme updates.
rmehta19 Nov 6, 2024
34f030f
Merge branch 'main' into grpc-channel-using-s2a
rmehta19 Nov 6, 2024
fb3d608
update CLIRR-ignored-differences.
rmehta19 Nov 6, 2024
926faca
mark endpoint as Obsolete.
rmehta19 Nov 6, 2024
4ee2ee9
fix remaining tests to mock endpointContext.resolvedEndpoint().
rmehta19 Nov 6, 2024
2f70bf8
modify obsolete statement.
rmehta19 Nov 6, 2024
cb5768f
Merge branch 'main' into grpc-channel-using-s2a
rmehta19 Nov 7, 2024
c96c760
revert usage of endpointContext.resolvedEndpoint().
rmehta19 Nov 7, 2024
30ff3d6
Merge branch 'main' into grpc-channel-using-s2a
rmehta19 Nov 8, 2024
791ab2c
don't propogate endpointContext.
rmehta19 Nov 11, 2024
a4ee6c5
log as warning.
rmehta19 Nov 11, 2024
6eeccb2
package private + comment on S2A env var.
rmehta19 Nov 11, 2024
03eb991
package private useS2A.
rmehta19 Nov 12, 2024
91175ef
check if files exist.
rmehta19 Nov 12, 2024
04d47a0
default unsupported exception.
rmehta19 Nov 12, 2024
7d7b233
log a warning.
rmehta19 Nov 12, 2024
a42dcbd
pass File to TlsChannelCredentials.
rmehta19 Nov 12, 2024
56551c6
test createPlaintextS2AChannelCredentials.
rmehta19 Nov 12, 2024
1ff7a92
fix file checking logic.
rmehta19 Nov 12, 2024
2958fb4
document mtls mds.
rmehta19 Nov 13, 2024
c9a7edd
rename mtls mds vars to be clear these are filepaths.
rmehta19 Nov 13, 2024
8bf770d
bump auth dep and add some tests for createS2ASecuredChannelCredentials.
rmehta19 Nov 13, 2024
33faba1
Merge branch 'main' into grpc-channel-using-s2a
rmehta19 Nov 13, 2024
924a4e4
fix BUILD.
rmehta19 Nov 13, 2024
b00fd53
Merge branch 'main' into grpc-channel-using-s2a
rmehta19 Nov 14, 2024
ca5be85
fix: Do not build s2a frpm grpc's Bazel target.
blakeli0 Nov 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ SHELL [ "/bin/bash", "-c" ]

ARG OWLBOT_CLI_COMMITTISH=38fe6f89a2339ee75c77739b31b371f601b01bb3
ARG PROTOC_VERSION=25.5
ARG GRPC_VERSION=1.67.1
ARG GRPC_VERSION=1.68.1
ARG JAVA_FORMAT_VERSION=1.7
ENV HOME=/home
ENV OS_ARCHITECTURE="linux-x86_64"
Expand Down
4 changes: 2 additions & 2 deletions gapic-generator-java-pom-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
<!-- External dependencies, especially gRPC and Protobuf version, should be
consistent across modules in this repository -->
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<grpc.version>1.67.1</grpc.version>
<google.auth.version>1.29.0</google.auth.version>
<grpc.version>1.68.1</grpc.version>
<google.auth.version>1.29.1-SNAPSHOT</google.auth.version>
<google.http-client.version>1.45.0</google.http-client.version>
<gson.version>2.11.0</gson.version>
<guava.version>33.3.1-jre</guava.version>
Expand Down
2 changes: 2 additions & 0 deletions gapic-generator-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,12 @@
<dependency>
<groupId>com.google.api</groupId>
<artifactId>gax-grpc</artifactId>
<version>2.57.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.api</groupId>
<artifactId>gax-grpc</artifactId>
<version>2.57.1-SNAPSHOT</version>
<!-- import the test code, https://maven.apache.org/plugins/maven-jar-plugin/examples/create-test-jar.html -->
<type>test-jar</type>
<classifier>testlib</classifier>
Expand Down
2 changes: 1 addition & 1 deletion gax-java/dependencies.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ version.gax_httpjson=2.57.1-SNAPSHOT

version.com_google_protobuf=3.25.5
version.google_java_format=1.15.0
version.io_grpc=1.67.1
version.io_grpc=1.68.1

# Maven artifacts.
# Note, the actual name of each property matters (bazel build scripts depend on it).
Expand Down
1 change: 1 addition & 0 deletions gax-java/gax-grpc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ _COMPILE_DEPS = [
"@io_grpc_grpc_netty_shaded//jar",
"@io_grpc_grpc_grpclb//jar",
"@io_grpc_grpc_java//alts:alts",
"@io_grpc_grpc_java//s2a:s2a",
"@io_netty_netty_tcnative_boringssl_static//jar",
"@javax_annotation_javax_annotation_api//jar",
"//gax:gax",
Expand Down
4 changes: 4 additions & 0 deletions gax-java/gax-grpc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-s2a</artifactId>
</dependency>
rmehta19 marked this conversation as resolved.
Show resolved Hide resolved
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.google.auth.ApiKeyCredentials;
import com.google.auth.Credentials;
import com.google.auth.oauth2.ComputeEngineCredentials;
import com.google.auth.oauth2.S2A;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
Expand All @@ -54,13 +55,18 @@
import io.grpc.CallCredentials;
import io.grpc.ChannelCredentials;
import io.grpc.Grpc;
import io.grpc.InsecureChannelCredentials;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.TlsChannelCredentials;
import io.grpc.alts.GoogleDefaultChannelCredentials;
import io.grpc.auth.MoreCallCredentials;
import io.grpc.s2a.S2AChannelCredentials;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
Expand Down Expand Up @@ -99,6 +105,13 @@ public final class InstantiatingGrpcChannelProvider implements TransportChannelP
@VisibleForTesting
static final String DIRECT_PATH_ENV_ENABLE_XDS = "GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS";

@VisibleForTesting static final String S2A_ENV_ENABLE_USE_S2A = "EXPERIMENTAL_GOOGLE_API_USE_S2A";

private static final String MTLS_MDS_ROOT = "/run/google-mds-mtls/root.crt";
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess these are well-known locations on GCE? Do we have any public or internal docs for these locations? If not, where do we get these? What I'm worried is that how do we get notified if they change?

Copy link
Contributor Author

@rmehta19 rmehta19 Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess these are well-known locations on GCE?

Yes

Do we have any public or internal docs for these locations?

I included a link down below in the logic, I've moved it up here as well: https://cloud.google.com/compute/docs/metadata/overview#https-mds. Specifically https://cloud.google.com/compute/docs/metadata/overview#https-mds-root-certs and https://cloud.google.com/compute/docs/metadata/overview#https-mds-client-certs

2958fb4

// The mTLS MDS credentials are formatted as the concatenation of a PEM-encoded certificate chain
// followed by a PEM-encoded private key.
private static final String MTLS_MDS_CERT_CHAIN_AND_KEY = "/run/google-mds-mtls/client.key";
blakeli0 marked this conversation as resolved.
Show resolved Hide resolved

static final long DIRECT_PATH_KEEP_ALIVE_TIME_SECONDS = 3600;
static final long DIRECT_PATH_KEEP_ALIVE_TIMEOUT_SECONDS = 20;
static final String GCE_PRODUCTION_NAME_PRIOR_2016 = "Google";
Expand All @@ -108,9 +121,13 @@ public final class InstantiatingGrpcChannelProvider implements TransportChannelP
private final Executor executor;
private final HeaderProvider headerProvider;
private final String endpoint;
// TODO: remove. envProvider currently provides DirectPath environment variable, and is only used
// during initial rollout for DirectPath. This provider will be removed once the DirectPath
// environment is not used.
private final String mtlsEndpoint;
private final String endpointOverride;
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved
// TODO: remove.
// envProvider currently provides DirectPath and S2A environment variables, and is only used
// during initial rollout for DirectPath and S2A. This provider will be removed once the
// DirectPath
// and S2A environment variables are not used.
private final EnvironmentProvider envProvider;
@Nullable private final GrpcInterceptorProvider interceptorProvider;
@Nullable private final Integer maxInboundMessageSize;
Expand All @@ -136,6 +153,8 @@ private InstantiatingGrpcChannelProvider(Builder builder) {
this.executor = builder.executor;
this.headerProvider = builder.headerProvider;
this.endpoint = builder.endpoint;
this.mtlsEndpoint = builder.mtlsEndpoint;
this.endpointOverride = builder.endpointOverride;
this.mtlsProvider = builder.mtlsProvider;
this.envProvider = builder.envProvider;
this.interceptorProvider = builder.interceptorProvider;
Expand Down Expand Up @@ -211,6 +230,16 @@ public boolean needsEndpoint() {
return endpoint == null;
}

@Override
public boolean needsMtlsEndpoint() {
return mtlsEndpoint == null;
}

@Override
public boolean needsEndpointOverride() {
return endpointOverride == null;
}

/**
* Specify the endpoint the channel should connect to.
*
Expand All @@ -225,6 +254,40 @@ public TransportChannelProvider withEndpoint(String endpoint) {
return toBuilder().setEndpoint(endpoint).build();
}

/**
* Specify the MTLS endpoint.
*
* <p>The value of {@code mtlsEndpoint} must be of the form {@code host:port}.
*
* @param mtlsEndpoint
* @return A new {@link InstantiatingGrpcChannelProvider} with the specified MTLS endpoint
* configured
*/
@Override
public TransportChannelProvider withMtlsEndpoint(String mtlsEndpoint) {
if (!mtlsEndpoint.isEmpty()) {
validateEndpoint(mtlsEndpoint);
}
return toBuilder().setMtlsEndpoint(mtlsEndpoint).build();
}
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Specify the endpoint override.
*
* <p>The value of {@code endpointOverride} must be of the form {@code host:port}.
*
* @param endpointOverride
* @return A new {@link InstantiatingGrpcChannelProvider} with the specified endpoint Override
* configured
*/
@Override
public TransportChannelProvider withEndpointOverride(String endpointOverride) {
if (!endpointOverride.isEmpty()) {
validateEndpoint(endpointOverride);
}
return toBuilder().setEndpointOverride(endpointOverride).build();
}

/** @deprecated Please modify pool settings via {@link #toBuilder()} */
@Deprecated
@Override
Expand Down Expand Up @@ -410,6 +473,89 @@ ChannelCredentials createMtlsChannelCredentials() throws IOException, GeneralSec
return null;
}

@VisibleForTesting
boolean isGoogleS2AEnabled() {
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved
String S2AEnv = envProvider.getenv(S2A_ENV_ENABLE_USE_S2A);
boolean isS2AEnv = Boolean.parseBoolean(S2AEnv);
if (isS2AEnv) {
return true;
}
return false;
}

@VisibleForTesting
boolean shouldUseS2A() {
// If EXPERIMENTAL_GOOGLE_API_USE_S2A is not set to true, skip S2A.
if (!isGoogleS2AEnabled()) {
return false;
}

// If {@code mtlsEndpoint} is not set, or {@code endpointOverride} is specified, skip S2A.
if (mtlsEndpoint.isEmpty() || !endpointOverride.isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I see endpointOverride is needed within transport channel. Is providing this override option a requirement for this feature or AIP?

nit: Do you mind to separate these 2 scenarios in single condition if statements for a bit more clarity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is providing this override option a requirement for this feature or AIP?

No, I would say that providing the endpointOverride is not a requirement for the S2A feature / AIP 4115. the transport just needs to know if endpointOverride is provided or not. If it is provided, we want to skip using S2A. To not introduce a dependency on gax-grpc in gax, I added endpointOverride to the TransportChannelProvider.

separating into 2 single condition if done in 33b710c

return false;
}
rmehta19 marked this conversation as resolved.
Show resolved Hide resolved

// mTLS via S2A is not supported in any universe other than googleapis.com.
if (!mtlsEndpoint.contains(Credentials.GOOGLE_DEFAULT_UNIVERSE)) {
return false;
}

return true;
}

@VisibleForTesting
ChannelCredentials createMtlsToS2AChannelCredentials(
InputStream trustBundle, InputStream privateKey, InputStream certChain) throws IOException {
if (trustBundle == null || privateKey == null || certChain == null) {
return null;
}
return TlsChannelCredentials.newBuilder()
.keyManager(privateKey, certChain)
blakeli0 marked this conversation as resolved.
Show resolved Hide resolved
.trustManager(trustBundle)
.build();
}

@VisibleForTesting
ChannelCredentials createS2ASecuredChannelCredentials() {
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved
S2A s2aUtils = S2A.newBuilder().build();
String plaintextAddress = s2aUtils.getPlaintextS2AAddress();
String mtlsAddress = s2aUtils.getMtlsS2AAddress();
if (!mtlsAddress.isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change this to:

if (mtlsAddress.isEmpty()) {
  return null;
}

to reduce nesting?

Also don't we need this for plaintextAddress as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic we want is to "fallback" to plaintextAddress in a few cases (basically on any failure to create mtls to S2A creds):

  • mtlsAddress.isEmpty()
  • if MTLS-MDS files are not found
  • error creating TlsChannelCredentials

In 30a37c2 I reduced the nesting, let me know what you think. I also reversed the negation checks (e.g. `if x != null) to make it a bit more readable.

// Currently, MTLS to MDS is only available on GCE. See:
// https://cloud.google.com/compute/docs/metadata/overview#https-mds
// Try to load MTLS-MDS creds.
InputStream trustBundle = null;
InputStream privateKey = null;
InputStream certChain = null;
try {
trustBundle = new FileInputStream(MTLS_MDS_ROOT);
privateKey = new FileInputStream(MTLS_MDS_CERT_CHAIN_AND_KEY);
certChain = new FileInputStream(MTLS_MDS_CERT_CHAIN_AND_KEY);
} catch (FileNotFoundException ignore) {
// Fallback to plaintext-to-S2A connection.
}
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved
ChannelCredentials mtlsToS2AChannelCredentials = null;
try {
// Try to connect to S2A using mTLS.
mtlsToS2AChannelCredentials =
createMtlsToS2AChannelCredentials(trustBundle, privateKey, certChain);
} catch (IOException ignore) {
// Fallback to plaintext-to-S2A connection.
}
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved
if (mtlsToS2AChannelCredentials != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can early exit with null in the exceptions above, I don't think we would need null check. mtlsToS2AChannelCredentials shouldn't be null as it would either return a TlsChannelCredentials or throw an IOException

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, done in 18a4cf2

return S2AChannelCredentials.newBuilder(mtlsAddress, mtlsToS2AChannelCredentials).build();
}
}

if (!plaintextAddress.isEmpty()) {
// Fallback to plaintext connection to S2A.
return S2AChannelCredentials.newBuilder(plaintextAddress, InsecureChannelCredentials.create())
.build();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to be the last check? Shouldn't this be one of the earliest checks? I assumed so since we are retrieving the S2AConfig in the first step.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plaintextAddress is a fallback, so we only really want to use if we fail to use mtlsAddress to create mtls to S2A channel credentials, which is why the check is at the very end. I did some refactoring in 30a37c2 which may help the readability.


return null;
}

private ManagedChannel createSingleChannel() throws IOException {
GrpcHeaderInterceptor headerInterceptor =
new GrpcHeaderInterceptor(headersWithDuplicatesRemoved);
Expand Down Expand Up @@ -447,16 +593,30 @@ private ManagedChannel createSingleChannel() throws IOException {
builder.keepAliveTime(DIRECT_PATH_KEEP_ALIVE_TIME_SECONDS, TimeUnit.SECONDS);
builder.keepAliveTimeout(DIRECT_PATH_KEEP_ALIVE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} else {
// Try and create credentials via DCA. See https://google.aip.dev/auth/4114.
ChannelCredentials channelCredentials;
try {
channelCredentials = createMtlsChannelCredentials();
} catch (GeneralSecurityException e) {
throw new IOException(e);
}
if (channelCredentials != null) {
// Create the channel using channel credentials created via DCA.
builder = Grpc.newChannelBuilder(endpoint, channelCredentials);
} else {
builder = ManagedChannelBuilder.forAddress(serviceAddress, port);
// Could not create channel credentials via DCA. In accordance with
// https://google.aip.dev/auth/4115, if credentials not available through
// DCA, try mTLS with credentials held by the S2A (Secure Session Agent).
if (shouldUseS2A()) {
channelCredentials = createS2ASecuredChannelCredentials();
}
if (channelCredentials != null) {
// Create the channel using S2A-secured channel credentials.
builder = Grpc.newChannelBuilder(mtlsEndpoint, channelCredentials);
} else {
// Use default if we cannot initialize channel credentials via DCA or S2A.
builder = ManagedChannelBuilder.forAddress(serviceAddress, port);
}
}
}
// google-c2p resolver requires service config lookup
Expand Down Expand Up @@ -547,6 +707,16 @@ public String getEndpoint() {
return endpoint;
}

/** The mTLS endpoint. */
public String getMtlsEndpoint() {
return mtlsEndpoint;
}

/** The endpoint override */
public String getEndpointOverride() {
return endpointOverride;
}

/** This method is obsolete. Use {@link #getKeepAliveTimeDuration()} instead. */
@ObsoleteApi("Use getKeepAliveTimeDuration() instead")
public org.threeten.bp.Duration getKeepAliveTime() {
Expand Down Expand Up @@ -604,6 +774,8 @@ public static final class Builder {
private Executor executor;
private HeaderProvider headerProvider;
private String endpoint;
private String mtlsEndpoint;
private String endpointOverride;
private EnvironmentProvider envProvider;
private MtlsProvider mtlsProvider = new MtlsProvider();
@Nullable private GrpcInterceptorProvider interceptorProvider;
Expand Down Expand Up @@ -632,6 +804,8 @@ private Builder(InstantiatingGrpcChannelProvider provider) {
this.executor = provider.executor;
this.headerProvider = provider.headerProvider;
this.endpoint = provider.endpoint;
this.mtlsEndpoint = provider.mtlsEndpoint;
this.endpointOverride = provider.endpointOverride;
this.envProvider = provider.envProvider;
this.interceptorProvider = provider.interceptorProvider;
this.maxInboundMessageSize = provider.maxInboundMessageSize;
Expand Down Expand Up @@ -700,6 +874,22 @@ public Builder setEndpoint(String endpoint) {
return this;
}

public Builder setMtlsEndpoint(String mtlsEndpoint) {
if (!mtlsEndpoint.isEmpty()) {
validateEndpoint(mtlsEndpoint);
}
this.mtlsEndpoint = mtlsEndpoint;
return this;
}

public Builder setEndpointOverride(String endpointOverride) {
if (!endpointOverride.isEmpty()) {
validateEndpoint(endpointOverride);
}
this.endpointOverride = endpointOverride;
return this;
}

@VisibleForTesting
Builder setMtlsProvider(MtlsProvider mtlsProvider) {
this.mtlsProvider = mtlsProvider;
Expand All @@ -722,6 +912,14 @@ public String getEndpoint() {
return endpoint;
}

public String getMtlsEndpoint() {
return mtlsEndpoint;
}

public String getEndpointOverride() {
return endpointOverride;
}

/** The maximum message size allowed to be received on the channel. */
public Builder setMaxInboundMessageSize(Integer max) {
this.maxInboundMessageSize = max;
Expand Down
Loading
Loading