blob: 44eab43f09d7968c1223416140ca038ef4dddd37 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
3 * Copyright 2014, 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"
ctillerd74729d2015-01-12 13:31:36 -080035
36#include <string.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080037#include <grpc/support/log.h>
38
ctillerd74729d2015-01-12 13:31:36 -080039typedef struct call_data {
40 int sent_status;
41 int seen_scheme;
42 int seen_method;
43 int seen_te_trailers;
44} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080045
klempner1e273282014-12-10 13:55:41 -080046typedef struct channel_data {
47 grpc_mdelem *te_trailers;
ctillerd74729d2015-01-12 13:31:36 -080048 grpc_mdelem *method;
49 grpc_mdelem *http_scheme;
50 grpc_mdelem *https_scheme;
51 /* TODO(klempner): Remove this once we stop using it */
52 grpc_mdelem *grpc_scheme;
53 grpc_mdelem *content_type;
54 grpc_mdelem *status;
klempner1e273282014-12-10 13:55:41 -080055} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080056
57/* used to silence 'variable not used' warnings */
58static void ignore_unused(void *ignored) {}
59
60/* Called either:
61 - in response to an API call (or similar) from above, to send something
62 - a network event (or similar) from below, to receive something
63 op contains type and call direction information, in addition to the data
64 that is being sent or received. */
ctillerf962f522014-12-10 15:28:27 -080065static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
66 grpc_call_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080067 /* grab pointers to our data from the call element */
68 call_data *calld = elem->call_data;
69 channel_data *channeld = elem->channel_data;
70 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
71
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080072 switch (op->type) {
73 case GRPC_RECV_METADATA:
ctillerd74729d2015-01-12 13:31:36 -080074 /* Check if it is one of the headers we care about. */
75 if (op->data.metadata == channeld->te_trailers ||
76 op->data.metadata == channeld->method ||
77 op->data.metadata == channeld->http_scheme ||
78 op->data.metadata == channeld->https_scheme ||
79 op->data.metadata == channeld->grpc_scheme ||
80 op->data.metadata == channeld->content_type) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080081 /* swallow it */
ctillerd74729d2015-01-12 13:31:36 -080082 if (op->data.metadata == channeld->method) {
83 calld->seen_method = 1;
84 } else if (op->data.metadata->key == channeld->http_scheme->key) {
85 calld->seen_scheme = 1;
86 } else if (op->data.metadata == channeld->te_trailers) {
87 calld->seen_te_trailers = 1;
88 }
89 /* TODO(klempner): Track that we've seen all the headers we should
90 require */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080091 grpc_mdelem_unref(op->data.metadata);
92 op->done_cb(op->user_data, GRPC_OP_OK);
ctillerd74729d2015-01-12 13:31:36 -080093 } else if (op->data.metadata->key == channeld->content_type->key) {
94 if (strncmp(grpc_mdstr_as_c_string(op->data.metadata->value),
95 "application/grpc+", 17) == 0) {
96 /* Although the C implementation doesn't (currently) generate them,
97 any
98 custom +-suffix is explicitly valid. */
99 /* TODO(klempner): We should consider preallocating common values such
100 as +proto or +json, or at least stashing them if we see them. */
101 /* TODO(klempner): Should we be surfacing this to application code? */
102 } else {
103 /* TODO(klempner): We're currently allowing this, but we shouldn't
104 see it without a proxy so log for now. */
105 gpr_log(GPR_INFO, "Unexpected content-type %s",
106 channeld->content_type->key);
107 }
108 grpc_mdelem_unref(op->data.metadata);
109 op->done_cb(op->user_data, GRPC_OP_OK);
110 } else if (op->data.metadata->key == channeld->te_trailers->key ||
111 op->data.metadata->key == channeld->method->key ||
112 op->data.metadata->key == channeld->http_scheme->key ||
113 op->data.metadata->key == channeld->content_type->key) {
114 gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
115 grpc_mdstr_as_c_string(op->data.metadata->key),
ctiller03dc30e2014-12-01 17:13:04 -0800116 grpc_mdstr_as_c_string(op->data.metadata->value));
ctillerd74729d2015-01-12 13:31:36 -0800117 /* swallow it and error everything out. */
118 /* TODO(klempner): We ought to generate more descriptive error messages
119 on the wire here. */
ctiller03dc30e2014-12-01 17:13:04 -0800120 grpc_mdelem_unref(op->data.metadata);
121 op->done_cb(op->user_data, GRPC_OP_OK);
122 grpc_call_element_send_cancel(elem);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800123 } else {
124 /* pass the event up */
125 grpc_call_next_op(elem, op);
126 }
127 break;
ctillerd74729d2015-01-12 13:31:36 -0800128 case GRPC_RECV_END_OF_INITIAL_METADATA:
129 /* Have we seen the required http2 transport headers?
130 (:method, :scheme, content-type, with :path and :authority covered
131 at the channel level right now) */
132 if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers) {
133 grpc_call_next_op(elem, op);
134 } else {
135 if (!calld->seen_method) {
136 gpr_log(GPR_ERROR, "Missing :method header");
137 } else if (!calld->seen_scheme) {
138 gpr_log(GPR_ERROR, "Missing :scheme header");
139 } else if (!calld->seen_te_trailers) {
140 gpr_log(GPR_ERROR, "Missing te trailers header");
141 }
142 /* Error this call out */
143 op->done_cb(op->user_data, GRPC_OP_OK);
144 grpc_call_element_send_cancel(elem);
145 }
146 break;
klempner1e273282014-12-10 13:55:41 -0800147 case GRPC_SEND_START:
148 case GRPC_SEND_METADATA:
149 /* If we haven't sent status 200 yet, we need to so so because it needs to
150 come before any non : prefixed metadata. */
151 if (!calld->sent_status) {
152 calld->sent_status = 1;
ctillerd74729d2015-01-12 13:31:36 -0800153 /* status is reffed by grpc_call_element_send_metadata */
154 grpc_call_element_send_metadata(elem, channeld->status);
klempner1e273282014-12-10 13:55:41 -0800155 }
156 grpc_call_next_op(elem, op);
157 break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800158 default:
159 /* pass control up or down the stack depending on op->dir */
160 grpc_call_next_op(elem, op);
161 break;
162 }
163}
164
165/* Called on special channel events, such as disconnection or new incoming
166 calls on the server */
ctillerf962f522014-12-10 15:28:27 -0800167static void channel_op(grpc_channel_element *elem,
168 grpc_channel_element *from_elem, grpc_channel_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800169 /* grab pointers to our data from the channel element */
170 channel_data *channeld = elem->channel_data;
171
172 ignore_unused(channeld);
173
174 switch (op->type) {
175 default:
176 /* pass control up or down the stack depending on op->dir */
177 grpc_channel_next_op(elem, op);
178 break;
179 }
180}
181
182/* Constructor for call_data */
183static void init_call_elem(grpc_call_element *elem,
184 const void *server_transport_data) {
185 /* grab pointers to our data from the call element */
186 call_data *calld = elem->call_data;
187 channel_data *channeld = elem->channel_data;
188
189 ignore_unused(channeld);
190
191 /* initialize members */
klempner1e273282014-12-10 13:55:41 -0800192 calld->sent_status = 0;
ctillerd74729d2015-01-12 13:31:36 -0800193 calld->seen_scheme = 0;
194 calld->seen_method = 0;
195 calld->seen_te_trailers = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800196}
197
198/* Destructor for call_data */
199static void destroy_call_elem(grpc_call_element *elem) {
200 /* grab pointers to our data from the call element */
201 call_data *calld = elem->call_data;
202 channel_data *channeld = elem->channel_data;
203
204 ignore_unused(calld);
205 ignore_unused(channeld);
206}
207
208/* Constructor for channel_data */
209static void init_channel_elem(grpc_channel_element *elem,
210 const grpc_channel_args *args, grpc_mdctx *mdctx,
211 int is_first, int is_last) {
212 /* grab pointers to our data from the channel element */
213 channel_data *channeld = elem->channel_data;
214
215 /* The first and the last filters tend to be implemented differently to
216 handle the case that there's no 'next' filter to call on the up or down
217 path */
218 GPR_ASSERT(!is_first);
219 GPR_ASSERT(!is_last);
220
221 /* initialize members */
222 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
ctillerd74729d2015-01-12 13:31:36 -0800223 channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
224 channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
225 channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
226 channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
227 channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
228 channeld->content_type =
229 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800230}
231
232/* Destructor for channel data */
233static void destroy_channel_elem(grpc_channel_element *elem) {
234 /* grab pointers to our data from the channel element */
235 channel_data *channeld = elem->channel_data;
236
237 grpc_mdelem_unref(channeld->te_trailers);
ctillerd74729d2015-01-12 13:31:36 -0800238 grpc_mdelem_unref(channeld->status);
239 grpc_mdelem_unref(channeld->method);
240 grpc_mdelem_unref(channeld->http_scheme);
241 grpc_mdelem_unref(channeld->https_scheme);
242 grpc_mdelem_unref(channeld->grpc_scheme);
243 grpc_mdelem_unref(channeld->content_type);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800244}
245
246const grpc_channel_filter grpc_http_server_filter = {
247 call_op, channel_op,
248
249 sizeof(call_data), init_call_elem, destroy_call_elem,
250
251 sizeof(channel_data), init_channel_elem, destroy_channel_elem,
252
253 "http-server"};