2323use Google \ApiCore \KnownTypes ;
2424use Google \ApiCore \InsecureCredentialsWrapper ;
2525use Google \ApiCore \InsecureRequestBuilder ;
26+ use Google \ApiCore \RequestBuilder ;
2627use Google \ApiCore \Transport \GrpcTransport ;
2728use Google \ApiCore \Transport \TransportInterface ;
2829use Google \ApiCore \Transport \RestTransport ;
2930use Google \Auth \HttpHandler \HttpHandlerFactory ;
3031use Google \Protobuf \Internal \Message ;
3132use Google \Showcase \V1beta1 \Client \EchoClient ;
33+ use Google \Showcase \V1beta1 \EchoRequest ;
3234use Google \Showcase \V1beta1 \FailEchoWithDetailsRequest ;
3335use Grpc \ChannelCredentials ;
36+ use GuzzleHttp \Client ;
3437
3538final class ShowcaseTest extends TestCase
3639{
40+ private const TLS_GROUP = 'x-showcase-tls-group ' ;
41+ private const PEM_PATH = __DIR__ . '/showcase.pem ' ;
42+
3743 public function provideTransport ()
3844 {
45+ $ host = 'localhost:7469 ' ;
46+ $ restConfigPath = __DIR__ . '/src/V1beta1/resources/echo_rest_client_config.php ' ;
47+
48+ if (file_exists (self ::PEM_PATH )) {
49+ $ pemContents = file_get_contents (self ::PEM_PATH );
50+ $ grpcCredentials = ChannelCredentials::createSsl ($ pemContents );
51+ $ requestBuilder = new RequestBuilder ($ host , $ restConfigPath );
52+ $ guzzleClient = new Client (['verify ' => self ::PEM_PATH ]);
53+ } else {
54+ $ grpcCredentials = ChannelCredentials::createInsecure ();
55+ $ requestBuilder = new InsecureRequestBuilder ($ host , $ restConfigPath );
56+ $ guzzleClient = null ;
57+ }
58+
3959 // build gRPC transport
4060 $ grpc = GrpcTransport::build (
41- 'localhost:7469 ' ,
42- ['stubOpts ' => ['credentials ' => ChannelCredentials::createInsecure ()]]
61+ $ host ,
62+ [
63+ 'stubOpts ' => [
64+ 'credentials ' => $ grpcCredentials
65+ ]
66+ ]
4367 );
4468
4569 // build REST transport
46- $ restConfigPath = __DIR__ . '/src/V1beta1/resources/echo_rest_client_config.php ' ;
47- $ requestBuilder = new InsecureRequestBuilder ('localhost:7469 ' , $ restConfigPath );
48- $ httpHandler = HttpHandlerFactory::build ();
70+ $ httpHandler = HttpHandlerFactory::build ($ guzzleClient );
4971 $ rest = new RestTransport ($ requestBuilder , [$ httpHandler , 'async ' ]);
5072
5173 return [[$ grpc ], [$ rest ]];
@@ -71,4 +93,48 @@ public function testFailWithDetails(TransportInterface $transport): void
7193 $ this ->assertInstanceOf (Message::class, $ detail );
7294 }
7395 }
96+
97+ /** @dataProvider provideTransport */
98+ public function testPqc (TransportInterface $ transport ): void
99+ {
100+ if (!file_exists (self ::PEM_PATH )) {
101+ $ this ->markTestSkipped (
102+ 'You need to run the showcase server with --tls ' .
103+ '--ca-cert-output-file tests/Conformance/showcase.pem to run PQC tests '
104+ );
105+ }
106+
107+ $ expected = 'This is a test ' ;
108+ $ expectedGroup = 'X25519MLKEM768 ' ;
109+ $ responseHeaders = null ;
110+ $ metadataCallback = function (array $ metadata ) use (&$ responseHeaders ) {
111+ $ responseHeaders = $ metadata ;
112+ };
113+
114+ $ echoClient = new EchoClient ([
115+ 'credentials ' => new InsecureCredentialsWrapper (),
116+ 'transport ' => $ transport
117+ ]);
118+
119+ $ echoRequest = new EchoRequest ();
120+ $ echoRequest ->setContent ($ expected );
121+ $ response = $ echoClient ->echo ($ echoRequest , [
122+ 'metadataCallback ' => $ metadataCallback
123+ ]);
124+
125+ $ this ->assertEquals ($ expected , $ response ->getContent ());
126+ $ this ->assertNotNull ($ responseHeaders );
127+
128+ /** @var array<string, array<int, string>> $responseHeaders */
129+ $ responseHeaders = array_change_key_case ($ responseHeaders , CASE_LOWER );
130+ $ negotiatedGroup = $ responseHeaders [self ::TLS_GROUP ][0 ];
131+
132+ if ($ transport instanceof GrpcTransport) {
133+ // gRPC must ALWAYS negotiate PQC
134+ $ this ->assertEquals ($ expectedGroup , $ negotiatedGroup );
135+ } else {
136+ // REST uses host system OpenSSL (X25519MLKEM768 when PQC is available, or X25519 fallback)
137+ $ this ->assertContains ($ negotiatedGroup , [$ expectedGroup , 'X25519 ' ]);
138+ }
139+ }
74140}
0 commit comments