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 int force_client_auth) { 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( 983 pem_root_certs, key_cert_pairs, num_key_cert_pairs, 984 force_client_auth 985 ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY 986 : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, 987 NULL); 988 gpr_free(key_cert_pairs); 989 return creds; 990 } 991 992 GPR_EXPORT void GPR_CALLTYPE 993 grpcsharp_server_credentials_release(grpc_server_credentials* creds) { 994 grpc_server_credentials_release(creds); 995 } 996 997 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_server_add_secure_http2_port( 998 grpc_server* server, const char* addr, grpc_server_credentials* creds) { 999 return grpc_server_add_secure_http2_port(server, addr, creds); 1000 } 1001 1002 GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE 1003 grpcsharp_composite_channel_credentials_create( 1004 grpc_channel_credentials* channel_creds, 1005 grpc_call_credentials* call_creds) { 1006 return grpc_composite_channel_credentials_create(channel_creds, call_creds, 1007 NULL); 1008 } 1009 1010 GPR_EXPORT grpc_call_credentials* GPR_CALLTYPE 1011 grpcsharp_composite_call_credentials_create(grpc_call_credentials* creds1, 1012 grpc_call_credentials* creds2) { 1013 return grpc_composite_call_credentials_create(creds1, creds2, NULL); 1014 } 1015 1016 /* Metadata credentials plugin */ 1017 1018 GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin( 1019 grpc_credentials_plugin_metadata_cb cb, void* user_data, 1020 grpc_metadata_array* metadata, grpc_status_code status, 1021 const char* error_details) { 1022 if (metadata) { 1023 cb(user_data, metadata->metadata, metadata->count, status, error_details); 1024 } else { 1025 cb(user_data, NULL, 0, status, error_details); 1026 } 1027 } 1028 1029 typedef void(GPR_CALLTYPE* grpcsharp_metadata_interceptor_func)( 1030 void* state, const char* service_url, const char* method_name, 1031 grpc_credentials_plugin_metadata_cb cb, void* user_data, 1032 int32_t is_destroy); 1033 1034 static int grpcsharp_get_metadata_handler( 1035 void* state, grpc_auth_metadata_context context, 1036 grpc_credentials_plugin_metadata_cb cb, void* user_data, 1037 grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], 1038 size_t* num_creds_md, grpc_status_code* status, 1039 const char** error_details) { 1040 grpcsharp_metadata_interceptor_func interceptor = 1041 (grpcsharp_metadata_interceptor_func)(intptr_t)state; 1042 interceptor(state, context.service_url, context.method_name, cb, user_data, 1043 0); 1044 return 0; /* Asynchronous return. */ 1045 } 1046 1047 static void grpcsharp_metadata_credentials_destroy_handler(void* state) { 1048 grpcsharp_metadata_interceptor_func interceptor = 1049 (grpcsharp_metadata_interceptor_func)(intptr_t)state; 1050 interceptor(state, NULL, NULL, NULL, NULL, 1); 1051 } 1052 1053 GPR_EXPORT grpc_call_credentials* GPR_CALLTYPE 1054 grpcsharp_metadata_credentials_create_from_plugin( 1055 grpcsharp_metadata_interceptor_func metadata_interceptor) { 1056 grpc_metadata_credentials_plugin plugin; 1057 plugin.get_metadata = grpcsharp_get_metadata_handler; 1058 plugin.destroy = grpcsharp_metadata_credentials_destroy_handler; 1059 plugin.state = (void*)(intptr_t)metadata_interceptor; 1060 plugin.type = ""; 1061 return grpc_metadata_credentials_create_from_plugin(plugin, NULL); 1062 } 1063 1064 /* Auth context */ 1065 1066 GPR_EXPORT grpc_auth_context* GPR_CALLTYPE 1067 grpcsharp_call_auth_context(grpc_call* call) { 1068 return grpc_call_auth_context(call); 1069 } 1070 1071 GPR_EXPORT const char* GPR_CALLTYPE 1072 grpcsharp_auth_context_peer_identity_property_name( 1073 const grpc_auth_context* ctx) { 1074 return grpc_auth_context_peer_identity_property_name(ctx); 1075 } 1076 1077 GPR_EXPORT grpc_auth_property_iterator GPR_CALLTYPE 1078 grpcsharp_auth_context_property_iterator(const grpc_auth_context* ctx) { 1079 return grpc_auth_context_property_iterator(ctx); 1080 } 1081 1082 GPR_EXPORT const grpc_auth_property* GPR_CALLTYPE 1083 grpcsharp_auth_property_iterator_next(grpc_auth_property_iterator* it) { 1084 return grpc_auth_property_iterator_next(it); 1085 } 1086 1087 GPR_EXPORT void GPR_CALLTYPE 1088 grpcsharp_auth_context_release(grpc_auth_context* ctx) { 1089 grpc_auth_context_release(ctx); 1090 } 1091 1092 /* Logging */ 1093 1094 typedef void(GPR_CALLTYPE* grpcsharp_log_func)(const char* file, int32_t line, 1095 uint64_t thd_id, 1096 const char* severity_string, 1097 const char* msg); 1098 static grpcsharp_log_func log_func = NULL; 1099 1100 /* Redirects gpr_log to log_func callback */ 1101 static void grpcsharp_log_handler(gpr_log_func_args* args) { 1102 log_func(args->file, args->line, gpr_thd_currentid(), 1103 gpr_log_severity_string(args->severity), args->message); 1104 } 1105 1106 GPR_EXPORT void GPR_CALLTYPE grpcsharp_redirect_log(grpcsharp_log_func func) { 1107 GPR_ASSERT(func); 1108 log_func = func; 1109 gpr_set_log_function(grpcsharp_log_handler); 1110 } 1111 1112 typedef void(GPR_CALLTYPE* test_callback_funcptr)(int32_t success); 1113 1114 /* Version info */ 1115 GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_version_string() { 1116 return grpc_version_string(); 1117 } 1118 1119 /* For testing */ 1120 GPR_EXPORT void GPR_CALLTYPE 1121 grpcsharp_test_callback(test_callback_funcptr callback) { 1122 callback(1); 1123 } 1124 1125 /* For testing */ 1126 GPR_EXPORT void* GPR_CALLTYPE grpcsharp_test_nop(void* ptr) { return ptr; } 1127 1128 /* For testing */ 1129 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_sizeof_grpc_event(void) { 1130 return sizeof(grpc_event); 1131 } 1132 1133 /* Override a method for testing */ 1134 GPR_EXPORT void GPR_CALLTYPE 1135 grpcsharp_test_override_method(const char* method_name, const char* variant) { 1136 if (strcmp("grpcsharp_call_start_batch", method_name) == 0) { 1137 if (strcmp("nop", variant) == 0) { 1138 g_call_start_batch_func = grpcsharp_call_start_batch_nop; 1139 } else { 1140 GPR_ASSERT(0); 1141 } 1142 } else { 1143 GPR_ASSERT(0); 1144 } 1145 } 1146