1<?php
2/*
3 *
4 * Copyright 2018 gRPC authors.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19/**
20 * Interface exported by the server.
21 */
22require_once(dirname(__FILE__).'/../../lib/Grpc/BaseStub.php');
23require_once(dirname(__FILE__).'/../../lib/Grpc/AbstractCall.php');
24require_once(dirname(__FILE__).'/../../lib/Grpc/UnaryCall.php');
25require_once(dirname(__FILE__).'/../../lib/Grpc/ClientStreamingCall.php');
26require_once(dirname(__FILE__).'/../../lib/Grpc/Interceptor.php');
27require_once(dirname(__FILE__).'/../../lib/Grpc/CallInvoker.php');
28require_once(dirname(__FILE__).'/../../lib/Grpc/Internal/InterceptorChannel.php');
29
30class SimpleRequest
31{
32    private $data;
33    public function __construct($data)
34    {
35        $this->data = $data;
36    }
37    public function setData($data)
38    {
39        $this->data = $data;
40    }
41    public function serializeToString()
42    {
43        return $this->data;
44    }
45}
46
47class InterceptorClient extends Grpc\BaseStub
48{
49
50    /**
51     * @param string $hostname hostname
52     * @param array $opts channel options
53     * @param Channel|InterceptorChannel $channel (optional) re-use channel object
54     */
55    public function __construct($hostname, $opts, $channel = null)
56    {
57        parent::__construct($hostname, $opts, $channel);
58    }
59
60    /**
61     * A simple RPC.
62     * @param SimpleRequest $argument input argument
63     * @param array $metadata metadata
64     * @param array $options call options
65     */
66    public function UnaryCall(
67        SimpleRequest $argument,
68        $metadata = [],
69        $options = []
70    ) {
71        return $this->_simpleRequest(
72            '/dummy_method',
73            $argument,
74            [],
75            $metadata,
76            $options
77        );
78    }
79
80    /**
81     * A client-to-server streaming RPC.
82     * @param array $metadata metadata
83     * @param array $options call options
84     */
85    public function StreamCall(
86        $metadata = [],
87        $options = []
88    ) {
89        return $this->_clientStreamRequest('/dummy_method', [], $metadata, $options);
90    }
91}
92
93
94class ChangeMetadataInterceptor extends Grpc\Interceptor
95{
96    public function interceptUnaryUnary($method,
97                                        $argument,
98                                        $deserialize,
99                                        array $metadata = [],
100                                        array $options = [],
101                                        $continuation)
102    {
103        $metadata["foo"] = array('interceptor_from_unary_request');
104        return $continuation($method, $argument, $deserialize, $metadata, $options);
105    }
106    public function interceptStreamUnary($method,
107                                         $deserialize,
108                                         array $metadata = [],
109                                         array $options = [],
110                                         $continuation)
111    {
112        $metadata["foo"] = array('interceptor_from_stream_request');
113        return $continuation($method, $deserialize, $metadata, $options);
114    }
115}
116
117class ChangeMetadataInterceptor2 extends Grpc\Interceptor
118{
119    public function interceptUnaryUnary($method,
120                                        $argument,
121                                        $deserialize,
122                                        array $metadata = [],
123                                        array $options = [],
124                                        $continuation)
125    {
126        if (array_key_exists('foo', $metadata)) {
127            $metadata['bar'] = array('ChangeMetadataInterceptor should be executed first');
128        } else {
129            $metadata["bar"] = array('interceptor_from_unary_request');
130        }
131        return $continuation($method, $argument, $deserialize, $metadata, $options);
132    }
133    public function interceptStreamUnary($method,
134                                         $deserialize,
135                                         array $metadata = [],
136                                         array $options = [],
137                                         $continuation)
138    {
139        if (array_key_exists('foo', $metadata)) {
140            $metadata['bar'] = array('ChangeMetadataInterceptor should be executed first');
141        } else {
142            $metadata["bar"] = array('interceptor_from_stream_request');
143        }
144        return $continuation($method, $deserialize, $metadata, $options);
145    }
146}
147
148class ChangeRequestCall
149{
150    private $call;
151
152    public function __construct($call)
153    {
154        $this->call = $call;
155    }
156    public function getCall()
157    {
158        return $this->call;
159    }
160
161    public function write($request)
162    {
163        $request->setData('intercepted_stream_request');
164        $this->getCall()->write($request);
165    }
166
167    public function wait()
168    {
169        return $this->getCall()->wait();
170    }
171}
172
173class ChangeRequestInterceptor extends Grpc\Interceptor
174{
175    public function interceptUnaryUnary($method,
176                                        $argument,
177                                        $deserialize,
178                                        array $metadata = [],
179                                        array $options = [],
180                                        $continuation)
181    {
182        $argument->setData('intercepted_unary_request');
183        return $continuation($method, $argument, $deserialize, $metadata, $options);
184    }
185    public function interceptStreamUnary($method,
186                                         $deserialize,
187                                         array $metadata = [],
188                                         array $options = [],
189                                         $continuation)
190    {
191        return new ChangeRequestCall(
192            $continuation($method, $deserialize, $metadata, $options)
193        );
194    }
195}
196
197class StopCallInterceptor extends Grpc\Interceptor
198{
199    public function interceptUnaryUnary($method,
200                                        $argument,
201                                        $deserialize,
202                                        array $metadata = [],
203                                        array $options = [],
204                                        $continuation)
205    {
206        $metadata["foo"] = array('interceptor_from_request_response');
207    }
208    public function interceptStreamUnary($method,
209                                         $deserialize,
210                                         array $metadata = [],
211                                         array $options = [],
212                                         $continuation)
213    {
214        $metadata["foo"] = array('interceptor_from_request_response');
215    }
216}
217
218class InterceptorTest extends PHPUnit_Framework_TestCase
219{
220    public function setUp()
221    {
222        $this->server = new Grpc\Server([]);
223        $this->port = $this->server->addHttp2Port('0.0.0.0:0');
224        $this->channel = new Grpc\Channel('localhost:'.$this->port, [
225            'force_new' => true,
226            'credentials' => Grpc\ChannelCredentials::createInsecure()]);
227        $this->server->start();
228    }
229
230    public function tearDown()
231    {
232        $this->channel->close();
233        unset($this->server);
234    }
235
236
237    public function testClientChangeMetadataOneInterceptor()
238    {
239        $req_text = 'client_request';
240        $channel_matadata_interceptor = new ChangeMetadataInterceptor();
241        $intercept_channel = Grpc\Interceptor::intercept($this->channel, $channel_matadata_interceptor);
242        $client = new InterceptorClient('localhost:'.$this->port, [
243            'force_new' => true,
244            'credentials' => Grpc\ChannelCredentials::createInsecure(),
245        ], $intercept_channel);
246        $req = new SimpleRequest($req_text);
247        $unary_call = $client->UnaryCall($req);
248        $event = $this->server->requestCall();
249        $this->assertSame('/dummy_method', $event->method);
250        $this->assertSame(['interceptor_from_unary_request'], $event->metadata['foo']);
251
252        $stream_call = $client->StreamCall();
253        $stream_call->write($req);
254        $event = $this->server->requestCall();
255        $this->assertSame('/dummy_method', $event->method);
256        $this->assertSame(['interceptor_from_stream_request'], $event->metadata['foo']);
257
258        unset($unary_call);
259        unset($stream_call);
260        unset($server_call);
261    }
262
263    public function testClientChangeMetadataTwoInterceptor()
264    {
265        $req_text = 'client_request';
266        $channel_matadata_interceptor = new ChangeMetadataInterceptor();
267        $channel_matadata_intercepto2 = new ChangeMetadataInterceptor2();
268        // test intercept separately.
269        $intercept_channel1 = Grpc\Interceptor::intercept($this->channel, $channel_matadata_interceptor);
270        $intercept_channel2 = Grpc\Interceptor::intercept($intercept_channel1, $channel_matadata_intercepto2);
271        $client = new InterceptorClient('localhost:'.$this->port, [
272            'force_new' => true,
273            'credentials' => Grpc\ChannelCredentials::createInsecure(),
274        ], $intercept_channel2);
275
276        $req = new SimpleRequest($req_text);
277        $unary_call = $client->UnaryCall($req);
278        $event = $this->server->requestCall();
279        $this->assertSame('/dummy_method', $event->method);
280        $this->assertSame(['interceptor_from_unary_request'], $event->metadata['foo']);
281        $this->assertSame(['interceptor_from_unary_request'], $event->metadata['bar']);
282
283        $stream_call = $client->StreamCall();
284        $stream_call->write($req);
285        $event = $this->server->requestCall();
286        $this->assertSame('/dummy_method', $event->method);
287        $this->assertSame(['interceptor_from_stream_request'], $event->metadata['foo']);
288        $this->assertSame(['interceptor_from_stream_request'], $event->metadata['bar']);
289
290        unset($unary_call);
291        unset($stream_call);
292        unset($server_call);
293
294        // test intercept by array.
295        $intercept_channel3 = Grpc\Interceptor::intercept($this->channel,
296            [$channel_matadata_intercepto2, $channel_matadata_interceptor]);
297        $client = new InterceptorClient('localhost:'.$this->port, [
298            'force_new' => true,
299            'credentials' => Grpc\ChannelCredentials::createInsecure(),
300        ], $intercept_channel3);
301
302        $req = new SimpleRequest($req_text);
303        $unary_call = $client->UnaryCall($req);
304        $event = $this->server->requestCall();
305        $this->assertSame('/dummy_method', $event->method);
306        $this->assertSame(['interceptor_from_unary_request'], $event->metadata['foo']);
307        $this->assertSame(['interceptor_from_unary_request'], $event->metadata['bar']);
308
309        $stream_call = $client->StreamCall();
310        $stream_call->write($req);
311        $event = $this->server->requestCall();
312        $this->assertSame('/dummy_method', $event->method);
313        $this->assertSame(['interceptor_from_stream_request'], $event->metadata['foo']);
314        $this->assertSame(['interceptor_from_stream_request'], $event->metadata['bar']);
315
316        unset($unary_call);
317        unset($stream_call);
318        unset($server_call);
319    }
320
321    public function testClientChangeRequestInterceptor()
322    {
323        $req_text = 'client_request';
324        $change_request_interceptor = new ChangeRequestInterceptor();
325        $intercept_channel = Grpc\Interceptor::intercept($this->channel,
326            $change_request_interceptor);
327        $client = new InterceptorClient('localhost:'.$this->port, [
328            'force_new' => true,
329            'credentials' => Grpc\ChannelCredentials::createInsecure(),
330        ], $intercept_channel);
331
332        $req = new SimpleRequest($req_text);
333        $unary_call = $client->UnaryCall($req);
334
335        $event = $this->server->requestCall();
336        $this->assertSame('/dummy_method', $event->method);
337        $server_call = $event->call;
338        $event = $server_call->startBatch([
339            Grpc\OP_SEND_INITIAL_METADATA => [],
340            Grpc\OP_SEND_STATUS_FROM_SERVER => [
341                'metadata' => [],
342                'code' => Grpc\STATUS_OK,
343                'details' => '',
344            ],
345            Grpc\OP_RECV_MESSAGE => true,
346            Grpc\OP_RECV_CLOSE_ON_SERVER => true,
347        ]);
348        $this->assertSame('intercepted_unary_request', $event->message);
349
350        $stream_call = $client->StreamCall();
351        $stream_call->write($req);
352        $event = $this->server->requestCall();
353        $this->assertSame('/dummy_method', $event->method);
354        $server_call = $event->call;
355        $event = $server_call->startBatch([
356            Grpc\OP_SEND_INITIAL_METADATA => [],
357            Grpc\OP_SEND_STATUS_FROM_SERVER => [
358                'metadata' => [],
359                'code' => Grpc\STATUS_OK,
360                'details' => '',
361            ],
362            Grpc\OP_RECV_MESSAGE => true,
363            Grpc\OP_RECV_CLOSE_ON_SERVER => true,
364        ]);
365        $this->assertSame('intercepted_stream_request', $event->message);
366
367        unset($unary_call);
368        unset($stream_call);
369        unset($server_call);
370    }
371
372    public function testClientChangeStopCallInterceptor()
373    {
374        $req_text = 'client_request';
375        $channel_request_interceptor = new StopCallInterceptor();
376        $intercept_channel = Grpc\Interceptor::intercept($this->channel,
377            $channel_request_interceptor);
378        $client = new InterceptorClient('localhost:'.$this->port, [
379            'force_new' => true,
380            'credentials' => Grpc\ChannelCredentials::createInsecure(),
381        ], $intercept_channel);
382
383        $req = new SimpleRequest($req_text);
384        $unary_call = $client->UnaryCall($req);
385        $this->assertNull($unary_call);
386
387
388        $stream_call = $client->StreamCall();
389        $this->assertNull($stream_call);
390
391        unset($unary_call);
392        unset($stream_call);
393        unset($server_call);
394    }
395
396    public function testGetInterceptorChannelConnectivityState()
397    {
398        $channel = new Grpc\Channel(
399            'localhost:0',
400            [
401                'force_new' => true,
402                'credentials' => Grpc\ChannelCredentials::createInsecure()
403            ]
404        );
405        $interceptor_channel = Grpc\Interceptor::intercept($channel, new Grpc\Interceptor());
406        $state = $interceptor_channel->getConnectivityState();
407        $this->assertEquals(0, $state);
408        $channel->close();
409    }
410
411    public function testInterceptorChannelWatchConnectivityState()
412    {
413        $channel = new Grpc\Channel(
414            'localhost:0',
415            [
416                'force_new' => true,
417                'credentials' => Grpc\ChannelCredentials::createInsecure()
418            ]
419        );
420        $interceptor_channel = Grpc\Interceptor::intercept($channel, new Grpc\Interceptor());
421        $now = Grpc\Timeval::now();
422        $deadline = $now->add(new Grpc\Timeval(100*1000));
423        $state = $interceptor_channel->watchConnectivityState(1, $deadline);
424        $this->assertTrue($state);
425        unset($time);
426        unset($deadline);
427        $channel->close();
428    }
429
430    public function testInterceptorChannelClose()
431    {
432        $channel = new Grpc\Channel(
433            'localhost:0',
434            [
435                'force_new' => true,
436                'credentials' => Grpc\ChannelCredentials::createInsecure()
437            ]
438        );
439        $interceptor_channel = Grpc\Interceptor::intercept($channel, new Grpc\Interceptor());
440        $this->assertNotNull($interceptor_channel);
441        $channel->close();
442    }
443
444    public function testInterceptorChannelGetTarget()
445    {
446        $channel = new Grpc\Channel(
447            'localhost:8888',
448            [
449                'force_new' => true,
450                'credentials' => Grpc\ChannelCredentials::createInsecure()
451            ]
452        );
453        $interceptor_channel = Grpc\Interceptor::intercept($channel, new Grpc\Interceptor());
454        $target = $interceptor_channel->getTarget();
455        $this->assertTrue(is_string($target));
456        $channel->close();
457    }
458}
459