blob: 270df0f59bc67cbdba56124c2bdab41a22725b7b [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
Craig Tillerc923cd72015-01-19 16:14:31 -080039typedef enum {
40 NOT_RECEIVED,
41 POST,
42 GET
43} known_method_type;
44
ctillerd74729d2015-01-12 13:31:36 -080045typedef struct call_data {
Craig Tillerc923cd72015-01-19 16:14:31 -080046 known_method_type seen_method;
Craig Tillerd2d0a112015-01-19 16:07:52 -080047 gpr_uint8 sent_status;
48 gpr_uint8 seen_scheme;
Craig Tillerd2d0a112015-01-19 16:07:52 -080049 gpr_uint8 seen_te_trailers;
50 grpc_mdelem *path;
ctillerd74729d2015-01-12 13:31:36 -080051} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080052
klempner1e273282014-12-10 13:55:41 -080053typedef struct channel_data {
54 grpc_mdelem *te_trailers;
Craig Tillerc923cd72015-01-19 16:14:31 -080055 grpc_mdelem *method_get;
56 grpc_mdelem *method_post;
ctillerd74729d2015-01-12 13:31:36 -080057 grpc_mdelem *http_scheme;
58 grpc_mdelem *https_scheme;
59 /* TODO(klempner): Remove this once we stop using it */
60 grpc_mdelem *grpc_scheme;
61 grpc_mdelem *content_type;
62 grpc_mdelem *status;
Craig Tillerd2d0a112015-01-19 16:07:52 -080063 grpc_mdstr *path_key;
klempner1e273282014-12-10 13:55:41 -080064} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080065
66/* used to silence 'variable not used' warnings */
67static void ignore_unused(void *ignored) {}
68
Craig Tillerce6e3502015-01-19 18:04:42 -080069/* Handle 'GET': not technically grpc, so probably a web browser hitting
70 us */
71static void handle_get(grpc_call_element *elem) {
72 channel_data *channeld = elem->channel_data;
73 grpc_call_element_send_metadata(elem, channeld->status);
74 grpc_call_element_send_finish(elem);
75}
76
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080077/* Called either:
78 - in response to an API call (or similar) from above, to send something
79 - a network event (or similar) from below, to receive something
80 op contains type and call direction information, in addition to the data
81 that is being sent or received. */
ctillerf962f522014-12-10 15:28:27 -080082static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
83 grpc_call_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080084 /* grab pointers to our data from the call element */
85 call_data *calld = elem->call_data;
86 channel_data *channeld = elem->channel_data;
87 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
88
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080089 switch (op->type) {
90 case GRPC_RECV_METADATA:
ctillerd74729d2015-01-12 13:31:36 -080091 /* Check if it is one of the headers we care about. */
92 if (op->data.metadata == channeld->te_trailers ||
Craig Tillerc923cd72015-01-19 16:14:31 -080093 op->data.metadata == channeld->method_get ||
94 op->data.metadata == channeld->method_post ||
ctillerd74729d2015-01-12 13:31:36 -080095 op->data.metadata == channeld->http_scheme ||
96 op->data.metadata == channeld->https_scheme ||
97 op->data.metadata == channeld->grpc_scheme ||
98 op->data.metadata == channeld->content_type) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080099 /* swallow it */
Craig Tillerc923cd72015-01-19 16:14:31 -0800100 if (op->data.metadata == channeld->method_get) {
101 calld->seen_method = GET;
102 } else if (op->data.metadata == channeld->method_post) {
103 calld->seen_method = POST;
ctillerd74729d2015-01-12 13:31:36 -0800104 } else if (op->data.metadata->key == channeld->http_scheme->key) {
105 calld->seen_scheme = 1;
106 } else if (op->data.metadata == channeld->te_trailers) {
107 calld->seen_te_trailers = 1;
108 }
109 /* TODO(klempner): Track that we've seen all the headers we should
110 require */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800111 grpc_mdelem_unref(op->data.metadata);
112 op->done_cb(op->user_data, GRPC_OP_OK);
ctillerd74729d2015-01-12 13:31:36 -0800113 } else if (op->data.metadata->key == channeld->content_type->key) {
114 if (strncmp(grpc_mdstr_as_c_string(op->data.metadata->value),
115 "application/grpc+", 17) == 0) {
116 /* Although the C implementation doesn't (currently) generate them,
117 any
118 custom +-suffix is explicitly valid. */
119 /* TODO(klempner): We should consider preallocating common values such
120 as +proto or +json, or at least stashing them if we see them. */
121 /* TODO(klempner): Should we be surfacing this to application code? */
122 } else {
123 /* TODO(klempner): We're currently allowing this, but we shouldn't
124 see it without a proxy so log for now. */
125 gpr_log(GPR_INFO, "Unexpected content-type %s",
126 channeld->content_type->key);
127 }
128 grpc_mdelem_unref(op->data.metadata);
129 op->done_cb(op->user_data, GRPC_OP_OK);
130 } else if (op->data.metadata->key == channeld->te_trailers->key ||
Craig Tillerc923cd72015-01-19 16:14:31 -0800131 op->data.metadata->key == channeld->method_post->key ||
ctillerd74729d2015-01-12 13:31:36 -0800132 op->data.metadata->key == channeld->http_scheme->key ||
133 op->data.metadata->key == channeld->content_type->key) {
134 gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
135 grpc_mdstr_as_c_string(op->data.metadata->key),
ctiller03dc30e2014-12-01 17:13:04 -0800136 grpc_mdstr_as_c_string(op->data.metadata->value));
ctillerd74729d2015-01-12 13:31:36 -0800137 /* swallow it and error everything out. */
138 /* TODO(klempner): We ought to generate more descriptive error messages
139 on the wire here. */
ctiller03dc30e2014-12-01 17:13:04 -0800140 grpc_mdelem_unref(op->data.metadata);
141 op->done_cb(op->user_data, GRPC_OP_OK);
142 grpc_call_element_send_cancel(elem);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800143 } else if (op->data.metadata->key == channeld->path_key) {
144 if (calld->path != NULL) {
145 gpr_log(GPR_ERROR, "Received :path twice");
146 grpc_mdelem_unref(calld->path);
147 }
Craig Tiller12b533d2015-01-19 20:31:44 -0800148 calld->path = op->data.metadata;
Craig Tillerd2d0a112015-01-19 16:07:52 -0800149 op->done_cb(op->user_data, GRPC_OP_OK);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800150 } else {
151 /* pass the event up */
152 grpc_call_next_op(elem, op);
153 }
154 break;
ctillerd74729d2015-01-12 13:31:36 -0800155 case GRPC_RECV_END_OF_INITIAL_METADATA:
156 /* Have we seen the required http2 transport headers?
157 (:method, :scheme, content-type, with :path and :authority covered
158 at the channel level right now) */
Craig Tillerc923cd72015-01-19 16:14:31 -0800159 if (calld->seen_method == POST && calld->seen_scheme && calld->seen_te_trailers &&
Craig Tillerd2d0a112015-01-19 16:07:52 -0800160 calld->path) {
161 grpc_call_element_recv_metadata(elem, calld->path);
162 calld->path = NULL;
ctillerd74729d2015-01-12 13:31:36 -0800163 grpc_call_next_op(elem, op);
Craig Tillerce6e3502015-01-19 18:04:42 -0800164 } else if (calld->seen_method == GET) {
165 handle_get(elem);
ctillerd74729d2015-01-12 13:31:36 -0800166 } else {
Craig Tillerc923cd72015-01-19 16:14:31 -0800167 if (calld->seen_method == NOT_RECEIVED) {
ctillerd74729d2015-01-12 13:31:36 -0800168 gpr_log(GPR_ERROR, "Missing :method header");
Craig Tillerc923cd72015-01-19 16:14:31 -0800169 }
170 if (!calld->seen_scheme) {
ctillerd74729d2015-01-12 13:31:36 -0800171 gpr_log(GPR_ERROR, "Missing :scheme header");
Craig Tillerc923cd72015-01-19 16:14:31 -0800172 }
173 if (!calld->seen_te_trailers) {
ctillerd74729d2015-01-12 13:31:36 -0800174 gpr_log(GPR_ERROR, "Missing te trailers header");
175 }
176 /* Error this call out */
177 op->done_cb(op->user_data, GRPC_OP_OK);
178 grpc_call_element_send_cancel(elem);
179 }
180 break;
klempner1e273282014-12-10 13:55:41 -0800181 case GRPC_SEND_START:
182 case GRPC_SEND_METADATA:
183 /* If we haven't sent status 200 yet, we need to so so because it needs to
184 come before any non : prefixed metadata. */
185 if (!calld->sent_status) {
186 calld->sent_status = 1;
ctillerd74729d2015-01-12 13:31:36 -0800187 /* status is reffed by grpc_call_element_send_metadata */
Craig Tiller12b533d2015-01-19 20:31:44 -0800188 grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->status));
klempner1e273282014-12-10 13:55:41 -0800189 }
190 grpc_call_next_op(elem, op);
191 break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800192 default:
193 /* pass control up or down the stack depending on op->dir */
194 grpc_call_next_op(elem, op);
195 break;
196 }
197}
198
199/* Called on special channel events, such as disconnection or new incoming
200 calls on the server */
ctillerf962f522014-12-10 15:28:27 -0800201static void channel_op(grpc_channel_element *elem,
202 grpc_channel_element *from_elem, grpc_channel_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800203 /* grab pointers to our data from the channel element */
204 channel_data *channeld = elem->channel_data;
205
206 ignore_unused(channeld);
207
208 switch (op->type) {
209 default:
210 /* pass control up or down the stack depending on op->dir */
211 grpc_channel_next_op(elem, op);
212 break;
213 }
214}
215
216/* Constructor for call_data */
217static void init_call_elem(grpc_call_element *elem,
218 const void *server_transport_data) {
219 /* grab pointers to our data from the call element */
220 call_data *calld = elem->call_data;
221 channel_data *channeld = elem->channel_data;
222
223 ignore_unused(channeld);
224
225 /* initialize members */
Craig Tillerd2d0a112015-01-19 16:07:52 -0800226 calld->path = NULL;
klempner1e273282014-12-10 13:55:41 -0800227 calld->sent_status = 0;
ctillerd74729d2015-01-12 13:31:36 -0800228 calld->seen_scheme = 0;
Craig Tillerc923cd72015-01-19 16:14:31 -0800229 calld->seen_method = NOT_RECEIVED;
ctillerd74729d2015-01-12 13:31:36 -0800230 calld->seen_te_trailers = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800231}
232
233/* Destructor for call_data */
234static void destroy_call_elem(grpc_call_element *elem) {
235 /* grab pointers to our data from the call element */
236 call_data *calld = elem->call_data;
237 channel_data *channeld = elem->channel_data;
238
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800239 ignore_unused(channeld);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800240
241 if (calld->path) {
242 grpc_mdelem_unref(calld->path);
243 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800244}
245
246/* Constructor for channel_data */
247static void init_channel_elem(grpc_channel_element *elem,
248 const grpc_channel_args *args, grpc_mdctx *mdctx,
249 int is_first, int is_last) {
250 /* grab pointers to our data from the channel element */
251 channel_data *channeld = elem->channel_data;
252
253 /* The first and the last filters tend to be implemented differently to
254 handle the case that there's no 'next' filter to call on the up or down
255 path */
256 GPR_ASSERT(!is_first);
257 GPR_ASSERT(!is_last);
258
259 /* initialize members */
260 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
ctillerd74729d2015-01-12 13:31:36 -0800261 channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
Craig Tillerc923cd72015-01-19 16:14:31 -0800262 channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST");
263 channeld->method_get = grpc_mdelem_from_strings(mdctx, ":method", "GET");
ctillerd74729d2015-01-12 13:31:36 -0800264 channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
265 channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
266 channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
Craig Tillerd2d0a112015-01-19 16:07:52 -0800267 channeld->path_key = grpc_mdstr_from_string(mdctx, ":path");
ctillerd74729d2015-01-12 13:31:36 -0800268 channeld->content_type =
269 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800270}
271
272/* Destructor for channel data */
273static void destroy_channel_elem(grpc_channel_element *elem) {
274 /* grab pointers to our data from the channel element */
275 channel_data *channeld = elem->channel_data;
276
277 grpc_mdelem_unref(channeld->te_trailers);
ctillerd74729d2015-01-12 13:31:36 -0800278 grpc_mdelem_unref(channeld->status);
Craig Tillerc923cd72015-01-19 16:14:31 -0800279 grpc_mdelem_unref(channeld->method_post);
280 grpc_mdelem_unref(channeld->method_get);
ctillerd74729d2015-01-12 13:31:36 -0800281 grpc_mdelem_unref(channeld->http_scheme);
282 grpc_mdelem_unref(channeld->https_scheme);
283 grpc_mdelem_unref(channeld->grpc_scheme);
284 grpc_mdelem_unref(channeld->content_type);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800285 grpc_mdstr_unref(channeld->path_key);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800286}
287
288const grpc_channel_filter grpc_http_server_filter = {
Craig Tillerecd49342015-01-18 14:36:47 -0800289 call_op, channel_op,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800290
Craig Tillerecd49342015-01-18 14:36:47 -0800291 sizeof(call_data), init_call_elem, destroy_call_elem,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800292
293 sizeof(channel_data), init_channel_elem, destroy_channel_elem,
294
295 "http-server"};