blob: ce96457c7c38f2d66f3ec306b37f611c120d8fb0 [file] [log] [blame]
Craig Tiller1a61b172015-02-16 11:53:47 -08001/*
2 *
Craig Tiller6169d5f2016-03-31 07:46:18 -07003 * Copyright 2015, Google Inc.
Craig Tiller1a61b172015-02-16 11:53:47 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
mlumishb892a272014-12-09 16:28:23 -080034#include "channel.h"
35
36#ifdef HAVE_CONFIG_H
37#include "config.h"
38#endif
39
murgatroid998242ba72015-04-01 15:29:44 -070040#include <php.h>
41#include <php_ini.h>
42#include <ext/standard/info.h>
43#include <ext/spl/spl_exceptions.h>
mlumishb892a272014-12-09 16:28:23 -080044#include "php_grpc.h"
45
murgatroid998242ba72015-04-01 15:29:44 -070046#include <zend_exceptions.h>
mlumishb892a272014-12-09 16:28:23 -080047
48#include <stdbool.h>
49
murgatroid998242ba72015-04-01 15:29:44 -070050#include <grpc/grpc.h>
murgatroid998242ba72015-04-01 15:29:44 -070051#include <grpc/grpc_security.h>
mlumishb892a272014-12-09 16:28:23 -080052
Stanley Cheunga63fdd02015-08-11 15:11:11 -070053#include "completion_queue.h"
Stanley Cheung9c0b35e2015-10-21 17:07:56 -070054#include "channel_credentials.h"
Stanley Cheunga63fdd02015-08-11 15:11:11 -070055#include "server.h"
56#include "timeval.h"
mlumishb892a272014-12-09 16:28:23 -080057
Xiaoguang Sun8a929a92015-03-13 14:22:31 +080058zend_class_entry *grpc_ce_channel;
59
thinkeroua3730b72016-07-20 16:59:54 +080060#if PHP_MAJOR_VERSION < 7
61
mlumishb892a272014-12-09 16:28:23 -080062/* Frees and destroys an instance of wrapped_grpc_channel */
Craig Tillerb5dcec52015-01-13 11:13:42 -080063void free_wrapped_grpc_channel(void *object TSRMLS_DC) {
64 wrapped_grpc_channel *channel = (wrapped_grpc_channel *)object;
65 if (channel->wrapped != NULL) {
mlumishb892a272014-12-09 16:28:23 -080066 grpc_channel_destroy(channel->wrapped);
67 }
thinkeroua3730b72016-07-20 16:59:54 +080068 zend_object_std_dtor(&channel->std TSRMLS_CC);
mlumishb892a272014-12-09 16:28:23 -080069 efree(channel);
70}
71
72/* Initializes an instance of wrapped_grpc_channel to be associated with an
73 * object of a class specified by class_type */
Craig Tillerb5dcec52015-01-13 11:13:42 -080074zend_object_value create_wrapped_grpc_channel(zend_class_entry *class_type
75 TSRMLS_DC) {
mlumishb892a272014-12-09 16:28:23 -080076 zend_object_value retval;
77 wrapped_grpc_channel *intern;
Craig Tillerb5dcec52015-01-13 11:13:42 -080078 intern = (wrapped_grpc_channel *)emalloc(sizeof(wrapped_grpc_channel));
mlumishb892a272014-12-09 16:28:23 -080079 memset(intern, 0, sizeof(wrapped_grpc_channel));
80 zend_object_std_init(&intern->std, class_type TSRMLS_CC);
81 object_properties_init(&intern->std, class_type);
82 retval.handle = zend_objects_store_put(
Craig Tillerb5dcec52015-01-13 11:13:42 -080083 intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
84 free_wrapped_grpc_channel, NULL TSRMLS_CC);
mlumishb892a272014-12-09 16:28:23 -080085 retval.handlers = zend_get_std_object_handlers();
86 return retval;
87}
88
Michael Bausor4f8e40b2016-05-16 11:41:25 -070089void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args TSRMLS_DC) {
mlumishb892a272014-12-09 16:28:23 -080090 HashTable *array_hash;
91 HashPosition array_pointer;
92 int args_index;
93 zval **data;
94 char *key;
95 uint key_len;
96 ulong index;
97 array_hash = Z_ARRVAL_P(args_array);
98 args->num_args = zend_hash_num_elements(array_hash);
99 args->args = ecalloc(args->num_args, sizeof(grpc_arg));
100 args_index = 0;
Craig Tillerb5dcec52015-01-13 11:13:42 -0800101 for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
102 zend_hash_get_current_data_ex(array_hash, (void **)&data,
103 &array_pointer) == SUCCESS;
104 zend_hash_move_forward_ex(array_hash, &array_pointer)) {
105 if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0,
106 &array_pointer) != HASH_KEY_IS_STRING) {
mlumishb892a272014-12-09 16:28:23 -0800107 zend_throw_exception(spl_ce_InvalidArgumentException,
Craig Tillerb5dcec52015-01-13 11:13:42 -0800108 "args keys must be strings", 1 TSRMLS_CC);
mlumishb892a272014-12-09 16:28:23 -0800109 return;
110 }
111 args->args[args_index].key = key;
Craig Tillerb5dcec52015-01-13 11:13:42 -0800112 switch (Z_TYPE_P(*data)) {
thinkeroua3730b72016-07-20 16:59:54 +0800113 case IS_LONG:
114 args->args[args_index].value.integer = (int)Z_LVAL_P(*data);
115 args->args[args_index].type = GRPC_ARG_INTEGER;
116 break;
117 case IS_STRING:
118 args->args[args_index].value.string = Z_STRVAL_P(*data);
119 args->args[args_index].type = GRPC_ARG_STRING;
120 break;
121 default:
122 zend_throw_exception(spl_ce_InvalidArgumentException,
123 "args values must be int or string", 1 TSRMLS_CC);
124 return;
mlumishb892a272014-12-09 16:28:23 -0800125 }
126 args_index++;
127 }
128}
129
thinkeroua3730b72016-07-20 16:59:54 +0800130#else
131
132static zend_object_handlers channel_ce_handlers;
133
134/* Frees and destroys an instance of wrapped_grpc_channel */
135static void free_wrapped_grpc_channel(zend_object *object) {
136 wrapped_grpc_channel *channel = wrapped_grpc_channel_from_obj(object);
137 if (channel->wrapped != NULL) {
138 grpc_channel_destroy(channel->wrapped);
139 }
140 zend_object_std_dtor(&channel->std);
141}
142
143/* Initializes an instance of wrapped_grpc_channel to be associated with an
144 * object of a class specified by class_type */
145zend_object *create_wrapped_grpc_channel(zend_class_entry *class_type) {
146 wrapped_grpc_channel *intern;
147 intern = ecalloc(1, sizeof(wrapped_grpc_channel) +
148 zend_object_properties_size(class_type));
149 zend_object_std_init(&intern->std, class_type);
150 object_properties_init(&intern->std, class_type);
151 intern->std.handlers = &channel_ce_handlers;
152 return &intern->std;
153}
154
155void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args) {
156 HashTable *array_hash;
157 int args_index;
158 zval *data;
159 zend_string *key;
160 array_hash = HASH_OF(args_array);
161 if (!array_hash) {
162 zend_throw_exception(spl_ce_InvalidArgumentException,
163 "array_hash is NULL", 1);
164 return;
165 }
166 args->num_args = zend_hash_num_elements(array_hash);
167 args->args = ecalloc(args->num_args, sizeof(grpc_arg));
168 args_index = 0;
169 ZEND_HASH_FOREACH_STR_KEY_VAL(array_hash, key, data) {
170 if (key == NULL) {
171 zend_throw_exception(spl_ce_InvalidArgumentException,
172 "args keys must be strings", 1);
173 }
174 args->args[args_index].key = ZSTR_VAL(key);
175 switch (Z_TYPE_P(data)) {
176 case IS_LONG:
177 args->args[args_index].value.integer = (int)Z_LVAL_P(data);
178 args->args[args_index].type = GRPC_ARG_INTEGER;
179 break;
180 case IS_STRING:
181 args->args[args_index].value.string = Z_STRVAL_P(data);
182 args->args[args_index].type = GRPC_ARG_STRING;
183 break;
184 default:
185 zend_throw_exception(spl_ce_InvalidArgumentException,
186 "args values must be int or string", 1);
187 return;
188 }
189 args_index++;
190 } ZEND_HASH_FOREACH_END();
191}
192
193#endif
194
mlumishb892a272014-12-09 16:28:23 -0800195/**
196 * Construct an instance of the Channel class. If the $args array contains a
Stanley Cheung9c0b35e2015-10-21 17:07:56 -0700197 * "credentials" key mapping to a ChannelCredentials object, a secure channel
198 * will be created with those credentials.
mlumishb892a272014-12-09 16:28:23 -0800199 * @param string $target The hostname to associate with this channel
200 * @param array $args The arguments to pass to the Channel (optional)
201 */
Craig Tillerb5dcec52015-01-13 11:13:42 -0800202PHP_METHOD(Channel, __construct) {
thinkeroua3730b72016-07-20 16:59:54 +0800203#if PHP_MAJOR_VERSION < 7
mlumishb892a272014-12-09 16:28:23 -0800204 wrapped_grpc_channel *channel =
Stanley Cheung9c0b35e2015-10-21 17:07:56 -0700205 (wrapped_grpc_channel *)zend_object_store_get_object(
206 getThis() TSRMLS_CC);
thinkeroua3730b72016-07-20 16:59:54 +0800207 zval **creds_obj = NULL;
mlumishb892a272014-12-09 16:28:23 -0800208 int target_length;
thinkeroua3730b72016-07-20 16:59:54 +0800209#else
210 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
211 zval *creds_obj = NULL;
212 size_t target_length;
213#endif
214 char *target;
mlumishb892a272014-12-09 16:28:23 -0800215 zval *args_array = NULL;
216 grpc_channel_args args;
217 HashTable *array_hash;
Stanley Cheungaeea1022015-10-21 17:00:49 -0700218 wrapped_grpc_channel_credentials *creds = NULL;
thinkeroua3730b72016-07-20 16:59:54 +0800219
Stanley Cheungf77a4ad2016-02-16 09:45:51 -0800220 /* "sa" == 1 string, 1 array */
221 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &target,
Craig Tillerb5dcec52015-01-13 11:13:42 -0800222 &target_length, &args_array) == FAILURE) {
mlumishb892a272014-12-09 16:28:23 -0800223 zend_throw_exception(spl_ce_InvalidArgumentException,
Craig Tillerb5dcec52015-01-13 11:13:42 -0800224 "Channel expects a string and an array", 1 TSRMLS_CC);
mlumishb892a272014-12-09 16:28:23 -0800225 return;
226 }
thinkeroua3730b72016-07-20 16:59:54 +0800227#if PHP_MAJOR_VERSION < 7
Stanley Cheungcccf9292016-02-12 16:37:19 -0800228 array_hash = Z_ARRVAL_P(args_array);
229 if (zend_hash_find(array_hash, "credentials", sizeof("credentials"),
230 (void **)&creds_obj) == SUCCESS) {
231 if (Z_TYPE_P(*creds_obj) == IS_NULL) {
232 creds = NULL;
233 zend_hash_del(array_hash, "credentials", 12);
234 } else if (zend_get_class_entry(*creds_obj TSRMLS_CC) !=
235 grpc_ce_channel_credentials) {
236 zend_throw_exception(spl_ce_InvalidArgumentException,
237 "credentials must be a ChannelCredentials object",
238 1 TSRMLS_CC);
239 return;
mlumishb892a272014-12-09 16:28:23 -0800240 } else {
Stanley Cheungcccf9292016-02-12 16:37:19 -0800241 creds = (wrapped_grpc_channel_credentials *)zend_object_store_get_object(
242 *creds_obj TSRMLS_CC);
243 zend_hash_del(array_hash, "credentials", 12);
mlumishb892a272014-12-09 16:28:23 -0800244 }
mlumishb892a272014-12-09 16:28:23 -0800245 }
thinkeroua3730b72016-07-20 16:59:54 +0800246#else
247 array_hash = HASH_OF(args_array);
248 if ((creds_obj = zend_hash_str_find(array_hash, "credentials",
249 sizeof("credentials") - 1)) != NULL) {
250 if (Z_TYPE_P(creds_obj) == IS_NULL) {
251 creds = NULL;
252 zend_hash_str_del(array_hash, "credentials", sizeof("credentials") - 1);
253 } else if (Z_OBJ_P(creds_obj)->ce != grpc_ce_channel_credentials) {
254 zend_throw_exception(spl_ce_InvalidArgumentException,
255 "credentials must be a ChannelCredentials object",
256 1);
257 return;
258 } else {
259 creds = Z_WRAPPED_GRPC_CHANNEL_CREDS_P(creds_obj);
260 zend_hash_str_del(array_hash, "credentials", sizeof("credentials") - 1);
261 }
262 }
263#endif
Michael Bausor4f8e40b2016-05-16 11:41:25 -0700264 php_grpc_read_args_array(args_array, &args TSRMLS_CC);
Stanley Cheungcccf9292016-02-12 16:37:19 -0800265 if (creds == NULL) {
266 channel->wrapped = grpc_insecure_channel_create(target, &args, NULL);
267 } else {
Stanley Cheungcccf9292016-02-12 16:37:19 -0800268 channel->wrapped =
269 grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
270 }
271 efree(args.args);
mlumishb892a272014-12-09 16:28:23 -0800272}
273
274/**
Stanley Cheungdb98e082015-07-27 10:19:45 -0700275 * Get the endpoint this call/stream is connected to
276 * @return string The URI of the endpoint
277 */
278PHP_METHOD(Channel, getTarget) {
thinkeroua3730b72016-07-20 16:59:54 +0800279#if PHP_MAJOR_VERSION < 7
Stanley Cheungdb98e082015-07-27 10:19:45 -0700280 wrapped_grpc_channel *channel =
thinkeroua3730b72016-07-20 16:59:54 +0800281 (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC);
Stanley Cheungdb98e082015-07-27 10:19:45 -0700282 RETURN_STRING(grpc_channel_get_target(channel->wrapped), 1);
thinkeroua3730b72016-07-20 16:59:54 +0800283#else
284 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
285 RETURN_STRING(grpc_channel_get_target(channel->wrapped));
286#endif
Stanley Cheungdb98e082015-07-27 10:19:45 -0700287}
288
289/**
Stanley Cheunge63354a2015-08-10 15:46:42 -0700290 * Get the connectivity state of the channel
291 * @param bool (optional) try to connect on the channel
292 * @return long The grpc connectivity state
293 */
294PHP_METHOD(Channel, getConnectivityState) {
thinkeroua3730b72016-07-20 16:59:54 +0800295#if PHP_MAJOR_VERSION < 7
Stanley Cheunge63354a2015-08-10 15:46:42 -0700296 wrapped_grpc_channel *channel =
297 (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC);
thinkeroua3730b72016-07-20 16:59:54 +0800298#else
299 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
300#endif
301 bool try_to_connect = false;
302
Stanley Cheunge63354a2015-08-10 15:46:42 -0700303 /* "|b" == 1 optional bool */
thinkeroua3730b72016-07-20 16:59:54 +0800304 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &try_to_connect)
305 == FAILURE) {
Stanley Cheunge63354a2015-08-10 15:46:42 -0700306 zend_throw_exception(spl_ce_InvalidArgumentException,
307 "getConnectivityState expects a bool", 1 TSRMLS_CC);
308 return;
309 }
310 RETURN_LONG(grpc_channel_check_connectivity_state(channel->wrapped,
311 (int)try_to_connect));
312}
313
314/**
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700315 * Watch the connectivity state of the channel until it changed
316 * @param long The previous connectivity state of the channel
317 * @param Timeval The deadline this function should wait until
Stanley Cheung4c5c7b82015-08-12 16:28:58 -0700318 * @return bool If the connectivity state changes from last_state
319 * before deadline
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700320 */
321PHP_METHOD(Channel, watchConnectivityState) {
thinkeroua3730b72016-07-20 16:59:54 +0800322#if PHP_MAJOR_VERSION < 7
323 long last_state;
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700324 wrapped_grpc_channel *channel =
325 (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC);
thinkeroua3730b72016-07-20 16:59:54 +0800326#else
327 zend_long last_state;
328 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
329#endif
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700330 zval *deadline_obj;
thinkeroua3730b72016-07-20 16:59:54 +0800331
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700332 /* "lO" == 1 long 1 object */
333 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO",
334 &last_state, &deadline_obj, grpc_ce_timeval) == FAILURE) {
335 zend_throw_exception(spl_ce_InvalidArgumentException,
336 "watchConnectivityState expects 1 long 1 timeval",
337 1 TSRMLS_CC);
338 return;
339 }
340
thinkeroua3730b72016-07-20 16:59:54 +0800341#if PHP_MAJOR_VERSION < 7
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700342 wrapped_grpc_timeval *deadline =
343 (wrapped_grpc_timeval *)zend_object_store_get_object(
344 deadline_obj TSRMLS_CC);
thinkeroua3730b72016-07-20 16:59:54 +0800345#else
346 wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj);
347#endif
348 grpc_channel_watch_connectivity_state(channel->wrapped,
349 (grpc_connectivity_state)last_state,
350 deadline->wrapped, completion_queue,
351 NULL);
352 grpc_event event =
353 grpc_completion_queue_pluck(completion_queue, NULL,
354 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
Stanley Cheung1567c0c2015-08-13 11:12:54 -0700355 RETURN_BOOL(event.success);
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700356}
357
358/**
mlumishb892a272014-12-09 16:28:23 -0800359 * Close the channel
360 */
Craig Tillerb5dcec52015-01-13 11:13:42 -0800361PHP_METHOD(Channel, close) {
thinkeroua3730b72016-07-20 16:59:54 +0800362#if PHP_MAJOR_VERSION < 7
mlumishb892a272014-12-09 16:28:23 -0800363 wrapped_grpc_channel *channel =
thinkeroua3730b72016-07-20 16:59:54 +0800364 (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC);
365#else
366 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
367#endif
Craig Tillerb5dcec52015-01-13 11:13:42 -0800368 if (channel->wrapped != NULL) {
mlumishb892a272014-12-09 16:28:23 -0800369 grpc_channel_destroy(channel->wrapped);
370 channel->wrapped = NULL;
371 }
372}
373
374static zend_function_entry channel_methods[] = {
thinkeroua3730b72016-07-20 16:59:54 +0800375 PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
376 PHP_ME(Channel, getTarget, NULL, ZEND_ACC_PUBLIC)
377 PHP_ME(Channel, getConnectivityState, NULL, ZEND_ACC_PUBLIC)
378 PHP_ME(Channel, watchConnectivityState, NULL, ZEND_ACC_PUBLIC)
379 PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC)
380 PHP_FE_END
381};
mlumishb892a272014-12-09 16:28:23 -0800382
Craig Tillerb5dcec52015-01-13 11:13:42 -0800383void grpc_init_channel(TSRMLS_D) {
mlumishb892a272014-12-09 16:28:23 -0800384 zend_class_entry ce;
385 INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods);
386 ce.create_object = create_wrapped_grpc_channel;
387 grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC);
thinkeroua3730b72016-07-20 16:59:54 +0800388#if PHP_MAJOR_VERSION >= 7
389 memcpy(&channel_ce_handlers, zend_get_std_object_handlers(),
390 sizeof(zend_object_handlers));
391 channel_ce_handlers.offset =
392 XtOffsetOf(wrapped_grpc_channel, std);
393 channel_ce_handlers.free_obj = free_wrapped_grpc_channel;
394#endif
mlumishb892a272014-12-09 16:28:23 -0800395}