Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc-cloned / deps / grpc / src / core / ext / filters / http / server / http_server_filter.cc
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/support/port_platform.h>
20
21 #include "src/core/ext/filters/http/server/http_server_filter.h"
22
23 #include <grpc/support/alloc.h>
24 #include <grpc/support/log.h>
25 #include <string.h>
26 #include "src/core/lib/channel/channel_args.h"
27 #include "src/core/lib/gprpp/manual_constructor.h"
28 #include "src/core/lib/profiling/timers.h"
29 #include "src/core/lib/slice/b64.h"
30 #include "src/core/lib/slice/percent_encoding.h"
31 #include "src/core/lib/slice/slice_internal.h"
32 #include "src/core/lib/slice/slice_string_helpers.h"
33 #include "src/core/lib/transport/static_metadata.h"
34
35 #define EXPECTED_CONTENT_TYPE "application/grpc"
36 #define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
37
38 static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err);
39 static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err);
40 static void hs_recv_message_ready(void* user_data, grpc_error* err);
41
42 namespace {
43
44 struct call_data {
45   call_data(grpc_call_element* elem, const grpc_call_element_args& args)
46       : call_combiner(args.call_combiner) {
47     GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
48                       hs_recv_initial_metadata_ready, elem,
49                       grpc_schedule_on_exec_ctx);
50     GRPC_CLOSURE_INIT(&recv_message_ready, hs_recv_message_ready, elem,
51                       grpc_schedule_on_exec_ctx);
52     GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
53                       hs_recv_trailing_metadata_ready, elem,
54                       grpc_schedule_on_exec_ctx);
55   }
56
57   ~call_data() {
58     GRPC_ERROR_UNREF(recv_initial_metadata_ready_error);
59     if (have_read_stream) {
60       read_stream->Orphan();
61     }
62   }
63
64   grpc_core::CallCombiner* call_combiner;
65
66   // Outgoing headers to add to send_initial_metadata.
67   grpc_linked_mdelem status;
68   grpc_linked_mdelem content_type;
69
70   // If we see the recv_message contents in the GET query string, we
71   // store it here.
72   grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> read_stream;
73   bool have_read_stream = false;
74
75   // State for intercepting recv_initial_metadata.
76   grpc_closure recv_initial_metadata_ready;
77   grpc_error* recv_initial_metadata_ready_error = GRPC_ERROR_NONE;
78   grpc_closure* original_recv_initial_metadata_ready;
79   grpc_metadata_batch* recv_initial_metadata = nullptr;
80   uint32_t* recv_initial_metadata_flags;
81   bool seen_recv_initial_metadata_ready = false;
82
83   // State for intercepting recv_message.
84   grpc_closure* original_recv_message_ready;
85   grpc_closure recv_message_ready;
86   grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
87   bool seen_recv_message_ready = false;
88
89   // State for intercepting recv_trailing_metadata
90   grpc_closure recv_trailing_metadata_ready;
91   grpc_closure* original_recv_trailing_metadata_ready;
92   grpc_error* recv_trailing_metadata_ready_error;
93   bool seen_recv_trailing_metadata_ready = false;
94 };
95
96 struct channel_data {
97   bool surface_user_agent;
98 };
99
100 }  // namespace
101
102 static grpc_error* hs_filter_outgoing_metadata(grpc_call_element* elem,
103                                                grpc_metadata_batch* b) {
104   if (b->idx.named.grpc_message != nullptr) {
105     grpc_slice pct_encoded_msg = grpc_percent_encode_slice(
106         GRPC_MDVALUE(b->idx.named.grpc_message->md),
107         grpc_compatible_percent_encoding_unreserved_bytes);
108     if (grpc_slice_is_equivalent(pct_encoded_msg,
109                                  GRPC_MDVALUE(b->idx.named.grpc_message->md))) {
110       grpc_slice_unref_internal(pct_encoded_msg);
111     } else {
112       grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_encoded_msg);
113     }
114   }
115   return GRPC_ERROR_NONE;
116 }
117
118 static void hs_add_error(const char* error_name, grpc_error** cumulative,
119                          grpc_error* new_err) {
120   if (new_err == GRPC_ERROR_NONE) return;
121   if (*cumulative == GRPC_ERROR_NONE) {
122     *cumulative = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_name);
123   }
124   *cumulative = grpc_error_add_child(*cumulative, new_err);
125 }
126
127 // Metadata equality within this filter leverages the fact that the sender was
128 // likely using the gRPC chttp2 transport, in which case the encoder would emit
129 // indexed values, in which case the local hpack parser would intern the
130 // relevant metadata, allowing a simple pointer comparison.
131 //
132 // That said, if the header was transmitted sans indexing/encoding, we still
133 // need to do the right thing.
134 //
135 // Assumptions:
136 // 1) The keys for a and b_static must match
137 // 2) b_static must be a statically allocated metadata object.
138 // 3) It is assumed that the remote end is indexing, but not necessary.
139 // TODO(arjunroy): Revisit this method when grpc_mdelem is strongly typed.
140 static bool md_strict_equal(grpc_mdelem a, grpc_mdelem b_static) {
141   // Hpack encoder on the remote side should emit indexed values, in which case
142   // hpack parser on this end should pick up interned values, in which case the
143   // pointer comparison alone is enough.
144   //
145   if (GPR_LIKELY(GRPC_MDELEM_IS_INTERNED(a))) {
146     return a.payload == b_static.payload;
147   } else {
148     return grpc_slice_eq_static_interned(GRPC_MDVALUE(a),
149                                          GRPC_MDVALUE(b_static));
150   }
151 }
152
153 static grpc_error* hs_filter_incoming_metadata(grpc_call_element* elem,
154                                                grpc_metadata_batch* b) {
155   call_data* calld = static_cast<call_data*>(elem->call_data);
156   grpc_error* error = GRPC_ERROR_NONE;
157   static const char* error_name = "Failed processing incoming headers";
158
159   if (b->idx.named.method != nullptr) {
160     if (md_strict_equal(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) {
161       *calld->recv_initial_metadata_flags &=
162           ~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST |
163             GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST);
164     } else if (md_strict_equal(b->idx.named.method->md,
165                                GRPC_MDELEM_METHOD_PUT)) {
166       *calld->recv_initial_metadata_flags &=
167           ~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
168       *calld->recv_initial_metadata_flags |=
169           GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
170     } else if (md_strict_equal(b->idx.named.method->md,
171                                GRPC_MDELEM_METHOD_GET)) {
172       *calld->recv_initial_metadata_flags |=
173           GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
174       *calld->recv_initial_metadata_flags &=
175           ~GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
176     } else {
177       hs_add_error(error_name, &error,
178                    grpc_attach_md_to_error(
179                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
180                        b->idx.named.method->md));
181     }
182     grpc_metadata_batch_remove(b, GRPC_BATCH_METHOD);
183   } else {
184     hs_add_error(
185         error_name, &error,
186         grpc_error_set_str(
187             GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
188             GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":method")));
189   }
190
191   if (b->idx.named.te != nullptr) {
192     if (!grpc_mdelem_static_value_eq(b->idx.named.te->md,
193                                      GRPC_MDELEM_TE_TRAILERS)) {
194       hs_add_error(error_name, &error,
195                    grpc_attach_md_to_error(
196                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
197                        b->idx.named.te->md));
198     }
199     grpc_metadata_batch_remove(b, GRPC_BATCH_TE);
200   } else {
201     hs_add_error(error_name, &error,
202                  grpc_error_set_str(
203                      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
204                      GRPC_ERROR_STR_KEY, grpc_slice_from_static_string("te")));
205   }
206
207   if (b->idx.named.scheme != nullptr) {
208     if (!md_strict_equal(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTP) &&
209         !md_strict_equal(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTPS) &&
210         !grpc_mdelem_static_value_eq(b->idx.named.scheme->md,
211                                      GRPC_MDELEM_SCHEME_GRPC)) {
212       hs_add_error(error_name, &error,
213                    grpc_attach_md_to_error(
214                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
215                        b->idx.named.scheme->md));
216     }
217     grpc_metadata_batch_remove(b, GRPC_BATCH_SCHEME);
218   } else {
219     hs_add_error(
220         error_name, &error,
221         grpc_error_set_str(
222             GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
223             GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":scheme")));
224   }
225
226   if (b->idx.named.content_type != nullptr) {
227     if (!grpc_mdelem_static_value_eq(
228             b->idx.named.content_type->md,
229             GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
230       if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
231                                   EXPECTED_CONTENT_TYPE,
232                                   EXPECTED_CONTENT_TYPE_LENGTH) &&
233           (GRPC_SLICE_START_PTR(GRPC_MDVALUE(
234                b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
235                '+' ||
236            GRPC_SLICE_START_PTR(GRPC_MDVALUE(
237                b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
238                ';')) {
239         /* Although the C implementation doesn't (currently) generate them,
240            any custom +-suffix is explicitly valid. */
241         /* TODO(klempner): We should consider preallocating common values such
242            as +proto or +json, or at least stashing them if we see them. */
243         /* TODO(klempner): Should we be surfacing this to application code? */
244       } else {
245         /* TODO(klempner): We're currently allowing this, but we shouldn't
246            see it without a proxy so log for now. */
247         char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md),
248                                     GPR_DUMP_ASCII);
249         gpr_log(GPR_INFO, "Unexpected content-type '%s'", val);
250         gpr_free(val);
251       }
252     }
253     grpc_metadata_batch_remove(b, GRPC_BATCH_CONTENT_TYPE);
254   }
255
256   if (b->idx.named.path == nullptr) {
257     hs_add_error(
258         error_name, &error,
259         grpc_error_set_str(
260             GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
261             GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":path")));
262   } else if (*calld->recv_initial_metadata_flags &
263              GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) {
264     /* We have a cacheable request made with GET verb. The path contains the
265      * query parameter which is base64 encoded request payload. */
266     const char k_query_separator = '?';
267     grpc_slice path_slice = GRPC_MDVALUE(b->idx.named.path->md);
268     uint8_t* path_ptr = GRPC_SLICE_START_PTR(path_slice);
269     size_t path_length = GRPC_SLICE_LENGTH(path_slice);
270     /* offset of the character '?' */
271     size_t offset = 0;
272     for (offset = 0; offset < path_length && *path_ptr != k_query_separator;
273          path_ptr++, offset++)
274       ;
275     if (offset < path_length) {
276       grpc_slice query_slice =
277           grpc_slice_sub(path_slice, offset + 1, path_length);
278
279       /* substitute path metadata with just the path (not query) */
280       grpc_mdelem mdelem_path_without_query = grpc_mdelem_from_slices(
281           GRPC_MDSTR_PATH, grpc_slice_sub(path_slice, 0, offset));
282
283       grpc_metadata_batch_substitute(b, b->idx.named.path,
284                                      mdelem_path_without_query);
285
286       /* decode payload from query and add to the slice buffer to be returned */
287       const int k_url_safe = 1;
288       grpc_slice_buffer read_slice_buffer;
289       grpc_slice_buffer_init(&read_slice_buffer);
290       grpc_slice_buffer_add(
291           &read_slice_buffer,
292           grpc_base64_decode_with_len(
293               reinterpret_cast<const char*> GRPC_SLICE_START_PTR(query_slice),
294               GRPC_SLICE_LENGTH(query_slice), k_url_safe));
295       calld->read_stream.Init(&read_slice_buffer, 0);
296       grpc_slice_buffer_destroy_internal(&read_slice_buffer);
297       calld->have_read_stream = true;
298       grpc_slice_unref_internal(query_slice);
299     } else {
300       gpr_log(GPR_ERROR, "GET request without QUERY");
301     }
302   }
303
304   if (b->idx.named.host != nullptr && b->idx.named.authority == nullptr) {
305     grpc_linked_mdelem* el = b->idx.named.host;
306     grpc_mdelem md = GRPC_MDELEM_REF(el->md);
307     grpc_metadata_batch_remove(b, el);
308     hs_add_error(
309         error_name, &error,
310         grpc_metadata_batch_add_head(
311             b, el,
312             grpc_mdelem_from_slices(GRPC_MDSTR_AUTHORITY,
313                                     grpc_slice_ref_internal(GRPC_MDVALUE(md))),
314             GRPC_BATCH_AUTHORITY));
315     GRPC_MDELEM_UNREF(md);
316   }
317
318   if (b->idx.named.authority == nullptr) {
319     hs_add_error(
320         error_name, &error,
321         grpc_error_set_str(
322             GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
323             GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":authority")));
324   }
325
326   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
327   if (!chand->surface_user_agent && b->idx.named.user_agent != nullptr) {
328     grpc_metadata_batch_remove(b, GRPC_BATCH_USER_AGENT);
329   }
330
331   return error;
332 }
333
334 static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err) {
335   grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
336   call_data* calld = static_cast<call_data*>(elem->call_data);
337   calld->seen_recv_initial_metadata_ready = true;
338   if (err == GRPC_ERROR_NONE) {
339     err = hs_filter_incoming_metadata(elem, calld->recv_initial_metadata);
340     calld->recv_initial_metadata_ready_error = GRPC_ERROR_REF(err);
341     if (calld->seen_recv_message_ready) {
342       // We've already seen the recv_message callback, but we previously
343       // deferred it, so we need to return it here.
344       // Replace the recv_message byte stream if needed.
345       if (calld->have_read_stream) {
346         calld->recv_message->reset(calld->read_stream.get());
347         calld->have_read_stream = false;
348       }
349       // Re-enter call combiner for original_recv_message_ready, since the
350       // surface code will release the call combiner for each callback it
351       // receives.
352       GRPC_CALL_COMBINER_START(
353           calld->call_combiner, calld->original_recv_message_ready,
354           GRPC_ERROR_REF(err),
355           "resuming recv_message_ready from recv_initial_metadata_ready");
356     }
357   } else {
358     GRPC_ERROR_REF(err);
359   }
360   if (calld->seen_recv_trailing_metadata_ready) {
361     GRPC_CALL_COMBINER_START(calld->call_combiner,
362                              &calld->recv_trailing_metadata_ready,
363                              calld->recv_trailing_metadata_ready_error,
364                              "resuming hs_recv_trailing_metadata_ready from "
365                              "hs_recv_initial_metadata_ready");
366   }
367   GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, err);
368 }
369
370 static void hs_recv_message_ready(void* user_data, grpc_error* err) {
371   grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
372   call_data* calld = static_cast<call_data*>(elem->call_data);
373   calld->seen_recv_message_ready = true;
374   if (calld->seen_recv_initial_metadata_ready) {
375     // We've already seen the recv_initial_metadata callback, so
376     // replace the recv_message byte stream if needed and invoke the
377     // original recv_message callback immediately.
378     if (calld->have_read_stream) {
379       calld->recv_message->reset(calld->read_stream.get());
380       calld->have_read_stream = false;
381     }
382     GRPC_CLOSURE_RUN(calld->original_recv_message_ready, GRPC_ERROR_REF(err));
383   } else {
384     // We have not yet seen the recv_initial_metadata callback, so we
385     // need to wait to see if this is a GET request.
386     // Note that we release the call combiner here, so that other
387     // callbacks can run.
388     GRPC_CALL_COMBINER_STOP(
389         calld->call_combiner,
390         "pausing recv_message_ready until recv_initial_metadata_ready");
391   }
392 }
393
394 static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err) {
395   grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
396   call_data* calld = static_cast<call_data*>(elem->call_data);
397   if (!calld->seen_recv_initial_metadata_ready) {
398     calld->recv_trailing_metadata_ready_error = GRPC_ERROR_REF(err);
399     calld->seen_recv_trailing_metadata_ready = true;
400     GRPC_CALL_COMBINER_STOP(calld->call_combiner,
401                             "deferring hs_recv_trailing_metadata_ready until "
402                             "ater hs_recv_initial_metadata_ready");
403     return;
404   }
405   err = grpc_error_add_child(
406       GRPC_ERROR_REF(err),
407       GRPC_ERROR_REF(calld->recv_initial_metadata_ready_error));
408   GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err);
409 }
410
411 static grpc_error* hs_mutate_op(grpc_call_element* elem,
412                                 grpc_transport_stream_op_batch* op) {
413   /* grab pointers to our data from the call element */
414   call_data* calld = static_cast<call_data*>(elem->call_data);
415
416   if (op->send_initial_metadata) {
417     grpc_error* error = GRPC_ERROR_NONE;
418     static const char* error_name = "Failed sending initial metadata";
419     hs_add_error(
420         error_name, &error,
421         grpc_metadata_batch_add_head(
422             op->payload->send_initial_metadata.send_initial_metadata,
423             &calld->status, GRPC_MDELEM_STATUS_200, GRPC_BATCH_STATUS));
424     hs_add_error(error_name, &error,
425                  grpc_metadata_batch_add_tail(
426                      op->payload->send_initial_metadata.send_initial_metadata,
427                      &calld->content_type,
428                      GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
429                      GRPC_BATCH_CONTENT_TYPE));
430     hs_add_error(
431         error_name, &error,
432         hs_filter_outgoing_metadata(
433             elem, op->payload->send_initial_metadata.send_initial_metadata));
434     if (error != GRPC_ERROR_NONE) return error;
435   }
436
437   if (op->recv_initial_metadata) {
438     /* substitute our callback for the higher callback */
439     GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags != nullptr);
440     calld->recv_initial_metadata =
441         op->payload->recv_initial_metadata.recv_initial_metadata;
442     calld->recv_initial_metadata_flags =
443         op->payload->recv_initial_metadata.recv_flags;
444     calld->original_recv_initial_metadata_ready =
445         op->payload->recv_initial_metadata.recv_initial_metadata_ready;
446     op->payload->recv_initial_metadata.recv_initial_metadata_ready =
447         &calld->recv_initial_metadata_ready;
448   }
449
450   if (op->recv_message) {
451     calld->recv_message = op->payload->recv_message.recv_message;
452     calld->original_recv_message_ready =
453         op->payload->recv_message.recv_message_ready;
454     op->payload->recv_message.recv_message_ready = &calld->recv_message_ready;
455   }
456
457   if (op->recv_trailing_metadata) {
458     calld->original_recv_trailing_metadata_ready =
459         op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
460     op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
461         &calld->recv_trailing_metadata_ready;
462   }
463
464   if (op->send_trailing_metadata) {
465     grpc_error* error = hs_filter_outgoing_metadata(
466         elem, op->payload->send_trailing_metadata.send_trailing_metadata);
467     if (error != GRPC_ERROR_NONE) return error;
468   }
469
470   return GRPC_ERROR_NONE;
471 }
472
473 static void hs_start_transport_stream_op_batch(
474     grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
475   GPR_TIMER_SCOPE("hs_start_transport_stream_op_batch", 0);
476   call_data* calld = static_cast<call_data*>(elem->call_data);
477   grpc_error* error = hs_mutate_op(elem, op);
478   if (error != GRPC_ERROR_NONE) {
479     grpc_transport_stream_op_batch_finish_with_failure(op, error,
480                                                        calld->call_combiner);
481   } else {
482     grpc_call_next_op(elem, op);
483   }
484 }
485
486 /* Constructor for call_data */
487 static grpc_error* hs_init_call_elem(grpc_call_element* elem,
488                                      const grpc_call_element_args* args) {
489   new (elem->call_data) call_data(elem, *args);
490   return GRPC_ERROR_NONE;
491 }
492
493 /* Destructor for call_data */
494 static void hs_destroy_call_elem(grpc_call_element* elem,
495                                  const grpc_call_final_info* final_info,
496                                  grpc_closure* ignored) {
497   call_data* calld = static_cast<call_data*>(elem->call_data);
498   calld->~call_data();
499 }
500
501 /* Constructor for channel_data */
502 static grpc_error* hs_init_channel_elem(grpc_channel_element* elem,
503                                         grpc_channel_element_args* args) {
504   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
505   GPR_ASSERT(!args->is_last);
506   chand->surface_user_agent = grpc_channel_arg_get_bool(
507       grpc_channel_args_find(args->channel_args,
508                              const_cast<char*>(GRPC_ARG_SURFACE_USER_AGENT)),
509       true);
510   return GRPC_ERROR_NONE;
511 }
512
513 /* Destructor for channel data */
514 static void hs_destroy_channel_elem(grpc_channel_element* elem) {}
515
516 const grpc_channel_filter grpc_http_server_filter = {
517     hs_start_transport_stream_op_batch,
518     grpc_channel_next_op,
519     sizeof(call_data),
520     hs_init_call_elem,
521     grpc_call_stack_ignore_set_pollset_or_pollset_set,
522     hs_destroy_call_elem,
523     sizeof(channel_data),
524     hs_init_channel_elem,
525     hs_destroy_channel_elem,
526     grpc_channel_next_get_info,
527     "http-server"};