dc690a6a608abd649e110ad9a8d68020c052436c
[platform/upstream/grpc.git] / src / csharp / ext / grpc_csharp_ext.c
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * 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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * 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.
16  *
17  */
18
19 #include <grpc/byte_buffer_reader.h>
20 #include <grpc/grpc.h>
21 #include <grpc/grpc_security.h>
22 #include <grpc/slice.h>
23 #include <grpc/support/alloc.h>
24 #include <grpc/support/log.h>
25 #include <grpc/support/port_platform.h>
26 #include <grpc/support/string_util.h>
27 #include <grpc/support/thd_id.h>
28
29 #include <string.h>
30
31 #ifdef GPR_WINDOWS
32 #define GPR_EXPORT __declspec(dllexport)
33 #define GPR_CALLTYPE __stdcall
34 #endif
35
36 #ifndef GPR_EXPORT
37 #define GPR_EXPORT
38 #endif
39
40 #ifndef GPR_CALLTYPE
41 #define GPR_CALLTYPE
42 #endif
43
44 grpc_byte_buffer* string_to_byte_buffer(const char* buffer, size_t len) {
45   grpc_slice slice = grpc_slice_from_copied_buffer(buffer, len);
46   grpc_byte_buffer* bb = grpc_raw_byte_buffer_create(&slice, 1);
47   grpc_slice_unref(slice);
48   return bb;
49 }
50
51 /*
52  * Helper to maintain lifetime of batch op inputs and store batch op outputs.
53  */
54 typedef struct grpcsharp_batch_context {
55   grpc_metadata_array send_initial_metadata;
56   grpc_byte_buffer* send_message;
57   struct {
58     grpc_metadata_array trailing_metadata;
59   } send_status_from_server;
60   grpc_metadata_array recv_initial_metadata;
61   grpc_byte_buffer* recv_message;
62   struct {
63     grpc_metadata_array trailing_metadata;
64     grpc_status_code status;
65     grpc_slice status_details;
66   } recv_status_on_client;
67   int recv_close_on_server_cancelled;
68 } grpcsharp_batch_context;
69
70 GPR_EXPORT grpcsharp_batch_context* GPR_CALLTYPE
71 grpcsharp_batch_context_create() {
72   grpcsharp_batch_context* ctx = gpr_malloc(sizeof(grpcsharp_batch_context));
73   memset(ctx, 0, sizeof(grpcsharp_batch_context));
74   return ctx;
75 }
76
77 typedef struct {
78   grpc_call* call;
79   grpc_call_details call_details;
80   grpc_metadata_array request_metadata;
81 } grpcsharp_request_call_context;
82
83 GPR_EXPORT grpcsharp_request_call_context* GPR_CALLTYPE
84 grpcsharp_request_call_context_create() {
85   grpcsharp_request_call_context* ctx =
86       gpr_malloc(sizeof(grpcsharp_request_call_context));
87   memset(ctx, 0, sizeof(grpcsharp_request_call_context));
88   return ctx;
89 }
90
91 /*
92  * Destroys array->metadata.
93  * The array pointer itself is not freed.
94  */
95 void grpcsharp_metadata_array_destroy_metadata_only(
96     grpc_metadata_array* array) {
97   gpr_free(array->metadata);
98 }
99
100 /*
101  * Destroys keys, values and array->metadata.
102  * The array pointer itself is not freed.
103  */
104 void grpcsharp_metadata_array_destroy_metadata_including_entries(
105     grpc_metadata_array* array) {
106   size_t i;
107   if (array->metadata) {
108     for (i = 0; i < array->count; i++) {
109       grpc_slice_unref(array->metadata[i].key);
110       grpc_slice_unref(array->metadata[i].value);
111     }
112   }
113   gpr_free(array->metadata);
114 }
115
116 /*
117  * Fully destroys the metadata array.
118  */
119 GPR_EXPORT void GPR_CALLTYPE
120 grpcsharp_metadata_array_destroy_full(grpc_metadata_array* array) {
121   if (!array) {
122     return;
123   }
124   grpcsharp_metadata_array_destroy_metadata_including_entries(array);
125   gpr_free(array);
126 }
127
128 /*
129  * Creates an empty metadata array with given capacity.
130  * Array can later be destroyed by grpc_metadata_array_destroy_full.
131  */
132 GPR_EXPORT grpc_metadata_array* GPR_CALLTYPE
133 grpcsharp_metadata_array_create(size_t capacity) {
134   grpc_metadata_array* array =
135       (grpc_metadata_array*)gpr_malloc(sizeof(grpc_metadata_array));
136   grpc_metadata_array_init(array);
137   array->capacity = capacity;
138   array->count = 0;
139   if (capacity > 0) {
140     array->metadata =
141         (grpc_metadata*)gpr_malloc(sizeof(grpc_metadata) * capacity);
142     memset(array->metadata, 0, sizeof(grpc_metadata) * capacity);
143   } else {
144     array->metadata = NULL;
145   }
146   return array;
147 }
148
149 GPR_EXPORT void GPR_CALLTYPE
150 grpcsharp_metadata_array_add(grpc_metadata_array* array, const char* key,
151                              const char* value, size_t value_length) {
152   size_t i = array->count;
153   GPR_ASSERT(array->count < array->capacity);
154   array->metadata[i].key = grpc_slice_from_copied_string(key);
155   array->metadata[i].value = grpc_slice_from_copied_buffer(value, value_length);
156   array->count++;
157 }
158
159 GPR_EXPORT intptr_t GPR_CALLTYPE
160 grpcsharp_metadata_array_count(grpc_metadata_array* array) {
161   return (intptr_t)array->count;
162 }
163
164 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_metadata_array_get_key(
165     grpc_metadata_array* array, size_t index, size_t* key_length) {
166   GPR_ASSERT(index < array->count);
167   *key_length = GRPC_SLICE_LENGTH(array->metadata[index].key);
168   return (char*)GRPC_SLICE_START_PTR(array->metadata[index].key);
169 }
170
171 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_metadata_array_get_value(
172     grpc_metadata_array* array, size_t index, size_t* value_length) {
173   GPR_ASSERT(index < array->count);
174   *value_length = GRPC_SLICE_LENGTH(array->metadata[index].value);
175   return (char*)GRPC_SLICE_START_PTR(array->metadata[index].value);
176 }
177
178 /* Move contents of metadata array */
179 void grpcsharp_metadata_array_move(grpc_metadata_array* dest,
180                                    grpc_metadata_array* src) {
181   if (!src) {
182     dest->capacity = 0;
183     dest->count = 0;
184     dest->metadata = NULL;
185     return;
186   }
187
188   dest->capacity = src->capacity;
189   dest->count = src->count;
190   dest->metadata = src->metadata;
191
192   src->capacity = 0;
193   src->count = 0;
194   src->metadata = NULL;
195 }
196
197 GPR_EXPORT void GPR_CALLTYPE
198 grpcsharp_batch_context_reset(grpcsharp_batch_context* ctx) {
199   grpcsharp_metadata_array_destroy_metadata_including_entries(
200       &(ctx->send_initial_metadata));
201
202   grpc_byte_buffer_destroy(ctx->send_message);
203
204   grpcsharp_metadata_array_destroy_metadata_including_entries(
205       &(ctx->send_status_from_server.trailing_metadata));
206
207   grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata));
208
209   grpc_byte_buffer_destroy(ctx->recv_message);
210
211   grpcsharp_metadata_array_destroy_metadata_only(
212       &(ctx->recv_status_on_client.trailing_metadata));
213   grpc_slice_unref(ctx->recv_status_on_client.status_details);
214   memset(ctx, 0, sizeof(grpcsharp_batch_context));
215 }
216
217 GPR_EXPORT void GPR_CALLTYPE
218 grpcsharp_batch_context_destroy(grpcsharp_batch_context* ctx) {
219   if (!ctx) {
220     return;
221   }
222   grpcsharp_batch_context_reset(ctx);
223   gpr_free(ctx);
224 }
225
226 GPR_EXPORT void GPR_CALLTYPE
227 grpcsharp_request_call_context_reset(grpcsharp_request_call_context* ctx) {
228   /* NOTE: ctx->server_rpc_new.call is not destroyed because callback handler is
229      supposed
230      to take its ownership. */
231
232   grpc_call_details_destroy(&(ctx->call_details));
233   grpcsharp_metadata_array_destroy_metadata_only(&(ctx->request_metadata));
234   memset(ctx, 0, sizeof(grpcsharp_request_call_context));
235 }
236
237 GPR_EXPORT void GPR_CALLTYPE
238 grpcsharp_request_call_context_destroy(grpcsharp_request_call_context* ctx) {
239   if (!ctx) {
240     return;
241   }
242   grpcsharp_request_call_context_reset(ctx);
243   gpr_free(ctx);
244 }
245
246 GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE
247 grpcsharp_batch_context_recv_initial_metadata(
248     const grpcsharp_batch_context* ctx) {
249   return &(ctx->recv_initial_metadata);
250 }
251
252 GPR_EXPORT intptr_t GPR_CALLTYPE grpcsharp_batch_context_recv_message_length(
253     const grpcsharp_batch_context* ctx) {
254   grpc_byte_buffer_reader reader;
255   if (!ctx->recv_message) {
256     return -1;
257   }
258
259   GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, ctx->recv_message));
260   intptr_t result = (intptr_t)grpc_byte_buffer_length(reader.buffer_out);
261   grpc_byte_buffer_reader_destroy(&reader);
262
263   return result;
264 }
265
266 /*
267  * Copies data from recv_message to a buffer. Fatal error occurs if
268  * buffer is too small.
269  */
270 GPR_EXPORT void GPR_CALLTYPE grpcsharp_batch_context_recv_message_to_buffer(
271     const grpcsharp_batch_context* ctx, char* buffer, size_t buffer_len) {
272   grpc_byte_buffer_reader reader;
273   grpc_slice slice;
274   size_t offset = 0;
275
276   GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, ctx->recv_message));
277
278   while (grpc_byte_buffer_reader_next(&reader, &slice)) {
279     size_t len = GRPC_SLICE_LENGTH(slice);
280     GPR_ASSERT(offset + len <= buffer_len);
281     memcpy(buffer + offset, GRPC_SLICE_START_PTR(slice),
282            GRPC_SLICE_LENGTH(slice));
283     offset += len;
284     grpc_slice_unref(slice);
285   }
286
287   grpc_byte_buffer_reader_destroy(&reader);
288 }
289
290 GPR_EXPORT grpc_status_code GPR_CALLTYPE
291 grpcsharp_batch_context_recv_status_on_client_status(
292     const grpcsharp_batch_context* ctx) {
293   return ctx->recv_status_on_client.status;
294 }
295
296 GPR_EXPORT const char* GPR_CALLTYPE
297 grpcsharp_batch_context_recv_status_on_client_details(
298     const grpcsharp_batch_context* ctx, size_t* details_length) {
299   *details_length =
300       GRPC_SLICE_LENGTH(ctx->recv_status_on_client.status_details);
301   return (char*)GRPC_SLICE_START_PTR(ctx->recv_status_on_client.status_details);
302 }
303
304 GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE
305 grpcsharp_batch_context_recv_status_on_client_trailing_metadata(
306     const grpcsharp_batch_context* ctx) {
307   return &(ctx->recv_status_on_client.trailing_metadata);
308 }
309
310 GPR_EXPORT grpc_call* GPR_CALLTYPE
311 grpcsharp_request_call_context_call(const grpcsharp_request_call_context* ctx) {
312   return ctx->call;
313 }
314
315 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_request_call_context_method(
316     const grpcsharp_request_call_context* ctx, size_t* method_length) {
317   *method_length = GRPC_SLICE_LENGTH(ctx->call_details.method);
318   return (char*)GRPC_SLICE_START_PTR(ctx->call_details.method);
319 }
320
321 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_request_call_context_host(
322     const grpcsharp_request_call_context* ctx, size_t* host_length) {
323   *host_length = GRPC_SLICE_LENGTH(ctx->call_details.host);
324   return (char*)GRPC_SLICE_START_PTR(ctx->call_details.host);
325 }
326
327 GPR_EXPORT gpr_timespec GPR_CALLTYPE grpcsharp_request_call_context_deadline(
328     const grpcsharp_request_call_context* ctx) {
329   return ctx->call_details.deadline;
330 }
331
332 GPR_EXPORT const grpc_metadata_array* GPR_CALLTYPE
333 grpcsharp_request_call_context_request_metadata(
334     const grpcsharp_request_call_context* ctx) {
335   return &(ctx->request_metadata);
336 }
337
338 GPR_EXPORT int32_t GPR_CALLTYPE
339 grpcsharp_batch_context_recv_close_on_server_cancelled(
340     const grpcsharp_batch_context* ctx) {
341   return (int32_t)ctx->recv_close_on_server_cancelled;
342 }
343
344 /* Init & shutdown */
345
346 GPR_EXPORT void GPR_CALLTYPE grpcsharp_init(void) { grpc_init(); }
347
348 GPR_EXPORT void GPR_CALLTYPE grpcsharp_shutdown(void) { grpc_shutdown(); }
349
350 /* Completion queue */
351
352 GPR_EXPORT grpc_completion_queue* GPR_CALLTYPE
353 grpcsharp_completion_queue_create_async(void) {
354   return grpc_completion_queue_create_for_next(NULL);
355 }
356
357 GPR_EXPORT grpc_completion_queue* GPR_CALLTYPE
358 grpcsharp_completion_queue_create_sync(void) {
359   return grpc_completion_queue_create_for_pluck(NULL);
360 }
361
362 GPR_EXPORT void GPR_CALLTYPE
363 grpcsharp_completion_queue_shutdown(grpc_completion_queue* cq) {
364   grpc_completion_queue_shutdown(cq);
365 }
366
367 GPR_EXPORT void GPR_CALLTYPE
368 grpcsharp_completion_queue_destroy(grpc_completion_queue* cq) {
369   grpc_completion_queue_destroy(cq);
370 }
371
372 GPR_EXPORT grpc_event GPR_CALLTYPE
373 grpcsharp_completion_queue_next(grpc_completion_queue* cq) {
374   return grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME),
375                                     NULL);
376 }
377
378 GPR_EXPORT grpc_event GPR_CALLTYPE
379 grpcsharp_completion_queue_pluck(grpc_completion_queue* cq, void* tag) {
380   return grpc_completion_queue_pluck(cq, tag,
381                                      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
382 }
383
384 /* Channel */
385
386 GPR_EXPORT grpc_channel* GPR_CALLTYPE
387
388 grpcsharp_insecure_channel_create(const char* target,
389                                   const grpc_channel_args* args) {
390   return grpc_insecure_channel_create(target, args, NULL);
391 }
392
393 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_destroy(grpc_channel* channel) {
394   grpc_channel_destroy(channel);
395 }
396
397 GPR_EXPORT grpc_call* GPR_CALLTYPE grpcsharp_channel_create_call(
398     grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask,
399     grpc_completion_queue* cq, const char* method, const char* host,
400     gpr_timespec deadline) {
401   grpc_slice method_slice = grpc_slice_from_copied_string(method);
402   grpc_slice* host_slice_ptr = NULL;
403   grpc_slice host_slice;
404   if (host != NULL) {
405     host_slice = grpc_slice_from_copied_string(host);
406     host_slice_ptr = &host_slice;
407   }
408   grpc_call* ret =
409       grpc_channel_create_call(channel, parent_call, propagation_mask, cq,
410                                method_slice, host_slice_ptr, deadline, NULL);
411   grpc_slice_unref(method_slice);
412   if (host != NULL) {
413     grpc_slice_unref(host_slice);
414   }
415   return ret;
416 }
417
418 GPR_EXPORT grpc_connectivity_state GPR_CALLTYPE
419 grpcsharp_channel_check_connectivity_state(grpc_channel* channel,
420                                            int32_t try_to_connect) {
421   return grpc_channel_check_connectivity_state(channel, try_to_connect);
422 }
423
424 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_watch_connectivity_state(
425     grpc_channel* channel, grpc_connectivity_state last_observed_state,
426     gpr_timespec deadline, grpc_completion_queue* cq,
427     grpcsharp_batch_context* ctx) {
428   grpc_channel_watch_connectivity_state(channel, last_observed_state, deadline,
429                                         cq, ctx);
430 }
431
432 GPR_EXPORT char* GPR_CALLTYPE
433 grpcsharp_channel_get_target(grpc_channel* channel) {
434   return grpc_channel_get_target(channel);
435 }
436
437 /* Channel args */
438
439 GPR_EXPORT grpc_channel_args* GPR_CALLTYPE
440 grpcsharp_channel_args_create(size_t num_args) {
441   grpc_channel_args* args =
442       (grpc_channel_args*)gpr_malloc(sizeof(grpc_channel_args));
443   memset(args, 0, sizeof(grpc_channel_args));
444
445   args->num_args = num_args;
446   args->args = (grpc_arg*)gpr_malloc(sizeof(grpc_arg) * num_args);
447   memset(args->args, 0, sizeof(grpc_arg) * num_args);
448   return args;
449 }
450
451 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_args_set_string(
452     grpc_channel_args* args, size_t index, const char* key, const char* value) {
453   GPR_ASSERT(args);
454   GPR_ASSERT(index < args->num_args);
455   args->args[index].type = GRPC_ARG_STRING;
456   args->args[index].key = gpr_strdup(key);
457   args->args[index].value.string = gpr_strdup(value);
458 }
459
460 GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_args_set_integer(
461     grpc_channel_args* args, size_t index, const char* key, int value) {
462   GPR_ASSERT(args);
463   GPR_ASSERT(index < args->num_args);
464   args->args[index].type = GRPC_ARG_INTEGER;
465   args->args[index].key = gpr_strdup(key);
466   args->args[index].value.integer = value;
467 }
468
469 GPR_EXPORT void GPR_CALLTYPE
470 grpcsharp_channel_args_destroy(grpc_channel_args* args) {
471   size_t i;
472   if (args) {
473     for (i = 0; i < args->num_args; i++) {
474       gpr_free(args->args[i].key);
475       if (args->args[i].type == GRPC_ARG_STRING) {
476         gpr_free(args->args[i].value.string);
477       }
478     }
479     gpr_free(args->args);
480     gpr_free(args);
481   }
482 }
483
484 /* Timespec */
485
486 GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(gpr_clock_type clock_type) {
487   return gpr_now(clock_type);
488 }
489
490 GPR_EXPORT gpr_timespec GPR_CALLTYPE
491 gprsharp_inf_future(gpr_clock_type clock_type) {
492   return gpr_inf_future(clock_type);
493 }
494
495 GPR_EXPORT gpr_timespec GPR_CALLTYPE
496 gprsharp_inf_past(gpr_clock_type clock_type) {
497   return gpr_inf_past(clock_type);
498 }
499
500 GPR_EXPORT gpr_timespec GPR_CALLTYPE
501 gprsharp_convert_clock_type(gpr_timespec t, gpr_clock_type target_clock) {
502   return gpr_convert_clock_type(t, target_clock);
503 }
504
505 GPR_EXPORT int32_t GPR_CALLTYPE gprsharp_sizeof_timespec(void) {
506   return sizeof(gpr_timespec);
507 }
508
509 /* Call */
510
511 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_cancel(grpc_call* call) {
512   return grpc_call_cancel(call, NULL);
513 }
514
515 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_cancel_with_status(
516     grpc_call* call, grpc_status_code status, const char* description) {
517   return grpc_call_cancel_with_status(call, status, description, NULL);
518 }
519
520 GPR_EXPORT char* GPR_CALLTYPE grpcsharp_call_get_peer(grpc_call* call) {
521   return grpc_call_get_peer(call);
522 }
523
524 GPR_EXPORT void GPR_CALLTYPE gprsharp_free(void* p) { gpr_free(p); }
525
526 GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call* call) {
527   grpc_call_unref(call);
528 }
529
530 typedef grpc_call_error (*grpcsharp_call_start_batch_func)(grpc_call* call,
531                                                            const grpc_op* ops,
532                                                            size_t nops,
533                                                            void* tag,
534                                                            void* reserved);
535
536 /* Only for testing */
537 static grpc_call_error grpcsharp_call_start_batch_nop(grpc_call* call,
538                                                       const grpc_op* ops,
539                                                       size_t nops, void* tag,
540                                                       void* reserved) {
541   return GRPC_CALL_OK;
542 }
543
544 static grpc_call_error grpcsharp_call_start_batch_default(grpc_call* call,
545                                                           const grpc_op* ops,
546                                                           size_t nops,
547                                                           void* tag,
548                                                           void* reserved) {
549   return grpc_call_start_batch(call, ops, nops, tag, reserved);
550 }
551
552 static grpcsharp_call_start_batch_func g_call_start_batch_func =
553     grpcsharp_call_start_batch_default;
554
555 static grpc_call_error grpcsharp_call_start_batch(grpc_call* call,
556                                                   const grpc_op* ops,
557                                                   size_t nops, void* tag,
558                                                   void* reserved) {
559   return g_call_start_batch_func(call, ops, nops, tag, reserved);
560 }
561
562 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary(
563     grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer,
564     size_t send_buffer_len, uint32_t write_flags,
565     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
566   /* TODO: don't use magic number */
567   grpc_op ops[6];
568   memset(ops, 0, sizeof(ops));
569   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
570   grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
571                                 initial_metadata);
572   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
573   ops[0].data.send_initial_metadata.metadata =
574       ctx->send_initial_metadata.metadata;
575   ops[0].flags = initial_metadata_flags;
576   ops[0].reserved = NULL;
577
578   ops[1].op = GRPC_OP_SEND_MESSAGE;
579   ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
580   ops[1].data.send_message.send_message = ctx->send_message;
581   ops[1].flags = write_flags;
582   ops[1].reserved = NULL;
583
584   ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
585   ops[2].flags = 0;
586   ops[2].reserved = NULL;
587
588   ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;
589   ops[3].data.recv_initial_metadata.recv_initial_metadata =
590       &(ctx->recv_initial_metadata);
591   ops[3].flags = 0;
592   ops[3].reserved = NULL;
593
594   ops[4].op = GRPC_OP_RECV_MESSAGE;
595   ops[4].data.recv_message.recv_message = &(ctx->recv_message);
596   ops[4].flags = 0;
597   ops[4].reserved = NULL;
598
599   ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
600   ops[5].data.recv_status_on_client.trailing_metadata =
601       &(ctx->recv_status_on_client.trailing_metadata);
602   ops[5].data.recv_status_on_client.status =
603       &(ctx->recv_status_on_client.status);
604   ops[5].data.recv_status_on_client.status_details =
605       &(ctx->recv_status_on_client.status_details);
606   ops[5].flags = 0;
607   ops[5].reserved = NULL;
608
609   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
610                                     ctx, NULL);
611 }
612
613 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_client_streaming(
614     grpc_call* call, grpcsharp_batch_context* ctx,
615     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
616   /* TODO: don't use magic number */
617   grpc_op ops[4];
618   memset(ops, 0, sizeof(ops));
619   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
620   grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
621                                 initial_metadata);
622   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
623   ops[0].data.send_initial_metadata.metadata =
624       ctx->send_initial_metadata.metadata;
625   ops[0].flags = initial_metadata_flags;
626   ops[0].reserved = NULL;
627
628   ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
629   ops[1].data.recv_initial_metadata.recv_initial_metadata =
630       &(ctx->recv_initial_metadata);
631   ops[1].flags = 0;
632   ops[1].reserved = NULL;
633
634   ops[2].op = GRPC_OP_RECV_MESSAGE;
635   ops[2].data.recv_message.recv_message = &(ctx->recv_message);
636   ops[2].flags = 0;
637   ops[2].reserved = NULL;
638
639   ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
640   ops[3].data.recv_status_on_client.trailing_metadata =
641       &(ctx->recv_status_on_client.trailing_metadata);
642   ops[3].data.recv_status_on_client.status =
643       &(ctx->recv_status_on_client.status);
644   ops[3].data.recv_status_on_client.status_details =
645       &(ctx->recv_status_on_client.status_details);
646   ops[3].flags = 0;
647   ops[3].reserved = NULL;
648
649   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
650                                     ctx, NULL);
651 }
652
653 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
654     grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer,
655     size_t send_buffer_len, uint32_t write_flags,
656     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
657   /* TODO: don't use magic number */
658   grpc_op ops[4];
659   memset(ops, 0, sizeof(ops));
660   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
661   grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
662                                 initial_metadata);
663   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
664   ops[0].data.send_initial_metadata.metadata =
665       ctx->send_initial_metadata.metadata;
666   ops[0].flags = initial_metadata_flags;
667   ops[0].reserved = NULL;
668
669   ops[1].op = GRPC_OP_SEND_MESSAGE;
670   ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
671   ops[1].data.send_message.send_message = ctx->send_message;
672   ops[1].flags = write_flags;
673   ops[1].reserved = NULL;
674
675   ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
676   ops[2].flags = 0;
677   ops[2].reserved = NULL;
678
679   ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
680   ops[3].data.recv_status_on_client.trailing_metadata =
681       &(ctx->recv_status_on_client.trailing_metadata);
682   ops[3].data.recv_status_on_client.status =
683       &(ctx->recv_status_on_client.status);
684   ops[3].data.recv_status_on_client.status_details =
685       &(ctx->recv_status_on_client.status_details);
686   ops[3].flags = 0;
687   ops[3].reserved = NULL;
688
689   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
690                                     ctx, NULL);
691 }
692
693 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_duplex_streaming(
694     grpc_call* call, grpcsharp_batch_context* ctx,
695     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
696   /* TODO: don't use magic number */
697   grpc_op ops[2];
698   memset(ops, 0, sizeof(ops));
699   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
700   grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
701                                 initial_metadata);
702   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
703   ops[0].data.send_initial_metadata.metadata =
704       ctx->send_initial_metadata.metadata;
705   ops[0].flags = initial_metadata_flags;
706   ops[0].reserved = NULL;
707
708   ops[1].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
709   ops[1].data.recv_status_on_client.trailing_metadata =
710       &(ctx->recv_status_on_client.trailing_metadata);
711   ops[1].data.recv_status_on_client.status =
712       &(ctx->recv_status_on_client.status);
713   ops[1].data.recv_status_on_client.status_details =
714       &(ctx->recv_status_on_client.status_details);
715   ops[1].flags = 0;
716   ops[1].reserved = NULL;
717
718   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
719                                     ctx, NULL);
720 }
721
722 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_recv_initial_metadata(
723     grpc_call* call, grpcsharp_batch_context* ctx) {
724   /* TODO: don't use magic number */
725   grpc_op ops[1];
726   ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
727   ops[0].data.recv_initial_metadata.recv_initial_metadata =
728       &(ctx->recv_initial_metadata);
729   ops[0].flags = 0;
730   ops[0].reserved = NULL;
731
732   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
733                                     ctx, NULL);
734 }
735
736 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_message(
737     grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer,
738     size_t send_buffer_len, uint32_t write_flags,
739     int32_t send_empty_initial_metadata) {
740   /* TODO: don't use magic number */
741   grpc_op ops[2];
742   memset(ops, 0, sizeof(ops));
743   size_t nops = send_empty_initial_metadata ? 2 : 1;
744   ops[0].op = GRPC_OP_SEND_MESSAGE;
745   ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
746   ops[0].data.send_message.send_message = ctx->send_message;
747   ops[0].flags = write_flags;
748   ops[0].reserved = NULL;
749   ops[1].op = GRPC_OP_SEND_INITIAL_METADATA;
750   ops[1].flags = 0;
751   ops[1].reserved = NULL;
752
753   return grpcsharp_call_start_batch(call, ops, nops, ctx, NULL);
754 }
755
756 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_close_from_client(
757     grpc_call* call, grpcsharp_batch_context* ctx) {
758   /* TODO: don't use magic number */
759   grpc_op ops[1];
760   ops[0].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
761   ops[0].flags = 0;
762   ops[0].reserved = NULL;
763
764   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
765                                     ctx, NULL);
766 }
767
768 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server(
769     grpc_call* call, grpcsharp_batch_context* ctx, grpc_status_code status_code,
770     const char* status_details, size_t status_details_len,
771     grpc_metadata_array* trailing_metadata, int32_t send_empty_initial_metadata,
772     const char* optional_send_buffer, size_t optional_send_buffer_len,
773     uint32_t write_flags) {
774   /* TODO: don't use magic number */
775   grpc_op ops[3];
776   memset(ops, 0, sizeof(ops));
777   size_t nops = 1;
778   grpc_slice status_details_slice =
779       grpc_slice_from_copied_buffer(status_details, status_details_len);
780   ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
781   ops[0].data.send_status_from_server.status = status_code;
782   ops[0].data.send_status_from_server.status_details = &status_details_slice;
783   grpcsharp_metadata_array_move(
784       &(ctx->send_status_from_server.trailing_metadata), trailing_metadata);
785   ops[0].data.send_status_from_server.trailing_metadata_count =
786       ctx->send_status_from_server.trailing_metadata.count;
787   ops[0].data.send_status_from_server.trailing_metadata =
788       ctx->send_status_from_server.trailing_metadata.metadata;
789   ops[0].flags = 0;
790   ops[0].reserved = NULL;
791   if (optional_send_buffer) {
792     ops[nops].op = GRPC_OP_SEND_MESSAGE;
793     ctx->send_message =
794         string_to_byte_buffer(optional_send_buffer, optional_send_buffer_len);
795     ops[nops].data.send_message.send_message = ctx->send_message;
796     ops[nops].flags = write_flags;
797     ops[nops].reserved = NULL;
798     nops++;
799   }
800   if (send_empty_initial_metadata) {
801     ops[nops].op = GRPC_OP_SEND_INITIAL_METADATA;
802     ops[nops].flags = 0;
803     ops[nops].reserved = NULL;
804     nops++;
805   }
806   grpc_call_error ret = grpcsharp_call_start_batch(call, ops, nops, ctx, NULL);
807   grpc_slice_unref(status_details_slice);
808   return ret;
809 }
810
811 GPR_EXPORT grpc_call_error GPR_CALLTYPE
812 grpcsharp_call_recv_message(grpc_call* call, grpcsharp_batch_context* ctx) {
813   /* TODO: don't use magic number */
814   grpc_op ops[1];
815   ops[0].op = GRPC_OP_RECV_MESSAGE;
816   ops[0].data.recv_message.recv_message = &(ctx->recv_message);
817   ops[0].flags = 0;
818   ops[0].reserved = NULL;
819   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
820                                     ctx, NULL);
821 }
822
823 GPR_EXPORT grpc_call_error GPR_CALLTYPE
824 grpcsharp_call_start_serverside(grpc_call* call, grpcsharp_batch_context* ctx) {
825   /* TODO: don't use magic number */
826   grpc_op ops[1];
827   ops[0].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
828   ops[0].data.recv_close_on_server.cancelled =
829       (&ctx->recv_close_on_server_cancelled);
830   ops[0].flags = 0;
831   ops[0].reserved = NULL;
832
833   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
834                                     ctx, NULL);
835 }
836
837 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_initial_metadata(
838     grpc_call* call, grpcsharp_batch_context* ctx,
839     grpc_metadata_array* initial_metadata) {
840   /* TODO: don't use magic number */
841   grpc_op ops[1];
842   memset(ops, 0, sizeof(ops));
843   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
844   grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
845                                 initial_metadata);
846   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
847   ops[0].data.send_initial_metadata.metadata =
848       ctx->send_initial_metadata.metadata;
849   ops[0].flags = 0;
850   ops[0].reserved = NULL;
851
852   return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]),
853                                     ctx, NULL);
854 }
855
856 GPR_EXPORT grpc_call_error GPR_CALLTYPE
857 grpcsharp_call_set_credentials(grpc_call* call, grpc_call_credentials* creds) {
858   return grpc_call_set_credentials(call, creds);
859 }
860
861 /* Server */
862
863 GPR_EXPORT grpc_server* GPR_CALLTYPE
864 grpcsharp_server_create(const grpc_channel_args* args) {
865   return grpc_server_create(args, NULL);
866 }
867
868 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_register_completion_queue(
869     grpc_server* server, grpc_completion_queue* cq) {
870   grpc_server_register_completion_queue(server, cq, NULL);
871 }
872
873 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_server_add_insecure_http2_port(
874     grpc_server* server, const char* addr) {
875   return grpc_server_add_insecure_http2_port(server, addr);
876 }
877
878 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server* server) {
879   grpc_server_start(server);
880 }
881
882 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_shutdown_and_notify_callback(
883     grpc_server* server, grpc_completion_queue* cq,
884     grpcsharp_batch_context* ctx) {
885   grpc_server_shutdown_and_notify(server, cq, ctx);
886 }
887
888 GPR_EXPORT void GPR_CALLTYPE
889 grpcsharp_server_cancel_all_calls(grpc_server* server) {
890   grpc_server_cancel_all_calls(server);
891 }
892
893 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server* server) {
894   grpc_server_destroy(server);
895 }
896
897 GPR_EXPORT grpc_call_error GPR_CALLTYPE
898 grpcsharp_server_request_call(grpc_server* server, grpc_completion_queue* cq,
899                               grpcsharp_request_call_context* ctx) {
900   return grpc_server_request_call(server, &(ctx->call), &(ctx->call_details),
901                                   &(ctx->request_metadata), cq, cq, ctx);
902 }
903
904 /* Security */
905
906 static char* default_pem_root_certs = NULL;
907
908 static grpc_ssl_roots_override_result override_ssl_roots_handler(
909     char** pem_root_certs) {
910   if (!default_pem_root_certs) {
911     *pem_root_certs = NULL;
912     return GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY;
913   }
914   *pem_root_certs = gpr_strdup(default_pem_root_certs);
915   return GRPC_SSL_ROOTS_OVERRIDE_OK;
916 }
917
918 GPR_EXPORT void GPR_CALLTYPE
919 grpcsharp_override_default_ssl_roots(const char* pem_root_certs) {
920   /*
921    * This currently wastes ~300kB of memory by keeping a copy of roots
922    * in a static variable, but for desktop/server use, the overhead
923    * is negligible. In the future, we might want to change the behavior
924    * for mobile (e.g. Xamarin).
925    */
926   default_pem_root_certs = gpr_strdup(pem_root_certs);
927   grpc_set_ssl_roots_override_callback(override_ssl_roots_handler);
928 }
929
930 GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE
931 grpcsharp_ssl_credentials_create(const char* pem_root_certs,
932                                  const char* key_cert_pair_cert_chain,
933                                  const char* key_cert_pair_private_key) {
934   grpc_ssl_pem_key_cert_pair key_cert_pair;
935   if (key_cert_pair_cert_chain || key_cert_pair_private_key) {
936     key_cert_pair.cert_chain = key_cert_pair_cert_chain;
937     key_cert_pair.private_key = key_cert_pair_private_key;
938     return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, NULL,
939                                        NULL);
940   } else {
941     GPR_ASSERT(!key_cert_pair_cert_chain);
942     GPR_ASSERT(!key_cert_pair_private_key);
943     return grpc_ssl_credentials_create(pem_root_certs, NULL, NULL, NULL);
944   }
945 }
946
947 GPR_EXPORT void GPR_CALLTYPE
948 grpcsharp_channel_credentials_release(grpc_channel_credentials* creds) {
949   grpc_channel_credentials_release(creds);
950 }
951
952 GPR_EXPORT void GPR_CALLTYPE
953 grpcsharp_call_credentials_release(grpc_call_credentials* creds) {
954   grpc_call_credentials_release(creds);
955 }
956
957 GPR_EXPORT grpc_channel* GPR_CALLTYPE grpcsharp_secure_channel_create(
958     grpc_channel_credentials* creds, const char* target,
959     const grpc_channel_args* args) {
960   return grpc_secure_channel_create(creds, target, args, NULL);
961 }
962
963 GPR_EXPORT grpc_server_credentials* GPR_CALLTYPE
964 grpcsharp_ssl_server_credentials_create(
965     const char* pem_root_certs, const char** key_cert_pair_cert_chain_array,
966     const char** key_cert_pair_private_key_array, size_t num_key_cert_pairs,
967     grpc_ssl_client_certificate_request_type client_request_type) {
968   size_t i;
969   grpc_server_credentials* creds;
970   grpc_ssl_pem_key_cert_pair* key_cert_pairs =
971       gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs);
972   memset(key_cert_pairs, 0,
973          sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs);
974
975   for (i = 0; i < num_key_cert_pairs; i++) {
976     if (key_cert_pair_cert_chain_array[i] ||
977         key_cert_pair_private_key_array[i]) {
978       key_cert_pairs[i].cert_chain = key_cert_pair_cert_chain_array[i];
979       key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i];
980     }
981   }
982   creds = grpc_ssl_server_credentials_create_ex(pem_root_certs, key_cert_pairs,
983                                                 num_key_cert_pairs,
984                                                 client_request_type, NULL);
985   gpr_free(key_cert_pairs);
986   return creds;
987 }
988
989 GPR_EXPORT void GPR_CALLTYPE
990 grpcsharp_server_credentials_release(grpc_server_credentials* creds) {
991   grpc_server_credentials_release(creds);
992 }
993
994 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_server_add_secure_http2_port(
995     grpc_server* server, const char* addr, grpc_server_credentials* creds) {
996   return grpc_server_add_secure_http2_port(server, addr, creds);
997 }
998
999 GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE
1000 grpcsharp_composite_channel_credentials_create(
1001     grpc_channel_credentials* channel_creds,
1002     grpc_call_credentials* call_creds) {
1003   return grpc_composite_channel_credentials_create(channel_creds, call_creds,
1004                                                    NULL);
1005 }
1006
1007 GPR_EXPORT grpc_call_credentials* GPR_CALLTYPE
1008 grpcsharp_composite_call_credentials_create(grpc_call_credentials* creds1,
1009                                             grpc_call_credentials* creds2) {
1010   return grpc_composite_call_credentials_create(creds1, creds2, NULL);
1011 }
1012
1013 /* Native callback dispatcher */
1014
1015 typedef int(GPR_CALLTYPE* grpcsharp_native_callback_dispatcher_func)(
1016     void* tag, void* arg0, void* arg1, void* arg2, void* arg3, void* arg4,
1017     void* arg5);
1018
1019 static grpcsharp_native_callback_dispatcher_func native_callback_dispatcher =
1020     NULL;
1021
1022 GPR_EXPORT void GPR_CALLTYPE grpcsharp_native_callback_dispatcher_init(
1023     grpcsharp_native_callback_dispatcher_func func) {
1024   GPR_ASSERT(func);
1025   native_callback_dispatcher = func;
1026 }
1027
1028 /* Metadata credentials plugin */
1029
1030 GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin(
1031     grpc_credentials_plugin_metadata_cb cb, void* user_data,
1032     grpc_metadata_array* metadata, grpc_status_code status,
1033     const char* error_details) {
1034   if (metadata) {
1035     cb(user_data, metadata->metadata, metadata->count, status, error_details);
1036   } else {
1037     cb(user_data, NULL, 0, status, error_details);
1038   }
1039 }
1040
1041 static int grpcsharp_get_metadata_handler(
1042     void* state, grpc_auth_metadata_context context,
1043     grpc_credentials_plugin_metadata_cb cb, void* user_data,
1044     grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
1045     size_t* num_creds_md, grpc_status_code* status,
1046     const char** error_details) {
1047   native_callback_dispatcher(state, (void*)context.service_url,
1048                              (void*)context.method_name, cb, user_data,
1049                              (void*)0, NULL);
1050   return 0; /* Asynchronous return. */
1051 }
1052
1053 static void grpcsharp_metadata_credentials_destroy_handler(void* state) {
1054   native_callback_dispatcher(state, NULL, NULL, NULL, NULL, (void*)1, NULL);
1055 }
1056
1057 GPR_EXPORT grpc_call_credentials* GPR_CALLTYPE
1058 grpcsharp_metadata_credentials_create_from_plugin(void* callback_tag) {
1059   grpc_metadata_credentials_plugin plugin;
1060   plugin.get_metadata = grpcsharp_get_metadata_handler;
1061   plugin.destroy = grpcsharp_metadata_credentials_destroy_handler;
1062   plugin.state = callback_tag;
1063   plugin.type = "";
1064   return grpc_metadata_credentials_create_from_plugin(plugin, NULL);
1065 }
1066
1067 /* Auth context */
1068
1069 GPR_EXPORT grpc_auth_context* GPR_CALLTYPE
1070 grpcsharp_call_auth_context(grpc_call* call) {
1071   return grpc_call_auth_context(call);
1072 }
1073
1074 GPR_EXPORT const char* GPR_CALLTYPE
1075 grpcsharp_auth_context_peer_identity_property_name(
1076     const grpc_auth_context* ctx) {
1077   return grpc_auth_context_peer_identity_property_name(ctx);
1078 }
1079
1080 GPR_EXPORT grpc_auth_property_iterator GPR_CALLTYPE
1081 grpcsharp_auth_context_property_iterator(const grpc_auth_context* ctx) {
1082   return grpc_auth_context_property_iterator(ctx);
1083 }
1084
1085 GPR_EXPORT const grpc_auth_property* GPR_CALLTYPE
1086 grpcsharp_auth_property_iterator_next(grpc_auth_property_iterator* it) {
1087   return grpc_auth_property_iterator_next(it);
1088 }
1089
1090 GPR_EXPORT void GPR_CALLTYPE
1091 grpcsharp_auth_context_release(grpc_auth_context* ctx) {
1092   grpc_auth_context_release(ctx);
1093 }
1094
1095 /* Logging */
1096
1097 typedef void(GPR_CALLTYPE* grpcsharp_log_func)(const char* file, int32_t line,
1098                                                uint64_t thd_id,
1099                                                const char* severity_string,
1100                                                const char* msg);
1101 static grpcsharp_log_func log_func = NULL;
1102
1103 /* Redirects gpr_log to log_func callback */
1104 static void grpcsharp_log_handler(gpr_log_func_args* args) {
1105   log_func(args->file, args->line, gpr_thd_currentid(),
1106            gpr_log_severity_string(args->severity), args->message);
1107 }
1108
1109 GPR_EXPORT void GPR_CALLTYPE grpcsharp_redirect_log(grpcsharp_log_func func) {
1110   GPR_ASSERT(func);
1111   log_func = func;
1112   gpr_set_log_function(grpcsharp_log_handler);
1113 }
1114
1115 typedef void(GPR_CALLTYPE* test_callback_funcptr)(int32_t success);
1116
1117 /* Version info */
1118 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_version_string() {
1119   return grpc_version_string();
1120 }
1121
1122 /* For testing */
1123 GPR_EXPORT void GPR_CALLTYPE
1124 grpcsharp_test_callback(test_callback_funcptr callback) {
1125   callback(1);
1126 }
1127
1128 /* For testing */
1129 GPR_EXPORT void* GPR_CALLTYPE grpcsharp_test_nop(void* ptr) { return ptr; }
1130
1131 /* For testing */
1132 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_sizeof_grpc_event(void) {
1133   return sizeof(grpc_event);
1134 }
1135
1136 /* Override a method for testing */
1137 GPR_EXPORT void GPR_CALLTYPE
1138 grpcsharp_test_override_method(const char* method_name, const char* variant) {
1139   if (strcmp("grpcsharp_call_start_batch", method_name) == 0) {
1140     if (strcmp("nop", variant) == 0) {
1141       g_call_start_batch_func = grpcsharp_call_start_batch_nop;
1142     } else {
1143       GPR_ASSERT(0);
1144     }
1145   } else {
1146     GPR_ASSERT(0);
1147   }
1148 }