aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/php
diff options
context:
space:
mode:
Diffstat (limited to 'src/php')
-rw-r--r--src/php/ext/grpc/call.c33
-rw-r--r--src/php/ext/grpc/call.h3
-rw-r--r--src/php/ext/grpc/call_credentials.c11
-rw-r--r--src/php/ext/grpc/channel.c135
-rwxr-xr-xsrc/php/ext/grpc/channel.h5
-rw-r--r--src/php/ext/grpc/channel_credentials.c14
-rw-r--r--src/php/ext/grpc/php7_wrapper.h2
-rw-r--r--src/php/ext/grpc/php_grpc.c3
-rw-r--r--src/php/lib/Grpc/BaseStub.php25
-rw-r--r--src/php/tests/unit_tests/ChannelTest.php5
10 files changed, 183 insertions, 53 deletions
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index c4997f720d..b802f04f53 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -99,6 +99,7 @@ zval *grpc_parse_metadata_array(grpc_metadata_array
1 TSRMLS_CC);
efree(str_key);
efree(str_val);
+ PHP_GRPC_FREE_STD_ZVAL(array);
return NULL;
}
php_grpc_add_next_index_stringl(data, str_val,
@@ -127,10 +128,12 @@ bool create_metadata_array(zval *array, grpc_metadata_array *metadata) {
HashTable *inner_array_hash;
zval *value;
zval *inner_array;
+ grpc_metadata_array_init(metadata);
+ metadata->count = 0;
+ metadata->metadata = NULL;
if (Z_TYPE_P(array) != IS_ARRAY) {
return false;
}
- grpc_metadata_array_init(metadata);
array_hash = Z_ARRVAL_P(array);
char *key;
@@ -174,6 +177,18 @@ bool create_metadata_array(zval *array, grpc_metadata_array *metadata) {
return true;
}
+void grpc_php_metadata_array_destroy_including_entries(
+ grpc_metadata_array* array) {
+ size_t i;
+ if (array->metadata) {
+ for (i = 0; i < array->count; i++) {
+ grpc_slice_unref(array->metadata[i].key);
+ grpc_slice_unref(array->metadata[i].value);
+ }
+ }
+ grpc_metadata_array_destroy(array);
+}
+
/* Wraps a grpc_call struct in a PHP object. Owned indicates whether the
struct should be destroyed at the end of the object's lifecycle */
zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned TSRMLS_DC) {
@@ -301,6 +316,11 @@ PHP_METHOD(Call, startBatch) {
"batch keys must be integers", 1 TSRMLS_CC);
goto cleanup;
}
+
+ ops[op_num].op = (grpc_op_type)index;
+ ops[op_num].flags = 0;
+ ops[op_num].reserved = NULL;
+
switch(index) {
case GRPC_OP_SEND_INITIAL_METADATA:
if (!create_metadata_array(value, &metadata)) {
@@ -414,9 +434,6 @@ PHP_METHOD(Call, startBatch) {
"Unrecognized key in batch", 1 TSRMLS_CC);
goto cleanup;
}
- ops[op_num].op = (grpc_op_type)index;
- ops[op_num].flags = 0;
- ops[op_num].reserved = NULL;
op_num++;
PHP_GRPC_HASH_FOREACH_END()
@@ -502,8 +519,8 @@ PHP_METHOD(Call, startBatch) {
}
cleanup:
- grpc_metadata_array_destroy(&metadata);
- grpc_metadata_array_destroy(&trailing_metadata);
+ grpc_php_metadata_array_destroy_including_entries(&metadata);
+ grpc_php_metadata_array_destroy_including_entries(&trailing_metadata);
grpc_metadata_array_destroy(&recv_metadata);
grpc_metadata_array_destroy(&recv_trailing_metadata);
grpc_slice_unref(recv_status_details);
@@ -526,7 +543,9 @@ cleanup:
*/
PHP_METHOD(Call, getPeer) {
wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
- PHP_GRPC_RETURN_STRING(grpc_call_get_peer(call->wrapped), 1);
+ char *peer = grpc_call_get_peer(call->wrapped);
+ PHP_GRPC_RETVAL_STRING(peer, 1);
+ gpr_free(peer);
}
/**
diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h
index 5bde5d5390..104ac301c1 100644
--- a/src/php/ext/grpc/call.h
+++ b/src/php/ext/grpc/call.h
@@ -69,5 +69,6 @@ void grpc_init_call(TSRMLS_D);
/* Populates a grpc_metadata_array with the data in a PHP array object.
Returns true on success and false on failure */
bool create_metadata_array(zval *array, grpc_metadata_array *metadata);
-
+void grpc_php_metadata_array_destroy_including_entries(
+ grpc_metadata_array* array);
#endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */
diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c
index a395d53614..41c488a79c 100644
--- a/src/php/ext/grpc/call_credentials.c
+++ b/src/php/ext/grpc/call_credentials.c
@@ -120,6 +120,8 @@ PHP_METHOD(CallCredentials, createFromPlugin) {
fci->params, fci->param_count) == FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"createFromPlugin expects 1 callback", 1 TSRMLS_CC);
+ free(fci);
+ free(fci_cache);
return;
}
@@ -183,15 +185,17 @@ int plugin_get_metadata(
*status = GRPC_STATUS_OK;
*error_details = NULL;
+ bool should_return = false;
grpc_metadata_array metadata;
if (retval == NULL || Z_TYPE_P(retval) != IS_ARRAY) {
*status = GRPC_STATUS_INVALID_ARGUMENT;
- return true; // Synchronous return.
+ should_return = true; // Synchronous return.
}
if (!create_metadata_array(retval, &metadata)) {
*status = GRPC_STATUS_INVALID_ARGUMENT;
- return true; // Synchronous return.
+ should_return = true; // Synchronous return.
+ grpc_php_metadata_array_destroy_including_entries(&metadata);
}
if (retval != NULL) {
@@ -204,6 +208,9 @@ int plugin_get_metadata(
PHP_GRPC_FREE_STD_ZVAL(retval);
#endif
}
+ if (should_return) {
+ return true;
+ }
if (metadata.count > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) {
*status = GRPC_STATUS_INTERNAL;
diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c
index db59869c7f..4054723b43 100644
--- a/src/php/ext/grpc/channel.c
+++ b/src/php/ext/grpc/channel.c
@@ -41,6 +41,7 @@
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
#include "completion_queue.h"
#include "channel_credentials.h"
@@ -56,22 +57,63 @@ int le_plink;
/* Frees and destroys an instance of wrapped_grpc_channel */
PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel)
+ bool is_last_wrapper = false;
+ // In_persistent_list is used when the user don't close the channel.
+ // In this case, le in the list won't be freed.
+ bool in_persistent_list = true;
if (p->wrapper != NULL) {
gpr_mu_lock(&p->wrapper->mu);
if (p->wrapper->wrapped != NULL) {
- php_grpc_zend_resource *rsrc;
- php_grpc_int key_len = strlen(p->wrapper->key);
- // only destroy the channel here if not found in the persistent list
- gpr_mu_lock(&global_persistent_list_mu);
- if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key,
- key_len, rsrc))) {
- grpc_channel_destroy(p->wrapper->wrapped);
- free(p->wrapper->target);
- free(p->wrapper->args_hashstr);
+ if (p->wrapper->is_valid) {
+ php_grpc_zend_resource *rsrc;
+ php_grpc_int key_len = strlen(p->wrapper->key);
+ // only destroy the channel here if not found in the persistent list
+ gpr_mu_lock(&global_persistent_list_mu);
+ if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key,
+ key_len, rsrc))) {
+ in_persistent_list = false;
+ grpc_channel_destroy(p->wrapper->wrapped);
+ free(p->wrapper->target);
+ free(p->wrapper->args_hashstr);
+ if(p->wrapper->creds_hashstr != NULL){
+ free(p->wrapper->creds_hashstr);
+ p->wrapper->creds_hashstr = NULL;
+ }
+ }
+ gpr_mu_unlock(&global_persistent_list_mu);
}
- gpr_mu_unlock(&global_persistent_list_mu);
+ }
+ p->wrapper->ref_count -= 1;
+ if (p->wrapper->ref_count == 0) {
+ is_last_wrapper = true;
}
gpr_mu_unlock(&p->wrapper->mu);
+ if (is_last_wrapper) {
+ if (in_persistent_list) {
+ // If ref_count==0 and the key still in the list, it means the user
+ // don't call channel->close().persistent list should free the
+ // allocation in such case, as well as related wrapped channel.
+ if (p->wrapper->wrapped != NULL) {
+ gpr_mu_lock(&p->wrapper->mu);
+ grpc_channel_destroy(p->wrapper->wrapped);
+ free(p->wrapper->target);
+ free(p->wrapper->args_hashstr);
+ if(p->wrapper->creds_hashstr != NULL){
+ free(p->wrapper->creds_hashstr);
+ p->wrapper->creds_hashstr = NULL;
+ }
+ p->wrapper->wrapped = NULL;
+ php_grpc_delete_persistent_list_entry(p->wrapper->key,
+ strlen(p->wrapper->key)
+ TSRMLS_CC);
+ gpr_mu_unlock(&p->wrapper->mu);
+ }
+ }
+ gpr_mu_destroy(&p->wrapper->mu);
+ free(p->wrapper->key);
+ free(p->wrapper);
+ }
+ p->wrapper = NULL;
}
PHP_GRPC_FREE_WRAPPED_FUNC_END()
@@ -276,9 +318,16 @@ PHP_METHOD(Channel, __construct) {
channel->wrapper->key = key;
channel->wrapper->target = strdup(target);
channel->wrapper->args_hashstr = strdup(sha1str);
+ channel->wrapper->creds_hashstr = NULL;
+ channel->wrapper->ref_count = 1;
+ channel->wrapper->is_valid = true;
if (creds != NULL && creds->hashstr != NULL) {
- channel->wrapper->creds_hashstr = creds->hashstr;
+ php_grpc_int creds_hashstr_len = strlen(creds->hashstr);
+ char *channel_creds_hashstr = malloc(creds_hashstr_len + 1);
+ strcpy(channel_creds_hashstr, creds->hashstr);
+ channel->wrapper->creds_hashstr = channel_creds_hashstr;
}
+
gpr_mu_init(&channel->wrapper->mu);
smart_str_free(&buf);
@@ -303,7 +352,17 @@ PHP_METHOD(Channel, __construct) {
channel, target, args, creds, key, key_len TSRMLS_CC);
} else {
efree(args.args);
+ if (channel->wrapper->creds_hashstr != NULL){
+ free(channel->wrapper->creds_hashstr);
+ channel->wrapper->creds_hashstr = NULL;
+ }
+ free(channel->wrapper->creds_hashstr);
+ free(channel->wrapper->key);
+ free(channel->wrapper->target);
+ free(channel->wrapper->args_hashstr);
+ free(channel->wrapper);
channel->wrapper = le->channel;
+ channel->wrapper->ref_count += 1;
}
}
}
@@ -323,7 +382,8 @@ PHP_METHOD(Channel, getTarget) {
}
char *target = grpc_channel_get_target(channel->wrapper->wrapped);
gpr_mu_unlock(&channel->wrapper->mu);
- PHP_GRPC_RETURN_STRING(target, 1);
+ PHP_GRPC_RETVAL_STRING(target, 1);
+ gpr_free(target);
}
/**
@@ -411,18 +471,46 @@ PHP_METHOD(Channel, watchConnectivityState) {
*/
PHP_METHOD(Channel, close) {
wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
- gpr_mu_lock(&channel->wrapper->mu);
- if (channel->wrapper->wrapped != NULL) {
- grpc_channel_destroy(channel->wrapper->wrapped);
- free(channel->wrapper->target);
- free(channel->wrapper->args_hashstr);
- channel->wrapper->wrapped = NULL;
-
- php_grpc_delete_persistent_list_entry(channel->wrapper->key,
- strlen(channel->wrapper->key)
- TSRMLS_CC);
+ bool is_last_wrapper = false;
+ if (channel->wrapper != NULL) {
+ // Channel_wrapper hasn't call close before.
+ gpr_mu_lock(&channel->wrapper->mu);
+ if (channel->wrapper->wrapped != NULL) {
+ if (channel->wrapper->is_valid) {
+ // Wrapped channel hasn't been destoryed by other wrapper.
+ grpc_channel_destroy(channel->wrapper->wrapped);
+ free(channel->wrapper->target);
+ free(channel->wrapper->args_hashstr);
+ if(channel->wrapper->creds_hashstr != NULL){
+ free(channel->wrapper->creds_hashstr);
+ channel->wrapper->creds_hashstr = NULL;
+ }
+ channel->wrapper->wrapped = NULL;
+ channel->wrapper->is_valid = false;
+
+ php_grpc_delete_persistent_list_entry(channel->wrapper->key,
+ strlen(channel->wrapper->key)
+ TSRMLS_CC);
+ }
+ }
+ channel->wrapper->ref_count -= 1;
+ if(channel->wrapper->ref_count == 0){
+ // Mark that the wrapper can be freed because mu should be
+ // destroyed outside the lock.
+ is_last_wrapper = true;
+ }
+ gpr_mu_unlock(&channel->wrapper->mu);
}
- gpr_mu_unlock(&channel->wrapper->mu);
+ gpr_mu_lock(&global_persistent_list_mu);
+ if (is_last_wrapper) {
+ gpr_mu_destroy(&channel->wrapper->mu);
+ free(channel->wrapper->key);
+ free(channel->wrapper);
+ }
+ // Set channel->wrapper to NULL to avoid call close twice for the same
+ // channel.
+ channel->wrapper = NULL;
+ gpr_mu_unlock(&global_persistent_list_mu);
}
// Delete an entry from the persistent list
@@ -437,6 +525,7 @@ void php_grpc_delete_persistent_list_entry(char *key, php_grpc_int key_len
le = (channel_persistent_le_t *)rsrc->ptr;
le->channel = NULL;
php_grpc_zend_hash_del(&EG(persistent_list), key, key_len+1);
+ free(le);
}
gpr_mu_unlock(&global_persistent_list_mu);
}
diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h
index 69adc4782c..86bfdea51a 100755
--- a/src/php/ext/grpc/channel.h
+++ b/src/php/ext/grpc/channel.h
@@ -40,6 +40,11 @@ typedef struct _grpc_channel_wrapper {
char *args_hashstr;
char *creds_hashstr;
gpr_mu mu;
+ // is_valid is used to check the wrapped channel has been freed
+ // before to avoid double free.
+ bool is_valid;
+ // ref_count is used to let the last wrapper free related channel and key.
+ size_t ref_count;
} grpc_channel_wrapper;
/* Wrapper struct for grpc_channel that can be associated with a PHP object */
diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c
index d120d6e90f..624d7cc75c 100644
--- a/src/php/ext/grpc/channel_credentials.c
+++ b/src/php/ext/grpc/channel_credentials.c
@@ -57,8 +57,13 @@ static grpc_ssl_roots_override_result get_ssl_roots_override(
/* Frees and destroys an instance of wrapped_grpc_channel_credentials */
PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel_credentials)
+ if (p->hashstr != NULL) {
+ free(p->hashstr);
+ p->hashstr = NULL;
+ }
if (p->wrapped != NULL) {
grpc_channel_credentials_release(p->wrapped);
+ p->wrapped = NULL;
}
PHP_GRPC_FREE_WRAPPED_FUNC_END()
@@ -152,7 +157,7 @@ PHP_METHOD(ChannelCredentials, createSsl) {
}
php_grpc_int hashkey_len = root_certs_length + cert_chain_length;
- char *hashkey = emalloc(hashkey_len);
+ char *hashkey = emalloc(hashkey_len + 1);
if (root_certs_length > 0) {
strcpy(hashkey, pem_root_certs);
}
@@ -199,8 +204,13 @@ PHP_METHOD(ChannelCredentials, createComposite) {
grpc_channel_credentials *creds =
grpc_composite_channel_credentials_create(cred1->wrapped, cred2->wrapped,
NULL);
+ // wrapped_grpc_channel_credentials object should keeps it's own
+ // allocation. Otherwise it conflicts free hashstr with call.c.
+ php_grpc_int cred1_len = strlen(cred1->hashstr);
+ char *cred1_hashstr = malloc(cred1_len+1);
+ strcpy(cred1_hashstr, cred1->hashstr);
zval *creds_object =
- grpc_php_wrap_channel_credentials(creds, cred1->hashstr, true
+ grpc_php_wrap_channel_credentials(creds, cred1_hashstr, true
TSRMLS_CC);
RETURN_DESTROY_ZVAL(creds_object);
}
diff --git a/src/php/ext/grpc/php7_wrapper.h b/src/php/ext/grpc/php7_wrapper.h
index 96091f9dad..2f4a53611c 100644
--- a/src/php/ext/grpc/php7_wrapper.h
+++ b/src/php/ext/grpc/php7_wrapper.h
@@ -33,6 +33,7 @@
#define php_grpc_add_next_index_stringl(data, str, len, b) \
add_next_index_stringl(data, str, len, b)
+#define PHP_GRPC_RETVAL_STRING(val, dup) RETVAL_STRING(val, dup)
#define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val, dup)
#define PHP_GRPC_MAKE_STD_ZVAL(pzv) MAKE_STD_ZVAL(pzv)
#define PHP_GRPC_FREE_STD_ZVAL(pzv)
@@ -145,6 +146,7 @@ static inline int php_grpc_zend_hash_find(HashTable *ht, char *key, int len,
#define php_grpc_add_next_index_stringl(data, str, len, b) \
add_next_index_stringl(data, str, len)
+#define PHP_GRPC_RETVAL_STRING(val, dup) RETVAL_STRING(val)
#define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val)
#define PHP_GRPC_MAKE_STD_ZVAL(pzv) \
pzv = (zval *)emalloc(sizeof(zval));
diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c
index 0f2c5b8114..5971babc00 100644
--- a/src/php/ext/grpc/php_grpc.c
+++ b/src/php/ext/grpc/php_grpc.c
@@ -253,7 +253,8 @@ PHP_MSHUTDOWN_FUNCTION(grpc) {
*/
PHP_MINFO_FUNCTION(grpc) {
php_info_print_table_start();
- php_info_print_table_header(2, "grpc support", "enabled");
+ php_info_print_table_row(2, "grpc support", "enabled");
+ php_info_print_table_row(2, "grpc module version", PHP_GRPC_VERSION);
php_info_print_table_end();
/* Remove comments if you have entries in php.ini
diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php
index 67378a34a8..5f3a96feaa 100644
--- a/src/php/lib/Grpc/BaseStub.php
+++ b/src/php/lib/Grpc/BaseStub.php
@@ -54,6 +54,18 @@ class BaseStub
}
unset($opts['update_metadata']);
}
+ if (!empty($opts['grpc.ssl_target_name_override'])) {
+ $this->hostname_override = $opts['grpc.ssl_target_name_override'];
+ }
+ if ($channel) {
+ if (!is_a($channel, 'Grpc\Channel')) {
+ throw new \Exception('The channel argument is not a'.
+ 'Channel object');
+ }
+ $this->channel = $channel;
+ return;
+ }
+
$package_config = json_decode(
file_get_contents(dirname(__FILE__).'/../../composer.json'), true);
if (!empty($opts['grpc.primary_user_agent'])) {
@@ -61,9 +73,6 @@ class BaseStub
} else {
$opts['grpc.primary_user_agent'] = '';
}
- if (!empty($opts['grpc.ssl_target_name_override'])) {
- $this->hostname_override = $opts['grpc.ssl_target_name_override'];
- }
$opts['grpc.primary_user_agent'] .=
'grpc-php/'.$package_config['version'];
if (!array_key_exists('credentials', $opts)) {
@@ -71,15 +80,7 @@ class BaseStub
'required. Please see one of the '.
'ChannelCredentials::create methods');
}
- if ($channel) {
- if (!is_a($channel, 'Grpc\Channel')) {
- throw new \Exception('The channel argument is not a'.
- 'Channel object');
- }
- $this->channel = $channel;
- } else {
- $this->channel = new Channel($hostname, $opts);
- }
+ $this->channel = new Channel($hostname, $opts);
}
/**
diff --git a/src/php/tests/unit_tests/ChannelTest.php b/src/php/tests/unit_tests/ChannelTest.php
index 13a770caff..5baff1fbd9 100644
--- a/src/php/tests/unit_tests/ChannelTest.php
+++ b/src/php/tests/unit_tests/ChannelTest.php
@@ -380,11 +380,6 @@ class ChannelTest extends PHPUnit_Framework_TestCase
// close channel1
$this->channel1->close();
- // channel2 is now in SHUTDOWN state
- $state = $this->channel2->getConnectivityState();
- $this->assertEquals(GRPC\CHANNEL_FATAL_FAILURE, $state);
-
- // calling it again will result in an exception because the
// channel is already closed
$state = $this->channel2->getConnectivityState();
}