LCOV - code coverage report
Current view: top level - src/core/channel - http_client_filter.c (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 120 124 96.8 %
Date: 2015-10-10 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2015, Google Inc.
       3             :  * All rights reserved.
       4             :  *
       5             :  * Redistribution and use in source and binary forms, with or without
       6             :  * modification, are permitted provided that the following conditions are
       7             :  * met:
       8             :  *
       9             :  *     * Redistributions of source code must retain the above copyright
      10             :  * notice, this list of conditions and the following disclaimer.
      11             :  *     * Redistributions in binary form must reproduce the above
      12             :  * copyright notice, this list of conditions and the following disclaimer
      13             :  * in the documentation and/or other materials provided with the
      14             :  * distribution.
      15             :  *     * Neither the name of Google Inc. nor the names of its
      16             :  * contributors may be used to endorse or promote products derived from
      17             :  * this software without specific prior written permission.
      18             :  *
      19             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      20             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      21             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      22             :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      23             :  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      24             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      25             :  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      26             :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      27             :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      28             :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      29             :  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      30             :  *
      31             :  */
      32             : 
      33             : #include "src/core/channel/http_client_filter.h"
      34             : #include <string.h>
      35             : #include <grpc/support/alloc.h>
      36             : #include <grpc/support/log.h>
      37             : #include <grpc/support/string_util.h>
      38             : #include "src/core/support/string.h"
      39             : 
      40             : typedef struct call_data {
      41             :   grpc_linked_mdelem method;
      42             :   grpc_linked_mdelem scheme;
      43             :   grpc_linked_mdelem authority;
      44             :   grpc_linked_mdelem te_trailers;
      45             :   grpc_linked_mdelem content_type;
      46             :   grpc_linked_mdelem user_agent;
      47             :   int sent_initial_metadata;
      48             : 
      49             :   int got_initial_metadata;
      50             :   grpc_stream_op_buffer *recv_ops;
      51             : 
      52             :   /** Closure to call when finished with the hc_on_recv hook */
      53             :   grpc_closure *on_done_recv;
      54             :   /** Receive closures are chained: we inject this closure as the on_done_recv
      55             :       up-call on transport_op, and remember to call our on_done_recv member
      56             :       after handling it. */
      57             :   grpc_closure hc_on_recv;
      58             : } call_data;
      59             : 
      60             : typedef struct channel_data {
      61             :   grpc_mdelem *te_trailers;
      62             :   grpc_mdelem *method;
      63             :   grpc_mdelem *scheme;
      64             :   grpc_mdelem *content_type;
      65             :   grpc_mdelem *status;
      66             :   /** complete user agent mdelem */
      67             :   grpc_mdelem *user_agent;
      68             : } channel_data;
      69             : 
      70             : typedef struct {
      71             :   grpc_call_element *elem;
      72             :   grpc_exec_ctx *exec_ctx;
      73             : } client_recv_filter_args;
      74             : 
      75     7105320 : static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
      76     7105320 :   client_recv_filter_args *a = user_data;
      77     7105320 :   grpc_call_element *elem = a->elem;
      78     7105320 :   channel_data *channeld = elem->channel_data;
      79     7105320 :   if (md == channeld->status) {
      80     1402442 :     return NULL;
      81     5702878 :   } else if (md->key == channeld->status->key) {
      82           0 :     grpc_call_element_send_cancel(a->exec_ctx, elem);
      83           0 :     return NULL;
      84     5702878 :   } else if (md->key == channeld->content_type->key) {
      85     1402164 :     return NULL;
      86             :   }
      87     4300714 :   return md;
      88             : }
      89             : 
      90     1404004 : static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) {
      91     1404004 :   grpc_call_element *elem = user_data;
      92     1404004 :   call_data *calld = elem->call_data;
      93             :   size_t i;
      94     1404004 :   size_t nops = calld->recv_ops->nops;
      95     1404004 :   grpc_stream_op *ops = calld->recv_ops->ops;
      96     6991989 :   for (i = 0; i < nops; i++) {
      97     5589360 :     grpc_stream_op *op = &ops[i];
      98             :     client_recv_filter_args a;
      99     5589360 :     if (op->type != GRPC_OP_METADATA) continue;
     100     2798837 :     calld->got_initial_metadata = 1;
     101     2798837 :     a.elem = elem;
     102     2798837 :     a.exec_ctx = exec_ctx;
     103     2798837 :     grpc_metadata_batch_filter(&op->data.metadata, client_recv_filter, &a);
     104             :   }
     105     1402629 :   calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
     106     1402984 : }
     107             : 
     108     5607818 : static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
     109     5607818 :   grpc_call_element *elem = user_data;
     110     5607818 :   channel_data *channeld = elem->channel_data;
     111             :   /* eat the things we'd like to set ourselves */
     112     5607818 :   if (md->key == channeld->method->key) return NULL;
     113     5607818 :   if (md->key == channeld->scheme->key) return NULL;
     114     5607818 :   if (md->key == channeld->te_trailers->key) return NULL;
     115     5607818 :   if (md->key == channeld->content_type->key) return NULL;
     116     5607818 :   if (md->key == channeld->user_agent->key) return NULL;
     117     5607532 :   return md;
     118             : }
     119             : 
     120     3348454 : static void hc_mutate_op(grpc_call_element *elem,
     121             :                          grpc_transport_stream_op *op) {
     122             :   /* grab pointers to our data from the call element */
     123     3348454 :   call_data *calld = elem->call_data;
     124     3348454 :   channel_data *channeld = elem->channel_data;
     125             :   size_t i;
     126     3348454 :   if (op->send_ops && !calld->sent_initial_metadata) {
     127     1402925 :     size_t nops = op->send_ops->nops;
     128     1402925 :     grpc_stream_op *ops = op->send_ops->ops;
     129     2805850 :     for (i = 0; i < nops; i++) {
     130     1401593 :       grpc_stream_op *stream_op = &ops[i];
     131     1401593 :       if (stream_op->type != GRPC_OP_METADATA) continue;
     132     1401593 :       calld->sent_initial_metadata = 1;
     133     1401593 :       grpc_metadata_batch_filter(&stream_op->data.metadata, client_strip_filter,
     134             :                                  elem);
     135             :       /* Send : prefixed headers, which have to be before any application
     136             :          layer headers. */
     137     1402693 :       grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->method,
     138             :                                    GRPC_MDELEM_REF(channeld->method));
     139     1402731 :       grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->scheme,
     140             :                                    GRPC_MDELEM_REF(channeld->scheme));
     141     1402575 :       grpc_metadata_batch_add_tail(&stream_op->data.metadata,
     142             :                                    &calld->te_trailers,
     143             :                                    GRPC_MDELEM_REF(channeld->te_trailers));
     144     1402580 :       grpc_metadata_batch_add_tail(&stream_op->data.metadata,
     145             :                                    &calld->content_type,
     146             :                                    GRPC_MDELEM_REF(channeld->content_type));
     147     1402604 :       grpc_metadata_batch_add_tail(&stream_op->data.metadata,
     148             :                                    &calld->user_agent,
     149             :                                    GRPC_MDELEM_REF(channeld->user_agent));
     150     1402662 :       break;
     151             :     }
     152             :   }
     153             : 
     154     3349523 :   if (op->recv_ops && !calld->got_initial_metadata) {
     155             :     /* substitute our callback for the higher callback */
     156     1402877 :     calld->recv_ops = op->recv_ops;
     157     1402877 :     calld->on_done_recv = op->on_done_recv;
     158     1402877 :     op->on_done_recv = &calld->hc_on_recv;
     159             :   }
     160     3349523 : }
     161             : 
     162     3346935 : static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
     163             :                                   grpc_call_element *elem,
     164             :                                   grpc_transport_stream_op *op) {
     165     3346935 :   GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
     166     3346935 :   hc_mutate_op(elem, op);
     167     3347174 :   grpc_call_next_op(exec_ctx, elem, op);
     168     3349154 : }
     169             : 
     170             : /* Constructor for call_data */
     171     1402142 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     172             :                            const void *server_transport_data,
     173             :                            grpc_transport_stream_op *initial_op) {
     174     1402142 :   call_data *calld = elem->call_data;
     175     1402142 :   calld->sent_initial_metadata = 0;
     176     1402142 :   calld->got_initial_metadata = 0;
     177     1402142 :   calld->on_done_recv = NULL;
     178     1402142 :   grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
     179     1402632 :   if (initial_op) hc_mutate_op(elem, initial_op);
     180     1402632 : }
     181             : 
     182             : /* Destructor for call_data */
     183     1402624 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
     184     1402624 :                               grpc_call_element *elem) {}
     185             : 
     186        1992 : static const char *scheme_from_args(const grpc_channel_args *args) {
     187             :   unsigned i;
     188        1992 :   if (args != NULL) {
     189        2753 :     for (i = 0; i < args->num_args; ++i) {
     190        2493 :       if (args->args[i].type == GRPC_ARG_STRING &&
     191         971 :           strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
     192         365 :         return args->args[i].value.string;
     193             :       }
     194             :     }
     195             :   }
     196        1627 :   return "http";
     197             : }
     198             : 
     199        1992 : static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
     200             :                                         const grpc_channel_args *args) {
     201             :   gpr_strvec v;
     202             :   size_t i;
     203        1992 :   int is_first = 1;
     204             :   char *tmp;
     205             :   grpc_mdstr *result;
     206             : 
     207        1992 :   gpr_strvec_init(&v);
     208             : 
     209        3877 :   for (i = 0; args && i < args->num_args; i++) {
     210        1887 :     if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
     211         169 :       if (args->args[i].type != GRPC_ARG_STRING) {
     212           0 :         gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
     213             :                 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
     214             :       } else {
     215         169 :         if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
     216         169 :         is_first = 0;
     217         169 :         gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
     218             :       }
     219             :     }
     220             :   }
     221             : 
     222        1990 :   gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
     223             :                grpc_version_string(), GPR_PLATFORM_STRING);
     224        1992 :   is_first = 0;
     225        1992 :   gpr_strvec_add(&v, tmp);
     226             : 
     227        3879 :   for (i = 0; args && i < args->num_args; i++) {
     228        1887 :     if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
     229          72 :       if (args->args[i].type != GRPC_ARG_STRING) {
     230           0 :         gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
     231             :                 GRPC_ARG_SECONDARY_USER_AGENT_STRING);
     232             :       } else {
     233          72 :         if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
     234          72 :         is_first = 0;
     235          72 :         gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
     236             :       }
     237             :     }
     238             :   }
     239             : 
     240        1992 :   tmp = gpr_strvec_flatten(&v, NULL);
     241        1992 :   gpr_strvec_destroy(&v);
     242        1992 :   result = grpc_mdstr_from_string(mdctx, tmp);
     243        1992 :   gpr_free(tmp);
     244             : 
     245        1992 :   return result;
     246             : }
     247             : 
     248             : /* Constructor for channel_data */
     249        1990 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
     250             :                               grpc_channel_element *elem, grpc_channel *master,
     251             :                               const grpc_channel_args *channel_args,
     252             :                               grpc_mdctx *mdctx, int is_first, int is_last) {
     253             :   /* grab pointers to our data from the channel element */
     254        1990 :   channel_data *channeld = elem->channel_data;
     255             : 
     256             :   /* The first and the last filters tend to be implemented differently to
     257             :      handle the case that there's no 'next' filter to call on the up or down
     258             :      path */
     259        1990 :   GPR_ASSERT(!is_last);
     260             : 
     261             :   /* initialize members */
     262        1990 :   channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
     263        1992 :   channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
     264        1992 :   channeld->scheme = grpc_mdelem_from_strings(mdctx, ":scheme",
     265             :                                               scheme_from_args(channel_args));
     266        1992 :   channeld->content_type =
     267        1992 :       grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
     268        1992 :   channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
     269        1992 :   channeld->user_agent = grpc_mdelem_from_metadata_strings(
     270             :       mdctx, grpc_mdstr_from_string(mdctx, "user-agent"),
     271             :       user_agent_from_args(mdctx, channel_args));
     272        1992 : }
     273             : 
     274             : /* Destructor for channel data */
     275        1992 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
     276             :                                  grpc_channel_element *elem) {
     277             :   /* grab pointers to our data from the channel element */
     278        1992 :   channel_data *channeld = elem->channel_data;
     279             : 
     280        1992 :   GRPC_MDELEM_UNREF(channeld->te_trailers);
     281        1992 :   GRPC_MDELEM_UNREF(channeld->method);
     282        1992 :   GRPC_MDELEM_UNREF(channeld->scheme);
     283        1992 :   GRPC_MDELEM_UNREF(channeld->content_type);
     284        1992 :   GRPC_MDELEM_UNREF(channeld->status);
     285        1992 :   GRPC_MDELEM_UNREF(channeld->user_agent);
     286        1992 : }
     287             : 
     288             : const grpc_channel_filter grpc_http_client_filter = {
     289             :     hc_start_transport_op, grpc_channel_next_op, sizeof(call_data),
     290             :     init_call_elem,        destroy_call_elem,    sizeof(channel_data),
     291             :     init_channel_elem,     destroy_channel_elem, grpc_call_next_get_peer,
     292             :     "http-client"};

Generated by: LCOV version 1.10