blob: aa4de349ea7f60b3f8ae41c8ac292f8b5aa05ef6 [file] [log] [blame]
mlumishb892a272014-12-09 16:28:23 -08001<?php
Craig Tiller2e498aa2015-02-16 12:09:31 -08002/*
3 *
4 * Copyright 2015, Google Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following disclaimer
15 * in the documentation and/or other materials provided with the
16 * distribution.
17 * * Neither the name of Google Inc. nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
Stanley Cheungd5b20562015-10-27 13:27:05 -070034
mlumishb892a272014-12-09 16:28:23 -080035namespace Grpc;
mlumishb892a272014-12-09 16:28:23 -080036
37/**
38 * Base class for generated client stubs. Stub methods are expected to call
39 * _simpleRequest or _streamRequest and return the result.
40 */
Stanley Cheungd5b20562015-10-27 13:27:05 -070041class BaseStub
42{
43 private $hostname;
44 private $channel;
mlumishb892a272014-12-09 16:28:23 -080045
Stanley Cheungd5b20562015-10-27 13:27:05 -070046 // a callback function
47 private $update_metadata;
mlumishb892a272014-12-09 16:28:23 -080048
Stanley Cheungd5b20562015-10-27 13:27:05 -070049 /**
50 * @param $hostname string
51 * @param $opts array
52 * - 'update_metadata': (optional) a callback function which takes in a
53 * metadata array, and returns an updated metadata array
Stanley Cheung3baf7672015-11-08 18:16:58 -080054 * - 'grpc.primary_user_agent': (optional) a user-agent string
Stanley Cheungd5b20562015-10-27 13:27:05 -070055 */
56 public function __construct($hostname, $opts)
57 {
58 $this->hostname = $hostname;
59 $this->update_metadata = null;
60 if (isset($opts['update_metadata'])) {
61 if (is_callable($opts['update_metadata'])) {
62 $this->update_metadata = $opts['update_metadata'];
63 }
64 unset($opts['update_metadata']);
65 }
66 $package_config = json_decode(
67 file_get_contents(dirname(__FILE__).'/../../composer.json'), true);
Stanley Cheung3baf7672015-11-08 18:16:58 -080068 if (!empty($opts['grpc.primary_user_agent'])) {
69 $opts['grpc.primary_user_agent'] .= ' ';
70 } else {
71 $opts['grpc.primary_user_agent'] = '';
72 }
73 $opts['grpc.primary_user_agent'] .=
Stanley Cheungd5b20562015-10-27 13:27:05 -070074 'grpc-php/'.$package_config['version'];
75 $this->channel = new Channel($hostname, $opts);
Stanley Cheung04b7a412015-08-13 09:39:04 -070076 }
77
Stanley Cheungd5b20562015-10-27 13:27:05 -070078 /**
79 * @return string The URI of the endpoint.
80 */
81 public function getTarget()
82 {
83 return $this->channel->getTarget();
Stanley Cheung04b7a412015-08-13 09:39:04 -070084 }
Stanley Cheung04b7a412015-08-13 09:39:04 -070085
Stanley Cheungd5b20562015-10-27 13:27:05 -070086 /**
87 * @param $try_to_connect bool
88 *
89 * @return int The grpc connectivity state
90 */
91 public function getConnectivityState($try_to_connect = false)
92 {
93 return $this->channel->getConnectivityState($try_to_connect);
Stanley Cheung04b7a412015-08-13 09:39:04 -070094 }
Stanley Cheungd5b20562015-10-27 13:27:05 -070095
96 /**
97 * @param $timeout in microseconds
98 *
99 * @return bool true if channel is ready
100 * @throw Exception if channel is in FATAL_ERROR state
101 */
102 public function waitForReady($timeout)
103 {
104 $new_state = $this->getConnectivityState(true);
105 if ($this->_checkConnectivityState($new_state)) {
106 return true;
107 }
108
109 $now = Timeval::now();
110 $delta = new Timeval($timeout);
111 $deadline = $now->add($delta);
112
113 while ($this->channel->watchConnectivityState($new_state, $deadline)) {
114 // state has changed before deadline
115 $new_state = $this->getConnectivityState();
116 if ($this->_checkConnectivityState($new_state)) {
117 return true;
118 }
119 }
120 // deadline has passed
121 $new_state = $this->getConnectivityState();
122
123 return $this->_checkConnectivityState($new_state);
Stanley Cheung04b7a412015-08-13 09:39:04 -0700124 }
Stanley Cheung04b7a412015-08-13 09:39:04 -0700125
Stanley Cheungd5b20562015-10-27 13:27:05 -0700126 private function _checkConnectivityState($new_state)
127 {
128 if ($new_state == \Grpc\CHANNEL_READY) {
129 return true;
130 }
131 if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) {
132 throw new \Exception('Failed to connect to server');
133 }
mlumishb892a272014-12-09 16:28:23 -0800134
Stanley Cheungd5b20562015-10-27 13:27:05 -0700135 return false;
Stanley Cheungf4206872015-05-12 17:39:30 -0700136 }
Stanley Cheungf4206872015-05-12 17:39:30 -0700137
Stanley Cheungd5b20562015-10-27 13:27:05 -0700138 /**
139 * Close the communication channel associated with this stub.
140 */
141 public function close()
142 {
143 $this->channel->close();
Stanley Cheungcc019af2015-06-15 11:45:00 -0700144 }
Stanley Cheungcc019af2015-06-15 11:45:00 -0700145
Stanley Cheungd5b20562015-10-27 13:27:05 -0700146 /**
147 * constructs the auth uri for the jwt.
148 */
149 private function _get_jwt_aud_uri($method)
150 {
151 $last_slash_idx = strrpos($method, '/');
152 if ($last_slash_idx === false) {
153 throw new \InvalidArgumentException(
154 'service name must have a slash');
155 }
156 $service_name = substr($method, 0, $last_slash_idx);
157
158 return 'https://'.$this->hostname.$service_name;
Stanley Cheungb0cd08a2015-10-09 16:58:01 -0700159 }
Stanley Cheungb0cd08a2015-10-09 16:58:01 -0700160
Stanley Cheungd5b20562015-10-27 13:27:05 -0700161 /**
162 * extract $timeout from $metadata.
163 *
164 * @param $metadata The metadata map
165 *
166 * @return list($metadata_copy, $timeout)
167 */
168 private function _extract_timeout_from_metadata($metadata)
169 {
170 $timeout = false;
171 $metadata_copy = $metadata;
172 if (isset($metadata['timeout'])) {
173 $timeout = $metadata['timeout'];
174 unset($metadata_copy['timeout']);
175 }
mlumishb892a272014-12-09 16:28:23 -0800176
Stanley Cheungd5b20562015-10-27 13:27:05 -0700177 return [$metadata_copy, $timeout];
178 }
179
180 /**
181 * validate and normalize the metadata array.
182 *
183 * @param $metadata The metadata map
184 *
185 * @return $metadata Validated and key-normalized metadata map
186 * @throw InvalidArgumentException if key contains invalid characters
187 */
188 private function _validate_and_normalize_metadata($metadata)
189 {
190 $metadata_copy = [];
191 foreach ($metadata as $key => $value) {
192 if (!preg_match('/^[A-Za-z\d_-]+$/', $key)) {
193 throw new \InvalidArgumentException(
194 'Metadata keys must be nonempty strings containing only '.
195 'alphanumeric characters, hyphens and underscores');
196 }
197 $metadata_copy[strtolower($key)] = $value;
198 }
199
200 return $metadata_copy;
201 }
202
203 /* This class is intended to be subclassed by generated code, so
204 * all functions begin with "_" to avoid name collisions. */
205
206 /**
207 * Call a remote method that takes a single argument and has a
208 * single output.
209 *
210 * @param string $method The name of the method to call
211 * @param $argument The argument to the method
212 * @param callable $deserialize A function that deserializes the response
213 * @param array $metadata A metadata map to send to the server
214 *
215 * @return SimpleSurfaceActiveCall The active call object
216 */
217 public function _simpleRequest($method,
218 $argument,
219 callable $deserialize,
220 $metadata = [],
221 $options = [])
222 {
223 list($actual_metadata, $timeout) =
224 $this->_extract_timeout_from_metadata($metadata);
225 $call = new UnaryCall($this->channel,
226 $method,
227 $deserialize,
228 $timeout);
229 $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
230 if (is_callable($this->update_metadata)) {
231 $actual_metadata = call_user_func($this->update_metadata,
232 $actual_metadata,
233 $jwt_aud_uri);
234 }
235 $actual_metadata = $this->_validate_and_normalize_metadata(
236 $actual_metadata);
237 $call->start($argument, $actual_metadata, $options);
238
239 return $call;
240 }
241
242 /**
243 * Call a remote method that takes a stream of arguments and has a single
244 * output.
245 *
246 * @param string $method The name of the method to call
247 * @param $arguments An array or Traversable of arguments to stream to the
248 * server
249 * @param callable $deserialize A function that deserializes the response
250 * @param array $metadata A metadata map to send to the server
251 *
252 * @return ClientStreamingSurfaceActiveCall The active call object
253 */
254 public function _clientStreamRequest($method,
255 callable $deserialize,
256 $metadata = [])
257 {
258 list($actual_metadata, $timeout) =
259 $this->_extract_timeout_from_metadata($metadata);
260 $call = new ClientStreamingCall($this->channel,
261 $method,
262 $deserialize,
263 $timeout);
264 $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
265 if (is_callable($this->update_metadata)) {
266 $actual_metadata = call_user_func($this->update_metadata,
267 $actual_metadata,
268 $jwt_aud_uri);
269 }
270 $actual_metadata = $this->_validate_and_normalize_metadata(
271 $actual_metadata);
272 $call->start($actual_metadata);
273
274 return $call;
275 }
276
277 /**
278 * Call a remote method that takes a single argument and returns a stream of
279 * responses.
280 *
281 * @param string $method The name of the method to call
282 * @param $argument The argument to the method
283 * @param callable $deserialize A function that deserializes the responses
284 * @param array $metadata A metadata map to send to the server
285 *
286 * @return ServerStreamingSurfaceActiveCall The active call object
287 */
288 public function _serverStreamRequest($method,
289 $argument,
290 callable $deserialize,
291 $metadata = [],
292 $options = [])
293 {
294 list($actual_metadata, $timeout) =
295 $this->_extract_timeout_from_metadata($metadata);
296 $call = new ServerStreamingCall($this->channel,
297 $method,
298 $deserialize,
299 $timeout);
300 $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
301 if (is_callable($this->update_metadata)) {
302 $actual_metadata = call_user_func($this->update_metadata,
303 $actual_metadata,
304 $jwt_aud_uri);
305 }
306 $actual_metadata = $this->_validate_and_normalize_metadata(
307 $actual_metadata);
308 $call->start($argument, $actual_metadata, $options);
309
310 return $call;
311 }
312
313 /**
314 * Call a remote method with messages streaming in both directions.
315 *
316 * @param string $method The name of the method to call
317 * @param callable $deserialize A function that deserializes the responses
318 * @param array $metadata A metadata map to send to the server
319 *
320 * @return BidiStreamingSurfaceActiveCall The active call object
321 */
322 public function _bidiRequest($method,
murgatroid9914d2ce22015-01-30 15:36:23 -0800323 callable $deserialize,
Stanley Cheungd5b20562015-10-27 13:27:05 -0700324 $metadata = [])
325 {
326 list($actual_metadata, $timeout) =
327 $this->_extract_timeout_from_metadata($metadata);
328 $call = new BidiStreamingCall($this->channel,
329 $method,
330 $deserialize,
331 $timeout);
332 $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
333 if (is_callable($this->update_metadata)) {
334 $actual_metadata = call_user_func($this->update_metadata,
Stanley Cheungf4206872015-05-12 17:39:30 -0700335 $actual_metadata,
336 $jwt_aud_uri);
Stanley Cheungd5b20562015-10-27 13:27:05 -0700337 }
338 $actual_metadata = $this->_validate_and_normalize_metadata(
339 $actual_metadata);
340 $call->start($actual_metadata);
mlumishb892a272014-12-09 16:28:23 -0800341
Stanley Cheungd5b20562015-10-27 13:27:05 -0700342 return $call;
Stanley Cheung2c9c7632015-04-20 14:13:54 -0700343 }
mlumishb892a272014-12-09 16:28:23 -0800344}