Skip to content

chore: extract channel building logic rough sketch DONT MERGE#4133

Draft
igorbernstein2 wants to merge 1 commit intogoogleapis:mainfrom
igorbernstein2:channel-builder
Draft

chore: extract channel building logic rough sketch DONT MERGE#4133
igorbernstein2 wants to merge 1 commit intogoogleapis:mainfrom
igorbernstein2:channel-builder

Conversation

@igorbernstein2
Copy link
Contributor

Thank you for opening a Pull Request! Before submitting your PR, please read our contributing guidelines.

There are a few things you can do to make sure it goes smoothly:

  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> ☕️

@product-auto-label product-auto-label bot added the size: l Pull request size is large. label Mar 9, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request represents an initial refactoring effort to extract and centralize the complex logic involved in building gRPC channels. The primary goal is to improve the modularity, testability, and maintainability of the channel creation process by separating concerns into dedicated classes. This is an exploratory 'rough sketch' and not intended for immediate merging, focusing on architectural improvements.

Highlights

  • New GrpcCapabilities Class: Introduced a new GrpcCapabilities class to centralize and encapsulate logic for determining system and environment capabilities relevant to gRPC channel creation, such as detecting if the application is running on Compute Engine or if Direct Path is enabled.
  • New GrpcChannelConfig Class: Created GrpcChannelConfig as an immutable AutoValue class to serve as a dedicated data structure for passing various configuration parameters required for gRPC channel building.
  • New GrpcChannelFactory Class: Developed GrpcChannelFactory to centralize the complex logic of building ManagedChannelBuilder instances. This factory handles different channel flavors, including Direct Path, mTLS DCA, S2A, and default connections, based on the provided configuration and capabilities.
  • Refactoring of InstantiatingGrpcChannelProvider: Significantly refactored InstantiatingGrpcChannelProvider to delegate its channel creation responsibilities to the newly introduced GrpcCapabilities and GrpcChannelFactory classes, thereby reducing its complexity and improving modularity.
Changelog
  • gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCapabilities.java
    • Added a new class GrpcCapabilities to determine system and environment capabilities relevant to gRPC channel creation, such as whether the application is running on Compute Engine or if Direct Path is enabled.
  • gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcChannelConfig.java
    • Added a new AutoValue class GrpcChannelConfig to serve as an immutable configuration object for gRPC channel parameters.
  • gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcChannelFactory.java
    • Added a new class GrpcChannelFactory responsible for constructing ManagedChannelBuilder instances based on the provided GrpcChannelConfig and determined GrpcCapabilities, handling different channel flavors like Direct Path, mTLS DCA, S2A, and default.
  • gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java
    • Refactored the channel creation logic by removing direct implementations of capability checks and channel building, instead delegating these responsibilities to the newly introduced GrpcCapabilities and GrpcChannelFactory classes.
    • Removed several fields and methods related to direct channel building, including altsCallCredentials, mtlsS2ACallCredentials, s2aChannelCredentials, and related helper methods.
  • gax-java/gax/src/test/java/com/google/api/gax/rpc/EndpointContextTest.java
    • Added a .toBuilder().build() call in an existing test method to ensure immutability and builder pattern correctness.
Activity
  • No human activity has been recorded on this pull request yet, as indicated by the standard PR template description.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant refactoring of gRPC channel creation and capability detection within the gax-grpc library. The core logic, previously embedded in InstantiatingGrpcChannelProvider, has been extracted into two new classes: GrpcCapabilities, which centralizes checks for Direct Path eligibility, Google Compute Engine environment, and XDS enablement, and GrpcChannelFactory, which abstracts the construction of ManagedChannelBuilder instances based on various channel types (Direct Path, mTLS DCA, S2A, or default). A new GrpcChannelConfig class was also added to consolidate channel configuration parameters. Review comments suggest improving the robustness of endpoint parsing to handle IPv6 literals, adding a null check for config.mtlsEndpoint() to prevent NullPointerException, making hardcoded mTLS MDS credential paths configurable, confirming the security implications of using an empty password for KeyManagerFactory.init(), verifying the content of certKeyFile for TlsChannelCredentials, and enhancing exception handling for IOException during S2A mTLS setup.

Comment on lines +109 to +110
int port = Integer.parseInt(endpoint.substring(colon + 1));

Copy link
Contributor

Choose a reason for hiding this comment

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

high

The endpoint parsing logic endpoint.substring(0, colon) and Integer.parseInt(endpoint.substring(colon + 1)) assumes a simple host:port format and does not correctly handle IPv6 literals (e.g., [2001:db8::1]:8080) where colons are part of the address, or cases where no colon is present. If endpoint.lastIndexOf(':') returns -1, colon + 1 would be 0, leading to a StringIndexOutOfBoundsException. For IPv6 literals, a simple lastIndexOf(':') would incorrectly identify a colon within the address as the port separator. It's crucial to validate the format and explicitly account for IPv6 literals to prevent truncation or parsing errors. This issue also applies to lines 179-180.

    String host;
    int port;
    int lastColon = endpoint.lastIndexOf(':');

    if (lastColon < 0) {
      throw new IllegalArgumentException("Invalid endpoint format: " + endpoint + ". Expected format 'host:port' or '[IPv6]:port'.");
    }

    // Check for IPv6 literal format [host]:port
    if (endpoint.startsWith("[") && lastColon > 0 && endpoint.charAt(lastColon - 1) == ']') {
      host = endpoint.substring(1, lastColon - 1); // Remove brackets
    } else {
      host = endpoint.substring(0, lastColon);
    }

    try {
      port = Integer.parseInt(endpoint.substring(lastColon + 1));
    } catch (NumberFormatException e) {
      throw new IllegalArgumentException("Invalid port number in endpoint: " + endpoint, e);
    }
    // Now 'host' and 'port' variables can be used.
    // Example: Grpc.newChannelBuilder(host, port, channelCredentials);
References
  1. When parsing server addresses from endpoint strings, ensure the logic explicitly accounts for IPv6 literals (e.g., [2001:db8::1]) to prevent truncation or parsing errors that occur with standard URL parsers or simple colon-based splitting.

CompositeChannelCredentials.create(channelCredentials, mtlsS2ACallCredentials);
}
}
return Grpc.newChannelBuilder(config.mtlsEndpoint(), channelCredentials);
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The config.mtlsEndpoint() is annotated as @Nullable in GrpcChannelConfig. If mtlsEndpoint is null, passing it directly to Grpc.newChannelBuilder(config.mtlsEndpoint(), channelCredentials) will result in a NullPointerException. An explicit null check should be added here, and appropriate fallback or error handling should be implemented if mtlsEndpoint is null, aligning with the preference for explicit if checks in simple null-handling cases.

      String mtlsEndpoint = config.mtlsEndpoint();
      if (mtlsEndpoint == null) {
        // Handle case where mtlsEndpoint is null, perhaps fall back to default builder or throw an exception
        LOG.log(Level.WARNING, "mTLS endpoint is null, falling back to default builder.");
        return createDefaultBuilder();
      }
      return Grpc.newChannelBuilder(mtlsEndpoint, channelCredentials);
References
  1. While java.util.Optional can offer conciseness for null-handling, explicit if checks may be preferred for better readability in simple cases.

Comment on lines +35 to +36
private static final String MTLS_MDS_ROOT_PATH = "/run/google-mds-mtls/root.crt";
private static final String MTLS_MDS_CERT_CHAIN_AND_KEY_PATH = "/run/google-mds-mtls/client.key";
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The file paths MTLS_MDS_ROOT_PATH and MTLS_MDS_CERT_CHAIN_AND_KEY_PATH are hardcoded. While they are constants, hardcoding system-specific file paths can make the code less flexible and harder to adapt to different environments or configurations. Consider making these paths configurable, perhaps through environment variables or a configuration object, to improve maintainability and portability.

if (mtlsKeyStore != null) {
KeyManagerFactory factory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
factory.init(mtlsKeyStore, new char[] {});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The KeyManagerFactory.init() method is called with an empty character array (new char[] {}) for the password. While this might be acceptable if the keystore is not password-protected, it's a potential security or correctness concern if the keystore is expected to be password-protected. Please confirm if the keystore is guaranteed to be unencrypted or if a proper password mechanism should be implemented.

ChannelCredentials mtlsToS2AChannelCredentials =
TlsChannelCredentials.newBuilder()
.keyManager(certKeyFile, certKeyFile)
.trustManager(rootFile)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The TlsChannelCredentials.newBuilder().keyManager(certKeyFile, certKeyFile) call implies that certKeyFile contains both the private key and the certificate chain. While this is a common practice for some keystore formats, it's important to explicitly confirm that MTLS_MDS_CERT_CHAIN_AND_KEY_PATH indeed points to a file containing both. If not, this could lead to incorrect mTLS setup.

Comment on lines +232 to +237
} catch (IOException ignore) {
LOG.log(
Level.WARNING,
"Fallback to plaintext connection to S2A due to error: " + ignore.getMessage());
s2aChannelCredentials = createPlaintextToS2AChannelCredentials(plaintextAddress);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The IOException caught on line 232 is logged as a warning, but the variable name ignore suggests it's not fully handled. While a fallback to plaintext is implemented, catching and ignoring exceptions (even with logging) can sometimes mask underlying issues. Consider logging this exception at a more severe level (e.g., Level.SEVERE) or rethrowing it as a more specific runtime exception if the failure to establish mTLS is critical, to ensure proper visibility and handling of such errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: l Pull request size is large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant