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