LCOV - code coverage report
Current view: top level - core/channel - http_server_filter.c (source / functions) Hit Total Coverage
Test: tmp.CaZ6RjdVn2 Lines: 80 87 92.0 %
Date: 2015-12-10 22:15:08 Functions: 9 9 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 <grpc/support/alloc.h>
      37             : #include <grpc/support/log.h>
      38             : #include <string.h>
      39             : #include "src/core/profiling/timers.h"
      40             : #include "src/core/transport/static_metadata.h"
      41             : 
      42             : typedef struct call_data {
      43             :   gpr_uint8 seen_path;
      44             :   gpr_uint8 seen_post;
      45             :   gpr_uint8 sent_status;
      46             :   gpr_uint8 seen_scheme;
      47             :   gpr_uint8 seen_te_trailers;
      48             :   gpr_uint8 seen_authority;
      49             :   grpc_linked_mdelem status;
      50             :   grpc_linked_mdelem content_type;
      51             : 
      52             :   grpc_metadata_batch *recv_initial_metadata;
      53             :   /** Closure to call when finished with the hs_on_recv hook */
      54             :   grpc_closure *on_done_recv;
      55             :   /** Receive closures are chained: we inject this closure as the on_done_recv
      56             :       up-call on transport_op, and remember to call our on_done_recv member
      57             :       after handling it. */
      58             :   grpc_closure hs_on_recv;
      59             : } call_data;
      60             : 
      61             : typedef struct channel_data { gpr_uint8 unused; } channel_data;
      62             : 
      63             : typedef struct {
      64             :   grpc_call_element *elem;
      65             :   grpc_exec_ctx *exec_ctx;
      66             : } server_filter_args;
      67             : 
      68    21115182 : static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
      69    21114355 :   server_filter_args *a = user_data;
      70    21115182 :   grpc_call_element *elem = a->elem;
      71    21115182 :   call_data *calld = elem->call_data;
      72             : 
      73             :   /* Check if it is one of the headers we care about. */
      74    21115182 :   if (md == GRPC_MDELEM_TE_TRAILERS || md == GRPC_MDELEM_METHOD_POST ||
      75    14730670 :       md == GRPC_MDELEM_SCHEME_HTTP || md == GRPC_MDELEM_SCHEME_HTTPS ||
      76             :       md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
      77             :     /* swallow it */
      78     8677822 :     if (md == GRPC_MDELEM_METHOD_POST) {
      79     2167493 :       calld->seen_post = 1;
      80     6510329 :     } else if (md->key == GRPC_MDSTR_SCHEME) {
      81     2170732 :       calld->seen_scheme = 1;
      82     4339597 :     } else if (md == GRPC_MDELEM_TE_TRAILERS) {
      83     2170455 :       calld->seen_te_trailers = 1;
      84             :     }
      85             :     /* TODO(klempner): Track that we've seen all the headers we should
      86             :        require */
      87     8677466 :     return NULL;
      88    12437360 :   } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
      89           1 :     if (strncmp(grpc_mdstr_as_c_string(md->value), "application/grpc+", 17) ==
      90             :         0) {
      91             :       /* Although the C implementation doesn't (currently) generate them,
      92             :          any custom +-suffix is explicitly valid. */
      93             :       /* TODO(klempner): We should consider preallocating common values such
      94             :          as +proto or +json, or at least stashing them if we see them. */
      95             :       /* TODO(klempner): Should we be surfacing this to application code? */
      96             :     } else {
      97             :       /* TODO(klempner): We're currently allowing this, but we shouldn't
      98             :          see it without a proxy so log for now. */
      99           0 :       gpr_log(GPR_INFO, "Unexpected content-type %s",
     100             :               grpc_mdstr_as_c_string(md->value));
     101             :     }
     102           1 :     return NULL;
     103    24874328 :   } else if (md->key == GRPC_MDSTR_TE || md->key == GRPC_MDSTR_METHOD ||
     104    12436969 :              md->key == GRPC_MDSTR_SCHEME) {
     105           0 :     gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
     106             :             grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value));
     107             :     /* swallow it and error everything out. */
     108             :     /* TODO(klempner): We ought to generate more descriptive error messages
     109             :        on the wire here. */
     110           0 :     grpc_call_element_send_cancel(a->exec_ctx, elem);
     111           0 :     return NULL;
     112    12440976 :   } else if (md->key == GRPC_MDSTR_PATH) {
     113     2170709 :     if (calld->seen_path) {
     114           0 :       gpr_log(GPR_ERROR, "Received :path twice");
     115           0 :       return NULL;
     116             :     }
     117     2170709 :     calld->seen_path = 1;
     118     2170709 :     return md;
     119    10270267 :   } else if (md->key == GRPC_MDSTR_AUTHORITY) {
     120     2170553 :     calld->seen_authority = 1;
     121     2170553 :     return md;
     122     8099714 :   } else if (md->key == GRPC_MDSTR_HOST) {
     123             :     /* translate host to :authority since :authority may be
     124             :        omitted */
     125           1 :     grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
     126             :         GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
     127           1 :     calld->seen_authority = 1;
     128           1 :     return authority;
     129             :   } else {
     130     8099420 :     return md;
     131             :   }
     132             : }
     133             : 
     134     2171090 : static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) {
     135     2171001 :   grpc_call_element *elem = user_data;
     136     2171090 :   call_data *calld = elem->call_data;
     137     2171090 :   if (success) {
     138             :     server_filter_args a;
     139     2171069 :     a.elem = elem;
     140     2171069 :     a.exec_ctx = exec_ctx;
     141     2171069 :     grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, &a);
     142             :     /* Have we seen the required http2 transport headers?
     143             :        (:method, :scheme, content-type, with :path and :authority covered
     144             :        at the channel level right now) */
     145     4341591 :     if (calld->seen_post && calld->seen_scheme && calld->seen_te_trailers &&
     146     4341578 :         calld->seen_path && calld->seen_authority) {
     147             :       /* do nothing */
     148             :     } else {
     149           0 :       if (!calld->seen_path) {
     150          48 :         gpr_log(GPR_ERROR, "Missing :path header");
     151             :       }
     152          48 :       if (!calld->seen_authority) {
     153          46 :         gpr_log(GPR_ERROR, "Missing :authority header");
     154             :       }
     155          48 :       if (!calld->seen_post) {
     156          48 :         gpr_log(GPR_ERROR, "Missing :method header");
     157             :       }
     158          48 :       if (!calld->seen_scheme) {
     159          48 :         gpr_log(GPR_ERROR, "Missing :scheme header");
     160             :       }
     161          48 :       if (!calld->seen_te_trailers) {
     162          48 :         gpr_log(GPR_ERROR, "Missing te trailers header");
     163             :       }
     164             :       /* Error this call out */
     165          48 :       success = 0;
     166          48 :       grpc_call_element_send_cancel(exec_ctx, elem);
     167             :     }
     168             :   }
     169     2170590 :   calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
     170     2171150 : }
     171             : 
     172     8090402 : static void hs_mutate_op(grpc_call_element *elem,
     173             :                          grpc_transport_stream_op *op) {
     174             :   /* grab pointers to our data from the call element */
     175     8090402 :   call_data *calld = elem->call_data;
     176             : 
     177     8090402 :   if (op->send_initial_metadata != NULL && !calld->sent_status) {
     178     2170485 :     calld->sent_status = 1;
     179     2170485 :     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->status,
     180             :                                  GRPC_MDELEM_STATUS_200);
     181     2170095 :     grpc_metadata_batch_add_tail(
     182             :         op->send_initial_metadata, &calld->content_type,
     183             :         GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
     184             :   }
     185             : 
     186     8096569 :   if (op->recv_initial_metadata) {
     187             :     /* substitute our callback for the higher callback */
     188     2171081 :     calld->recv_initial_metadata = op->recv_initial_metadata;
     189     2171081 :     calld->on_done_recv = op->on_complete;
     190     2171081 :     op->on_complete = &calld->hs_on_recv;
     191             :   }
     192     8096569 : }
     193             : 
     194     8082208 : static void hs_start_transport_op(grpc_exec_ctx *exec_ctx,
     195             :                                   grpc_call_element *elem,
     196             :                                   grpc_transport_stream_op *op) {
     197     8082208 :   GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
     198             :   GPR_TIMER_BEGIN("hs_start_transport_op", 0);
     199     8082208 :   hs_mutate_op(elem, op);
     200     8092262 :   grpc_call_next_op(exec_ctx, elem, op);
     201             :   GPR_TIMER_END("hs_start_transport_op", 0);
     202     8104250 : }
     203             : 
     204             : /* Constructor for call_data */
     205     2170663 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     206             :                            grpc_call_element_args *args) {
     207             :   /* grab pointers to our data from the call element */
     208     2170663 :   call_data *calld = elem->call_data;
     209             :   /* initialize members */
     210     2170663 :   memset(calld, 0, sizeof(*calld));
     211     2170663 :   grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem);
     212     2170935 : }
     213             : 
     214             : /* Destructor for call_data */
     215     2170835 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
     216     2170835 :                               grpc_call_element *elem) {}
     217             : 
     218             : /* Constructor for channel_data */
     219        3056 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
     220             :                               grpc_channel_element *elem,
     221             :                               grpc_channel_element_args *args) {
     222        3056 :   GPR_ASSERT(!args->is_last);
     223        3056 : }
     224             : 
     225             : /* Destructor for channel data */
     226        3024 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
     227             :                                  grpc_channel_element *elem) {
     228        3024 : }
     229             : 
     230             : const grpc_channel_filter grpc_http_server_filter = {
     231             :     hs_start_transport_op, grpc_channel_next_op, sizeof(call_data),
     232             :     init_call_elem, grpc_call_stack_ignore_set_pollset, destroy_call_elem,
     233             :     sizeof(channel_data), init_channel_elem, destroy_channel_elem,
     234             :     grpc_call_next_get_peer, "http-server"};

Generated by: LCOV version 1.11