GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming( grpc_call *call, grpcsharp_batch_context *ctx, const char *send_buffer, size_t send_buffer_len, grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[5]; ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), initial_metadata); ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; ops[0].data.send_initial_metadata.metadata = ctx->send_initial_metadata.metadata; ops[1].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); ops[1].data.send_message = ctx->send_message; ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; ops[3].op = GRPC_OP_RECV_INITIAL_METADATA; ops[3].data.recv_initial_metadata = &(ctx->recv_initial_metadata); ops[4].op = GRPC_OP_RECV_STATUS_ON_CLIENT; ops[4].data.recv_status_on_client.trailing_metadata = &(ctx->recv_status_on_client.trailing_metadata); ops[4].data.recv_status_on_client.status = &(ctx->recv_status_on_client.status); /* not using preallocation for status_details */ ops[4].data.recv_status_on_client.status_details = &(ctx->recv_status_on_client.status_details); ops[4].data.recv_status_on_client.status_details_capacity = &(ctx->recv_status_on_client.status_details_capacity); return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx); }
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_message(grpc_call *call, grpcsharp_batch_context *ctx, const char *send_buffer, size_t send_buffer_len) { /* TODO: don't use magic number */ grpc_op ops[1]; ops[0].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); ops[0].data.send_message = ctx->send_message; return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx); }
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary( grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer, size_t send_buffer_len, uint32_t write_flags, grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) { /* TODO: don't use magic number */ grpc_op ops[6]; memset(ops, 0, sizeof(ops)); ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), initial_metadata); ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; ops[0].data.send_initial_metadata.metadata = ctx->send_initial_metadata.metadata; ops[0].flags = initial_metadata_flags; ops[0].reserved = NULL; ops[1].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); ops[1].data.send_message.send_message = ctx->send_message; ops[1].flags = write_flags; ops[1].reserved = NULL; ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; ops[2].flags = 0; ops[2].reserved = NULL; ops[3].op = GRPC_OP_RECV_INITIAL_METADATA; ops[3].data.recv_initial_metadata.recv_initial_metadata = &(ctx->recv_initial_metadata); ops[3].flags = 0; ops[3].reserved = NULL; ops[4].op = GRPC_OP_RECV_MESSAGE; ops[4].data.recv_message.recv_message = &(ctx->recv_message); ops[4].flags = 0; ops[4].reserved = NULL; ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT; ops[5].data.recv_status_on_client.trailing_metadata = &(ctx->recv_status_on_client.trailing_metadata); ops[5].data.recv_status_on_client.status = &(ctx->recv_status_on_client.status); ops[5].data.recv_status_on_client.status_details = &(ctx->recv_status_on_client.status_details); ops[5].flags = 0; ops[5].reserved = NULL; return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, NULL); }
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_message( grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer, size_t send_buffer_len, uint32_t write_flags, int32_t send_empty_initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[2]; memset(ops, 0, sizeof(ops)); size_t nops = send_empty_initial_metadata ? 2 : 1; ops[0].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); ops[0].data.send_message.send_message = ctx->send_message; ops[0].flags = write_flags; ops[0].reserved = NULL; ops[1].op = GRPC_OP_SEND_INITIAL_METADATA; ops[1].flags = 0; ops[1].reserved = NULL; return grpcsharp_call_start_batch(call, ops, nops, ctx, NULL); }
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server( grpc_call* call, grpcsharp_batch_context* ctx, grpc_status_code status_code, const char* status_details, size_t status_details_len, grpc_metadata_array* trailing_metadata, int32_t send_empty_initial_metadata, const char* optional_send_buffer, size_t optional_send_buffer_len, uint32_t write_flags) { /* TODO: don't use magic number */ grpc_op ops[3]; memset(ops, 0, sizeof(ops)); size_t nops = 1; grpc_slice status_details_slice = grpc_slice_from_copied_buffer(status_details, status_details_len); ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER; ops[0].data.send_status_from_server.status = status_code; ops[0].data.send_status_from_server.status_details = &status_details_slice; grpcsharp_metadata_array_move( &(ctx->send_status_from_server.trailing_metadata), trailing_metadata); ops[0].data.send_status_from_server.trailing_metadata_count = ctx->send_status_from_server.trailing_metadata.count; ops[0].data.send_status_from_server.trailing_metadata = ctx->send_status_from_server.trailing_metadata.metadata; ops[0].flags = 0; ops[0].reserved = NULL; if (optional_send_buffer) { ops[nops].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(optional_send_buffer, optional_send_buffer_len); ops[nops].data.send_message.send_message = ctx->send_message; ops[nops].flags = write_flags; ops[nops].reserved = NULL; nops++; } if (send_empty_initial_metadata) { ops[nops].op = GRPC_OP_SEND_INITIAL_METADATA; ops[nops].flags = 0; ops[nops].reserved = NULL; nops++; } grpc_call_error ret = grpcsharp_call_start_batch(call, ops, nops, ctx, NULL); grpc_slice_unref(status_details_slice); return ret; }
/** * Start a batch of RPC actions. * @param array batch Array of actions to take * @return object Object with results of all actions */ PHP_METHOD(Call, startBatch) { wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); grpc_op ops[8]; size_t op_num = 0; zval *array; zval *value; zval *inner_value; HashTable *array_hash; HashPosition array_pointer; HashTable *status_hash; HashTable *message_hash; zval *message_value; zval *message_flags; zend_string *key; zend_ulong index; grpc_metadata_array metadata; grpc_metadata_array trailing_metadata; grpc_metadata_array recv_metadata; grpc_metadata_array recv_trailing_metadata; grpc_status_code status; char *status_details = NULL; size_t status_details_capacity = 0; grpc_byte_buffer *message; int cancelled; grpc_call_error error; char *message_str; size_t message_len; zval recv_status; grpc_metadata_array_init(&metadata); grpc_metadata_array_init(&trailing_metadata); grpc_metadata_array_init(&recv_metadata); grpc_metadata_array_init(&recv_trailing_metadata); object_init(return_value); /* "a" == 1 array */ #ifndef FAST_ZPP if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "start_batch expects an array", 1); goto cleanup; } #else ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY(array) ZEND_PARSE_PARAMETERS_END(); #endif array_hash = HASH_OF(array); //TODO(thinkerou): why phpunit wrong when use ZEND_HASH_FOREACH_VAL marco? //ZEND_HASH_FOREACH_VAL(array_hash, value) { for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); (value = zend_hash_get_current_data_ex(array_hash, &array_pointer)) != NULL; zend_hash_move_forward_ex(array_hash, &array_pointer)) { if (zend_hash_get_current_key_ex(array_hash, &key, &index, &array_pointer) != HASH_KEY_IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, "batch keys must be integers", 1); goto cleanup; } switch(index) { case GRPC_OP_SEND_INITIAL_METADATA: if (!create_metadata_array(value, &metadata)) { zend_throw_exception(spl_ce_InvalidArgumentException, "Bad metadata value given", 1); goto cleanup; } ops[op_num].data.send_initial_metadata.count = metadata.count; ops[op_num].data.send_initial_metadata.metadata = metadata.metadata; break; case GRPC_OP_SEND_MESSAGE: if (Z_TYPE_P(value) != IS_ARRAY) { zend_throw_exception(spl_ce_InvalidArgumentException, "Expected an array for send message", 1); goto cleanup; } message_hash = HASH_OF(value); if ((message_flags = zend_hash_str_find(message_hash, "flags", sizeof("flags") - 1)) != NULL) { if (Z_TYPE_P(message_flags) != IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, "Expected an int for message flags", 1); } ops[op_num].flags = Z_LVAL_P(message_flags) & GRPC_WRITE_USED_MASK; } if ((message_value = zend_hash_str_find( message_hash, "message", sizeof("message") - 1)) == NULL || Z_TYPE_P(message_value) != IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, "Expected a string for send message", 1); goto cleanup; } ops[op_num].data.send_message = string_to_byte_buffer(Z_STRVAL_P(message_value), Z_STRLEN_P(message_value)); break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: break; case GRPC_OP_SEND_STATUS_FROM_SERVER: status_hash = HASH_OF(value); if ((inner_value = zend_hash_str_find( status_hash, "metadata", sizeof("metadata") - 1)) != NULL) { if (!create_metadata_array(inner_value, &trailing_metadata)) { zend_throw_exception(spl_ce_InvalidArgumentException, "Bad trailing metadata value given", 1); goto cleanup; } ops[op_num].data.send_status_from_server.trailing_metadata = trailing_metadata.metadata; ops[op_num].data.send_status_from_server.trailing_metadata_count = trailing_metadata.count; } if ((inner_value = zend_hash_str_find( status_hash, "code", sizeof("code") - 1)) != NULL) { if (Z_TYPE_P(inner_value) != IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, "Status code must be an integer", 1); goto cleanup; } ops[op_num].data.send_status_from_server.status = Z_LVAL_P(inner_value); } else { zend_throw_exception(spl_ce_InvalidArgumentException, "Integer status code is required", 1); goto cleanup; } if ((inner_value = zend_hash_str_find( status_hash, "details", sizeof("details") - 1)) != NULL) { if (Z_TYPE_P(inner_value) != IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, "Status details must be a string", 1); goto cleanup; } ops[op_num].data.send_status_from_server.status_details = Z_STRVAL_P(inner_value); } else { zend_throw_exception(spl_ce_InvalidArgumentException, "String status details is required", 1); goto cleanup; } break; case GRPC_OP_RECV_INITIAL_METADATA: ops[op_num].data.recv_initial_metadata = &recv_metadata; break; case GRPC_OP_RECV_MESSAGE: ops[op_num].data.recv_message = &message; break; case GRPC_OP_RECV_STATUS_ON_CLIENT: ops[op_num].data.recv_status_on_client.trailing_metadata = &recv_trailing_metadata; ops[op_num].data.recv_status_on_client.status = &status; ops[op_num].data.recv_status_on_client.status_details = &status_details; ops[op_num].data.recv_status_on_client.status_details_capacity = &status_details_capacity; break; case GRPC_OP_RECV_CLOSE_ON_SERVER: ops[op_num].data.recv_close_on_server.cancelled = &cancelled; break; default: zend_throw_exception(spl_ce_InvalidArgumentException, "Unrecognized key in batch", 1); goto cleanup; } ops[op_num].op = (grpc_op_type)index; ops[op_num].flags = 0; ops[op_num].reserved = NULL; op_num++; } //ZEND_HASH_FOREACH_END(); error = grpc_call_start_batch(call->wrapped, ops, op_num, call->wrapped, NULL); if (error != GRPC_CALL_OK) { zend_throw_exception(spl_ce_LogicException, "start_batch was called incorrectly", (long)error); goto cleanup; } grpc_completion_queue_pluck(completion_queue, call->wrapped, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); for (int i = 0; i < op_num; i++) { switch(ops[i].op) { case GRPC_OP_SEND_INITIAL_METADATA: add_property_bool(return_value, "send_metadata", true); break; case GRPC_OP_SEND_MESSAGE: add_property_bool(return_value, "send_message", true); break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: add_property_bool(return_value, "send_close", true); break; case GRPC_OP_SEND_STATUS_FROM_SERVER: add_property_bool(return_value, "send_status", true); break; case GRPC_OP_RECV_INITIAL_METADATA: grpc_parse_metadata_array(&recv_metadata, array); add_property_zval(return_value, "metadata", array); //Z_DELREF_P(array); break; case GRPC_OP_RECV_MESSAGE: byte_buffer_to_string(message, &message_str, &message_len); if (message_str == NULL) { add_property_null(return_value, "message"); } else { add_property_stringl(return_value, "message", message_str, message_len); } break; case GRPC_OP_RECV_STATUS_ON_CLIENT: object_init(&recv_status); grpc_parse_metadata_array(&recv_trailing_metadata, array); add_property_zval(&recv_status, "metadata", array); //Z_DELREF_P(array); add_property_long(&recv_status, "code", status); add_property_string(&recv_status, "details", status_details); add_property_zval(return_value, "status", &recv_status); //Z_DELREF_P(&recv_status); break; case GRPC_OP_RECV_CLOSE_ON_SERVER: add_property_bool(return_value, "cancelled", cancelled); break; default: break; } } cleanup: grpc_metadata_array_destroy(&metadata); grpc_metadata_array_destroy(&trailing_metadata); grpc_metadata_array_destroy(&recv_metadata); grpc_metadata_array_destroy(&recv_trailing_metadata); if (status_details != NULL) { gpr_free(status_details); } for (int i = 0; i < op_num; i++) { if (ops[i].op == GRPC_OP_SEND_MESSAGE) { grpc_byte_buffer_destroy(ops[i].data.send_message); } if (ops[i].op == GRPC_OP_RECV_MESSAGE) { grpc_byte_buffer_destroy(message); } } RETURN_DESTROY_ZVAL(return_value); }