|  | @@ -32,13 +32,17 @@
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "src/core/channel/http_client_filter.h"
 | 
	
		
			
				|  |  |  #include <string.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/alloc.h>
 | 
	
		
			
				|  |  |  #include <grpc/support/log.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/string_util.h>
 | 
	
		
			
				|  |  | +#include "src/core/support/string.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  typedef struct call_data {
 | 
	
		
			
				|  |  |    grpc_linked_mdelem method;
 | 
	
		
			
				|  |  |    grpc_linked_mdelem scheme;
 | 
	
		
			
				|  |  |    grpc_linked_mdelem te_trailers;
 | 
	
		
			
				|  |  |    grpc_linked_mdelem content_type;
 | 
	
		
			
				|  |  | +  grpc_linked_mdelem user_agent;
 | 
	
		
			
				|  |  |    int sent_initial_metadata;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    int got_initial_metadata;
 | 
	
	
		
			
				|  | @@ -58,6 +62,8 @@ typedef struct channel_data {
 | 
	
		
			
				|  |  |    grpc_mdelem *scheme;
 | 
	
		
			
				|  |  |    grpc_mdelem *content_type;
 | 
	
		
			
				|  |  |    grpc_mdelem *status;
 | 
	
		
			
				|  |  | +  /** complete user agent mdelem */
 | 
	
		
			
				|  |  | +  grpc_mdelem *user_agent;
 | 
	
		
			
				|  |  |  } channel_data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* used to silence 'variable not used' warnings */
 | 
	
	
		
			
				|  | @@ -115,6 +121,8 @@ static void hc_mutate_op(grpc_call_element *elem,
 | 
	
		
			
				|  |  |                                     GRPC_MDELEM_REF(channeld->te_trailers));
 | 
	
		
			
				|  |  |        grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
 | 
	
		
			
				|  |  |                                     GRPC_MDELEM_REF(channeld->content_type));
 | 
	
		
			
				|  |  | +      grpc_metadata_batch_add_tail(&op->data.metadata, &calld->user_agent,
 | 
	
		
			
				|  |  | +                                   GRPC_MDELEM_REF(channeld->user_agent));
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -169,6 +177,55 @@ static const char *scheme_from_args(const grpc_channel_args *args) {
 | 
	
		
			
				|  |  |    return "http";
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
 | 
	
		
			
				|  |  | +                                        const grpc_channel_args *args) {
 | 
	
		
			
				|  |  | +  gpr_strvec v;
 | 
	
		
			
				|  |  | +  size_t i;
 | 
	
		
			
				|  |  | +  int is_first = 1;
 | 
	
		
			
				|  |  | +  char *tmp;
 | 
	
		
			
				|  |  | +  grpc_mdstr *result;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_strvec_init(&v);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (i = 0; args && i < args->num_args; i++) {
 | 
	
		
			
				|  |  | +    if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
 | 
	
		
			
				|  |  | +      if (args->args[i].type != GRPC_ARG_STRING) {
 | 
	
		
			
				|  |  | +        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
 | 
	
		
			
				|  |  | +                GRPC_ARG_PRIMARY_USER_AGENT_STRING);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
 | 
	
		
			
				|  |  | +        is_first = 0;
 | 
	
		
			
				|  |  | +        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
 | 
	
		
			
				|  |  | +               grpc_version_string(), GPR_PLATFORM_STRING);
 | 
	
		
			
				|  |  | +  is_first = 0;
 | 
	
		
			
				|  |  | +  gpr_strvec_add(&v, tmp);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (i = 0; args && i < args->num_args; i++) {
 | 
	
		
			
				|  |  | +    if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
 | 
	
		
			
				|  |  | +      if (args->args[i].type != GRPC_ARG_STRING) {
 | 
	
		
			
				|  |  | +        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
 | 
	
		
			
				|  |  | +                GRPC_ARG_SECONDARY_USER_AGENT_STRING);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
 | 
	
		
			
				|  |  | +        is_first = 0;
 | 
	
		
			
				|  |  | +        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tmp = gpr_strvec_flatten(&v, NULL);
 | 
	
		
			
				|  |  | +  gpr_strvec_destroy(&v);
 | 
	
		
			
				|  |  | +  result = grpc_mdstr_from_string(mdctx, tmp);
 | 
	
		
			
				|  |  | +  gpr_free(tmp);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return result;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* Constructor for channel_data */
 | 
	
		
			
				|  |  |  static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
 | 
	
		
			
				|  |  |                                const grpc_channel_args *args, grpc_mdctx *mdctx,
 | 
	
	
		
			
				|  | @@ -189,6 +246,9 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
 | 
	
		
			
				|  |  |    channeld->content_type =
 | 
	
		
			
				|  |  |        grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
 | 
	
		
			
				|  |  |    channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
 | 
	
		
			
				|  |  | +  channeld->user_agent = grpc_mdelem_from_metadata_strings(
 | 
	
		
			
				|  |  | +      mdctx, grpc_mdstr_from_string(mdctx, "user-agent"),
 | 
	
		
			
				|  |  | +      user_agent_from_args(mdctx, args));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Destructor for channel data */
 | 
	
	
		
			
				|  | @@ -201,6 +261,7 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
 | 
	
		
			
				|  |  |    GRPC_MDELEM_UNREF(channeld->scheme);
 | 
	
		
			
				|  |  |    GRPC_MDELEM_UNREF(channeld->content_type);
 | 
	
		
			
				|  |  |    GRPC_MDELEM_UNREF(channeld->status);
 | 
	
		
			
				|  |  | +  GRPC_MDELEM_UNREF(channeld->user_agent);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const grpc_channel_filter grpc_http_client_filter = {
 |