blob: 85fb994afda20caedcd3b7177a9387f8a41a2db2 [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 "call.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"
Stanley Cheung35805802015-12-10 11:42:55 -080045#include "call_credentials.h"
mlumishb892a272014-12-09 16:28:23 -080046
murgatroid998242ba72015-04-01 15:29:44 -070047#include <zend_exceptions.h>
48#include <zend_hash.h>
mlumishb892a272014-12-09 16:28:23 -080049
50#include <stdbool.h>
51
murgatroid998242ba72015-04-01 15:29:44 -070052#include <grpc/support/alloc.h>
53#include <grpc/grpc.h>
mlumishb892a272014-12-09 16:28:23 -080054
murgatroid99268acd52015-05-14 15:05:00 -070055#include "completion_queue.h"
mlumishb892a272014-12-09 16:28:23 -080056#include "timeval.h"
57#include "channel.h"
mlumishb892a272014-12-09 16:28:23 -080058#include "byte_buffer.h"
59
Xiaoguang Sun8a929a92015-03-13 14:22:31 +080060zend_class_entry *grpc_ce_call;
thinkeroudba5b0c2016-07-27 18:39:16 +080061#if PHP_MAJOR_VERSION >= 7
62static zend_object_handlers call_ce_handlers;
63#endif
Xiaoguang Sun8a929a92015-03-13 14:22:31 +080064
mlumishb892a272014-12-09 16:28:23 -080065/* Frees and destroys an instance of wrapped_grpc_call */
thinkerou011d1ef2016-07-27 09:44:49 +080066PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_call)
67 if (p->owned && p->wrapped != NULL) {
68 grpc_call_destroy(p->wrapped);
mlumishb892a272014-12-09 16:28:23 -080069 }
thinkerou011d1ef2016-07-27 09:44:49 +080070PHP_GRPC_FREE_WRAPPED_FUNC_END()
71
thinkerou03dc2192016-08-16 19:31:44 +080072/* Initializes an instance of wrapped_grpc_call to be associated with an
73 * object of a class specified by class_type */
thinkeroudba5b0c2016-07-27 18:39:16 +080074php_grpc_zend_object create_wrapped_grpc_call(zend_class_entry *class_type
75 TSRMLS_DC) {
thinkerouba75c012016-07-28 02:30:08 +080076 PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_call);
mlumishb892a272014-12-09 16:28:23 -080077 zend_object_std_init(&intern->std, class_type TSRMLS_CC);
78 object_properties_init(&intern->std, class_type);
thinkeroudc673c52016-07-28 09:49:38 +080079 PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_call, call_ce_handlers);
thinkeroudba5b0c2016-07-27 18:39:16 +080080}
thinkeroua3730b72016-07-20 16:59:54 +080081
82/* Creates and returns a PHP array object with the data in a
83 * grpc_metadata_array. Returns NULL on failure */
thinkerou6f9d30b2016-07-27 03:19:03 +080084zval *grpc_parse_metadata_array(grpc_metadata_array
85 *metadata_array TSRMLS_DC) {
thinkeroua3730b72016-07-20 16:59:54 +080086 int count = metadata_array->count;
87 grpc_metadata *elements = metadata_array->metadata;
thinkerou6f9d30b2016-07-27 03:19:03 +080088 zval *array;
89 PHP_GRPC_MAKE_STD_ZVAL(array);
90 array_init(array);
thinkeroua3730b72016-07-20 16:59:54 +080091 int i;
thinkeroua3730b72016-07-20 16:59:54 +080092 HashTable *array_hash;
thinkerou6f9d30b2016-07-27 03:19:03 +080093 zval *inner_array;
thinkeroua3730b72016-07-20 16:59:54 +080094 char *str_key;
95 char *str_val;
96 size_t key_len;
thinkerouba75c012016-07-28 02:30:08 +080097 zval *data = NULL;
thinkeroua3730b72016-07-20 16:59:54 +080098
thinkerou6f9d30b2016-07-27 03:19:03 +080099 array_hash = Z_ARRVAL_P(array);
thinkeroua3730b72016-07-20 16:59:54 +0800100 grpc_metadata *elem;
101 for (i = 0; i < count; i++) {
102 elem = &elements[i];
Craig Tiller9e78d692016-12-08 17:05:02 -0800103 key_len = GRPC_SLICE_LENGTH(elem->key);
thinkeroua3730b72016-07-20 16:59:54 +0800104 str_key = ecalloc(key_len + 1, sizeof(char));
Craig Tiller9e78d692016-12-08 17:05:02 -0800105 memcpy(str_key, GRPC_SLICE_START_PTR(elem->key), key_len);
106 str_val = ecalloc(GRPC_SLICE_LENGTH(elem->value) + 1, sizeof(char));
107 memcpy(str_val, GRPC_SLICE_START_PTR(elem->value), GRPC_SLICE_LENGTH(elem->value));
thinkerou5dafd822016-07-28 22:43:38 +0800108 if (php_grpc_zend_hash_find(array_hash, str_key, key_len, (void **)&data)
109 == SUCCESS) {
thinkeroua3730b72016-07-20 16:59:54 +0800110 if (Z_TYPE_P(data) != IS_ARRAY) {
thinkerou6f9d30b2016-07-27 03:19:03 +0800111 zend_throw_exception(zend_exception_get_default(TSRMLS_C),
thinkeroua3730b72016-07-20 16:59:54 +0800112 "Metadata hash somehow contains wrong types.",
thinkerou6f9d30b2016-07-27 03:19:03 +0800113 1 TSRMLS_CC);
thinkeroua3730b72016-07-20 16:59:54 +0800114 efree(str_key);
115 efree(str_val);
thinkerou6f9d30b2016-07-27 03:19:03 +0800116 return NULL;
thinkeroua3730b72016-07-20 16:59:54 +0800117 }
Craig Tiller9e78d692016-12-08 17:05:02 -0800118 php_grpc_add_next_index_stringl(data, str_val, GRPC_SLICE_LENGTH(elem->value),
thinkerou6f9d30b2016-07-27 03:19:03 +0800119 false);
thinkeroua3730b72016-07-20 16:59:54 +0800120 } else {
thinkerou6f9d30b2016-07-27 03:19:03 +0800121 PHP_GRPC_MAKE_STD_ZVAL(inner_array);
122 array_init(inner_array);
123 php_grpc_add_next_index_stringl(inner_array, str_val,
Craig Tiller9e78d692016-12-08 17:05:02 -0800124 GRPC_SLICE_LENGTH(elem->value), false);
thinkerou6f9d30b2016-07-27 03:19:03 +0800125 add_assoc_zval(array, str_key, inner_array);
thinkeroua3730b72016-07-20 16:59:54 +0800126 }
127 }
thinkerou6f9d30b2016-07-27 03:19:03 +0800128 return array;
thinkeroua3730b72016-07-20 16:59:54 +0800129}
130
131/* Populates a grpc_metadata_array with the data in a PHP array object.
132 Returns true on success and false on failure */
133bool create_metadata_array(zval *array, grpc_metadata_array *metadata) {
thinkeroua3730b72016-07-20 16:59:54 +0800134 HashTable *array_hash;
135 HashTable *inner_array_hash;
thinkerou6f9d30b2016-07-27 03:19:03 +0800136 zval *value;
thinkerouba75c012016-07-28 02:30:08 +0800137 zval *inner_array;
thinkeroua3730b72016-07-20 16:59:54 +0800138 if (Z_TYPE_P(array) != IS_ARRAY) {
139 return false;
140 }
141 grpc_metadata_array_init(metadata);
thinkerou6f9d30b2016-07-27 03:19:03 +0800142 array_hash = Z_ARRVAL_P(array);
thinkerouba75c012016-07-28 02:30:08 +0800143
thinkerou11cb5c52016-07-28 07:29:17 +0800144 char *key;
thinkerouba75c012016-07-28 02:30:08 +0800145 int key_type;
146 PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(array_hash, key, key_type,
147 inner_array)
thinkerou11cb5c52016-07-28 07:29:17 +0800148 if (key_type != HASH_KEY_IS_STRING || key == NULL) {
thinkeroua3730b72016-07-20 16:59:54 +0800149 return false;
150 }
151 if (Z_TYPE_P(inner_array) != IS_ARRAY) {
152 return false;
153 }
thinkerouba75c012016-07-28 02:30:08 +0800154 inner_array_hash = Z_ARRVAL_P(inner_array);
thinkeroua3730b72016-07-20 16:59:54 +0800155 metadata->capacity += zend_hash_num_elements(inner_array_hash);
thinkerouba75c012016-07-28 02:30:08 +0800156 PHP_GRPC_HASH_FOREACH_END()
thinkeroua3730b72016-07-20 16:59:54 +0800157
158 metadata->metadata = gpr_malloc(metadata->capacity * sizeof(grpc_metadata));
159
thinkerouba75c012016-07-28 02:30:08 +0800160 char *key1 = NULL;
161 int key_type1;
162 PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(array_hash, key1, key_type1,
163 inner_array)
164 if (key_type1 != HASH_KEY_IS_STRING) {
thinkerou6f9d30b2016-07-27 03:19:03 +0800165 return false;
166 }
Stanley Cheung129bca62016-08-26 19:54:57 -0700167 if (!grpc_header_key_is_legal(key1, strlen(key1))) {
168 return false;
169 }
thinkerouba75c012016-07-28 02:30:08 +0800170 inner_array_hash = Z_ARRVAL_P(inner_array);
171 PHP_GRPC_HASH_FOREACH_VAL_START(inner_array_hash, value)
thinkeroua3730b72016-07-20 16:59:54 +0800172 if (Z_TYPE_P(value) != IS_STRING) {
173 return false;
174 }
thinkerouba75c012016-07-28 02:30:08 +0800175 metadata->metadata[metadata->count].key = key1;
thinkeroua3730b72016-07-20 16:59:54 +0800176 metadata->metadata[metadata->count].value = Z_STRVAL_P(value);
177 metadata->metadata[metadata->count].value_length = Z_STRLEN_P(value);
178 metadata->count += 1;
thinkerouba75c012016-07-28 02:30:08 +0800179 PHP_GRPC_HASH_FOREACH_END()
180 PHP_GRPC_HASH_FOREACH_END()
thinkeroua3730b72016-07-20 16:59:54 +0800181 return true;
182}
183
thinkerou6f9d30b2016-07-27 03:19:03 +0800184/* Wraps a grpc_call struct in a PHP object. Owned indicates whether the
185 struct should be destroyed at the end of the object's lifecycle */
186zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned TSRMLS_DC) {
187 zval *call_object;
188 PHP_GRPC_MAKE_STD_ZVAL(call_object);
189 object_init_ex(call_object, grpc_ce_call);
190 wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(call_object);
191 call->wrapped = wrapped;
192 call->owned = owned;
193 return call_object;
194}
thinkeroua3730b72016-07-20 16:59:54 +0800195
mlumishb892a272014-12-09 16:28:23 -0800196/**
197 * Constructs a new instance of the Call class.
thinkerou03dc2192016-08-16 19:31:44 +0800198 * @param Channel $channel_obj The channel to associate the call with.
199 * Must not be closed.
mlumishb892a272014-12-09 16:28:23 -0800200 * @param string $method The method to call
thinkerou03dc2192016-08-16 19:31:44 +0800201 * @param Timeval $deadline_obj The deadline for completing the call
thinkerouefbc9e72016-08-16 20:00:36 +0800202 * @param string $host_override The host is set by user (optional)
mlumishb892a272014-12-09 16:28:23 -0800203 */
Craig Tillerb5dcec52015-01-13 11:13:42 -0800204PHP_METHOD(Call, __construct) {
mlumishb892a272014-12-09 16:28:23 -0800205 zval *channel_obj;
206 char *method;
thinkerou19304682016-07-22 02:43:19 +0800207 php_grpc_int method_len;
mlumishb892a272014-12-09 16:28:23 -0800208 zval *deadline_obj;
Stanley Cheung478fb002015-08-19 14:25:00 -0700209 char *host_override = NULL;
thinkerou19304682016-07-22 02:43:19 +0800210 php_grpc_int host_override_len = 0;
thinkeroua3730b72016-07-20 16:59:54 +0800211 wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
thinkeroua3730b72016-07-20 16:59:54 +0800212
Stanley Cheung478fb002015-08-19 14:25:00 -0700213 /* "OsO|s" == 1 Object, 1 string, 1 Object, 1 optional string */
thinkeroua3730b72016-07-20 16:59:54 +0800214 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OsO|s", &channel_obj,
215 grpc_ce_channel, &method, &method_len,
216 &deadline_obj, grpc_ce_timeval, &host_override,
217 &host_override_len) == FAILURE) {
218 zend_throw_exception(spl_ce_InvalidArgumentException,
219 "Call expects a Channel, a String, a Timeval and "
220 "an optional String", 1 TSRMLS_CC);
mlumishb892a272014-12-09 16:28:23 -0800221 return;
222 }
thinkeroua3730b72016-07-20 16:59:54 +0800223 wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(channel_obj);
Craig Tillerb5dcec52015-01-13 11:13:42 -0800224 if (channel->wrapped == NULL) {
mlumishb892a272014-12-09 16:28:23 -0800225 zend_throw_exception(spl_ce_InvalidArgumentException,
226 "Call cannot be constructed from a closed Channel",
227 1 TSRMLS_CC);
228 return;
229 }
230 add_property_zval(getThis(), "channel", channel_obj);
thinkeroua3730b72016-07-20 16:59:54 +0800231 wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj);
thinkeroua3730b72016-07-20 16:59:54 +0800232 call->wrapped =
233 grpc_channel_create_call(channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS,
234 completion_queue, method, host_override,
235 deadline->wrapped, NULL);
Stanley Cheung51b36912016-06-29 15:05:59 -0700236 call->owned = true;
mlumishb892a272014-12-09 16:28:23 -0800237}
238
239/**
murgatroid99afd541c2015-03-03 18:16:09 -0800240 * Start a batch of RPC actions.
thinkerouefbc9e72016-08-16 20:00:36 +0800241 * @param array $array Array of actions to take
murgatroid99afd541c2015-03-03 18:16:09 -0800242 * @return object Object with results of all actions
mlumishb892a272014-12-09 16:28:23 -0800243 */
murgatroid99c1d7e242015-04-02 10:02:43 -0700244PHP_METHOD(Call, startBatch) {
thinkerou6f9d30b2016-07-27 03:19:03 +0800245 zval *result;
246 PHP_GRPC_MAKE_STD_ZVAL(result);
247 object_init(result);
248 php_grpc_ulong index;
thinkeroua3730b72016-07-20 16:59:54 +0800249 zval *recv_status;
thinkerouba75c012016-07-28 02:30:08 +0800250 PHP_GRPC_MAKE_STD_ZVAL(recv_status);
251 object_init(recv_status);
thinkeroua3730b72016-07-20 16:59:54 +0800252 zval *value;
253 zval *inner_value;
254 zval *message_value;
255 zval *message_flags;
thinkeroub9c7f3a2016-07-22 09:22:41 +0800256 wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
thinkeroua3730b72016-07-20 16:59:54 +0800257
258 grpc_op ops[8];
259 size_t op_num = 0;
260 zval *array;
261 HashTable *array_hash;
262 HashTable *status_hash;
263 HashTable *message_hash;
264
murgatroid99afd541c2015-03-03 18:16:09 -0800265 grpc_metadata_array metadata;
266 grpc_metadata_array trailing_metadata;
267 grpc_metadata_array recv_metadata;
268 grpc_metadata_array recv_trailing_metadata;
269 grpc_status_code status;
270 char *status_details = NULL;
murgatroid999fe516a2015-03-11 14:47:10 -0700271 size_t status_details_capacity = 0;
murgatroid99afd541c2015-03-03 18:16:09 -0800272 grpc_byte_buffer *message;
273 int cancelled;
274 grpc_call_error error;
murgatroid99afd541c2015-03-03 18:16:09 -0800275 char *message_str;
276 size_t message_len;
thinkeroua3730b72016-07-20 16:59:54 +0800277
murgatroid99afd541c2015-03-03 18:16:09 -0800278 grpc_metadata_array_init(&metadata);
279 grpc_metadata_array_init(&trailing_metadata);
murgatroid99d8bb9572015-03-11 09:18:06 -0700280 grpc_metadata_array_init(&recv_metadata);
281 grpc_metadata_array_init(&recv_trailing_metadata);
David Garcia Quintasa301eaa2016-05-06 16:59:03 -0700282 memset(ops, 0, sizeof(ops));
thinkeroua3730b72016-07-20 16:59:54 +0800283
murgatroid99afd541c2015-03-03 18:16:09 -0800284 /* "a" == 1 array */
285 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) ==
Craig Tillerb5dcec52015-01-13 11:13:42 -0800286 FAILURE) {
mlumishb892a272014-12-09 16:28:23 -0800287 zend_throw_exception(spl_ce_InvalidArgumentException,
thinkerou11cb5c52016-07-28 07:29:17 +0800288 "start_batch expects an array", 1 TSRMLS_CC);
289 goto cleanup;
mlumishb892a272014-12-09 16:28:23 -0800290 }
thinkeroua3730b72016-07-20 16:59:54 +0800291
mlumishb892a272014-12-09 16:28:23 -0800292 array_hash = Z_ARRVAL_P(array);
thinkerouba75c012016-07-28 02:30:08 +0800293
294 char *key = NULL;
295 int key_type;
296 PHP_GRPC_HASH_FOREACH_LONG_KEY_VAL_START(array_hash, key, key_type, index,
thinkerou5dafd822016-07-28 22:43:38 +0800297 value)
thinkerou11cb5c52016-07-28 07:29:17 +0800298 if (key_type != HASH_KEY_IS_LONG || key != NULL) {
murgatroid995ca9f922015-02-03 11:21:11 -0800299 zend_throw_exception(spl_ce_InvalidArgumentException,
murgatroid99afd541c2015-03-03 18:16:09 -0800300 "batch keys must be integers", 1 TSRMLS_CC);
301 goto cleanup;
murgatroid995ca9f922015-02-03 11:21:11 -0800302 }
murgatroid99afd541c2015-03-03 18:16:09 -0800303 switch(index) {
thinkeroua3730b72016-07-20 16:59:54 +0800304 case GRPC_OP_SEND_INITIAL_METADATA:
thinkerouba75c012016-07-28 02:30:08 +0800305 if (!create_metadata_array(value, &metadata)) {
murgatroid995ca9f922015-02-03 11:21:11 -0800306 zend_throw_exception(spl_ce_InvalidArgumentException,
thinkeroua3730b72016-07-20 16:59:54 +0800307 "Bad metadata value given", 1 TSRMLS_CC);
murgatroid99afd541c2015-03-03 18:16:09 -0800308 goto cleanup;
thinkeroua3730b72016-07-20 16:59:54 +0800309 }
thinkerou5dafd822016-07-28 22:43:38 +0800310 ops[op_num].data.send_initial_metadata.count = metadata.count;
311 ops[op_num].data.send_initial_metadata.metadata = metadata.metadata;
thinkeroua3730b72016-07-20 16:59:54 +0800312 break;
313 case GRPC_OP_SEND_MESSAGE:
thinkerouba75c012016-07-28 02:30:08 +0800314 if (Z_TYPE_P(value) != IS_ARRAY) {
thinkeroua3730b72016-07-20 16:59:54 +0800315 zend_throw_exception(spl_ce_InvalidArgumentException,
316 "Expected an array for send message",
317 1 TSRMLS_CC);
318 goto cleanup;
319 }
thinkerouba75c012016-07-28 02:30:08 +0800320 message_hash = Z_ARRVAL_P(value);
321 if (php_grpc_zend_hash_find(message_hash, "flags", sizeof("flags"),
thinkeroua3730b72016-07-20 16:59:54 +0800322 (void **)&message_flags) == SUCCESS) {
thinkerouba75c012016-07-28 02:30:08 +0800323 if (Z_TYPE_P(message_flags) != IS_LONG) {
thinkeroua3730b72016-07-20 16:59:54 +0800324 zend_throw_exception(spl_ce_InvalidArgumentException,
325 "Expected an int for message flags",
326 1 TSRMLS_CC);
327 }
thinkerouba75c012016-07-28 02:30:08 +0800328 ops[op_num].flags = Z_LVAL_P(message_flags) & GRPC_WRITE_USED_MASK;
thinkeroua3730b72016-07-20 16:59:54 +0800329 }
thinkerouba75c012016-07-28 02:30:08 +0800330 if (php_grpc_zend_hash_find(message_hash, "message", sizeof("message"),
thinkeroua3730b72016-07-20 16:59:54 +0800331 (void **)&message_value) != SUCCESS ||
thinkerouba75c012016-07-28 02:30:08 +0800332 Z_TYPE_P(message_value) != IS_STRING) {
thinkeroua3730b72016-07-20 16:59:54 +0800333 zend_throw_exception(spl_ce_InvalidArgumentException,
334 "Expected a string for send message",
335 1 TSRMLS_CC);
336 goto cleanup;
337 }
338 ops[op_num].data.send_message =
thinkerouba75c012016-07-28 02:30:08 +0800339 string_to_byte_buffer(Z_STRVAL_P(message_value),
340 Z_STRLEN_P(message_value));
thinkeroua3730b72016-07-20 16:59:54 +0800341 break;
342 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
343 break;
344 case GRPC_OP_SEND_STATUS_FROM_SERVER:
thinkerouba75c012016-07-28 02:30:08 +0800345 status_hash = Z_ARRVAL_P(value);
346 if (php_grpc_zend_hash_find(status_hash, "metadata", sizeof("metadata"),
thinkeroua3730b72016-07-20 16:59:54 +0800347 (void **)&inner_value) == SUCCESS) {
thinkerouba75c012016-07-28 02:30:08 +0800348 if (!create_metadata_array(inner_value, &trailing_metadata)) {
thinkeroua3730b72016-07-20 16:59:54 +0800349 zend_throw_exception(spl_ce_InvalidArgumentException,
350 "Bad trailing metadata value given",
351 1 TSRMLS_CC);
352 goto cleanup;
353 }
354 ops[op_num].data.send_status_from_server.trailing_metadata =
355 trailing_metadata.metadata;
356 ops[op_num].data.send_status_from_server.trailing_metadata_count =
357 trailing_metadata.count;
358 }
thinkerouba75c012016-07-28 02:30:08 +0800359 if (php_grpc_zend_hash_find(status_hash, "code", sizeof("code"),
thinkeroua3730b72016-07-20 16:59:54 +0800360 (void**)&inner_value) == SUCCESS) {
thinkerouba75c012016-07-28 02:30:08 +0800361 if (Z_TYPE_P(inner_value) != IS_LONG) {
thinkeroua3730b72016-07-20 16:59:54 +0800362 zend_throw_exception(spl_ce_InvalidArgumentException,
363 "Status code must be an integer",
364 1 TSRMLS_CC);
365 goto cleanup;
366 }
367 ops[op_num].data.send_status_from_server.status =
thinkerouba75c012016-07-28 02:30:08 +0800368 Z_LVAL_P(inner_value);
thinkeroua3730b72016-07-20 16:59:54 +0800369 } else {
370 zend_throw_exception(spl_ce_InvalidArgumentException,
371 "Integer status code is required",
372 1 TSRMLS_CC);
373 goto cleanup;
374 }
thinkerouba75c012016-07-28 02:30:08 +0800375 if (php_grpc_zend_hash_find(status_hash, "details", sizeof("details"),
thinkeroua3730b72016-07-20 16:59:54 +0800376 (void**)&inner_value) == SUCCESS) {
thinkerouba75c012016-07-28 02:30:08 +0800377 if (Z_TYPE_P(inner_value) != IS_STRING) {
thinkeroua3730b72016-07-20 16:59:54 +0800378 zend_throw_exception(spl_ce_InvalidArgumentException,
379 "Status details must be a string",
380 1 TSRMLS_CC);
381 goto cleanup;
382 }
383 ops[op_num].data.send_status_from_server.status_details =
thinkerouba75c012016-07-28 02:30:08 +0800384 Z_STRVAL_P(inner_value);
thinkeroua3730b72016-07-20 16:59:54 +0800385 } else {
386 zend_throw_exception(spl_ce_InvalidArgumentException,
387 "String status details is required",
388 1 TSRMLS_CC);
389 goto cleanup;
390 }
391 break;
392 case GRPC_OP_RECV_INITIAL_METADATA:
393 ops[op_num].data.recv_initial_metadata = &recv_metadata;
394 break;
395 case GRPC_OP_RECV_MESSAGE:
396 ops[op_num].data.recv_message = &message;
397 break;
398 case GRPC_OP_RECV_STATUS_ON_CLIENT:
399 ops[op_num].data.recv_status_on_client.trailing_metadata =
400 &recv_trailing_metadata;
401 ops[op_num].data.recv_status_on_client.status = &status;
402 ops[op_num].data.recv_status_on_client.status_details =
403 &status_details;
404 ops[op_num].data.recv_status_on_client.status_details_capacity =
405 &status_details_capacity;
406 break;
407 case GRPC_OP_RECV_CLOSE_ON_SERVER:
408 ops[op_num].data.recv_close_on_server.cancelled = &cancelled;
409 break;
410 default:
411 zend_throw_exception(spl_ce_InvalidArgumentException,
412 "Unrecognized key in batch", 1 TSRMLS_CC);
413 goto cleanup;
murgatroid99afd541c2015-03-03 18:16:09 -0800414 }
415 ops[op_num].op = (grpc_op_type)index;
David Garcia Quintasba710e52015-06-15 13:31:15 -0700416 ops[op_num].flags = 0;
Craig Tiller42758992015-08-18 10:34:32 -0700417 ops[op_num].reserved = NULL;
murgatroid99afd541c2015-03-03 18:16:09 -0800418 op_num++;
thinkerouba75c012016-07-28 02:30:08 +0800419 PHP_GRPC_HASH_FOREACH_END()
thinkeroua3730b72016-07-20 16:59:54 +0800420
Nicolas "Pixel" Noble150b7c42015-08-01 01:15:10 +0200421 error = grpc_call_start_batch(call->wrapped, ops, op_num, call->wrapped,
422 NULL);
murgatroid99afd541c2015-03-03 18:16:09 -0800423 if (error != GRPC_CALL_OK) {
424 zend_throw_exception(spl_ce_LogicException,
425 "start_batch was called incorrectly",
426 (long)error TSRMLS_CC);
427 goto cleanup;
428 }
Stanley Cheungc0c9ba92015-08-18 16:19:38 -0700429 grpc_completion_queue_pluck(completion_queue, call->wrapped,
430 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
thinkerouba75c012016-07-28 02:30:08 +0800431#if PHP_MAJOR_VERSION >= 7
432 zval recv_md;
433#endif
murgatroid99afd541c2015-03-03 18:16:09 -0800434 for (int i = 0; i < op_num; i++) {
435 switch(ops[i].op) {
thinkeroua3730b72016-07-20 16:59:54 +0800436 case GRPC_OP_SEND_INITIAL_METADATA:
437 add_property_bool(result, "send_metadata", true);
438 break;
439 case GRPC_OP_SEND_MESSAGE:
440 add_property_bool(result, "send_message", true);
441 break;
442 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
443 add_property_bool(result, "send_close", true);
444 break;
445 case GRPC_OP_SEND_STATUS_FROM_SERVER:
446 add_property_bool(result, "send_status", true);
447 break;
448 case GRPC_OP_RECV_INITIAL_METADATA:
thinkerouba75c012016-07-28 02:30:08 +0800449#if PHP_MAJOR_VERSION < 7
thinkeroua3730b72016-07-20 16:59:54 +0800450 array = grpc_parse_metadata_array(&recv_metadata TSRMLS_CC);
451 add_property_zval(result, "metadata", array);
thinkeroua3730b72016-07-20 16:59:54 +0800452#else
thinkerou6f9d30b2016-07-27 03:19:03 +0800453 recv_md = *grpc_parse_metadata_array(&recv_metadata);
454 add_property_zval(result, "metadata", &recv_md);
thinkerouba75c012016-07-28 02:30:08 +0800455#endif
456 PHP_GRPC_DELREF(array);
thinkeroua3730b72016-07-20 16:59:54 +0800457 break;
458 case GRPC_OP_RECV_MESSAGE:
459 byte_buffer_to_string(message, &message_str, &message_len);
460 if (message_str == NULL) {
thinkerou6f9d30b2016-07-27 03:19:03 +0800461 add_property_null(result, "message");
thinkeroua3730b72016-07-20 16:59:54 +0800462 } else {
thinkerouba75c012016-07-28 02:30:08 +0800463 php_grpc_add_property_stringl(result, "message", message_str,
464 message_len, false);
thinkeroua3730b72016-07-20 16:59:54 +0800465 }
466 break;
467 case GRPC_OP_RECV_STATUS_ON_CLIENT:
thinkerouba75c012016-07-28 02:30:08 +0800468#if PHP_MAJOR_VERSION < 7
469 array = grpc_parse_metadata_array(&recv_trailing_metadata TSRMLS_CC);
470 add_property_zval(recv_status, "metadata", array);
471#else
thinkerou6f9d30b2016-07-27 03:19:03 +0800472 recv_md = *grpc_parse_metadata_array(&recv_trailing_metadata);
thinkerouba75c012016-07-28 02:30:08 +0800473 add_property_zval(recv_status, "metadata", &recv_md);
474#endif
475 PHP_GRPC_DELREF(array);
476 add_property_long(recv_status, "code", status);
Stanley Cheung8e565022016-07-28 10:32:43 -0700477 php_grpc_add_property_string(recv_status, "details", status_details,
478 true);
thinkerouba75c012016-07-28 02:30:08 +0800479 add_property_zval(result, "status", recv_status);
480 PHP_GRPC_DELREF(recv_status);
thinkeroua3730b72016-07-20 16:59:54 +0800481 break;
482 case GRPC_OP_RECV_CLOSE_ON_SERVER:
thinkerou6f9d30b2016-07-27 03:19:03 +0800483 add_property_bool(result, "cancelled", cancelled);
thinkeroua3730b72016-07-20 16:59:54 +0800484 break;
485 default:
486 break;
487 }
488 }
thinkeroua3730b72016-07-20 16:59:54 +0800489
murgatroid99afd541c2015-03-03 18:16:09 -0800490cleanup:
murgatroid99afd541c2015-03-03 18:16:09 -0800491 grpc_metadata_array_destroy(&metadata);
murgatroid99afd541c2015-03-03 18:16:09 -0800492 grpc_metadata_array_destroy(&trailing_metadata);
493 grpc_metadata_array_destroy(&recv_metadata);
494 grpc_metadata_array_destroy(&recv_trailing_metadata);
495 if (status_details != NULL) {
496 gpr_free(status_details);
mlumishdba87892015-01-02 13:27:28 -0800497 }
Stanley Cheung82e6f322016-04-06 11:51:57 -0700498 for (int i = 0; i < op_num; i++) {
499 if (ops[i].op == GRPC_OP_SEND_MESSAGE) {
500 grpc_byte_buffer_destroy(ops[i].data.send_message);
501 }
502 if (ops[i].op == GRPC_OP_RECV_MESSAGE) {
503 grpc_byte_buffer_destroy(message);
504 }
505 }
murgatroid99afd541c2015-03-03 18:16:09 -0800506 RETURN_DESTROY_ZVAL(result);
mlumishb892a272014-12-09 16:28:23 -0800507}
508
murgatroid99c1da8f22015-03-25 11:33:05 -0700509/**
Stanley Cheungdb98e082015-07-27 10:19:45 -0700510 * Get the endpoint this call/stream is connected to
511 * @return string The URI of the endpoint
512 */
513PHP_METHOD(Call, getPeer) {
thinkeroua3730b72016-07-20 16:59:54 +0800514 wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
thinkeroub9c7f3a2016-07-22 09:22:41 +0800515 PHP_GRPC_RETURN_STRING(grpc_call_get_peer(call->wrapped), 1);
Stanley Cheungdb98e082015-07-27 10:19:45 -0700516}
517
518/**
thinkerouefbc9e72016-08-16 20:00:36 +0800519 * Cancel the call. This will cause the call to end with STATUS_CANCELLED
520 * if it has not already ended with another status.
521 * @return void
murgatroid99c1da8f22015-03-25 11:33:05 -0700522 */
523PHP_METHOD(Call, cancel) {
thinkeroua3730b72016-07-20 16:59:54 +0800524 wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
Nicolas "Pixel" Noble150b7c42015-08-01 01:15:10 +0200525 grpc_call_cancel(call->wrapped, NULL);
murgatroid99c1da8f22015-03-25 11:33:05 -0700526}
527
Stanley Cheung35805802015-12-10 11:42:55 -0800528/**
529 * Set the CallCredentials for this call.
thinkerouefbc9e72016-08-16 20:00:36 +0800530 * @param CallCredentials $creds_obj The CallCredentials object
531 * @return int The error code
Stanley Cheung35805802015-12-10 11:42:55 -0800532 */
533PHP_METHOD(Call, setCredentials) {
534 zval *creds_obj;
535
536 /* "O" == 1 Object */
537 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &creds_obj,
538 grpc_ce_call_credentials) == FAILURE) {
539 zend_throw_exception(spl_ce_InvalidArgumentException,
540 "setCredentials expects 1 CallCredentials",
541 1 TSRMLS_CC);
542 return;
543 }
544
thinkeroua3730b72016-07-20 16:59:54 +0800545 wrapped_grpc_call_credentials *creds =
546 Z_WRAPPED_GRPC_CALL_CREDS_P(creds_obj);
547 wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
Stanley Cheung35805802015-12-10 11:42:55 -0800548
549 grpc_call_error error = GRPC_CALL_ERROR;
550 error = grpc_call_set_credentials(call->wrapped, creds->wrapped);
551 RETURN_LONG(error);
552}
553
mlumishb892a272014-12-09 16:28:23 -0800554static zend_function_entry call_methods[] = {
thinkeroua3730b72016-07-20 16:59:54 +0800555 PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
556 PHP_ME(Call, startBatch, NULL, ZEND_ACC_PUBLIC)
557 PHP_ME(Call, getPeer, NULL, ZEND_ACC_PUBLIC)
558 PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC)
559 PHP_ME(Call, setCredentials, NULL, ZEND_ACC_PUBLIC)
560 PHP_FE_END
561};
mlumishb892a272014-12-09 16:28:23 -0800562
Craig Tillerb5dcec52015-01-13 11:13:42 -0800563void grpc_init_call(TSRMLS_D) {
mlumishb892a272014-12-09 16:28:23 -0800564 zend_class_entry ce;
565 INIT_CLASS_ENTRY(ce, "Grpc\\Call", call_methods);
566 ce.create_object = create_wrapped_grpc_call;
567 grpc_ce_call = zend_register_internal_class(&ce TSRMLS_CC);
thinkerou5dafd822016-07-28 22:43:38 +0800568 PHP_GRPC_INIT_HANDLER(wrapped_grpc_call, call_ce_handlers);
mlumishb892a272014-12-09 16:28:23 -0800569}