1 /*
2  *
3  * Copyright 2015, Google Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 
34 module interop.functors;
35 
36 import interop.headers;
37 import std.typecons : Tuple;
38 import core.stdc.string;
39 
40 extern(C) {
41 
42 auto GRPC_SLICE_LENGTH(const(grpc_slice) slice) {
43     return ((slice.refcount) ? slice.data.refcounted.length : slice.data.inlined.length);
44 }
45 
46 auto GRPC_SLICE_START_PTR(ref const(grpc_slice) slice) {
47     return ((slice.refcount) ? slice.data.refcounted.bytes : 
48             cast(const(ubyte*))slice.data.inlined.bytes);
49 }
50 
51 auto GRPC_SLICE_SET_LENGTH(ref grpc_slice slice, size_t len) {
52     return ((slice.refcount) ? (slice.data.refcounted.length = len) : (slice.data.inlined.length = cast(uint8_t)len)); 
53 }
54 
55 auto GRPC_SLICE_END_PTR(ref const(grpc_slice) slice) {
56     return GRPC_SLICE_START_PTR(slice) + GRPC_SLICE_LENGTH(slice);
57 }
58 
59 auto GRPC_SLICE_IS_EMPTY(const(grpc_slice) slice) {
60     return (GRPC_SLICE_LENGTH(slice) == 0);
61 }
62 
63 grpc_byte_buffer* string_to_byte_buffer(const(char*) buffer, size_t len) {
64   grpc_slice slice = grpc_slice_from_copied_buffer(buffer, len);
65   grpc_byte_buffer* bb = grpc_raw_byte_buffer_create(&slice, 1);
66   grpc_slice_unref(slice);
67   return bb;
68 }
69 
70 struct grpcwrap_batch_context {
71   grpc_metadata_array send_initial_metadata;
72   grpc_byte_buffer* send_message;
73   struct __anon0 {
74           grpc_metadata_array trailing_metadata;
75   };
76   __anon0 send_status_from_server;
77 
78   grpc_metadata_array recv_initial_metadata;
79   grpc_byte_buffer* recv_message;
80   struct __anon1 {
81         grpc_metadata_array trailing_metadata;
82         grpc_status_code status;
83         grpc_slice status_details;
84   }
85 
86   __anon1 recv_status_on_client;
87   int recv_close_on_server_cancelled;
88 };
89 
90 grpcwrap_batch_context* grpcwrap_batch_context_create() {
91   auto ctx =
92       cast(grpcwrap_batch_context*)gpr_malloc(grpcwrap_batch_context.sizeof);
93   memset(ctx, 0, grpcwrap_batch_context.sizeof);
94   return ctx;
95 }
96 
97 struct grpcwrap_request_call_context {
98   grpc_call* call;
99   grpc_call_details call_details;
100   grpc_metadata_array request_metadata;
101 };
102 
103 grpcwrap_request_call_context* grpcwrap_request_call_context_create() {
104   auto ctx = cast(grpcwrap_request_call_context*)gpr_malloc(
105       grpcwrap_request_call_context.sizeof);
106   memset(ctx, 0, (grpcwrap_request_call_context).sizeof);
107   return ctx;
108 }
109 
110 /*
111  * Destroys array.metadata.
112  * The array pointer itself is not freed.
113  */
114 void grpcwrap_metadata_array_destroy_metadata_only(grpc_metadata_array* array) {
115   gpr_free(array.metadata);
116 }
117 
118 /*
119  * Destroys keys, values and array.metadata.
120  * The array pointer itself is not freed.
121  */
122 void grpcwrap_metadata_array_destroy_metadata_including_entries(
123     grpc_metadata_array* array) {
124   size_t i;
125   if (array.metadata) {
126     for (i = 0; i < array.count; i++) {
127       grpc_slice_unref(array.metadata[i].key);
128       grpc_slice_unref(array.metadata[i].value);
129     }
130   }
131   gpr_free(array.metadata);
132 }
133 
134 /*
135  * Fully destroys the metadata array.
136  */
137 void grpcwrap_metadata_array_destroy_full(grpc_metadata_array* array) {
138   if (!array) {
139     return;
140   }
141   grpcwrap_metadata_array_destroy_metadata_including_entries(array);
142   gpr_free(array);
143 }
144 
145 /*
146  * Allocate metadata array with given capacity.
147  */
148 void grpcwrap_metadata_array_init(grpc_metadata_array* array, size_t capacity) {
149   array.count = 0;
150   array.capacity = capacity;
151   if (!capacity) {
152     array.metadata = null;
153     return;
154   }
155 
156   auto arr = cast(grpc_metadata*)gpr_malloc((grpc_metadata).sizeof * capacity);
157   memset(arr, 0, (grpc_metadata).sizeof * capacity);
158   array.metadata = arr;
159 }
160 
161 void grpcwrap_metadata_array_add(
162     grpc_metadata_array* array, const(char*) key, size_t key_length,
163     const(char*) value, size_t value_length) {
164   assert(array.count <= array.capacity);
165   size_t i = array.count;
166   if (i == array.capacity) {
167     array.capacity = array.capacity ? array.capacity * 2 : 4;
168     array.metadata = cast(grpc_metadata*)gpr_realloc(
169         array.metadata, array.capacity * (grpc_metadata).sizeof);
170     memset(array.metadata + i, 0,
171            (grpc_metadata).sizeof * (array.capacity - i));
172   }
173   array.metadata[i].key = grpc_slice_from_copied_buffer(key, key_length);
174   array.metadata[i].value = grpc_slice_from_copied_buffer(value, value_length);
175   array.count++;
176 }
177 
178 const(char*) grpcwrap_metadata_array_get_key(
179     const(grpc_metadata_array*) array, size_t index, size_t* key_length) {
180   assert(index < array.count);
181   *key_length = GRPC_SLICE_LENGTH(array.metadata[index].key);
182   return cast(char*)GRPC_SLICE_START_PTR(array.metadata[index].key);
183 }
184 
185 const(char*) grpcwrap_metadata_array_get_value(
186     const(grpc_metadata_array*) array, size_t index, size_t* value_length) {
187   assert(index < array.count);
188   *value_length = GRPC_SLICE_LENGTH(array.metadata[index].value);
189   return cast(char*)GRPC_SLICE_START_PTR(array.metadata[index].value);
190 }
191 
192 void grpcwrap_metadata_array_cleanup(grpc_metadata_array* array) {
193   grpcwrap_metadata_array_destroy_metadata_including_entries(array);
194 }
195 
196 void grpcwrap_metadata_array_shrink_to_fit(grpc_metadata_array* array) {
197   assert(array.count <= array.capacity);
198   if (array.count == array.capacity) {
199     return;
200   }
201   if (array.count) {
202     array.metadata = cast(grpc_metadata*)gpr_realloc(
203         array.metadata, array.count * (grpc_metadata).sizeof);
204     array.capacity = array.count;
205   } else {
206     grpcwrap_metadata_array_cleanup(array);
207     array.capacity = 0;
208     array.metadata = null;
209   }
210 }
211 
212 /* Move contents of metadata array */
213 void grpcwrap_metadata_array_move(grpc_metadata_array* dest,
214                                   grpc_metadata_array* src) {
215   if (!src) {
216     dest.capacity = 0;
217     dest.count = 0;
218     dest.metadata = null;
219     return;
220   }
221 
222   dest.capacity = src.capacity;
223   dest.count = src.count;
224   dest.metadata = src.metadata;
225 
226   src.capacity = 0;
227   src.count = 0;
228   src.metadata = null;
229 }
230 
231 void grpcwrap_batch_context_destroy(grpcwrap_batch_context* ctx) {
232   if (!ctx) {
233     return;
234   }
235   grpcwrap_metadata_array_destroy_metadata_including_entries(
236       &(ctx.send_initial_metadata));
237 
238   grpc_byte_buffer_destroy(ctx.send_message);
239 
240   grpcwrap_metadata_array_destroy_metadata_including_entries(
241       &(ctx.send_status_from_server.trailing_metadata));
242 
243   grpcwrap_metadata_array_destroy_metadata_only(&(ctx.recv_initial_metadata));
244 
245   grpc_byte_buffer_destroy(ctx.recv_message);
246 
247   grpcwrap_metadata_array_destroy_metadata_only(
248       &(ctx.recv_status_on_client.trailing_metadata));
249   grpc_slice_unref(ctx.recv_status_on_client.status_details);
250 
251   gpr_free(ctx);
252 }
253 
254 void grpcwrap_request_call_context_destroy(grpcwrap_request_call_context* ctx) {
255   if (!ctx) {
256     return;
257   }
258 
259   if (ctx.call) {
260     grpc_call_unref(ctx.call);
261   }
262 
263   grpc_call_details_destroy(&(ctx.call_details));
264   grpcwrap_metadata_array_destroy_metadata_only(&(ctx.request_metadata));
265 
266   gpr_free(ctx);
267 }
268 
269 const(grpc_metadata_array*) grpcwrap_batch_context_recv_initial_metadata(
270     const(grpcwrap_batch_context*) ctx) {
271   return &(ctx.recv_initial_metadata);
272 }
273 
274 const(char*) grpcwrap_slice_raw_offset(const grpc_slice* slice, size_t offset, size_t* len) {
275   *len = GRPC_SLICE_LENGTH(*slice) - offset;
276   return cast(const(char*))(GRPC_SLICE_START_PTR(*slice)) + offset;
277 }
278 
279 grpc_slice grpcwrap_slice_copy(const(grpc_slice*) slice) {
280   return grpc_slice_copy(cast(grpc_slice)*slice);
281 }
282 
283 void grpcwrap_slice_unref(const(grpc_slice*) slice) {
284   grpc_slice_unref(cast(grpc_slice)*slice);
285 }
286 
287 grpc_slice grpcwrap_slice_ref(const(grpc_slice*) slice) {
288   return grpc_slice_ref(cast(grpc_slice)*slice);
289 }
290 
291 size_t grpcwrap_slice_length(const(grpc_slice*) slice) {
292   return GRPC_SLICE_LENGTH(*slice);
293 }
294 
295 grpc_byte_buffer* grpcwrap_batch_context_take_recv_message(grpcwrap_batch_context* ctx) {
296   grpc_byte_buffer* buf = null;
297   if (ctx.recv_message) {
298     buf = ctx.recv_message;
299     ctx.recv_message = null;
300   }
301   return buf;
302 }
303 
304 grpc_status_code grpcwrap_batch_context_recv_status_on_client_status(
305     const(grpcwrap_batch_context*) ctx) {
306   return ctx.recv_status_on_client.status;
307 }
308 
309 const(char*) grpcwrap_batch_context_recv_status_on_client_details(
310     const(grpcwrap_batch_context*) ctx, size_t* details_length) {
311   *details_length =
312       GRPC_SLICE_LENGTH(ctx.recv_status_on_client.status_details);
313   return cast(char*)GRPC_SLICE_START_PTR(ctx.recv_status_on_client.status_details);
314 }
315 
316 const(grpc_metadata_array*) grpcwrap_batch_context_recv_status_on_client_trailing_metadata(
317     const(grpcwrap_batch_context*) ctx) {
318   return &(ctx.recv_status_on_client.trailing_metadata);
319 }
320 
321 grpc_call* grpcwrap_request_call_context_ref_call(grpcwrap_request_call_context* ctx) {
322   grpc_call* call = ctx.call;
323   grpc_call_ref(call);
324   return call;
325 }
326 
327 grpc_call* grpcwrap_request_call_context_get_call(grpcwrap_request_call_context* ctx) {
328   return ctx.call;
329 }
330 
331 const(char*) grpcwrap_request_call_context_method(
332     const(grpcwrap_request_call_context*) ctx, size_t* method_length) {
333   *method_length = GRPC_SLICE_LENGTH(ctx.call_details.method);
334   return cast(char*)GRPC_SLICE_START_PTR(ctx.call_details.method);
335 }
336 
337 const(char*) grpcwrap_request_call_context_host(
338     const(grpcwrap_request_call_context*) ctx, size_t* host_length) {
339   *host_length = GRPC_SLICE_LENGTH(ctx.call_details.host);
340   return cast(char*)GRPC_SLICE_START_PTR(ctx.call_details.host);
341 }
342 
343 gpr_timespec grpcwrap_request_call_context_deadline(
344     const(grpcwrap_request_call_context*) ctx) {
345   return ctx.call_details.deadline;
346 }
347 
348 const(grpc_metadata_array*) grpcwrap_request_call_context_metadata_array(
349     const(grpcwrap_request_call_context*) ctx) {
350   return &(ctx.request_metadata);
351 }
352 
353 int32_t grpcwrap_batch_context_recv_close_on_server_cancelled(
354     const(grpcwrap_batch_context*) ctx) {
355   return cast(int32_t)ctx.recv_close_on_server_cancelled;
356 }
357 
358 /* Channel */
359 
360 grpc_call* grpcwrap_channel_create_call(
361     grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask,
362     grpc_completion_queue* cq, const(char*) method, size_t method_len,
363     const(char*) host, size_t host_len, gpr_timespec deadline) {
364   grpc_slice method_slice = grpc_slice_from_copied_buffer(method, method_len);
365   grpc_slice* host_slice_ptr = null;
366   grpc_slice host_slice;
367   if (host != null) {
368     host_slice = grpc_slice_from_copied_buffer(host, host_len);
369     host_slice_ptr = &host_slice;
370   } else {
371     // to silent msvc false warning
372     host_slice = grpc_empty_slice();
373   }
374   grpc_call* ret =
375       grpc_channel_create_call(channel, parent_call, propagation_mask, cq,
376                                method_slice, host_slice_ptr, deadline, null);
377   grpc_slice_unref(method_slice);
378   if (host != null) {
379     grpc_slice_unref(host_slice);
380   }
381   return ret;
382 }
383 
384 /* Channel args */
385 
386 grpc_channel_args* grpcwrap_channel_args_create(size_t num_args) {
387   auto args = cast(grpc_channel_args*)gpr_malloc((grpc_channel_args).sizeof);
388   memset(args, 0, (grpc_channel_args).sizeof);
389 
390   args.num_args = num_args;
391   args.args = cast(grpc_arg*)gpr_malloc((grpc_arg).sizeof * num_args);
392   memset(args.args, 0, (grpc_arg).sizeof * num_args);
393   return args;
394 }
395 
396 void grpcwrap_channel_args_set_string(
397     grpc_channel_args* args, size_t index, const(char*) key, const char* value) {
398   assert(args);
399   assert(index < args.num_args);
400   args.args[index].type = GRPC_ARG_STRING;
401   args.args[index].key = gpr_strdup(key);
402   args.args[index].value..string = gpr_strdup(value);
403 }
404 
405 void grpcwrap_channel_args_set_integer(
406     grpc_channel_args* args, size_t index, const(char*) key, int value) {
407   assert(args);
408   assert(index < args.num_args);
409   args.args[index].type = GRPC_ARG_INTEGER;
410   args.args[index].key = gpr_strdup(key);
411   args.args[index].value.integer = value;
412 }
413 
414 void grpcwrap_channel_args_set_pointer_vtable(
415     grpc_channel_args* args, size_t index, const(char*) key, void* value,
416     const(grpc_arg_pointer_vtable*) vtable) {
417   assert(args);
418   assert(index < args.num_args);
419   args.args[index].type = GRPC_ARG_POINTER;
420   args.args[index].key = gpr_strdup(key);
421   args.args[index].value.pointer.p = vtable.copy(value);
422   args.args[index].value.pointer.vtable = vtable;
423 }
424 
425 void grpcwrap_channel_args_destroy(grpc_channel_args* args) {
426   size_t i;
427   if (args) {
428     for (i = 0; i < args.num_args; i++) {
429       gpr_free(args.args[i].key);
430       if (args.args[i].type == GRPC_ARG_STRING) {
431         gpr_free(args.args[i].value..string);
432       }
433       if (args.args[i].type == GRPC_ARG_POINTER) {
434         args.args[i].value.pointer.vtable.destroy(
435             args.args[i].value.pointer.p);
436       }
437     }
438     gpr_free(args.args);
439     gpr_free(args);
440   }
441 }
442 
443 /* Call */
444 
445 grpc_call_error grpcwrap_call_start_unary(
446     grpc_call* call, grpcwrap_batch_context* ctx, const(char*) send_buffer,
447     size_t send_buffer_len, uint32_t write_flags,
448     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags,
449     void* tag) {
450   /* TODO: don't use magic number */
451   grpc_op[6] ops;
452   memset(ops.ptr, 0, (ops).sizeof);
453   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
454   grpcwrap_metadata_array_move(&(ctx.send_initial_metadata), initial_metadata);
455   ops[0].data.send_initial_metadata.count = ctx.send_initial_metadata.count;
456   ops[0].data.send_initial_metadata.metadata =
457       ctx.send_initial_metadata.metadata;
458   ops[0].flags = initial_metadata_flags;
459   ops[0].reserved = null;
460 
461   ops[1].op = GRPC_OP_SEND_MESSAGE;
462   ctx.send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
463   ops[1].data.send_message.send_message = ctx.send_message;
464   ops[1].flags = write_flags;
465   ops[1].reserved = null;
466 
467   ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
468   ops[2].flags = 0;
469   ops[2].reserved = null;
470 
471   ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;
472   ops[3].data.recv_initial_metadata.recv_initial_metadata =
473       &(ctx.recv_initial_metadata);
474   ops[3].flags = 0;
475   ops[3].reserved = null;
476 
477   ops[4].op = GRPC_OP_RECV_MESSAGE;
478   ops[4].data.recv_message.recv_message = &(ctx.recv_message);
479   ops[4].flags = 0;
480   ops[4].reserved = null;
481 
482   ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
483   ops[5].data.recv_status_on_client.trailing_metadata =
484       &(ctx.recv_status_on_client.trailing_metadata);
485   ops[5].data.recv_status_on_client.status =
486       &(ctx.recv_status_on_client.status);
487   ops[5].data.recv_status_on_client.status_details =
488       &(ctx.recv_status_on_client.status_details);
489   ops[5].flags = 0;
490   ops[5].reserved = null;
491 
492   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
493                                null);
494 }
495 
496 grpc_call_error grpcwrap_call_start_client_streaming(
497     grpc_call* call, grpcwrap_batch_context* ctx,
498     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags,
499     void* tag) {
500   /* TODO: don't use magic number */
501   grpc_op[4] ops;
502   memset(ops.ptr, 0, (ops).sizeof);
503   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
504   grpcwrap_metadata_array_move(&(ctx.send_initial_metadata), initial_metadata);
505   ops[0].data.send_initial_metadata.count = ctx.send_initial_metadata.count;
506   ops[0].data.send_initial_metadata.metadata =
507       ctx.send_initial_metadata.metadata;
508   ops[0].flags = initial_metadata_flags;
509   ops[0].reserved = null;
510 
511   ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
512   ops[1].data.recv_initial_metadata.recv_initial_metadata =
513       &(ctx.recv_initial_metadata);
514   ops[1].flags = 0;
515   ops[1].reserved = null;
516 
517   ops[2].op = GRPC_OP_RECV_MESSAGE;
518   ops[2].data.recv_message.recv_message = &(ctx.recv_message);
519   ops[2].flags = 0;
520   ops[2].reserved = null;
521 
522   ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
523   ops[3].data.recv_status_on_client.trailing_metadata =
524       &(ctx.recv_status_on_client.trailing_metadata);
525   ops[3].data.recv_status_on_client.status =
526       &(ctx.recv_status_on_client.status);
527   ops[3].data.recv_status_on_client.status_details =
528       &(ctx.recv_status_on_client.status_details);
529   ops[3].flags = 0;
530   ops[3].reserved = null;
531 
532   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
533                                null);
534 }
535 
536 grpc_call_error grpcwrap_call_start_server_streaming(
537     grpc_call* call, grpcwrap_batch_context* ctx, const(char*) send_buffer,
538     size_t send_buffer_len, uint32_t write_flags,
539     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags,
540     void* tag) {
541   /* TODO: don't use magic number */
542   grpc_op[4] ops;
543   memset(ops.ptr, 0, (ops).sizeof);
544   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
545   grpcwrap_metadata_array_move(&(ctx.send_initial_metadata), initial_metadata);
546   ops[0].data.send_initial_metadata.count = ctx.send_initial_metadata.count;
547   ops[0].data.send_initial_metadata.metadata =
548       ctx.send_initial_metadata.metadata;
549   ops[0].flags = initial_metadata_flags;
550   ops[0].reserved = null;
551 
552   ops[1].op = GRPC_OP_SEND_MESSAGE;
553   ctx.send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
554   ops[1].data.send_message.send_message = ctx.send_message;
555   ops[1].flags = write_flags;
556   ops[1].reserved = null;
557 
558   ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
559   ops[2].flags = 0;
560   ops[2].reserved = null;
561 
562   ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
563   ops[3].data.recv_status_on_client.trailing_metadata =
564       &(ctx.recv_status_on_client.trailing_metadata);
565   ops[3].data.recv_status_on_client.status =
566       &(ctx.recv_status_on_client.status);
567   ops[3].data.recv_status_on_client.status_details =
568       &(ctx.recv_status_on_client.status_details);
569   ops[3].flags = 0;
570   ops[3].reserved = null;
571 
572   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
573                                null);
574 }
575 
576 grpc_call_error grpcwrap_call_start_duplex_streaming(
577     grpc_call* call, grpcwrap_batch_context* ctx,
578     grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags,
579     void* tag) {
580   /* TODO: don't use magic number */
581   grpc_op[2] ops;
582   memset(ops.ptr, 0, (ops).sizeof);
583   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
584   grpcwrap_metadata_array_move(&(ctx.send_initial_metadata), initial_metadata);
585   ops[0].data.send_initial_metadata.count = ctx.send_initial_metadata.count;
586   ops[0].data.send_initial_metadata.metadata =
587       ctx.send_initial_metadata.metadata;
588   ops[0].flags = initial_metadata_flags;
589   ops[0].reserved = null;
590 
591   ops[1].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
592   ops[1].data.recv_status_on_client.trailing_metadata =
593       &(ctx.recv_status_on_client.trailing_metadata);
594   ops[1].data.recv_status_on_client.status =
595       &(ctx.recv_status_on_client.status);
596   ops[1].data.recv_status_on_client.status_details =
597       &(ctx.recv_status_on_client.status_details);
598   ops[1].flags = 0;
599   ops[1].reserved = null;
600 
601   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
602                                null);
603 }
604 
605 grpc_call_error grpcwrap_call_recv_initial_metadata(
606     grpc_call* call, grpcwrap_batch_context* ctx, void* tag) {
607   /* TODO: don't use magic number */
608   grpc_op[1] ops;
609   ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
610   ops[0].data.recv_initial_metadata.recv_initial_metadata =
611       &(ctx.recv_initial_metadata);
612   ops[0].flags = 0;
613   ops[0].reserved = null;
614 
615   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
616                                null);
617 }
618 
619 grpc_call_error grpcwrap_call_send_message(
620     grpc_call* call, grpcwrap_batch_context* ctx, const(char*) send_buffer,
621     size_t send_buffer_len, uint32_t write_flags,
622     int32_t send_empty_initial_metadata, void* tag) {
623   /* TODO: don't use magic number */
624   grpc_op[2] ops;
625   memset(ops.ptr, 0, (ops).sizeof);
626   size_t nops = send_empty_initial_metadata ? 2 : 1;
627   ops[0].op = GRPC_OP_SEND_MESSAGE;
628   ctx.send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
629   ops[0].data.send_message.send_message = ctx.send_message;
630   ops[0].flags = write_flags;
631   ops[0].reserved = null;
632   ops[1].op = GRPC_OP_SEND_INITIAL_METADATA;
633   ops[1].flags = 0;
634   ops[1].reserved = null;
635 
636   return grpc_call_start_batch(call, ops.ptr, nops, tag, null);
637 }
638 
639 grpc_call_error grpcwrap_call_send_close_from_client(grpc_call* call, void* tag) {
640   /* TODO: don't use magic number */
641   grpc_op[1] ops;
642   ops[0].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
643   ops[0].flags = 0;
644   ops[0].reserved = null;
645 
646   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
647                                null);
648 }
649 
650 grpc_call_error grpcwrap_call_send_status_from_server(
651     grpc_call* call, grpcwrap_batch_context* ctx, grpc_status_code status_code,
652     const(char*) status_details, size_t status_details_len,
653     grpc_metadata_array* trailing_metadata, int32_t send_empty_initial_metadata,
654     const(char*) optional_send_buffer, size_t optional_send_buffer_len,
655     uint32_t write_flags, void* tag) {
656   /* TODO: don't use magic number */
657   grpc_op[3] ops;
658   memset(ops.ptr, 0, (ops).sizeof);
659   size_t nops = 1;
660   grpc_slice status_details_slice =
661       grpc_slice_from_copied_buffer(status_details, status_details_len);
662   ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
663   ops[0].data.send_status_from_server.status = status_code;
664   ops[0].data.send_status_from_server.status_details = &status_details_slice;
665   grpcwrap_metadata_array_move(
666       &(ctx.send_status_from_server.trailing_metadata), trailing_metadata);
667   ops[0].data.send_status_from_server.trailing_metadata_count =
668       ctx.send_status_from_server.trailing_metadata.count;
669   ops[0].data.send_status_from_server.trailing_metadata =
670       ctx.send_status_from_server.trailing_metadata.metadata;
671   ops[0].flags = 0;
672   ops[0].reserved = null;
673   if (optional_send_buffer) {
674     ops[nops].op = GRPC_OP_SEND_MESSAGE;
675     ctx.send_message =
676         string_to_byte_buffer(optional_send_buffer, optional_send_buffer_len);
677     ops[nops].data.send_message.send_message = ctx.send_message;
678     ops[nops].flags = write_flags;
679     ops[nops].reserved = null;
680     nops++;
681   }
682   if (send_empty_initial_metadata) {
683     ops[nops].op = GRPC_OP_SEND_INITIAL_METADATA;
684     ops[nops].flags = 0;
685     ops[nops].reserved = null;
686     nops++;
687   }
688   grpc_call_error ret = grpc_call_start_batch(call, ops.ptr, nops, tag, null);
689   grpc_slice_unref(status_details_slice);
690   return ret;
691 }
692 
693 grpc_call_error grpcwrap_call_recv_message(
694     grpc_call* call, grpcwrap_batch_context* ctx, void* tag) {
695   /* TODO: don't use magic number */
696   grpc_op[1] ops;
697   ops[0].op = GRPC_OP_RECV_MESSAGE;
698   ops[0].data.recv_message.recv_message = &(ctx.recv_message);
699   ops[0].flags = 0;
700   ops[0].reserved = null;
701   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
702                                null);
703 }
704 
705 grpc_call_error grpcwrap_call_start_serverside(
706     grpc_call* call, grpcwrap_batch_context* ctx, void* tag) {
707   /* TODO: don't use magic number */
708   grpc_op[1] ops;
709   ops[0].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
710   ops[0].data.recv_close_on_server.cancelled =
711       (&ctx.recv_close_on_server_cancelled);
712   ops[0].flags = 0;
713   ops[0].reserved = null;
714 
715   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
716                                null);
717 }
718 
719 grpc_call_error grpcwrap_call_send_initial_metadata(
720     grpc_call* call, grpcwrap_batch_context* ctx,
721     grpc_metadata_array* initial_metadata, void* tag) {
722   /* TODO: don't use magic number */
723   grpc_op[1] ops;
724   memset(ops.ptr, 0, (ops).sizeof);
725   ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
726   grpcwrap_metadata_array_move(&(ctx.send_initial_metadata), initial_metadata);
727   ops[0].data.send_initial_metadata.count = ctx.send_initial_metadata.count;
728   ops[0].data.send_initial_metadata.metadata =
729       ctx.send_initial_metadata.metadata;
730   ops[0].flags = 0;
731   ops[0].reserved = null;
732 
733   return grpc_call_start_batch(call, ops.ptr, (ops).sizeof / (ops[0]).sizeof, tag,
734                                null);
735 }
736 
737 /** Kick call's completion queue, it should be called after there is an event
738     ready to poll.
739     THREAD SAFETY: grpcwrap_call_kick_completion_queue is thread-safe
740     because it does not change the call's state. */
741 grpc_call_error grpcwrap_call_kick_completion_queue(grpc_call* call, void* tag) {
742   // Empty batch grpc_op kicks call's completion queue immediately.
743   return grpc_call_start_batch(call, null, 0, tag, null);
744 }
745 
746 /* Server */
747 
748 grpc_call_error grpcwrap_server_request_call(grpc_server* server, grpc_completion_queue* cq,
749                              grpcwrap_request_call_context* ctx, void* tag) {
750   return grpc_server_request_call(server, &(ctx.call), &(ctx.call_details),
751                                   &(ctx.request_metadata), cq, cq, tag);
752 }
753 
754 /* Security */
755 
756 static char* default_pem_root_certs = null;
757 
758 extern(C) {
759     static grpc_ssl_roots_override_result override_ssl_roots_handler(
760         char** pem_root_certs) {
761       if (!default_pem_root_certs) {
762         *pem_root_certs = null;
763         return GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY;
764       }
765       *pem_root_certs = gpr_strdup(default_pem_root_certs);
766       return GRPC_SSL_ROOTS_OVERRIDE_OK;
767     }
768 }
769 
770 void grpcwrap_override_default_ssl_roots(const(char*) pem_root_certs) {
771   /*
772    * This currently wastes ~300kB of memory by keeping a copy of roots
773    * in a static variable, but for desktop/server use, the overhead
774    * is negligible. In the future, we might want to change the behavior
775    * for mobile (e.g. Xamarin).
776    */
777   default_pem_root_certs = gpr_strdup(pem_root_certs);
778   grpc_set_ssl_roots_override_callback(&override_ssl_roots_handler);
779 }
780 
781 grpc_channel_credentials* grpcwrap_ssl_credentials_create(const(char*) pem_root_certs,
782                                 const(char*) key_cert_pair_cert_chain,
783                                 const(char*) key_cert_pair_private_key) {
784   grpc_ssl_pem_key_cert_pair key_cert_pair;
785   if (key_cert_pair_cert_chain || key_cert_pair_private_key) {
786     key_cert_pair.cert_chain = key_cert_pair_cert_chain;
787     key_cert_pair.private_key = key_cert_pair_private_key;
788     return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, null,
789                                        null);
790   } else {
791     assert(!key_cert_pair_cert_chain);
792     assert(!key_cert_pair_private_key);
793     return grpc_ssl_credentials_create(pem_root_certs, null, null, null);
794   }
795 }
796 
797 grpc_server_credentials* grpcwrap_ssl_server_credentials_create(
798     const(char*) pem_root_certs, const char** key_cert_pair_cert_chain_array,
799     const(char**) key_cert_pair_private_key_array, size_t num_key_cert_pairs,
800     int force_client_auth) {
801   size_t i;
802   grpc_server_credentials* creds;
803   grpc_ssl_pem_key_cert_pair* key_cert_pairs =
804       cast(grpc_ssl_pem_key_cert_pair*)gpr_malloc(
805           (grpc_ssl_pem_key_cert_pair).sizeof * num_key_cert_pairs);
806   memset(key_cert_pairs, 0,
807          (grpc_ssl_pem_key_cert_pair).sizeof * num_key_cert_pairs);
808 
809   for (i = 0; i < num_key_cert_pairs; i++) {
810     if (key_cert_pair_cert_chain_array[i] ||
811         key_cert_pair_private_key_array[i]) {
812       key_cert_pairs[i].cert_chain = key_cert_pair_cert_chain_array[i];
813       key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i];
814     }
815   }
816   creds = grpc_ssl_server_credentials_create_ex(
817       pem_root_certs, key_cert_pairs, num_key_cert_pairs,
818       force_client_auth
819           ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
820           : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
821       null);
822   gpr_free(key_cert_pairs);
823   return creds;
824 }
825 
826 void grpcwrap_sanity_check_slice(size_t _size, size_t _align) {
827   assert((grpc_slice).sizeof == _size);
828   assert((grpc_slice).alignof == _align);
829 }
830 
831 void grpcwrap_sanity_check_byte_buffer_reader(size_t _size, size_t _align) {
832   assert((grpc_byte_buffer_reader).sizeof == _size);
833   assert((grpc_byte_buffer_reader).alignof == _align);
834 }
835 
836 }