-
Notifications
You must be signed in to change notification settings - Fork 1.1k
chore: poc for gax-grpc using grpc's preferJdkSslProvider() #13388
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
base: main
Are you sure you want to change the base?
Changes from all commits
519e72e
87bb491
54830ea
4c13044
31b6487
bf98978
b3e7d27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <parent> | ||
| <groupId>com.google.cloud</groupId> | ||
| <artifactId>google-cloud-shared-config</artifactId> | ||
| <version>1.17.0</version> | ||
| </parent> | ||
|
|
||
| <groupId>com.google.api</groupId> | ||
| <artifactId>pqc-test-parent</artifactId> | ||
| <packaging>pom</packaging> | ||
| <version>2.81.0-SNAPSHOT</version> | ||
|
|
||
| <modules> | ||
| <module>pqc-test-common</module> | ||
| <module>pqc-test-snapshot</module> | ||
| <module>pqc-test-release</module> | ||
| </modules> | ||
|
|
||
| <dependencyManagement> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.bouncycastle</groupId> | ||
| <artifactId>bcprov-jdk18on</artifactId> | ||
| <version>1.84</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.bouncycastle</groupId> | ||
| <artifactId>bctls-jdk18on</artifactId> | ||
| <version>1.84</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.bouncycastle</groupId> | ||
| <artifactId>bcutil-jdk18on</artifactId> | ||
| <version>1.84</version> | ||
| </dependency> | ||
| </dependencies> | ||
| </dependencyManagement> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-surefire-plugin</artifactId> | ||
| <configuration> | ||
| <argLine> | ||
| -Djavax.net.ssl.trustStore=${project.basedir}/../pqc-test-common/target/classes/pqctest.p12 | ||
| -Djavax.net.ssl.trustStorePassword=password | ||
| -Djavax.net.ssl.trustStoreType=PKCS12 | ||
| </argLine> | ||
| </configuration> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <parent> | ||
| <groupId>com.google.api</groupId> | ||
| <artifactId>pqc-test-parent</artifactId> | ||
| <version>2.81.0-SNAPSHOT</version> | ||
| <relativePath>../pom.xml</relativePath> | ||
| </parent> | ||
|
|
||
| <artifactId>pqc-test-common</artifactId> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-api</artifactId> | ||
| <version>5.10.2</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.grpc</groupId> | ||
| <artifactId>grpc-netty</artifactId> | ||
| <version>1.81.0</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.grpc</groupId> | ||
| <artifactId>grpc-stub</artifactId> | ||
| <version>1.81.0</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.bouncycastle</groupId> | ||
| <artifactId>bcprov-jdk18on</artifactId> | ||
| <version>1.84</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.bouncycastle</groupId> | ||
| <artifactId>bctls-jdk18on</artifactId> | ||
| <version>1.84</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.google.cloud</groupId> | ||
| <artifactId>google-cloud-bigquery</artifactId> | ||
| <version>2.66.0</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.google.cloud</groupId> | ||
| <artifactId>google-cloud-translate</artifactId> | ||
| <version>2.92.0</version> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Redistribution and use in source and binary forms, with or without | ||
| * modification, are permitted provided that the following conditions are | ||
| * met: | ||
| * | ||
| * * Redistributions of source code must retain the above copyright | ||
| * notice, this list of conditions and the following disclaimer. | ||
| * * Redistributions in binary form must reproduce the above | ||
| * copyright notice, this list of conditions and the following disclaimer | ||
| * in the documentation and/or other materials provided with the | ||
| * distribution. | ||
| * * Neither the name of Google LLC nor the names of its | ||
| * contributors may be used to endorse or promote products derived from | ||
| * this software without specific prior written permission. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| */ | ||
|
|
||
| package com.google.api.gax.httpjson; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
| import static org.junit.jupiter.api.Assertions.fail; | ||
|
|
||
| import com.google.api.gax.core.NoCredentialsProvider; | ||
| import com.google.cloud.translate.v3.LocationName; | ||
| import com.google.cloud.translate.v3.TranslationServiceClient; | ||
| import com.google.cloud.translate.v3.TranslationServiceSettings; | ||
| import java.io.InputStream; | ||
| import java.security.Provider; | ||
| import java.security.Security; | ||
| import java.util.Collections; | ||
| import java.util.TreeSet; | ||
| import org.junit.jupiter.api.AfterAll; | ||
| import org.junit.jupiter.api.BeforeAll; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| public abstract class PqcConnectivityTest { | ||
|
|
||
| private static Process serverProcess; | ||
| protected static int grpcPqcPort; | ||
| protected static int grpcClassicalPort; | ||
|
|
||
| protected boolean clientSupportsPqc() { | ||
| return true; | ||
| } | ||
|
|
||
| protected abstract boolean grpcTestShouldSucceed(); | ||
|
|
||
| @BeforeAll | ||
| public static void setup() throws Exception { | ||
|
|
||
| // 6. Spawn PqcTestServer in a separate background process to ensure physical | ||
| // JVM runtime isolation! | ||
| ProcessBuilder pb = | ||
| new ProcessBuilder( | ||
| "java", | ||
| "-cp", | ||
| System.getProperty("java.class.path"), | ||
| "com.google.api.gax.pqc.PqcTestServer"); | ||
|
|
||
| // Force merging of error stream to ease debugging in test output | ||
| pb.redirectErrorStream(true); | ||
| serverProcess = pb.start(); | ||
|
|
||
| // Read server's stdout to dynamically capture the allocated ephemeral ports | ||
| java.io.BufferedReader reader = | ||
| new java.io.BufferedReader( | ||
| new java.io.InputStreamReader( | ||
| serverProcess.getInputStream(), java.nio.charset.StandardCharsets.UTF_8)); | ||
|
|
||
| String line; | ||
| boolean grpcPqcFound = false; | ||
| boolean grpcClassicalFound = false; | ||
|
|
||
| // Wait for the server process to output its HTTP and gRPC ports | ||
| long startTime = System.currentTimeMillis(); | ||
| while ((line = reader.readLine()) != null) { | ||
| System.out.println("[SERVER-OUT] " + line); | ||
| if (line.startsWith("GRPC_PQC_PORT: ")) { | ||
| grpcPqcPort = Integer.parseInt(line.substring(15).trim()); | ||
| grpcPqcFound = true; | ||
| } else if (line.startsWith("GRPC_CLASSICAL_PORT: ")) { | ||
| grpcClassicalPort = Integer.parseInt(line.substring(21).trim()); | ||
| grpcClassicalFound = true; | ||
| } | ||
|
|
||
| if (grpcPqcFound && grpcClassicalFound) { | ||
| break; | ||
| } | ||
|
|
||
| // Ephemeral port detection timeout (10 seconds) to fail-fast on server startup | ||
| // errors | ||
| if (System.currentTimeMillis() - startTime > 10000) { | ||
| throw new RuntimeException( | ||
| "Timeout waiting for PqcTestServer ephemeral ports to be printed!"); | ||
| } | ||
| } | ||
|
|
||
| if (!grpcPqcFound || !grpcClassicalFound) { | ||
| throw new RuntimeException("PqcTestServer failed to initialize ephemeral ports!"); | ||
| } | ||
|
|
||
| // Start a background thread to continuously drain the server's stdout | ||
| Thread drainThread = | ||
| new Thread( | ||
| () -> { | ||
| try { | ||
| String l; | ||
| while ((l = reader.readLine()) != null) { | ||
| System.out.println("[SERVER-OUT] " + l); | ||
| } | ||
| } catch (java.io.IOException e) { | ||
| // Ignore stream closed | ||
| } | ||
| }); | ||
| drainThread.setDaemon(true); | ||
| drainThread.start(); | ||
| } | ||
|
Comment on lines
+63
to
+131
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If an exception is thrown or a timeout occurs during @BeforeAll
public static void setup() throws Exception {
// 6. Spawn PqcTestServer in a separate background process to ensure physical
// JVM runtime isolation!
ProcessBuilder pb =
new ProcessBuilder(
"java",
"-cp",
System.getProperty("java.class.path"),
"com.google.api.gax.pqc.PqcTestServer");
// Force merging of error stream to ease debugging in test output
pb.redirectErrorStream(true);
serverProcess = pb.start();
try {
// Read server's stdout to dynamically capture the allocated ephemeral ports
java.io.BufferedReader reader =
new java.io.BufferedReader(
new java.io.InputStreamReader(
serverProcess.getInputStream(), java.nio.charset.StandardCharsets.UTF_8));
String line;
boolean grpcPqcFound = false;
boolean grpcClassicalFound = false;
// Wait for the server process to output its HTTP and gRPC ports
long startTime = System.currentTimeMillis();
while ((line = reader.readLine()) != null) {
System.out.println("[SERVER-OUT] " + line);
if (line.startsWith("GRPC_PQC_PORT: ")) {
grpcPqcPort = Integer.parseInt(line.substring(15).trim());
grpcPqcFound = true;
} else if (line.startsWith("GRPC_CLASSICAL_PORT: ")) {
grpcClassicalPort = Integer.parseInt(line.substring(21).trim());
grpcClassicalFound = true;
}
if (grpcPqcFound && grpcClassicalFound) {
break;
}
// Ephemeral port detection timeout (10 seconds) to fail-fast on server startup
// errors
if (System.currentTimeMillis() - startTime > 10000) {
throw new RuntimeException(
"Timeout waiting for PqcTestServer ephemeral ports to be printed!");
}
}
if (!grpcPqcFound || !grpcClassicalFound) {
throw new RuntimeException("PqcTestServer failed to initialize ephemeral ports!");
}
// Start a background thread to continuously drain the server's stdout
Thread drainThread =
new Thread(
() -> {
try {
String l;
while ((l = reader.readLine()) != null) {
System.out.println("[SERVER-OUT] " + l);
}
} catch (java.io.IOException e) {
// Ignore stream closed
} finally {
try {
reader.close();
} catch (java.io.IOException e) {
// Ignore
}
}
});
drainThread.setDaemon(true);
drainThread.start();
} catch (Throwable t) {
serverProcess.destroyForcibly();
throw t;
}
} |
||
|
|
||
| @AfterAll | ||
| public static void teardown() { | ||
| if (serverProcess != null) { | ||
| // Forcibly destroy the background process and close standard streams to allow | ||
| // clean exit | ||
| serverProcess.destroyForcibly(); | ||
| } | ||
| } | ||
|
|
||
| private void runGrpcTest(int port, boolean shouldSucceed) throws Exception { | ||
| TranslationServiceSettings settings = | ||
| TranslationServiceSettings.newBuilder() | ||
| .setEndpoint("localhost:" + port) | ||
| .setCredentialsProvider(NoCredentialsProvider.create()) | ||
| .build(); | ||
|
|
||
| try (TranslationServiceClient client = TranslationServiceClient.create(settings)) { | ||
| LocationName parent = LocationName.of("test-project", "global"); | ||
| client.translateText(parent, "es", Collections.singletonList("hello")); | ||
| if (!shouldSucceed) { | ||
| fail("Expected gRPC call to fail!"); | ||
| } | ||
| } catch (Exception e) { | ||
| if (shouldSucceed) { | ||
| fail("Expected gRPC call to succeed, but failed: " + e.getMessage(), e); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| @Test | ||
| public void testGrpcPqcServerEnforced() throws Exception { | ||
| runGrpcTest(grpcPqcPort, grpcTestShouldSucceed()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGrpcClassicalServer() throws Exception { | ||
| runGrpcTest(grpcClassicalPort, true); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instantiating
BouncyCastleProviderandBouncyCastleJsseProvideron every call toconfigurePqc(which runs whenever a channel is decorated/created) is highly inefficient and can lead to significant performance overhead and memory leaks. Instead, use a lazy static holder class to cache these provider instances safely and efficiently.References