| /* |
| * |
| * Copyright 2015, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include <math.h> |
| #include <string.h> |
| |
| #define PY_SSIZE_T_CLEAN |
| #include <Python.h> |
| #include <grpc/grpc.h> |
| #include <grpc/byte_buffer_reader.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/slice.h> |
| #include <grpc/support/time.h> |
| #include <grpc/support/string_util.h> |
| |
| #include "grpc/_adapter/_c/types.h" |
| |
| pygrpc_tag *pygrpc_produce_batch_tag( |
| PyObject *user_tag, Call *call, grpc_op *ops, size_t nops) { |
| pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); |
| tag->user_tag = user_tag; |
| Py_XINCREF(tag->user_tag); |
| tag->call = call; |
| Py_XINCREF(tag->call); |
| tag->ops = gpr_malloc(sizeof(grpc_op)*nops); |
| memcpy(tag->ops, ops, sizeof(grpc_op)*nops); |
| tag->nops = nops; |
| grpc_call_details_init(&tag->request_call_details); |
| grpc_metadata_array_init(&tag->request_metadata); |
| tag->is_new_call = 0; |
| return tag; |
| } |
| |
| pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call) { |
| pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); |
| tag->user_tag = user_tag; |
| Py_XINCREF(tag->user_tag); |
| tag->call = empty_call; |
| Py_XINCREF(tag->call); |
| tag->ops = NULL; |
| tag->nops = 0; |
| grpc_call_details_init(&tag->request_call_details); |
| grpc_metadata_array_init(&tag->request_metadata); |
| tag->is_new_call = 1; |
| return tag; |
| } |
| |
| pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag) { |
| pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); |
| tag->user_tag = user_tag; |
| Py_XINCREF(tag->user_tag); |
| tag->call = NULL; |
| tag->ops = NULL; |
| tag->nops = 0; |
| grpc_call_details_init(&tag->request_call_details); |
| grpc_metadata_array_init(&tag->request_metadata); |
| tag->is_new_call = 0; |
| return tag; |
| } |
| |
| pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag) { |
| pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); |
| tag->user_tag = user_tag; |
| Py_XINCREF(tag->user_tag); |
| tag->call = NULL; |
| tag->ops = NULL; |
| tag->nops = 0; |
| grpc_call_details_init(&tag->request_call_details); |
| grpc_metadata_array_init(&tag->request_metadata); |
| tag->is_new_call = 0; |
| return tag; |
| } |
| |
| void pygrpc_discard_tag(pygrpc_tag *tag) { |
| if (!tag) { |
| return; |
| } |
| Py_XDECREF(tag->user_tag); |
| Py_XDECREF(tag->call); |
| gpr_free(tag->ops); |
| grpc_call_details_destroy(&tag->request_call_details); |
| grpc_metadata_array_destroy(&tag->request_metadata); |
| gpr_free(tag); |
| } |
| |
| PyObject *pygrpc_consume_event(grpc_event event) { |
| pygrpc_tag *tag; |
| PyObject *result; |
| if (event.type == GRPC_QUEUE_TIMEOUT) { |
| Py_RETURN_NONE; |
| } |
| tag = event.tag; |
| switch (event.type) { |
| case GRPC_QUEUE_SHUTDOWN: |
| result = Py_BuildValue("iOOOOO", GRPC_QUEUE_SHUTDOWN, |
| Py_None, Py_None, Py_None, Py_None, Py_True); |
| break; |
| case GRPC_OP_COMPLETE: |
| if (tag->is_new_call) { |
| result = Py_BuildValue( |
| "iOO(ssd)[(iNOOOO)]O", GRPC_OP_COMPLETE, tag->user_tag, tag->call, |
| tag->request_call_details.method, tag->request_call_details.host, |
| pygrpc_cast_gpr_timespec_to_double(tag->request_call_details.deadline), |
| GRPC_OP_RECV_INITIAL_METADATA, |
| pygrpc_cast_metadata_array_to_pyseq(tag->request_metadata), Py_None, |
| Py_None, Py_None, Py_None, |
| event.success ? Py_True : Py_False); |
| } else { |
| result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag, |
| tag->call ? (PyObject*)tag->call : Py_None, Py_None, |
| pygrpc_consume_ops(tag->ops, tag->nops), |
| event.success ? Py_True : Py_False); |
| } |
| break; |
| default: |
| PyErr_SetString(PyExc_ValueError, |
| "unknown completion type; could not translate event"); |
| return NULL; |
| } |
| pygrpc_discard_tag(tag); |
| return result; |
| } |
| |
| int pygrpc_produce_op(PyObject *op, grpc_op *result) { |
| static const int OP_TUPLE_SIZE = 6; |
| static const int STATUS_TUPLE_SIZE = 2; |
| static const int TYPE_INDEX = 0; |
| static const int INITIAL_METADATA_INDEX = 1; |
| static const int TRAILING_METADATA_INDEX = 2; |
| static const int MESSAGE_INDEX = 3; |
| static const int STATUS_INDEX = 4; |
| static const int STATUS_CODE_INDEX = 0; |
| static const int STATUS_DETAILS_INDEX = 1; |
| static const int WRITE_FLAGS_INDEX = 5; |
| int type; |
| Py_ssize_t message_size; |
| char *message; |
| char *status_details; |
| gpr_slice message_slice; |
| grpc_op c_op; |
| if (!PyTuple_Check(op)) { |
| PyErr_SetString(PyExc_TypeError, "expected tuple op"); |
| return 0; |
| } |
| if (PyTuple_Size(op) != OP_TUPLE_SIZE) { |
| char *buf; |
| gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE); |
| PyErr_SetString(PyExc_ValueError, buf); |
| gpr_free(buf); |
| return 0; |
| } |
| type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX)); |
| if (PyErr_Occurred()) { |
| return 0; |
| } |
| c_op.op = type; |
| c_op.reserved = NULL; |
| c_op.flags = PyInt_AsLong(PyTuple_GET_ITEM(op, WRITE_FLAGS_INDEX)); |
| if (PyErr_Occurred()) { |
| return 0; |
| } |
| switch (type) { |
| case GRPC_OP_SEND_INITIAL_METADATA: |
| if (!pygrpc_cast_pyseq_to_send_metadata( |
| PyTuple_GetItem(op, INITIAL_METADATA_INDEX), |
| &c_op.data.send_initial_metadata.metadata, |
| &c_op.data.send_initial_metadata.count)) { |
| return 0; |
| } |
| break; |
| case GRPC_OP_SEND_MESSAGE: |
| PyString_AsStringAndSize( |
| PyTuple_GET_ITEM(op, MESSAGE_INDEX), &message, &message_size); |
| message_slice = gpr_slice_from_copied_buffer(message, message_size); |
| c_op.data.send_message = grpc_raw_byte_buffer_create(&message_slice, 1); |
| gpr_slice_unref(message_slice); |
| break; |
| case GRPC_OP_SEND_CLOSE_FROM_CLIENT: |
| /* Don't need to fill in any other fields. */ |
| break; |
| case GRPC_OP_SEND_STATUS_FROM_SERVER: |
| if (!pygrpc_cast_pyseq_to_send_metadata( |
| PyTuple_GetItem(op, TRAILING_METADATA_INDEX), |
| &c_op.data.send_status_from_server.trailing_metadata, |
| &c_op.data.send_status_from_server.trailing_metadata_count)) { |
| return 0; |
| } |
| if (!PyTuple_Check(PyTuple_GET_ITEM(op, STATUS_INDEX))) { |
| char *buf; |
| gpr_asprintf(&buf, "expected tuple status in op of length %d", |
| STATUS_TUPLE_SIZE); |
| PyErr_SetString(PyExc_ValueError, buf); |
| gpr_free(buf); |
| return 0; |
| } |
| c_op.data.send_status_from_server.status = PyInt_AsLong( |
| PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_CODE_INDEX)); |
| status_details = PyString_AsString( |
| PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_DETAILS_INDEX)); |
| if (PyErr_Occurred()) { |
| return 0; |
| } |
| c_op.data.send_status_from_server.status_details = |
| gpr_malloc(strlen(status_details) + 1); |
| strcpy((char *)c_op.data.send_status_from_server.status_details, |
| status_details); |
| break; |
| case GRPC_OP_RECV_INITIAL_METADATA: |
| c_op.data.recv_initial_metadata = gpr_malloc(sizeof(grpc_metadata_array)); |
| grpc_metadata_array_init(c_op.data.recv_initial_metadata); |
| break; |
| case GRPC_OP_RECV_MESSAGE: |
| c_op.data.recv_message = gpr_malloc(sizeof(grpc_byte_buffer *)); |
| break; |
| case GRPC_OP_RECV_STATUS_ON_CLIENT: |
| c_op.data.recv_status_on_client.trailing_metadata = |
| gpr_malloc(sizeof(grpc_metadata_array)); |
| grpc_metadata_array_init(c_op.data.recv_status_on_client.trailing_metadata); |
| c_op.data.recv_status_on_client.status = |
| gpr_malloc(sizeof(grpc_status_code *)); |
| c_op.data.recv_status_on_client.status_details = |
| gpr_malloc(sizeof(char *)); |
| *c_op.data.recv_status_on_client.status_details = NULL; |
| c_op.data.recv_status_on_client.status_details_capacity = |
| gpr_malloc(sizeof(size_t)); |
| *c_op.data.recv_status_on_client.status_details_capacity = 0; |
| break; |
| case GRPC_OP_RECV_CLOSE_ON_SERVER: |
| c_op.data.recv_close_on_server.cancelled = gpr_malloc(sizeof(int)); |
| break; |
| default: |
| return 0; |
| } |
| *result = c_op; |
| return 1; |
| } |
| |
| void pygrpc_discard_op(grpc_op op) { |
| size_t i; |
| switch(op.op) { |
| case GRPC_OP_SEND_INITIAL_METADATA: |
| /* Whenever we produce send-metadata, we allocate new strings (to handle |
| arbitrary sequence input as opposed to just lists or just tuples). We |
| thus must free those elements. */ |
| for (i = 0; i < op.data.send_initial_metadata.count; ++i) { |
| gpr_free((void *)op.data.send_initial_metadata.metadata[i].key); |
| gpr_free((void *)op.data.send_initial_metadata.metadata[i].value); |
| } |
| gpr_free(op.data.send_initial_metadata.metadata); |
| break; |
| case GRPC_OP_SEND_MESSAGE: |
| grpc_byte_buffer_destroy(op.data.send_message); |
| break; |
| case GRPC_OP_SEND_CLOSE_FROM_CLIENT: |
| /* Don't need to free any fields. */ |
| break; |
| case GRPC_OP_SEND_STATUS_FROM_SERVER: |
| /* Whenever we produce send-metadata, we allocate new strings (to handle |
| arbitrary sequence input as opposed to just lists or just tuples). We |
| thus must free those elements. */ |
| for (i = 0; i < op.data.send_status_from_server.trailing_metadata_count; |
| ++i) { |
| gpr_free( |
| (void *)op.data.send_status_from_server.trailing_metadata[i].key); |
| gpr_free( |
| (void *)op.data.send_status_from_server.trailing_metadata[i].value); |
| } |
| gpr_free(op.data.send_status_from_server.trailing_metadata); |
| gpr_free((char *)op.data.send_status_from_server.status_details); |
| break; |
| case GRPC_OP_RECV_INITIAL_METADATA: |
| grpc_metadata_array_destroy(op.data.recv_initial_metadata); |
| gpr_free(op.data.recv_initial_metadata); |
| break; |
| case GRPC_OP_RECV_MESSAGE: |
| grpc_byte_buffer_destroy(*op.data.recv_message); |
| gpr_free(op.data.recv_message); |
| break; |
| case GRPC_OP_RECV_STATUS_ON_CLIENT: |
| grpc_metadata_array_destroy(op.data.recv_status_on_client.trailing_metadata); |
| gpr_free(op.data.recv_status_on_client.trailing_metadata); |
| gpr_free(op.data.recv_status_on_client.status); |
| gpr_free(*op.data.recv_status_on_client.status_details); |
| gpr_free(op.data.recv_status_on_client.status_details); |
| gpr_free(op.data.recv_status_on_client.status_details_capacity); |
| break; |
| case GRPC_OP_RECV_CLOSE_ON_SERVER: |
| gpr_free(op.data.recv_close_on_server.cancelled); |
| break; |
| } |
| } |
| |
| PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops) { |
| static const int TYPE_INDEX = 0; |
| static const int INITIAL_METADATA_INDEX = 1; |
| static const int TRAILING_METADATA_INDEX = 2; |
| static const int MESSAGE_INDEX = 3; |
| static const int STATUS_INDEX = 4; |
| static const int CANCELLED_INDEX = 5; |
| static const int OPRESULT_LENGTH = 6; |
| PyObject *list; |
| size_t i; |
| size_t j; |
| char *bytes; |
| size_t bytes_size; |
| PyObject *results = PyList_New(nops); |
| if (!results) { |
| return NULL; |
| } |
| for (i = 0; i < nops; ++i) { |
| PyObject *result = PyTuple_Pack(OPRESULT_LENGTH, Py_None, Py_None, Py_None, |
| Py_None, Py_None, Py_None); |
| PyTuple_SetItem(result, TYPE_INDEX, PyInt_FromLong(op[i].op)); |
| switch(op[i].op) { |
| case GRPC_OP_RECV_INITIAL_METADATA: |
| PyTuple_SetItem(result, INITIAL_METADATA_INDEX, |
| list=PyList_New(op[i].data.recv_initial_metadata->count)); |
| for (j = 0; j < op[i].data.recv_initial_metadata->count; ++j) { |
| grpc_metadata md = op[i].data.recv_initial_metadata->metadata[j]; |
| PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value, |
| (Py_ssize_t)md.value_length)); |
| } |
| break; |
| case GRPC_OP_RECV_MESSAGE: |
| if (*op[i].data.recv_message) { |
| pygrpc_byte_buffer_to_bytes( |
| *op[i].data.recv_message, &bytes, &bytes_size); |
| PyTuple_SetItem(result, MESSAGE_INDEX, |
| PyString_FromStringAndSize(bytes, bytes_size)); |
| gpr_free(bytes); |
| } else { |
| PyTuple_SetItem(result, MESSAGE_INDEX, Py_BuildValue("")); |
| } |
| break; |
| case GRPC_OP_RECV_STATUS_ON_CLIENT: |
| PyTuple_SetItem( |
| result, TRAILING_METADATA_INDEX, |
| list = PyList_New(op[i].data.recv_status_on_client.trailing_metadata->count)); |
| for (j = 0; j < op[i].data.recv_status_on_client.trailing_metadata->count; ++j) { |
| grpc_metadata md = |
| op[i].data.recv_status_on_client.trailing_metadata->metadata[j]; |
| PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value, |
| (Py_ssize_t)md.value_length)); |
| } |
| PyTuple_SetItem( |
| result, STATUS_INDEX, Py_BuildValue( |
| "is", *op[i].data.recv_status_on_client.status, |
| *op[i].data.recv_status_on_client.status_details)); |
| break; |
| case GRPC_OP_RECV_CLOSE_ON_SERVER: |
| PyTuple_SetItem( |
| result, CANCELLED_INDEX, |
| PyBool_FromLong(*op[i].data.recv_close_on_server.cancelled)); |
| break; |
| default: |
| break; |
| } |
| pygrpc_discard_op(op[i]); |
| PyList_SetItem(results, i, result); |
| } |
| return results; |
| } |
| |
| double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) { |
| timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); |
| return timespec.tv_sec + 1e-9*timespec.tv_nsec; |
| } |
| |
| /* Because C89 doesn't have a way to check for infinity... */ |
| static int pygrpc_isinf(double x) { |
| return x * 0 != 0; |
| } |
| |
| gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) { |
| gpr_timespec result; |
| if (pygrpc_isinf(seconds)) { |
| result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME) |
| : gpr_inf_past(GPR_CLOCK_REALTIME); |
| } else { |
| result.tv_sec = (time_t)seconds; |
| result.tv_nsec = ((seconds - result.tv_sec) * 1e9); |
| result.clock_type = GPR_CLOCK_REALTIME; |
| } |
| return result; |
| } |
| |
| int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args) { |
| size_t num_args = PyList_Size(py_args); |
| size_t i; |
| grpc_channel_args args; |
| args.num_args = num_args; |
| args.args = gpr_malloc(sizeof(grpc_arg) * num_args); |
| for (i = 0; i < args.num_args; ++i) { |
| char *key; |
| PyObject *value; |
| if (!PyArg_ParseTuple(PyList_GetItem(py_args, i), "zO", &key, &value)) { |
| gpr_free(args.args); |
| args.num_args = 0; |
| args.args = NULL; |
| PyErr_SetString(PyExc_TypeError, |
| "expected a list of 2-tuple of str and str|int|None"); |
| return 0; |
| } |
| args.args[i].key = key; |
| if (PyInt_Check(value)) { |
| args.args[i].type = GRPC_ARG_INTEGER; |
| args.args[i].value.integer = PyInt_AsLong(value); |
| } else if (PyString_Check(value)) { |
| args.args[i].type = GRPC_ARG_STRING; |
| args.args[i].value.string = PyString_AsString(value); |
| } else if (value == Py_None) { |
| --args.num_args; |
| --i; |
| continue; |
| } else { |
| gpr_free(args.args); |
| args.num_args = 0; |
| args.args = NULL; |
| PyErr_SetString(PyExc_TypeError, |
| "expected a list of 2-tuple of str and str|int|None"); |
| return 0; |
| } |
| } |
| *c_args = args; |
| return 1; |
| } |
| |
| void pygrpc_discard_channel_args(grpc_channel_args args) { |
| gpr_free(args.args); |
| } |
| |
| int pygrpc_cast_pyseq_to_send_metadata( |
| PyObject *pyseq, grpc_metadata **metadata, size_t *count) { |
| size_t i; |
| Py_ssize_t value_length; |
| char *key; |
| char *value; |
| if (!PySequence_Check(pyseq)) { |
| return 0; |
| } |
| *count = PySequence_Size(pyseq); |
| *metadata = gpr_malloc(sizeof(grpc_metadata) * *count); |
| for (i = 0; i < *count; ++i) { |
| PyObject *item = PySequence_GetItem(pyseq, i); |
| if (!PyArg_ParseTuple(item, "ss#", &key, &value, &value_length)) { |
| Py_DECREF(item); |
| gpr_free(*metadata); |
| *count = 0; |
| *metadata = NULL; |
| return 0; |
| } else { |
| (*metadata)[i].key = gpr_strdup(key); |
| (*metadata)[i].value = gpr_malloc(value_length); |
| memcpy((void *)(*metadata)[i].value, value, value_length); |
| Py_DECREF(item); |
| } |
| (*metadata)[i].value_length = value_length; |
| } |
| return 1; |
| } |
| |
| PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) { |
| PyObject *result = PyTuple_New(metadata.count); |
| size_t i; |
| for (i = 0; i < metadata.count; ++i) { |
| PyTuple_SetItem( |
| result, i, Py_BuildValue( |
| "ss#", metadata.metadata[i].key, metadata.metadata[i].value, |
| (Py_ssize_t)metadata.metadata[i].value_length)); |
| if (PyErr_Occurred()) { |
| Py_DECREF(result); |
| return NULL; |
| } |
| } |
| return result; |
| } |
| |
| void pygrpc_byte_buffer_to_bytes( |
| grpc_byte_buffer *buffer, char **result, size_t *result_size) { |
| grpc_byte_buffer_reader reader; |
| gpr_slice slice; |
| char *read_result = NULL; |
| size_t size = 0; |
| grpc_byte_buffer_reader_init(&reader, buffer); |
| while (grpc_byte_buffer_reader_next(&reader, &slice)) { |
| read_result = gpr_realloc(read_result, size + GPR_SLICE_LENGTH(slice)); |
| memcpy(read_result + size, GPR_SLICE_START_PTR(slice), |
| GPR_SLICE_LENGTH(slice)); |
| size = size + GPR_SLICE_LENGTH(slice); |
| gpr_slice_unref(slice); |
| } |
| *result_size = size; |
| *result = read_result; |
| } |