Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc-cloned / deps / grpc / src / core / ext / filters / http / client / http_client_filter.cc
1 /*
2  * Copyright 2015 gRPC authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <grpc/support/port_platform.h>
19
20 #include <grpc/support/alloc.h>
21 #include <grpc/support/log.h>
22 #include <grpc/support/string_util.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include "src/core/ext/filters/http/client/http_client_filter.h"
26 #include "src/core/lib/gpr/string.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 #include "src/core/lib/transport/transport_impl.h"
35
36 #define EXPECTED_CONTENT_TYPE "application/grpc"
37 #define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
38
39 /* default maximum size of payload eligible for GET request */
40 static constexpr size_t kMaxPayloadSizeForGet = 2048;
41
42 static void recv_initial_metadata_ready(void* user_data, grpc_error* error);
43 static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
44 static void on_send_message_next_done(void* arg, grpc_error* error);
45 static void send_message_on_complete(void* arg, grpc_error* error);
46
47 namespace {
48 struct call_data {
49   call_data(grpc_call_element* elem, const grpc_call_element_args& args)
50       : call_combiner(args.call_combiner) {
51     GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
52                       ::recv_initial_metadata_ready, elem,
53                       grpc_schedule_on_exec_ctx);
54     GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
55                       ::recv_trailing_metadata_ready, elem,
56                       grpc_schedule_on_exec_ctx);
57     GRPC_CLOSURE_INIT(&on_send_message_next_done, ::on_send_message_next_done,
58                       elem, grpc_schedule_on_exec_ctx);
59     GRPC_CLOSURE_INIT(&send_message_on_complete, ::send_message_on_complete,
60                       elem, grpc_schedule_on_exec_ctx);
61   }
62
63   ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
64
65   grpc_core::CallCombiner* call_combiner;
66   // State for handling send_initial_metadata ops.
67   grpc_linked_mdelem method;
68   grpc_linked_mdelem scheme;
69   grpc_linked_mdelem authority;
70   grpc_linked_mdelem te_trailers;
71   grpc_linked_mdelem content_type;
72   grpc_linked_mdelem user_agent;
73   // State for handling recv_initial_metadata ops.
74   grpc_metadata_batch* recv_initial_metadata;
75   grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
76   grpc_closure* original_recv_initial_metadata_ready = nullptr;
77   grpc_closure recv_initial_metadata_ready;
78   // State for handling recv_trailing_metadata ops.
79   grpc_metadata_batch* recv_trailing_metadata;
80   grpc_closure* original_recv_trailing_metadata_ready;
81   grpc_closure recv_trailing_metadata_ready;
82   grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE;
83   bool seen_recv_trailing_metadata_ready = false;
84   // State for handling send_message ops.
85   grpc_transport_stream_op_batch* send_message_batch;
86   size_t send_message_bytes_read = 0;
87   grpc_core::ManualConstructor<grpc_core::ByteStreamCache> send_message_cache;
88   grpc_core::ManualConstructor<grpc_core::ByteStreamCache::CachingByteStream>
89       send_message_caching_stream;
90   grpc_closure on_send_message_next_done;
91   grpc_closure* original_send_message_on_complete;
92   grpc_closure send_message_on_complete;
93 };
94
95 struct channel_data {
96   grpc_mdelem static_scheme;
97   grpc_mdelem user_agent;
98   size_t max_payload_size_for_get;
99 };
100 }  // namespace
101
102 static grpc_error* client_filter_incoming_metadata(grpc_call_element* elem,
103                                                    grpc_metadata_batch* b) {
104   if (b->idx.named.status != nullptr) {
105     /* If both gRPC status and HTTP status are provided in the response, we
106      * should prefer the gRPC status code, as mentioned in
107      * https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
108      */
109     if (b->idx.named.grpc_status != nullptr ||
110         grpc_mdelem_static_value_eq(b->idx.named.status->md,
111                                     GRPC_MDELEM_STATUS_200)) {
112       grpc_metadata_batch_remove(b, GRPC_BATCH_STATUS);
113     } else {
114       char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.status->md),
115                                   GPR_DUMP_ASCII);
116       char* msg;
117       gpr_asprintf(&msg, "Received http2 header with status: %s", val);
118       grpc_error* e = grpc_error_set_str(
119           grpc_error_set_int(
120               grpc_error_set_str(
121                   GRPC_ERROR_CREATE_FROM_STATIC_STRING(
122                       "Received http2 :status header with non-200 OK status"),
123                   GRPC_ERROR_STR_VALUE, grpc_slice_from_copied_string(val)),
124               GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED),
125           GRPC_ERROR_STR_GRPC_MESSAGE, grpc_slice_from_copied_string(msg));
126       gpr_free(val);
127       gpr_free(msg);
128       return e;
129     }
130   }
131
132   if (b->idx.named.grpc_message != nullptr) {
133     grpc_slice pct_decoded_msg = grpc_permissive_percent_decode_slice(
134         GRPC_MDVALUE(b->idx.named.grpc_message->md));
135     if (grpc_slice_is_equivalent(pct_decoded_msg,
136                                  GRPC_MDVALUE(b->idx.named.grpc_message->md))) {
137       grpc_slice_unref_internal(pct_decoded_msg);
138     } else {
139       grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_decoded_msg);
140     }
141   }
142
143   if (b->idx.named.content_type != nullptr) {
144     if (!grpc_mdelem_static_value_eq(
145             b->idx.named.content_type->md,
146             GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
147       if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
148                                   EXPECTED_CONTENT_TYPE,
149                                   EXPECTED_CONTENT_TYPE_LENGTH) &&
150           (GRPC_SLICE_START_PTR(GRPC_MDVALUE(
151                b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
152                '+' ||
153            GRPC_SLICE_START_PTR(GRPC_MDVALUE(
154                b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
155                ';')) {
156         /* Although the C implementation doesn't (currently) generate them,
157            any custom +-suffix is explicitly valid. */
158         /* TODO(klempner): We should consider preallocating common values such
159            as +proto or +json, or at least stashing them if we see them. */
160         /* TODO(klempner): Should we be surfacing this to application code? */
161       } else {
162         /* TODO(klempner): We're currently allowing this, but we shouldn't
163            see it without a proxy so log for now. */
164         char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md),
165                                     GPR_DUMP_ASCII);
166         gpr_log(GPR_INFO, "Unexpected content-type '%s'", val);
167         gpr_free(val);
168       }
169     }
170     grpc_metadata_batch_remove(b, GRPC_BATCH_CONTENT_TYPE);
171   }
172
173   return GRPC_ERROR_NONE;
174 }
175
176 static void recv_initial_metadata_ready(void* user_data, grpc_error* error) {
177   grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
178   call_data* calld = static_cast<call_data*>(elem->call_data);
179   if (error == GRPC_ERROR_NONE) {
180     error = client_filter_incoming_metadata(elem, calld->recv_initial_metadata);
181     calld->recv_initial_metadata_error = GRPC_ERROR_REF(error);
182   } else {
183     GRPC_ERROR_REF(error);
184   }
185   grpc_closure* closure = calld->original_recv_initial_metadata_ready;
186   calld->original_recv_initial_metadata_ready = nullptr;
187   if (calld->seen_recv_trailing_metadata_ready) {
188     GRPC_CALL_COMBINER_START(
189         calld->call_combiner, &calld->recv_trailing_metadata_ready,
190         calld->recv_trailing_metadata_error, "continue recv_trailing_metadata");
191   }
192   GRPC_CLOSURE_RUN(closure, error);
193 }
194
195 static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) {
196   grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
197   call_data* calld = static_cast<call_data*>(elem->call_data);
198   if (calld->original_recv_initial_metadata_ready != nullptr) {
199     calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error);
200     calld->seen_recv_trailing_metadata_ready = true;
201     GRPC_CALL_COMBINER_STOP(calld->call_combiner,
202                             "deferring recv_trailing_metadata_ready until "
203                             "after recv_initial_metadata_ready");
204     return;
205   }
206   if (error == GRPC_ERROR_NONE) {
207     error =
208         client_filter_incoming_metadata(elem, calld->recv_trailing_metadata);
209   } else {
210     GRPC_ERROR_REF(error);
211   }
212   error = grpc_error_add_child(
213       error, GRPC_ERROR_REF(calld->recv_initial_metadata_error));
214   GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error);
215 }
216
217 static void send_message_on_complete(void* arg, grpc_error* error) {
218   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
219   call_data* calld = static_cast<call_data*>(elem->call_data);
220   calld->send_message_cache.Destroy();
221   GRPC_CLOSURE_RUN(calld->original_send_message_on_complete,
222                    GRPC_ERROR_REF(error));
223 }
224
225 // Pulls a slice from the send_message byte stream, updating
226 // calld->send_message_bytes_read.
227 static grpc_error* pull_slice_from_send_message(call_data* calld) {
228   grpc_slice incoming_slice;
229   grpc_error* error = calld->send_message_caching_stream->Pull(&incoming_slice);
230   if (error == GRPC_ERROR_NONE) {
231     calld->send_message_bytes_read += GRPC_SLICE_LENGTH(incoming_slice);
232     grpc_slice_unref_internal(incoming_slice);
233   }
234   return error;
235 }
236
237 // Reads as many slices as possible from the send_message byte stream.
238 // Upon successful return, if calld->send_message_bytes_read ==
239 // calld->send_message_caching_stream->length(), then we have completed
240 // reading from the byte stream; otherwise, an async read has been dispatched
241 // and on_send_message_next_done() will be invoked when it is complete.
242 static grpc_error* read_all_available_send_message_data(call_data* calld) {
243   while (calld->send_message_caching_stream->Next(
244       SIZE_MAX, &calld->on_send_message_next_done)) {
245     grpc_error* error = pull_slice_from_send_message(calld);
246     if (error != GRPC_ERROR_NONE) return error;
247     if (calld->send_message_bytes_read ==
248         calld->send_message_caching_stream->length()) {
249       break;
250     }
251   }
252   return GRPC_ERROR_NONE;
253 }
254
255 // Async callback for ByteStream::Next().
256 static void on_send_message_next_done(void* arg, grpc_error* error) {
257   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
258   call_data* calld = static_cast<call_data*>(elem->call_data);
259   if (error != GRPC_ERROR_NONE) {
260     grpc_transport_stream_op_batch_finish_with_failure(
261         calld->send_message_batch, error, calld->call_combiner);
262     return;
263   }
264   error = pull_slice_from_send_message(calld);
265   if (error != GRPC_ERROR_NONE) {
266     grpc_transport_stream_op_batch_finish_with_failure(
267         calld->send_message_batch, error, calld->call_combiner);
268     return;
269   }
270   // There may or may not be more to read, but we don't care.  If we got
271   // here, then we know that all of the data was not available
272   // synchronously, so we were not able to do a cached call.  Instead,
273   // we just reset the byte stream and then send down the batch as-is.
274   calld->send_message_caching_stream->Reset();
275   grpc_call_next_op(elem, calld->send_message_batch);
276 }
277
278 static char* slice_buffer_to_string(grpc_slice_buffer* slice_buffer) {
279   char* payload_bytes =
280       static_cast<char*>(gpr_malloc(slice_buffer->length + 1));
281   size_t offset = 0;
282   for (size_t i = 0; i < slice_buffer->count; ++i) {
283     memcpy(payload_bytes + offset,
284            GRPC_SLICE_START_PTR(slice_buffer->slices[i]),
285            GRPC_SLICE_LENGTH(slice_buffer->slices[i]));
286     offset += GRPC_SLICE_LENGTH(slice_buffer->slices[i]);
287   }
288   *(payload_bytes + offset) = '\0';
289   return payload_bytes;
290 }
291
292 // Modifies the path entry in the batch's send_initial_metadata to
293 // append the base64-encoded query for a GET request.
294 static grpc_error* update_path_for_get(grpc_call_element* elem,
295                                        grpc_transport_stream_op_batch* batch) {
296   call_data* calld = static_cast<call_data*>(elem->call_data);
297   grpc_slice path_slice =
298       GRPC_MDVALUE(batch->payload->send_initial_metadata.send_initial_metadata
299                        ->idx.named.path->md);
300   /* sum up individual component's lengths and allocate enough memory to
301    * hold combined path+query */
302   size_t estimated_len = GRPC_SLICE_LENGTH(path_slice);
303   estimated_len++; /* for the '?' */
304   estimated_len += grpc_base64_estimate_encoded_size(
305       batch->payload->send_message.send_message->length(), true /* url_safe */,
306       false /* multi_line */);
307   grpc_core::UnmanagedMemorySlice path_with_query_slice(estimated_len);
308   /* memcopy individual pieces into this slice */
309   char* write_ptr =
310       reinterpret_cast<char*> GRPC_SLICE_START_PTR(path_with_query_slice);
311   char* original_path =
312       reinterpret_cast<char*> GRPC_SLICE_START_PTR(path_slice);
313   memcpy(write_ptr, original_path, GRPC_SLICE_LENGTH(path_slice));
314   write_ptr += GRPC_SLICE_LENGTH(path_slice);
315   *write_ptr++ = '?';
316   char* payload_bytes =
317       slice_buffer_to_string(calld->send_message_cache->cache_buffer());
318   grpc_base64_encode_core(write_ptr, payload_bytes,
319                           batch->payload->send_message.send_message->length(),
320                           true /* url_safe */, false /* multi_line */);
321   gpr_free(payload_bytes);
322   /* remove trailing unused memory and add trailing 0 to terminate string */
323   char* t = reinterpret_cast<char*> GRPC_SLICE_START_PTR(path_with_query_slice);
324   /* safe to use strlen since base64_encode will always add '\0' */
325   path_with_query_slice =
326       grpc_slice_sub_no_ref(path_with_query_slice, 0, strlen(t));
327   /* substitute previous path with the new path+query */
328   grpc_mdelem mdelem_path_and_query =
329       grpc_mdelem_from_slices(GRPC_MDSTR_PATH, path_with_query_slice);
330   grpc_metadata_batch* b =
331       batch->payload->send_initial_metadata.send_initial_metadata;
332   return grpc_metadata_batch_substitute(b, b->idx.named.path,
333                                         mdelem_path_and_query);
334 }
335
336 static void remove_if_present(grpc_metadata_batch* batch,
337                               grpc_metadata_batch_callouts_index idx) {
338   if (batch->idx.array[idx] != nullptr) {
339     grpc_metadata_batch_remove(batch, idx);
340   }
341 }
342
343 static void hc_start_transport_stream_op_batch(
344     grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
345   call_data* calld = static_cast<call_data*>(elem->call_data);
346   channel_data* channeld = static_cast<channel_data*>(elem->channel_data);
347   GPR_TIMER_SCOPE("hc_start_transport_stream_op_batch", 0);
348
349   if (batch->recv_initial_metadata) {
350     /* substitute our callback for the higher callback */
351     calld->recv_initial_metadata =
352         batch->payload->recv_initial_metadata.recv_initial_metadata;
353     calld->original_recv_initial_metadata_ready =
354         batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
355     batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
356         &calld->recv_initial_metadata_ready;
357   }
358
359   if (batch->recv_trailing_metadata) {
360     /* substitute our callback for the higher callback */
361     calld->recv_trailing_metadata =
362         batch->payload->recv_trailing_metadata.recv_trailing_metadata;
363     calld->original_recv_trailing_metadata_ready =
364         batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
365     batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
366         &calld->recv_trailing_metadata_ready;
367   }
368
369   grpc_error* error = GRPC_ERROR_NONE;
370   bool batch_will_be_handled_asynchronously = false;
371   if (batch->send_initial_metadata) {
372     // Decide which HTTP VERB to use. We use GET if the request is marked
373     // cacheable, and the operation contains both initial metadata and send
374     // message, and the payload is below the size threshold, and all the data
375     // for this request is immediately available.
376     grpc_mdelem method = GRPC_MDELEM_METHOD_POST;
377     if (batch->send_message &&
378         (batch->payload->send_initial_metadata.send_initial_metadata_flags &
379          GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) &&
380         batch->payload->send_message.send_message->length() <
381             channeld->max_payload_size_for_get) {
382       calld->send_message_bytes_read = 0;
383       calld->send_message_cache.Init(
384           std::move(batch->payload->send_message.send_message));
385       calld->send_message_caching_stream.Init(calld->send_message_cache.get());
386       batch->payload->send_message.send_message.reset(
387           calld->send_message_caching_stream.get());
388       calld->original_send_message_on_complete = batch->on_complete;
389       batch->on_complete = &calld->send_message_on_complete;
390       calld->send_message_batch = batch;
391       error = read_all_available_send_message_data(calld);
392       if (error != GRPC_ERROR_NONE) goto done;
393       // If all the data has been read, then we can use GET.
394       if (calld->send_message_bytes_read ==
395           calld->send_message_caching_stream->length()) {
396         method = GRPC_MDELEM_METHOD_GET;
397         error = update_path_for_get(elem, batch);
398         if (error != GRPC_ERROR_NONE) goto done;
399         batch->send_message = false;
400         calld->send_message_caching_stream->Orphan();
401       } else {
402         // Not all data is available.  The batch will be sent down
403         // asynchronously in on_send_message_next_done().
404         batch_will_be_handled_asynchronously = true;
405         // Fall back to POST.
406         gpr_log(GPR_DEBUG,
407                 "Request is marked Cacheable but not all data is available.  "
408                 "Falling back to POST");
409       }
410     } else if (batch->payload->send_initial_metadata
411                    .send_initial_metadata_flags &
412                GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) {
413       method = GRPC_MDELEM_METHOD_PUT;
414     }
415
416     remove_if_present(
417         batch->payload->send_initial_metadata.send_initial_metadata,
418         GRPC_BATCH_METHOD);
419     remove_if_present(
420         batch->payload->send_initial_metadata.send_initial_metadata,
421         GRPC_BATCH_SCHEME);
422     remove_if_present(
423         batch->payload->send_initial_metadata.send_initial_metadata,
424         GRPC_BATCH_TE);
425     remove_if_present(
426         batch->payload->send_initial_metadata.send_initial_metadata,
427         GRPC_BATCH_CONTENT_TYPE);
428     remove_if_present(
429         batch->payload->send_initial_metadata.send_initial_metadata,
430         GRPC_BATCH_USER_AGENT);
431
432     /* Send : prefixed headers, which have to be before any application
433        layer headers. */
434     error = grpc_metadata_batch_add_head(
435         batch->payload->send_initial_metadata.send_initial_metadata,
436         &calld->method, method, GRPC_BATCH_METHOD);
437     if (error != GRPC_ERROR_NONE) goto done;
438     error = grpc_metadata_batch_add_head(
439         batch->payload->send_initial_metadata.send_initial_metadata,
440         &calld->scheme, channeld->static_scheme, GRPC_BATCH_SCHEME);
441     if (error != GRPC_ERROR_NONE) goto done;
442     error = grpc_metadata_batch_add_tail(
443         batch->payload->send_initial_metadata.send_initial_metadata,
444         &calld->te_trailers, GRPC_MDELEM_TE_TRAILERS, GRPC_BATCH_TE);
445     if (error != GRPC_ERROR_NONE) goto done;
446     error = grpc_metadata_batch_add_tail(
447         batch->payload->send_initial_metadata.send_initial_metadata,
448         &calld->content_type, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
449         GRPC_BATCH_CONTENT_TYPE);
450     if (error != GRPC_ERROR_NONE) goto done;
451     error = grpc_metadata_batch_add_tail(
452         batch->payload->send_initial_metadata.send_initial_metadata,
453         &calld->user_agent, GRPC_MDELEM_REF(channeld->user_agent),
454         GRPC_BATCH_USER_AGENT);
455     if (error != GRPC_ERROR_NONE) goto done;
456   }
457
458 done:
459   if (error != GRPC_ERROR_NONE) {
460     grpc_transport_stream_op_batch_finish_with_failure(
461         calld->send_message_batch, error, calld->call_combiner);
462   } else if (!batch_will_be_handled_asynchronously) {
463     grpc_call_next_op(elem, batch);
464   }
465 }
466
467 /* Constructor for call_data */
468 static grpc_error* init_call_elem(grpc_call_element* elem,
469                                   const grpc_call_element_args* args) {
470   new (elem->call_data) call_data(elem, *args);
471   return GRPC_ERROR_NONE;
472 }
473
474 /* Destructor for call_data */
475 static void destroy_call_elem(grpc_call_element* elem,
476                               const grpc_call_final_info* final_info,
477                               grpc_closure* ignored) {
478   call_data* calld = static_cast<call_data*>(elem->call_data);
479   calld->~call_data();
480 }
481
482 static grpc_mdelem scheme_from_args(const grpc_channel_args* args) {
483   unsigned i;
484   size_t j;
485   grpc_mdelem valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
486                                  GRPC_MDELEM_SCHEME_HTTPS};
487   if (args != nullptr) {
488     for (i = 0; i < args->num_args; ++i) {
489       if (args->args[i].type == GRPC_ARG_STRING &&
490           strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
491         for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
492           if (0 == grpc_slice_str_cmp(GRPC_MDVALUE(valid_schemes[j]),
493                                       args->args[i].value.string)) {
494             return valid_schemes[j];
495           }
496         }
497       }
498     }
499   }
500   return GRPC_MDELEM_SCHEME_HTTP;
501 }
502
503 static size_t max_payload_size_from_args(const grpc_channel_args* args) {
504   if (args != nullptr) {
505     for (size_t i = 0; i < args->num_args; ++i) {
506       if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) {
507         if (args->args[i].type != GRPC_ARG_INTEGER) {
508           gpr_log(GPR_ERROR, "%s: must be an integer",
509                   GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET);
510         } else {
511           return static_cast<size_t>(args->args[i].value.integer);
512         }
513       }
514     }
515   }
516   return kMaxPayloadSizeForGet;
517 }
518
519 static grpc_core::ManagedMemorySlice user_agent_from_args(
520     const grpc_channel_args* args, const char* transport_name) {
521   gpr_strvec v;
522   size_t i;
523   int is_first = 1;
524   char* tmp;
525
526   gpr_strvec_init(&v);
527
528   for (i = 0; args && i < args->num_args; i++) {
529     if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
530       if (args->args[i].type != GRPC_ARG_STRING) {
531         gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
532                 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
533       } else {
534         if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
535         is_first = 0;
536         gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
537       }
538     }
539   }
540
541   gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s; %s)", is_first ? "" : " ",
542                grpc_version_string(), GPR_PLATFORM_STRING, transport_name,
543                grpc_g_stands_for());
544   is_first = 0;
545   gpr_strvec_add(&v, tmp);
546
547   for (i = 0; args && i < args->num_args; i++) {
548     if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
549       if (args->args[i].type != GRPC_ARG_STRING) {
550         gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
551                 GRPC_ARG_SECONDARY_USER_AGENT_STRING);
552       } else {
553         if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
554         is_first = 0;
555         gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
556       }
557     }
558   }
559
560   tmp = gpr_strvec_flatten(&v, nullptr);
561   gpr_strvec_destroy(&v);
562   grpc_core::ManagedMemorySlice result(tmp);
563   gpr_free(tmp);
564
565   return result;
566 }
567
568 /* Constructor for channel_data */
569 static grpc_error* init_channel_elem(grpc_channel_element* elem,
570                                      grpc_channel_element_args* args) {
571   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
572   GPR_ASSERT(!args->is_last);
573   GPR_ASSERT(args->optional_transport != nullptr);
574   chand->static_scheme = scheme_from_args(args->channel_args);
575   chand->max_payload_size_for_get =
576       max_payload_size_from_args(args->channel_args);
577   chand->user_agent = grpc_mdelem_from_slices(
578       GRPC_MDSTR_USER_AGENT,
579       user_agent_from_args(args->channel_args,
580                            args->optional_transport->vtable->name));
581   return GRPC_ERROR_NONE;
582 }
583
584 /* Destructor for channel data */
585 static void destroy_channel_elem(grpc_channel_element* elem) {
586   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
587   GRPC_MDELEM_UNREF(chand->user_agent);
588 }
589
590 const grpc_channel_filter grpc_http_client_filter = {
591     hc_start_transport_stream_op_batch,
592     grpc_channel_next_op,
593     sizeof(call_data),
594     init_call_elem,
595     grpc_call_stack_ignore_set_pollset_or_pollset_set,
596     destroy_call_elem,
597     sizeof(channel_data),
598     init_channel_elem,
599     destroy_channel_elem,
600     grpc_channel_next_get_info,
601     "http-client"};