LCOV - code coverage report
Current view: top level - src/core/channel - http_server_filter.c (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 109 134 81.3 %
Date: 2015-10-10 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  * Copyright 2015, Google Inc.
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions are
       8             :  * met:
       9             :  *
      10             :  *     * Redistributions of source code must retain the above copyright
      11             :  * notice, this list of conditions and the following disclaimer.
      12             :  *     * Redistributions in binary form must reproduce the above
      13             :  * copyright notice, this list of conditions and the following disclaimer
      14             :  * in the documentation and/or other materials provided with the
      15             :  * distribution.
      16             :  *     * Neither the name of Google Inc. nor the names of its
      17             :  * contributors may be used to endorse or promote products derived from
      18             :  * this software without specific prior written permission.
      19             :  *
      20             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      21             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      22             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      23             :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      24             :  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      25             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      26             :  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      27             :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      28             :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      29             :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      30             :  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      31             :  *
      32             :  */
      33             : 
      34             : #include "src/core/channel/http_server_filter.h"
      35             : 
      36             : #include <string.h>
      37             : #include <grpc/support/alloc.h>
      38             : #include <grpc/support/log.h>
      39             : 
      40             : typedef struct call_data {
      41             :   gpr_uint8 got_initial_metadata;
      42             :   gpr_uint8 seen_path;
      43             :   gpr_uint8 seen_post;
      44             :   gpr_uint8 sent_status;
      45             :   gpr_uint8 seen_scheme;
      46             :   gpr_uint8 seen_te_trailers;
      47             :   gpr_uint8 seen_authority;
      48             :   grpc_linked_mdelem status;
      49             :   grpc_linked_mdelem content_type;
      50             : 
      51             :   grpc_stream_op_buffer *recv_ops;
      52             :   /** Closure to call when finished with the hs_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 hs_on_recv;
      58             : } call_data;
      59             : 
      60             : typedef struct channel_data {
      61             :   grpc_mdelem *te_trailers;
      62             :   grpc_mdelem *method_post;
      63             :   grpc_mdelem *http_scheme;
      64             :   grpc_mdelem *https_scheme;
      65             :   /* TODO(klempner): Remove this once we stop using it */
      66             :   grpc_mdelem *grpc_scheme;
      67             :   grpc_mdelem *content_type;
      68             :   grpc_mdelem *status_ok;
      69             :   grpc_mdelem *status_not_found;
      70             :   grpc_mdstr *path_key;
      71             :   grpc_mdstr *authority_key;
      72             :   grpc_mdstr *host_key;
      73             : 
      74             :   grpc_mdctx *mdctx;
      75             : } channel_data;
      76             : 
      77             : typedef struct {
      78             :   grpc_call_element *elem;
      79             :   grpc_exec_ctx *exec_ctx;
      80             : } server_filter_args;
      81             : 
      82    11710770 : static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
      83    11710770 :   server_filter_args *a = user_data;
      84    11710770 :   grpc_call_element *elem = a->elem;
      85    11710770 :   channel_data *channeld = elem->channel_data;
      86    11710770 :   call_data *calld = elem->call_data;
      87             : 
      88             :   /* Check if it is one of the headers we care about. */
      89    20816231 :   if (md == channeld->te_trailers || md == channeld->method_post ||
      90    24718904 :       md == channeld->http_scheme || md == channeld->https_scheme ||
      91    15612828 :       md == channeld->grpc_scheme || md == channeld->content_type) {
      92             :     /* swallow it */
      93     5204784 :     if (md == channeld->method_post) {
      94     1300202 :       calld->seen_post = 1;
      95     3904582 :     } else if (md->key == channeld->http_scheme->key) {
      96     1301711 :       calld->seen_scheme = 1;
      97     2602871 :     } else if (md == channeld->te_trailers) {
      98     1301517 :       calld->seen_te_trailers = 1;
      99             :     }
     100             :     /* TODO(klempner): Track that we've seen all the headers we should
     101             :        require */
     102     5204784 :     return NULL;
     103     6505986 :   } else if (md->key == channeld->content_type->key) {
     104           0 :     if (strncmp(grpc_mdstr_as_c_string(md->value), "application/grpc+", 17) ==
     105             :         0) {
     106             :       /* Although the C implementation doesn't (currently) generate them,
     107             :          any custom +-suffix is explicitly valid. */
     108             :       /* TODO(klempner): We should consider preallocating common values such
     109             :          as +proto or +json, or at least stashing them if we see them. */
     110             :       /* TODO(klempner): Should we be surfacing this to application code? */
     111             :     } else {
     112             :       /* TODO(klempner): We're currently allowing this, but we shouldn't
     113             :          see it without a proxy so log for now. */
     114           0 :       gpr_log(GPR_INFO, "Unexpected content-type %s",
     115           0 :               channeld->content_type->key);
     116             :     }
     117           0 :     return NULL;
     118    13012016 :   } else if (md->key == channeld->te_trailers->key ||
     119    13012074 :              md->key == channeld->method_post->key ||
     120     6506044 :              md->key == channeld->http_scheme->key) {
     121           0 :     gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
     122             :             grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value));
     123             :     /* swallow it and error everything out. */
     124             :     /* TODO(klempner): We ought to generate more descriptive error messages
     125             :        on the wire here. */
     126           0 :     grpc_call_element_send_cancel(a->exec_ctx, elem);
     127           0 :     return NULL;
     128     6507720 :   } else if (md->key == channeld->path_key) {
     129     1301545 :     if (calld->seen_path) {
     130           0 :       gpr_log(GPR_ERROR, "Received :path twice");
     131           0 :       return NULL;
     132             :     }
     133     1301545 :     calld->seen_path = 1;
     134     1301545 :     return md;
     135     5206175 :   } else if (md->key == channeld->authority_key) {
     136     1301676 :     calld->seen_authority = 1;
     137     1301676 :     return md;
     138     3904499 :   } else if (md->key == channeld->host_key) {
     139             :     /* translate host to :authority since :authority may be
     140             :        omitted */
     141           0 :     grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
     142             :         channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key),
     143             :         GRPC_MDSTR_REF(md->value));
     144           0 :     GRPC_MDELEM_UNREF(md);
     145           0 :     calld->seen_authority = 1;
     146           0 :     return authority;
     147             :   } else {
     148     3904499 :     return md;
     149             :   }
     150             : }
     151             : 
     152     1301851 : static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) {
     153     1301851 :   grpc_call_element *elem = user_data;
     154     1301851 :   call_data *calld = elem->call_data;
     155     1301851 :   if (success) {
     156             :     size_t i;
     157     1301808 :     size_t nops = calld->recv_ops->nops;
     158     1301808 :     grpc_stream_op *ops = calld->recv_ops->ops;
     159     5200278 :     for (i = 0; i < nops; i++) {
     160     3898151 :       grpc_stream_op *op = &ops[i];
     161             :       server_filter_args a;
     162     3898151 :       if (op->type != GRPC_OP_METADATA) continue;
     163     1301394 :       calld->got_initial_metadata = 1;
     164     1301394 :       a.elem = elem;
     165     1301394 :       a.exec_ctx = exec_ctx;
     166     1301394 :       grpc_metadata_batch_filter(&op->data.metadata, server_filter, &a);
     167             :       /* Have we seen the required http2 transport headers?
     168             :          (:method, :scheme, content-type, with :path and :authority covered
     169             :          at the channel level right now) */
     170     2603392 :       if (calld->seen_post && calld->seen_scheme && calld->seen_te_trailers &&
     171     2603416 :           calld->seen_path && calld->seen_authority) {
     172             :         /* do nothing */
     173             :       } else {
     174           0 :         if (!calld->seen_path) {
     175           0 :           gpr_log(GPR_ERROR, "Missing :path header");
     176             :         }
     177           0 :         if (!calld->seen_authority) {
     178           0 :           gpr_log(GPR_ERROR, "Missing :authority header");
     179             :         }
     180           0 :         if (!calld->seen_post) {
     181           0 :           gpr_log(GPR_ERROR, "Missing :method header");
     182             :         }
     183           0 :         if (!calld->seen_scheme) {
     184           0 :           gpr_log(GPR_ERROR, "Missing :scheme header");
     185             :         }
     186           0 :         if (!calld->seen_te_trailers) {
     187           0 :           gpr_log(GPR_ERROR, "Missing te trailers header");
     188             :         }
     189             :         /* Error this call out */
     190           0 :         success = 0;
     191           0 :         grpc_call_element_send_cancel(exec_ctx, elem);
     192             :       }
     193             :     }
     194             :   }
     195     1302170 :   calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
     196     1301887 : }
     197             : 
     198     6200879 : static void hs_mutate_op(grpc_call_element *elem,
     199             :                          grpc_transport_stream_op *op) {
     200             :   /* grab pointers to our data from the call element */
     201     6200879 :   call_data *calld = elem->call_data;
     202     6200879 :   channel_data *channeld = elem->channel_data;
     203             :   size_t i;
     204             : 
     205     6200879 :   if (op->send_ops && !calld->sent_status) {
     206     1301491 :     size_t nops = op->send_ops->nops;
     207     1301491 :     grpc_stream_op *ops = op->send_ops->ops;
     208     2602982 :     for (i = 0; i < nops; i++) {
     209     1301485 :       grpc_stream_op *stream_op = &ops[i];
     210     1301485 :       if (stream_op->type != GRPC_OP_METADATA) continue;
     211     1301485 :       calld->sent_status = 1;
     212     1301485 :       grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->status,
     213             :                                    GRPC_MDELEM_REF(channeld->status_ok));
     214     1301561 :       grpc_metadata_batch_add_tail(&stream_op->data.metadata,
     215             :                                    &calld->content_type,
     216             :                                    GRPC_MDELEM_REF(channeld->content_type));
     217     1301481 :       break;
     218             :     }
     219             :   }
     220             : 
     221     6200875 :   if (op->recv_ops && !calld->got_initial_metadata) {
     222             :     /* substitute our callback for the higher callback */
     223     1301818 :     calld->recv_ops = op->recv_ops;
     224     1301818 :     calld->on_done_recv = op->on_done_recv;
     225     1301818 :     op->on_done_recv = &calld->hs_on_recv;
     226             :   }
     227     6200875 : }
     228             : 
     229     4905432 : static void hs_start_transport_op(grpc_exec_ctx *exec_ctx,
     230             :                                   grpc_call_element *elem,
     231             :                                   grpc_transport_stream_op *op) {
     232     4905432 :   GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
     233     4905432 :   hs_mutate_op(elem, op);
     234     4906993 :   grpc_call_next_op(exec_ctx, elem, op);
     235     4910871 : }
     236             : 
     237             : /* Constructor for call_data */
     238     1301799 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     239             :                            const void *server_transport_data,
     240             :                            grpc_transport_stream_op *initial_op) {
     241             :   /* grab pointers to our data from the call element */
     242     1301799 :   call_data *calld = elem->call_data;
     243             :   /* initialize members */
     244     1301799 :   memset(calld, 0, sizeof(*calld));
     245     1301799 :   grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem);
     246     1301815 :   if (initial_op) hs_mutate_op(elem, initial_op);
     247     1301821 : }
     248             : 
     249             : /* Destructor for call_data */
     250     1301612 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
     251     1301612 :                               grpc_call_element *elem) {}
     252             : 
     253             : /* Constructor for channel_data */
     254        2022 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
     255             :                               grpc_channel_element *elem, grpc_channel *master,
     256             :                               const grpc_channel_args *args, grpc_mdctx *mdctx,
     257             :                               int is_first, int is_last) {
     258             :   /* grab pointers to our data from the channel element */
     259        2022 :   channel_data *channeld = elem->channel_data;
     260             : 
     261             :   /* The first and the last filters tend to be implemented differently to
     262             :      handle the case that there's no 'next' filter to call on the up or down
     263             :      path */
     264        2022 :   GPR_ASSERT(!is_first);
     265        2022 :   GPR_ASSERT(!is_last);
     266             : 
     267             :   /* initialize members */
     268        2022 :   channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
     269        2022 :   channeld->status_ok = grpc_mdelem_from_strings(mdctx, ":status", "200");
     270        2022 :   channeld->status_not_found =
     271        2022 :       grpc_mdelem_from_strings(mdctx, ":status", "404");
     272        2022 :   channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST");
     273        2022 :   channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
     274        2022 :   channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
     275        2022 :   channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
     276        2022 :   channeld->path_key = grpc_mdstr_from_string(mdctx, ":path");
     277        2022 :   channeld->authority_key = grpc_mdstr_from_string(mdctx, ":authority");
     278        2022 :   channeld->host_key = grpc_mdstr_from_string(mdctx, "host");
     279        2022 :   channeld->content_type =
     280        2022 :       grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
     281             : 
     282        2022 :   channeld->mdctx = mdctx;
     283        2022 : }
     284             : 
     285             : /* Destructor for channel data */
     286        2022 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
     287             :                                  grpc_channel_element *elem) {
     288             :   /* grab pointers to our data from the channel element */
     289        2022 :   channel_data *channeld = elem->channel_data;
     290             : 
     291        2022 :   GRPC_MDELEM_UNREF(channeld->te_trailers);
     292        2022 :   GRPC_MDELEM_UNREF(channeld->status_ok);
     293        2022 :   GRPC_MDELEM_UNREF(channeld->status_not_found);
     294        2022 :   GRPC_MDELEM_UNREF(channeld->method_post);
     295        2022 :   GRPC_MDELEM_UNREF(channeld->http_scheme);
     296        2022 :   GRPC_MDELEM_UNREF(channeld->https_scheme);
     297        2022 :   GRPC_MDELEM_UNREF(channeld->grpc_scheme);
     298        2022 :   GRPC_MDELEM_UNREF(channeld->content_type);
     299        2022 :   GRPC_MDSTR_UNREF(channeld->path_key);
     300        2022 :   GRPC_MDSTR_UNREF(channeld->authority_key);
     301        2022 :   GRPC_MDSTR_UNREF(channeld->host_key);
     302        2022 : }
     303             : 
     304             : const grpc_channel_filter grpc_http_server_filter = {
     305             :     hs_start_transport_op, grpc_channel_next_op, sizeof(call_data),
     306             :     init_call_elem,        destroy_call_elem,    sizeof(channel_data),
     307             :     init_channel_elem,     destroy_channel_elem, grpc_call_next_get_peer,
     308             :     "http-server"};

Generated by: LCOV version 1.10