blob: ed30888c43a2b24d4e7de8861e588994d630b85a [file] [log] [blame]
Craig Tiller1a61b172015-02-16 11:53:47 -08001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Craig Tiller1a61b172015-02-16 11:53:47 -08004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
Craig Tiller1a61b172015-02-16 11:53:47 -08008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Craig Tiller1a61b172015-02-16 11:53:47 -080010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Craig Tiller1a61b172015-02-16 11:53:47 -080016 *
17 */
18
mlumishb892a272014-12-09 16:28:23 -080019#include "channel.h"
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
murgatroid998242ba72015-04-01 15:29:44 -070025#include <php.h>
26#include <php_ini.h>
27#include <ext/standard/info.h>
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -070028#include <ext/standard/php_var.h>
29#include <ext/standard/sha1.h>
30#if PHP_MAJOR_VERSION < 7
31#include <ext/standard/php_smart_str.h>
32#else
33#include <zend_smart_str.h>
34#endif
murgatroid998242ba72015-04-01 15:29:44 -070035#include <ext/spl/spl_exceptions.h>
mlumishb892a272014-12-09 16:28:23 -080036#include "php_grpc.h"
37
murgatroid998242ba72015-04-01 15:29:44 -070038#include <zend_exceptions.h>
mlumishb892a272014-12-09 16:28:23 -080039
40#include <stdbool.h>
41
murgatroid998242ba72015-04-01 15:29:44 -070042#include <grpc/grpc.h>
murgatroid998242ba72015-04-01 15:29:44 -070043#include <grpc/grpc_security.h>
Zhouyihai Dingd3b55242018-01-22 12:52:56 -080044#include <grpc/support/alloc.h>
mlumishb892a272014-12-09 16:28:23 -080045
Stanley Cheunga63fdd02015-08-11 15:11:11 -070046#include "completion_queue.h"
Stanley Cheung9c0b35e2015-10-21 17:07:56 -070047#include "channel_credentials.h"
Stanley Cheunga63fdd02015-08-11 15:11:11 -070048#include "server.h"
49#include "timeval.h"
mlumishb892a272014-12-09 16:28:23 -080050
Xiaoguang Sun8a929a92015-03-13 14:22:31 +080051zend_class_entry *grpc_ce_channel;
thinkeroudba5b0c2016-07-27 18:39:16 +080052#if PHP_MAJOR_VERSION >= 7
53static zend_object_handlers channel_ce_handlers;
54#endif
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -070055static gpr_mu global_persistent_list_mu;
56int le_plink;
Xiaoguang Sun8a929a92015-03-13 14:22:31 +080057
mlumishb892a272014-12-09 16:28:23 -080058/* Frees and destroys an instance of wrapped_grpc_channel */
thinkerou011d1ef2016-07-27 09:44:49 +080059PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel)
ZhouyihaiDingb00a9252018-04-02 16:14:57 -070060 // In_persistent_list is used when the user don't close the channel,
61 // In this case, channels not in the list should be freed.
Zhouyihai Ding7c647d32018-01-22 13:11:40 -080062 bool in_persistent_list = true;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -070063 if (p->wrapper != NULL) {
64 gpr_mu_lock(&p->wrapper->mu);
65 if (p->wrapper->wrapped != NULL) {
Zhouyihai Ding7c647d32018-01-22 13:11:40 -080066 if (p->wrapper->is_valid) {
67 php_grpc_zend_resource *rsrc;
68 php_grpc_int key_len = strlen(p->wrapper->key);
69 // only destroy the channel here if not found in the persistent list
70 gpr_mu_lock(&global_persistent_list_mu);
71 if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key,
72 key_len, rsrc))) {
73 in_persistent_list = false;
74 grpc_channel_destroy(p->wrapper->wrapped);
75 free(p->wrapper->target);
76 free(p->wrapper->args_hashstr);
77 if(p->wrapper->creds_hashstr != NULL){
78 free(p->wrapper->creds_hashstr);
79 p->wrapper->creds_hashstr = NULL;
80 }
ZhouyihaiDingb00a9252018-04-02 16:14:57 -070081 free(p->wrapper->key);
Zhouyihai Ding4b9f8d82018-01-22 12:58:21 -080082 }
Zhouyihai Ding7c647d32018-01-22 13:11:40 -080083 gpr_mu_unlock(&global_persistent_list_mu);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -070084 }
Zhouyihai Ding7c647d32018-01-22 13:11:40 -080085 }
86 p->wrapper->ref_count -= 1;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -070087 gpr_mu_unlock(&p->wrapper->mu);
ZhouyihaiDingb00a9252018-04-02 16:14:57 -070088 if (!in_persistent_list) {
Zhouyihai Ding7c647d32018-01-22 13:11:40 -080089 gpr_mu_destroy(&p->wrapper->mu);
Zhouyihai Ding7c647d32018-01-22 13:11:40 -080090 free(p->wrapper);
ZhouyihaiDingb00a9252018-04-02 16:14:57 -070091 p->wrapper = NULL;
Zhouyihai Ding7c647d32018-01-22 13:11:40 -080092 }
mlumishb892a272014-12-09 16:28:23 -080093 }
thinkerou011d1ef2016-07-27 09:44:49 +080094PHP_GRPC_FREE_WRAPPED_FUNC_END()
95
mlumishb892a272014-12-09 16:28:23 -080096/* Initializes an instance of wrapped_grpc_channel to be associated with an
97 * object of a class specified by class_type */
thinkeroudba5b0c2016-07-27 18:39:16 +080098php_grpc_zend_object create_wrapped_grpc_channel(zend_class_entry *class_type
99 TSRMLS_DC) {
thinkerouba75c012016-07-28 02:30:08 +0800100 PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_channel);
mlumishb892a272014-12-09 16:28:23 -0800101 zend_object_std_init(&intern->std, class_type TSRMLS_CC);
102 object_properties_init(&intern->std, class_type);
thinkeroudc673c52016-07-28 09:49:38 +0800103 PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_channel, channel_ce_handlers);
thinkeroudba5b0c2016-07-27 18:39:16 +0800104}
thinkerou6f9d30b2016-07-27 03:19:03 +0800105
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700106int php_grpc_read_args_array(zval *args_array,
107 grpc_channel_args *args TSRMLS_DC) {
mlumishb892a272014-12-09 16:28:23 -0800108 HashTable *array_hash;
mlumishb892a272014-12-09 16:28:23 -0800109 int args_index;
mlumishb892a272014-12-09 16:28:23 -0800110 array_hash = Z_ARRVAL_P(args_array);
thinkerou6f9d30b2016-07-27 03:19:03 +0800111 if (!array_hash) {
112 zend_throw_exception(spl_ce_InvalidArgumentException,
Stanley Cheungc1f25fb2016-07-29 13:41:22 -0700113 "array_hash is NULL", 1 TSRMLS_CC);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700114 return FAILURE;
thinkerou6f9d30b2016-07-27 03:19:03 +0800115 }
mlumishb892a272014-12-09 16:28:23 -0800116 args->num_args = zend_hash_num_elements(array_hash);
117 args->args = ecalloc(args->num_args, sizeof(grpc_arg));
118 args_index = 0;
thinkerou6f9d30b2016-07-27 03:19:03 +0800119
thinkerouba75c012016-07-28 02:30:08 +0800120 char *key = NULL;
121 zval *data;
122 int key_type;
123
124 PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(array_hash, key, key_type, data)
125 if (key_type != HASH_KEY_IS_STRING) {
mlumishb892a272014-12-09 16:28:23 -0800126 zend_throw_exception(spl_ce_InvalidArgumentException,
Craig Tillerb5dcec52015-01-13 11:13:42 -0800127 "args keys must be strings", 1 TSRMLS_CC);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700128 return FAILURE;
mlumishb892a272014-12-09 16:28:23 -0800129 }
130 args->args[args_index].key = key;
thinkeroua3730b72016-07-20 16:59:54 +0800131 switch (Z_TYPE_P(data)) {
132 case IS_LONG:
133 args->args[args_index].value.integer = (int)Z_LVAL_P(data);
134 args->args[args_index].type = GRPC_ARG_INTEGER;
135 break;
136 case IS_STRING:
137 args->args[args_index].value.string = Z_STRVAL_P(data);
138 args->args[args_index].type = GRPC_ARG_STRING;
139 break;
140 default:
141 zend_throw_exception(spl_ce_InvalidArgumentException,
thinkerouba75c012016-07-28 02:30:08 +0800142 "args values must be int or string", 1 TSRMLS_CC);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700143 return FAILURE;
thinkeroua3730b72016-07-20 16:59:54 +0800144 }
145 args_index++;
thinkerouba75c012016-07-28 02:30:08 +0800146 PHP_GRPC_HASH_FOREACH_END()
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700147 return SUCCESS;
148}
149
150void generate_sha1_str(char *sha1str, char *str, php_grpc_int len) {
151 PHP_SHA1_CTX context;
152 unsigned char digest[20];
153 sha1str[0] = '\0';
154 PHP_SHA1Init(&context);
155 PHP_GRPC_SHA1Update(&context, str, len);
156 PHP_SHA1Final(digest, &context);
157 make_sha1_digest(sha1str, digest);
158}
159
160void create_channel(
161 wrapped_grpc_channel *channel,
162 char *target,
163 grpc_channel_args args,
164 wrapped_grpc_channel_credentials *creds) {
165 if (creds == NULL) {
166 channel->wrapper->wrapped = grpc_insecure_channel_create(target, &args,
167 NULL);
168 } else {
169 channel->wrapper->wrapped =
170 grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
171 }
172 efree(args.args);
173}
174
175void create_and_add_channel_to_persistent_list(
176 wrapped_grpc_channel *channel,
177 char *target,
178 grpc_channel_args args,
179 wrapped_grpc_channel_credentials *creds,
180 char *key,
Stanley Cheung5d559482017-08-22 13:15:01 -0700181 php_grpc_int key_len TSRMLS_DC) {
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700182 php_grpc_zend_resource new_rsrc;
183 channel_persistent_le_t *le;
184 // this links each persistent list entry to a destructor
185 new_rsrc.type = le_plink;
186 le = malloc(sizeof(channel_persistent_le_t));
187
188 create_channel(channel, target, args, creds);
189
190 le->channel = channel->wrapper;
191 new_rsrc.ptr = le;
192 gpr_mu_lock(&global_persistent_list_mu);
193 PHP_GRPC_PERSISTENT_LIST_UPDATE(&EG(persistent_list), key, key_len,
194 (void *)&new_rsrc);
195 gpr_mu_unlock(&global_persistent_list_mu);
thinkerou6f9d30b2016-07-27 03:19:03 +0800196}
thinkeroua3730b72016-07-20 16:59:54 +0800197
mlumishb892a272014-12-09 16:28:23 -0800198/**
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700199 * Construct an instance of the Channel class.
200 *
201 * By default, the underlying grpc_channel is "persistent". That is, given
202 * the same set of parameters passed to the constructor, the same underlying
203 * grpc_channel will be returned.
204 *
205 * If the $args array contains a "credentials" key mapping to a
206 * ChannelCredentials object, a secure channel will be created with those
207 * credentials.
208 *
209 * If the $args array contains a "force_new" key mapping to a boolean value
Stanley Cheung5d559482017-08-22 13:15:01 -0700210 * of "true", a new and separate underlying grpc_channel will be created
211 * and returned. This will not affect existing channels.
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700212 *
mlumishb892a272014-12-09 16:28:23 -0800213 * @param string $target The hostname to associate with this channel
thinkerouefbc9e72016-08-16 20:00:36 +0800214 * @param array $args_array The arguments to pass to the Channel
mlumishb892a272014-12-09 16:28:23 -0800215 */
Craig Tillerb5dcec52015-01-13 11:13:42 -0800216PHP_METHOD(Channel, __construct) {
thinkeroua3730b72016-07-20 16:59:54 +0800217 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
218 zval *creds_obj = NULL;
thinkeroua3730b72016-07-20 16:59:54 +0800219 char *target;
thinkerou19304682016-07-22 02:43:19 +0800220 php_grpc_int target_length;
mlumishb892a272014-12-09 16:28:23 -0800221 zval *args_array = NULL;
222 grpc_channel_args args;
223 HashTable *array_hash;
Stanley Cheungaeea1022015-10-21 17:00:49 -0700224 wrapped_grpc_channel_credentials *creds = NULL;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700225 php_grpc_zend_resource *rsrc;
226 bool force_new = false;
227 zval *force_new_obj = NULL;
thinkeroua3730b72016-07-20 16:59:54 +0800228
Stanley Cheungf77a4ad2016-02-16 09:45:51 -0800229 /* "sa" == 1 string, 1 array */
230 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &target,
Craig Tillerb5dcec52015-01-13 11:13:42 -0800231 &target_length, &args_array) == FAILURE) {
mlumishb892a272014-12-09 16:28:23 -0800232 zend_throw_exception(spl_ce_InvalidArgumentException,
Craig Tillerb5dcec52015-01-13 11:13:42 -0800233 "Channel expects a string and an array", 1 TSRMLS_CC);
mlumishb892a272014-12-09 16:28:23 -0800234 return;
235 }
Stanley Cheungcccf9292016-02-12 16:37:19 -0800236 array_hash = Z_ARRVAL_P(args_array);
thinkerouba75c012016-07-28 02:30:08 +0800237 if (php_grpc_zend_hash_find(array_hash, "credentials", sizeof("credentials"),
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700238 (void **)&creds_obj) == SUCCESS) {
thinkerouba75c012016-07-28 02:30:08 +0800239 if (Z_TYPE_P(creds_obj) == IS_NULL) {
Stanley Cheungcccf9292016-02-12 16:37:19 -0800240 creds = NULL;
thinkerouba75c012016-07-28 02:30:08 +0800241 php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials"));
242 } else if (PHP_GRPC_GET_CLASS_ENTRY(creds_obj) !=
243 grpc_ce_channel_credentials) {
Stanley Cheungcccf9292016-02-12 16:37:19 -0800244 zend_throw_exception(spl_ce_InvalidArgumentException,
245 "credentials must be a ChannelCredentials object",
246 1 TSRMLS_CC);
247 return;
mlumishb892a272014-12-09 16:28:23 -0800248 } else {
thinkeroua3730b72016-07-20 16:59:54 +0800249 creds = Z_WRAPPED_GRPC_CHANNEL_CREDS_P(creds_obj);
thinkerouba75c012016-07-28 02:30:08 +0800250 php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials"));
thinkeroua3730b72016-07-20 16:59:54 +0800251 }
252 }
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700253 if (php_grpc_zend_hash_find(array_hash, "force_new", sizeof("force_new"),
254 (void **)&force_new_obj) == SUCCESS) {
255 if (PHP_GRPC_BVAL_IS_TRUE(force_new_obj)) {
256 force_new = true;
257 }
258 php_grpc_zend_hash_del(array_hash, "force_new", sizeof("force_new"));
Stanley Cheungcccf9292016-02-12 16:37:19 -0800259 }
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700260
261 // parse the rest of the channel args array
262 if (php_grpc_read_args_array(args_array, &args TSRMLS_CC) == FAILURE) {
Zhouyihai Dingb6cdfca2018-01-13 16:25:28 -0800263 efree(args.args);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700264 return;
265 }
266
267 // Construct a hashkey for the persistent channel
268 // Currently, the hashkey contains 3 parts:
269 // 1. hostname
270 // 2. hash value of the channel args array (excluding "credentials"
271 // and "force_new")
272 // 3. (optional) hash value of the ChannelCredentials object
273 php_serialize_data_t var_hash;
274 smart_str buf = {0};
275 PHP_VAR_SERIALIZE_INIT(var_hash);
276 PHP_GRPC_VAR_SERIALIZE(&buf, args_array, &var_hash);
277 PHP_VAR_SERIALIZE_DESTROY(var_hash);
278
279 char sha1str[41];
280 generate_sha1_str(sha1str, PHP_GRPC_SERIALIZED_BUF_STR(buf),
281 PHP_GRPC_SERIALIZED_BUF_LEN(buf));
282
283 php_grpc_int key_len = target_length + strlen(sha1str);
284 if (creds != NULL && creds->hashstr != NULL) {
285 key_len += strlen(creds->hashstr);
286 }
287 char *key = malloc(key_len + 1);
288 strcpy(key, target);
289 strcat(key, sha1str);
290 if (creds != NULL && creds->hashstr != NULL) {
291 strcat(key, creds->hashstr);
292 }
293 channel->wrapper = malloc(sizeof(grpc_channel_wrapper));
294 channel->wrapper->key = key;
Stanley Cheung5d559482017-08-22 13:15:01 -0700295 channel->wrapper->target = strdup(target);
296 channel->wrapper->args_hashstr = strdup(sha1str);
Zhouyihai Ding4b9f8d82018-01-22 12:58:21 -0800297 channel->wrapper->creds_hashstr = NULL;
Zhouyihai Ding7c647d32018-01-22 13:11:40 -0800298 channel->wrapper->ref_count = 1;
299 channel->wrapper->is_valid = true;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700300 if (creds != NULL && creds->hashstr != NULL) {
Zhouyihai Ding4b9f8d82018-01-22 12:58:21 -0800301 php_grpc_int creds_hashstr_len = strlen(creds->hashstr);
302 char *channel_creds_hashstr = malloc(creds_hashstr_len + 1);
303 strcpy(channel_creds_hashstr, creds->hashstr);
304 channel->wrapper->creds_hashstr = channel_creds_hashstr;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700305 }
Zhouyihai Ding4b9f8d82018-01-22 12:58:21 -0800306
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700307 gpr_mu_init(&channel->wrapper->mu);
308 smart_str_free(&buf);
Stanley Cheung5d559482017-08-22 13:15:01 -0700309 if (force_new || (creds != NULL && creds->has_call_creds)) {
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700310 // If the ChannelCredentials object was composed with a CallCredentials
311 // object, there is no way we can tell them apart. Do NOT persist
312 // them. They should be individually destroyed.
313 create_channel(channel, target, args, creds);
314 } else if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), key,
315 key_len, rsrc))) {
316 create_and_add_channel_to_persistent_list(
Stanley Cheung5d559482017-08-22 13:15:01 -0700317 channel, target, args, creds, key, key_len TSRMLS_CC);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700318 } else {
319 // Found a previously stored channel in the persistent list
320 channel_persistent_le_t *le = (channel_persistent_le_t *)rsrc->ptr;
321 if (strcmp(target, le->channel->target) != 0 ||
322 strcmp(sha1str, le->channel->args_hashstr) != 0 ||
323 (creds != NULL && creds->hashstr != NULL &&
324 strcmp(creds->hashstr, le->channel->creds_hashstr) != 0)) {
325 // somehow hash collision
326 create_and_add_channel_to_persistent_list(
Stanley Cheung5d559482017-08-22 13:15:01 -0700327 channel, target, args, creds, key, key_len TSRMLS_CC);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700328 } else {
Zhouyihai Dingb6cdfca2018-01-13 16:25:28 -0800329 efree(args.args);
Zhouyihai Ding4b9f8d82018-01-22 12:58:21 -0800330 if (channel->wrapper->creds_hashstr != NULL){
331 free(channel->wrapper->creds_hashstr);
332 channel->wrapper->creds_hashstr = NULL;
333 }
334 free(channel->wrapper->creds_hashstr);
Zhouyihai Ding8a845932018-01-22 13:39:37 -0800335 free(channel->wrapper->key);
336 free(channel->wrapper->target);
337 free(channel->wrapper->args_hashstr);
338 free(channel->wrapper);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700339 channel->wrapper = le->channel;
Zhouyihai Ding7c647d32018-01-22 13:11:40 -0800340 channel->wrapper->ref_count += 1;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700341 }
342 }
mlumishb892a272014-12-09 16:28:23 -0800343}
344
345/**
Stanley Cheungdb98e082015-07-27 10:19:45 -0700346 * Get the endpoint this call/stream is connected to
347 * @return string The URI of the endpoint
348 */
349PHP_METHOD(Channel, getTarget) {
thinkeroua3730b72016-07-20 16:59:54 +0800350 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700351 gpr_mu_lock(&channel->wrapper->mu);
352 if (channel->wrapper->wrapped == NULL) {
353 zend_throw_exception(spl_ce_RuntimeException,
354 "Channel already closed", 1 TSRMLS_CC);
355 gpr_mu_unlock(&channel->wrapper->mu);
356 return;
357 }
358 char *target = grpc_channel_get_target(channel->wrapper->wrapped);
359 gpr_mu_unlock(&channel->wrapper->mu);
Zhouyihai Dingd3b55242018-01-22 12:52:56 -0800360 PHP_GRPC_RETVAL_STRING(target, 1);
361 gpr_free(target);
Stanley Cheungdb98e082015-07-27 10:19:45 -0700362}
363
364/**
Stanley Cheunge63354a2015-08-10 15:46:42 -0700365 * Get the connectivity state of the channel
thinkerouefbc9e72016-08-16 20:00:36 +0800366 * @param bool $try_to_connect Try to connect on the channel (optional)
Stanley Cheunge63354a2015-08-10 15:46:42 -0700367 * @return long The grpc connectivity state
368 */
369PHP_METHOD(Channel, getConnectivityState) {
thinkeroua3730b72016-07-20 16:59:54 +0800370 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700371 gpr_mu_lock(&channel->wrapper->mu);
372 if (channel->wrapper->wrapped == NULL) {
373 zend_throw_exception(spl_ce_RuntimeException,
374 "Channel already closed", 1 TSRMLS_CC);
375 gpr_mu_unlock(&channel->wrapper->mu);
376 return;
377 }
378
thinkeroua3730b72016-07-20 16:59:54 +0800379 bool try_to_connect = false;
380
Stanley Cheunge63354a2015-08-10 15:46:42 -0700381 /* "|b" == 1 optional bool */
thinkeroua3730b72016-07-20 16:59:54 +0800382 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &try_to_connect)
383 == FAILURE) {
Stanley Cheunge63354a2015-08-10 15:46:42 -0700384 zend_throw_exception(spl_ce_InvalidArgumentException,
385 "getConnectivityState expects a bool", 1 TSRMLS_CC);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700386 gpr_mu_unlock(&channel->wrapper->mu);
Stanley Cheunge63354a2015-08-10 15:46:42 -0700387 return;
388 }
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700389 int state = grpc_channel_check_connectivity_state(channel->wrapper->wrapped,
390 (int)try_to_connect);
391 // this can happen if another shared Channel object close the underlying
392 // channel
393 if (state == GRPC_CHANNEL_SHUTDOWN) {
394 channel->wrapper->wrapped = NULL;
395 }
396 gpr_mu_unlock(&channel->wrapper->mu);
397 RETURN_LONG(state);
Stanley Cheunge63354a2015-08-10 15:46:42 -0700398}
399
400/**
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700401 * Watch the connectivity state of the channel until it changed
thinkerouefbc9e72016-08-16 20:00:36 +0800402 * @param long $last_state The previous connectivity state of the channel
403 * @param Timeval $deadline_obj The deadline this function should wait until
Stanley Cheung4c5c7b82015-08-12 16:28:58 -0700404 * @return bool If the connectivity state changes from last_state
405 * before deadline
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700406 */
407PHP_METHOD(Channel, watchConnectivityState) {
thinkeroua3730b72016-07-20 16:59:54 +0800408 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700409 gpr_mu_lock(&channel->wrapper->mu);
410 if (channel->wrapper->wrapped == NULL) {
411 zend_throw_exception(spl_ce_RuntimeException,
412 "Channel already closed", 1 TSRMLS_CC);
413 gpr_mu_unlock(&channel->wrapper->mu);
414 return;
415 }
416
thinkerou19304682016-07-22 02:43:19 +0800417 php_grpc_long last_state;
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700418 zval *deadline_obj;
thinkeroua3730b72016-07-20 16:59:54 +0800419
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700420 /* "lO" == 1 long 1 object */
421 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO",
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700422 &last_state, &deadline_obj,
423 grpc_ce_timeval) == FAILURE) {
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700424 zend_throw_exception(spl_ce_InvalidArgumentException,
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700425 "watchConnectivityState expects 1 long 1 timeval",
426 1 TSRMLS_CC);
427 gpr_mu_unlock(&channel->wrapper->mu);
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700428 return;
429 }
430
thinkeroua3730b72016-07-20 16:59:54 +0800431 wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700432 grpc_channel_watch_connectivity_state(channel->wrapper->wrapped,
thinkeroua3730b72016-07-20 16:59:54 +0800433 (grpc_connectivity_state)last_state,
434 deadline->wrapped, completion_queue,
435 NULL);
436 grpc_event event =
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700437 grpc_completion_queue_pluck(completion_queue, NULL,
438 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
439 gpr_mu_unlock(&channel->wrapper->mu);
Stanley Cheung1567c0c2015-08-13 11:12:54 -0700440 RETURN_BOOL(event.success);
Stanley Cheunga63fdd02015-08-11 15:11:11 -0700441}
442
443/**
mlumishb892a272014-12-09 16:28:23 -0800444 * Close the channel
thinkerou03dc2192016-08-16 19:31:44 +0800445 * @return void
mlumishb892a272014-12-09 16:28:23 -0800446 */
Craig Tillerb5dcec52015-01-13 11:13:42 -0800447PHP_METHOD(Channel, close) {
thinkeroua3730b72016-07-20 16:59:54 +0800448 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
Zhouyihai Ding7c647d32018-01-22 13:11:40 -0800449 bool is_last_wrapper = false;
450 if (channel->wrapper != NULL) {
451 // Channel_wrapper hasn't call close before.
452 gpr_mu_lock(&channel->wrapper->mu);
453 if (channel->wrapper->wrapped != NULL) {
454 if (channel->wrapper->is_valid) {
455 // Wrapped channel hasn't been destoryed by other wrapper.
456 grpc_channel_destroy(channel->wrapper->wrapped);
457 free(channel->wrapper->target);
458 free(channel->wrapper->args_hashstr);
459 if(channel->wrapper->creds_hashstr != NULL){
460 free(channel->wrapper->creds_hashstr);
461 channel->wrapper->creds_hashstr = NULL;
462 }
463 channel->wrapper->wrapped = NULL;
464 channel->wrapper->is_valid = false;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700465
Zhouyihai Ding7c647d32018-01-22 13:11:40 -0800466 php_grpc_delete_persistent_list_entry(channel->wrapper->key,
467 strlen(channel->wrapper->key)
468 TSRMLS_CC);
469 }
470 }
471 channel->wrapper->ref_count -= 1;
472 if(channel->wrapper->ref_count == 0){
473 // Mark that the wrapper can be freed because mu should be
474 // destroyed outside the lock.
475 is_last_wrapper = true;
476 }
477 gpr_mu_unlock(&channel->wrapper->mu);
Stanley Cheung5d559482017-08-22 13:15:01 -0700478 }
Zhouyihai Ding7c647d32018-01-22 13:11:40 -0800479 gpr_mu_lock(&global_persistent_list_mu);
480 if (is_last_wrapper) {
481 gpr_mu_destroy(&channel->wrapper->mu);
482 free(channel->wrapper->key);
483 free(channel->wrapper);
484 }
485 // Set channel->wrapper to NULL to avoid call close twice for the same
486 // channel.
487 channel->wrapper = NULL;
488 gpr_mu_unlock(&global_persistent_list_mu);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700489}
490
491// Delete an entry from the persistent list
492// Note: this does not destroy or close the underlying grpc_channel
493void php_grpc_delete_persistent_list_entry(char *key, php_grpc_int key_len
494 TSRMLS_DC) {
495 php_grpc_zend_resource *rsrc;
496 gpr_mu_lock(&global_persistent_list_mu);
497 if (PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), key,
498 key_len, rsrc)) {
499 channel_persistent_le_t *le;
500 le = (channel_persistent_le_t *)rsrc->ptr;
501 le->channel = NULL;
502 php_grpc_zend_hash_del(&EG(persistent_list), key, key_len+1);
Zhouyihai Ding33af9aa2018-01-22 12:54:42 -0800503 free(le);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700504 }
505 gpr_mu_unlock(&global_persistent_list_mu);
506}
507
508// A destructor associated with each list entry from the persistent list
509static void php_grpc_channel_plink_dtor(php_grpc_zend_resource *rsrc
510 TSRMLS_DC) {
511 channel_persistent_le_t *le = (channel_persistent_le_t *)rsrc->ptr;
512 if (le->channel != NULL) {
513 gpr_mu_lock(&le->channel->mu);
514 if (le->channel->wrapped != NULL) {
515 grpc_channel_destroy(le->channel->wrapped);
Stanley Cheung5d559482017-08-22 13:15:01 -0700516 free(le->channel->target);
517 free(le->channel->args_hashstr);
ZhouyihaiDingb00a9252018-04-02 16:14:57 -0700518 le->channel->wrapped = NULL;
519 le->channel->target = NULL;
520 le->channel->args_hashstr = NULL;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700521 }
ZhouyihaiDingb00a9252018-04-02 16:14:57 -0700522 free(le->channel->key);
523 le->channel->key = NULL;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700524 gpr_mu_unlock(&le->channel->mu);
ZhouyihaiDingb00a9252018-04-02 16:14:57 -0700525 gpr_mu_destroy(&le->channel->mu);
526 free(le->channel);
527 le->channel = NULL;
528 free(le);
529 le = NULL;
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700530 }
mlumishb892a272014-12-09 16:28:23 -0800531}
532
Stanley Cheung6a5c83d2017-02-16 12:25:32 -0800533ZEND_BEGIN_ARG_INFO_EX(arginfo_construct, 0, 0, 2)
534 ZEND_ARG_INFO(0, target)
535 ZEND_ARG_INFO(0, args)
536ZEND_END_ARG_INFO()
537
538ZEND_BEGIN_ARG_INFO_EX(arginfo_getTarget, 0, 0, 0)
539ZEND_END_ARG_INFO()
540
541ZEND_BEGIN_ARG_INFO_EX(arginfo_getConnectivityState, 0, 0, 0)
542 ZEND_ARG_INFO(0, try_to_connect)
543ZEND_END_ARG_INFO()
544
545ZEND_BEGIN_ARG_INFO_EX(arginfo_watchConnectivityState, 0, 0, 2)
546 ZEND_ARG_INFO(0, last_state)
547 ZEND_ARG_INFO(0, deadline)
548ZEND_END_ARG_INFO()
549
550ZEND_BEGIN_ARG_INFO_EX(arginfo_close, 0, 0, 0)
551ZEND_END_ARG_INFO()
552
mlumishb892a272014-12-09 16:28:23 -0800553static zend_function_entry channel_methods[] = {
Stanley Cheung6a5c83d2017-02-16 12:25:32 -0800554 PHP_ME(Channel, __construct, arginfo_construct,
555 ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
556 PHP_ME(Channel, getTarget, arginfo_getTarget,
557 ZEND_ACC_PUBLIC)
558 PHP_ME(Channel, getConnectivityState, arginfo_getConnectivityState,
559 ZEND_ACC_PUBLIC)
560 PHP_ME(Channel, watchConnectivityState, arginfo_watchConnectivityState,
561 ZEND_ACC_PUBLIC)
562 PHP_ME(Channel, close, arginfo_close,
563 ZEND_ACC_PUBLIC)
thinkeroua3730b72016-07-20 16:59:54 +0800564 PHP_FE_END
565};
mlumishb892a272014-12-09 16:28:23 -0800566
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700567GRPC_STARTUP_FUNCTION(channel) {
mlumishb892a272014-12-09 16:28:23 -0800568 zend_class_entry ce;
569 INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods);
570 ce.create_object = create_wrapped_grpc_channel;
571 grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC);
Stanley Cheung5d559482017-08-22 13:15:01 -0700572 gpr_mu_init(&global_persistent_list_mu);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700573 le_plink = zend_register_list_destructors_ex(
574 NULL, php_grpc_channel_plink_dtor, "Persistent Channel", module_number);
thinkerou5dafd822016-07-28 22:43:38 +0800575 PHP_GRPC_INIT_HANDLER(wrapped_grpc_channel, channel_ce_handlers);
Stanley Cheung5b3dc4a2017-08-03 18:00:25 -0700576 return SUCCESS;
mlumishb892a272014-12-09 16:28:23 -0800577}