diff options
author | Muxi Yan <mxyan@google.com> | 2017-04-27 11:14:18 -0700 |
---|---|---|
committer | Muxi Yan <mxyan@google.com> | 2017-04-27 11:14:18 -0700 |
commit | f63afec89d2dcd7afc0c082618286358241549fd (patch) | |
tree | ce67d4e4d8c56cc64a2b2d3bdf1f1ef82b45b495 /src/core/ext/filters/workarounds | |
parent | b53fd4df37617364e6de2d9f880c03f3a9907450 (diff) |
user-agent string filtering
Diffstat (limited to 'src/core/ext/filters/workarounds')
3 files changed, 186 insertions, 13 deletions
diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c index f78cfa2f55..6d00900ccc 100644 --- a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c +++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c @@ -31,22 +31,15 @@ #include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" +#include <stdio.h> #include <string.h> -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/channel/channel_stack_builder.h" -/* -#include <grpc/impl/codegen/grpc_types.h> #include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/service_config.h" -*/ - -#define GRPC_WORKAROUND_PRIORITY_HIGH 9999 +#include "src/core/ext/filters/workarounds/workaround_utils.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/transport/metadata.h" typedef struct call_data { // Receive closures are chained: we inject this closure as the @@ -57,18 +50,46 @@ typedef struct call_data { grpc_metadata_batch *recv_initial_metadata; // Original recv_initial_metadata_ready callback, invoked after our own. grpc_closure* next_recv_initial_metadata_ready; + + // Marks whether the workaround is active + bool workaround_active; } call_data; typedef struct channel_data { } channel_data; +// Find the user agent metadata element in the batch +static bool get_user_agent_mdelem(const grpc_metadata_batch *batch, + grpc_mdelem *md) { + grpc_linked_mdelem *t = batch->list.head; + while (t != NULL) { + *md = t->md; + if (grpc_slice_eq(GRPC_MDKEY(*md), GRPC_MDSTR_USER_AGENT)) { + return true; + } + t = t->next; + } + + return false; +} + // Callback invoked when we receive an initial metadata. static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, void* user_data, grpc_error* error) { grpc_call_element* elem = user_data; call_data* calld = elem->call_data; - + if (GRPC_ERROR_NONE == error) { + grpc_mdelem md; + if (get_user_agent_mdelem(calld->recv_initial_metadata, &md)) { + grpc_user_agent_md *user_agent_md = grpc_parse_user_agent(md); + if (user_agent_md->workaround_active[GRPC_WORKAROUND_ID_CRONET_COMPRESSION]) { + calld->workaround_active = true; + } + // Remove with caching + gpr_free(user_agent_md); + } + } // Invoke the next callback. grpc_closure_run(exec_ctx, calld->next_recv_initial_metadata_ready, GRPC_ERROR_REF(error)); @@ -98,6 +119,7 @@ static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, const grpc_call_element_args* args) { call_data* calld = elem->call_data; calld->next_recv_initial_metadata_ready = NULL; + calld->workaround_active = false; grpc_closure_init(&calld->recv_initial_metadata_ready, recv_initial_metadata_ready, elem, grpc_schedule_on_exec_ctx); return GRPC_ERROR_NONE; @@ -119,6 +141,44 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, grpc_channel_element* elem) {} +// Parse the user agent +static bool parse_user_agent(grpc_mdelem md) { + const char grpc_objc_specifier[] = "grpc-objc/"; + const size_t grpc_objc_specifier_len = sizeof(grpc_objc_specifier) - 1; + const char cronet_specifier[] = "cronet_http"; + const size_t cronet_specifier_len = sizeof(cronet_specifier) - 1; + + char *user_agent_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + bool grpc_objc_specifier_seen = false; + bool cronet_specifier_seen = false; + char *major_version = user_agent_str, *minor_version; + + char *head = strtok(user_agent_str, " "); + while (head != NULL) { + if (!grpc_objc_specifier_seen && + 0 == strncmp(head, grpc_objc_specifier, grpc_objc_specifier_len)) { + major_version = head + grpc_objc_specifier_len; + grpc_objc_specifier_seen = true; + } else if (grpc_objc_specifier_seen && + 0 == strncmp(head, cronet_specifier, cronet_specifier_len)) { + cronet_specifier_seen = true; + break; + } + + head = strtok(NULL, " "); + } + if (grpc_objc_specifier_seen) { + major_version = strtok(major_version, "."); + minor_version = strtok(NULL, "."); + } + + gpr_free(user_agent_str); + return (grpc_objc_specifier_seen && + cronet_specifier_seen && + (atol(major_version) < 1 || + (atol(major_version) == 1 && atol(minor_version) <= 3))); +} + const grpc_channel_filter grpc_workaround_cronet_compression_filter = { start_transport_stream_op_batch, grpc_channel_next_op, @@ -136,6 +196,8 @@ const grpc_channel_filter grpc_workaround_cronet_compression_filter = { static bool register_workaround_cronet_compression(grpc_exec_ctx* exec_ctx, grpc_channel_stack_builder* builder, void* arg) { + grpc_register_workaround(GRPC_WORKAROUND_ID_CRONET_COMPRESSION, + parse_user_agent); return grpc_channel_stack_builder_prepend_filter( builder, &grpc_workaround_cronet_compression_filter, NULL, NULL); } diff --git a/src/core/ext/filters/workarounds/workaround_utils.c b/src/core/ext/filters/workarounds/workaround_utils.c new file mode 100644 index 0000000000..14ed84599c --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_utils.c @@ -0,0 +1,57 @@ +// +// Copyright 2017, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "src/core/ext/filters/workarounds/workaround_utils.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +static user_agent_parser user_agent_parsers[GRPC_MAX_WORKAROUND_ID]; + +grpc_user_agent_md *grpc_parse_user_agent(grpc_mdelem md) { + grpc_user_agent_md *user_agent_md; + + // USE THE CACHE WHEN ABLE + + user_agent_md = gpr_malloc(sizeof(grpc_user_agent_md)); + for (int i = 0; i < GRPC_MAX_WORKAROUND_ID; i++) { + if (user_agent_parsers[i]) { + user_agent_md->workaround_active[i] = user_agent_parsers[i](md); + } + } + + return user_agent_md; +} + +void grpc_register_workaround(uint32_t id, user_agent_parser parser) { + GPR_ASSERT(id < GRPC_MAX_WORKAROUND_ID); + user_agent_parsers[id] = parser; +} diff --git a/src/core/ext/filters/workarounds/workaround_utils.h b/src/core/ext/filters/workarounds/workaround_utils.h new file mode 100644 index 0000000000..99363248cb --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_utils.h @@ -0,0 +1,54 @@ +// +// Copyright 2017, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS +#define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS + +#include "src/core/lib/transport/metadata.h" + +#define GRPC_WORKAROUND_PRIORITY_HIGH 9999 + +typedef enum { + GRPC_WORKAROUND_ID_CRONET_COMPRESSION = 0, + GRPC_MAX_WORKAROUND_ID, +} grpc_workaround_list; + +typedef struct grpc_user_agent_md { + bool workaround_active[GRPC_MAX_WORKAROUND_ID]; +} grpc_user_agent_md; + +grpc_user_agent_md *grpc_parse_user_agent(grpc_mdelem md); + +typedef bool (*user_agent_parser)(grpc_mdelem); + +void grpc_register_workaround(uint32_t id, user_agent_parser parser); + +#endif |