From 0dc5e6cf163f1451dfb1677ce61966be3ab1f062 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 10 Jul 2015 10:07:53 -0700 Subject: [PATCH] User agent string support --- include/grpc++/channel_arguments.h | 12 +++-- include/grpc/grpc.h | 6 +++ include/grpc/support/port_platform.h | 12 +++++ src/core/channel/http_client_filter.c | 61 +++++++++++++++++++++++ src/core/transport/chttp2/parsing.c | 2 +- src/cpp/client/channel_arguments.cc | 38 ++++++++++++++ src/cpp/client/create_channel.cc | 9 +++- test/core/end2end/dualstack_socket_test.c | 4 ++ test/cpp/end2end/end2end_test.cc | 7 +-- test/cpp/qps/qps_test.cc | 10 ++-- 10 files changed, 147 insertions(+), 14 deletions(-) diff --git a/include/grpc++/channel_arguments.h b/include/grpc++/channel_arguments.h index 68f24cde4af..c7e8d24b65b 100644 --- a/include/grpc++/channel_arguments.h +++ b/include/grpc++/channel_arguments.h @@ -54,6 +54,14 @@ class ChannelArguments { ChannelArguments() {} ~ChannelArguments() {} + ChannelArguments(const ChannelArguments& other); + ChannelArguments& operator=(ChannelArguments other) { + Swap(other); + return *this; + } + + void Swap(ChannelArguments& other); + // grpc specific channel argument setters // Set target name override for SSL host name checking. void SetSslTargetNameOverride(const grpc::string& name); @@ -73,10 +81,6 @@ class ChannelArguments { friend class SecureCredentials; friend class testing::ChannelArgumentsTest; - // TODO(yangg) implement copy and assign - ChannelArguments(const ChannelArguments&); - ChannelArguments& operator=(const ChannelArguments&); - // Returns empty string when it is not set. grpc::string GetSslTargetNameOverride() const; diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 3c72c1db27a..35a2c37ca9b 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -117,6 +117,12 @@ typedef struct { /* Initial sequence number for http2 transports */ #define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \ "grpc.http2.initial_sequence_number" +/** Primary user agent: goes at the start of the user-agent metadata + sent on each request */ +#define GRPC_ARG_PRIMARY_USER_AGENT_STRING "grpc.primary_user_agent" +/** Secondary user agent: goes at the end of the user-agent metadata + sent on each request */ +#define GRPC_ARG_SECONDARY_USER_AGENT_STRING "grpc.secondary_user_agent" /** Connectivity state of a channel. */ typedef enum { diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h index a5d1b627028..57fed18cf68 100644 --- a/include/grpc/support/port_platform.h +++ b/include/grpc/support/port_platform.h @@ -71,6 +71,7 @@ #if !defined(GPR_NO_AUTODETECT_PLATFORM) #if defined(_WIN64) || defined(WIN64) +#define GPR_PLATFORM_STRING "windows" #define GPR_WIN32 1 #define GPR_ARCH_64 1 #define GPR_GETPID_IN_PROCESS_H 1 @@ -84,6 +85,7 @@ #endif #define GPR_WINDOWS_CRASH_HANDLER 1 #elif defined(_WIN32) || defined(WIN32) +#define GPR_PLATFORM_STRING "windows" #define GPR_ARCH_32 1 #define GPR_WIN32 1 #define GPR_GETPID_IN_PROCESS_H 1 @@ -97,6 +99,7 @@ #endif #define GPR_WINDOWS_CRASH_HANDLER 1 #elif defined(ANDROID) || defined(__ANDROID__) +#define GPR_PLATFORM_STRING "android" #define GPR_ANDROID 1 #define GPR_ARCH_32 1 #define GPR_CPU_LINUX 1 @@ -117,6 +120,7 @@ #define GPR_GETPID_IN_UNISTD_H 1 #define GPR_HAVE_MSG_NOSIGNAL 1 #elif defined(__linux__) +#define GPR_PLATFORM_STRING "linux" #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif @@ -173,9 +177,11 @@ #define _BSD_SOURCE #endif #if TARGET_OS_IPHONE +#define GPR_PLATFORM_STRING "ios" #define GPR_CPU_IPHONE 1 #define GPR_PTHREAD_TLS 1 #else /* TARGET_OS_IPHONE */ +#define GPR_PLATFORM_STRING "osx" #define GPR_CPU_POSIX 1 #define GPR_GCC_TLS 1 #endif @@ -201,6 +207,7 @@ #define GPR_ARCH_32 1 #endif /* _LP64 */ #elif defined(__FreeBSD__) +#define GPR_PLATFORM_STRING "freebsd" #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif @@ -232,6 +239,11 @@ #endif #endif /* GPR_NO_AUTODETECT_PLATFORM */ +#ifndef GPR_PLATFORM_STRING +#warning "GPR_PLATFORM_STRING not auto-detected" +#define GPR_PLATFORM_STRING "unknown" +#endif + /* For a common case, assume that the platform has a C99-like stdint.h */ #include diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c index 63e4912397c..bc821b16fa2 100644 --- a/src/core/channel/http_client_filter.c +++ b/src/core/channel/http_client_filter.c @@ -32,13 +32,17 @@ #include "src/core/channel/http_client_filter.h" #include +#include #include +#include +#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 = { diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c index aa32f2e44a1..904b9afce7e 100644 --- a/src/core/transport/chttp2/parsing.c +++ b/src/core/transport/chttp2/parsing.c @@ -588,7 +588,7 @@ static void on_header(void *tp, grpc_mdelem *md) { GPR_ASSERT(stream_parsing); GRPC_CHTTP2_IF_TRACING(gpr_log( - GPR_INFO, "HTTP:%d:HDR: %s: %s", stream_parsing->id, + GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", stream_parsing->id, transport_parsing->is_client ? "CLI" : "SVR", grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value))); diff --git a/src/cpp/client/channel_arguments.cc b/src/cpp/client/channel_arguments.cc index b271650673c..22abe407a76 100644 --- a/src/cpp/client/channel_arguments.cc +++ b/src/cpp/client/channel_arguments.cc @@ -33,10 +33,48 @@ #include +#include + #include "src/core/channel/channel_args.h" namespace grpc { +ChannelArguments::ChannelArguments(const ChannelArguments& other) + : strings_(other.strings_) { + args_.reserve(other.args_.size()); + auto list_it_dst = strings_.begin(); + auto list_it_src = other.strings_.begin(); + for (auto a = other.args_.begin(); a != other.args_.end(); ++a) { + grpc_arg ap; + ap.type = a->type; + GPR_ASSERT(list_it_src->c_str() == a->key); + ap.key = const_cast(list_it_dst->c_str()); + ++list_it_src; + ++list_it_dst; + switch (a->type) { + case GRPC_ARG_INTEGER: + ap.value.integer = a->value.integer; + break; + case GRPC_ARG_STRING: + GPR_ASSERT(list_it_src->c_str() == a->value.string); + ap.value.string = const_cast(list_it_dst->c_str()); + ++list_it_src; + ++list_it_dst; + break; + case GRPC_ARG_POINTER: + ap.value.pointer = a->value.pointer; + ap.value.pointer.p = a->value.pointer.copy(ap.value.pointer.p); + break; + } + args_.push_back(ap); + } +} + +void ChannelArguments::Swap(ChannelArguments& other) { + args_.swap(other.args_); + strings_.swap(other.strings_); +} + void ChannelArguments::SetCompressionLevel(grpc_compression_level level) { SetInt(GRPC_COMPRESSION_LEVEL_ARG, level); } diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc index 510af2bb00a..38eeda0dc0e 100644 --- a/src/cpp/client/create_channel.cc +++ b/src/cpp/client/create_channel.cc @@ -32,9 +32,11 @@ */ #include +#include #include "src/cpp/client/channel.h" #include +#include #include namespace grpc { @@ -43,7 +45,12 @@ class ChannelArguments; std::shared_ptr CreateChannel( const grpc::string& target, const std::shared_ptr& creds, const ChannelArguments& args) { - return creds ? creds->CreateChannel(target, args) + ChannelArguments cp_args = args; + std::ostringstream user_agent_prefix; + user_agent_prefix << "grpc-c++/" << grpc_version_string(); + cp_args.SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, + user_agent_prefix.str()); + return creds ? creds->CreateChannel(target, cp_args) : std::shared_ptr( new Channel(target, grpc_lame_client_channel_create())); } diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c index 7d3568c22e1..05ad42ca1f3 100644 --- a/test/core/end2end/dualstack_socket_test.c +++ b/test/core/end2end/dualstack_socket_test.c @@ -211,6 +211,10 @@ void test_connect(const char *server_host, const char *client_host, int port, drain_cq(cq); grpc_completion_queue_destroy(cq); + grpc_metadata_array_destroy(&initial_metadata_recv); + grpc_metadata_array_destroy(&trailing_metadata_recv); + grpc_metadata_array_destroy(&request_metadata_recv); + grpc_call_details_destroy(&call_details); gpr_free(details); } diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index 89d9d635af3..00088cfb15b 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -249,9 +249,10 @@ class End2endTest : public ::testing::Test { void TearDown() GRPC_OVERRIDE { server_->Shutdown(); } void ResetStub() { - std::shared_ptr channel = - CreateChannel(server_address_.str(), FakeTransportSecurityCredentials(), - ChannelArguments()); + ChannelArguments args; + args.SetString(GRPC_ARG_SECONDARY_USER_AGENT_STRING, "end2end_test"); + std::shared_ptr channel = CreateChannel( + server_address_.str(), FakeTransportSecurityCredentials(), args); stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel)); } diff --git a/test/cpp/qps/qps_test.cc b/test/cpp/qps/qps_test.cc index 07b4834cc00..7b93443f7ce 100644 --- a/test/cpp/qps/qps_test.cc +++ b/test/cpp/qps/qps_test.cc @@ -44,8 +44,8 @@ namespace grpc { namespace testing { -static const int WARMUP = 5; -static const int BENCHMARK = 10; +static const int WARMUP = 20; +static const int BENCHMARK = 40; static void RunQPS() { gpr_log(GPR_INFO, "Running QPS test"); @@ -53,8 +53,8 @@ static void RunQPS() { ClientConfig client_config; client_config.set_client_type(ASYNC_CLIENT); client_config.set_enable_ssl(false); - client_config.set_outstanding_rpcs_per_channel(1000); - client_config.set_client_channels(8); + client_config.set_outstanding_rpcs_per_channel(10); + client_config.set_client_channels(800); client_config.set_payload_size(1); client_config.set_async_client_threads(8); client_config.set_rpc_type(UNARY); @@ -62,7 +62,7 @@ static void RunQPS() { ServerConfig server_config; server_config.set_server_type(ASYNC_SERVER); server_config.set_enable_ssl(false); - server_config.set_threads(4); + server_config.set_threads(8); const auto result = RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);