blob: aef001b7cdf36bf14a8ec10618a4f4faa5ba70f5 [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>
Craig Tillerc50e3982015-01-20 16:30:54 -080037#include <grpc/grpc_http.h>
38#include <grpc/support/alloc.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080039#include <grpc/support/log.h>
40
Craig Tillerc923cd72015-01-19 16:14:31 -080041typedef enum {
42 NOT_RECEIVED,
43 POST,
44 GET
45} known_method_type;
46
Craig Tillerc50e3982015-01-20 16:30:54 -080047typedef struct {
48 grpc_mdelem *path;
49 grpc_mdelem *content_type;
50 grpc_byte_buffer *content;
51} gettable;
52
ctillerd74729d2015-01-12 13:31:36 -080053typedef struct call_data {
Craig Tillerc923cd72015-01-19 16:14:31 -080054 known_method_type seen_method;
Craig Tillerd2d0a112015-01-19 16:07:52 -080055 gpr_uint8 sent_status;
56 gpr_uint8 seen_scheme;
Craig Tillerd2d0a112015-01-19 16:07:52 -080057 gpr_uint8 seen_te_trailers;
58 grpc_mdelem *path;
ctillerd74729d2015-01-12 13:31:36 -080059} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080060
klempner1e273282014-12-10 13:55:41 -080061typedef struct channel_data {
62 grpc_mdelem *te_trailers;
Craig Tillerc923cd72015-01-19 16:14:31 -080063 grpc_mdelem *method_get;
64 grpc_mdelem *method_post;
ctillerd74729d2015-01-12 13:31:36 -080065 grpc_mdelem *http_scheme;
66 grpc_mdelem *https_scheme;
67 /* TODO(klempner): Remove this once we stop using it */
68 grpc_mdelem *grpc_scheme;
69 grpc_mdelem *content_type;
70 grpc_mdelem *status;
Craig Tillerd2d0a112015-01-19 16:07:52 -080071 grpc_mdstr *path_key;
Craig Tillerc50e3982015-01-20 16:30:54 -080072
73 size_t gettable_count;
74 gettable *gettables;
klempner1e273282014-12-10 13:55:41 -080075} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080076
77/* used to silence 'variable not used' warnings */
78static void ignore_unused(void *ignored) {}
79
Craig Tillerce6e3502015-01-19 18:04:42 -080080/* Handle 'GET': not technically grpc, so probably a web browser hitting
81 us */
82static void handle_get(grpc_call_element *elem) {
83 channel_data *channeld = elem->channel_data;
84 grpc_call_element_send_metadata(elem, channeld->status);
85 grpc_call_element_send_finish(elem);
86}
87
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080088/* Called either:
89 - in response to an API call (or similar) from above, to send something
90 - a network event (or similar) from below, to receive something
91 op contains type and call direction information, in addition to the data
92 that is being sent or received. */
ctillerf962f522014-12-10 15:28:27 -080093static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
94 grpc_call_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080095 /* grab pointers to our data from the call element */
96 call_data *calld = elem->call_data;
97 channel_data *channeld = elem->channel_data;
98 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
99
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800100 switch (op->type) {
101 case GRPC_RECV_METADATA:
ctillerd74729d2015-01-12 13:31:36 -0800102 /* Check if it is one of the headers we care about. */
103 if (op->data.metadata == channeld->te_trailers ||
Craig Tillerc923cd72015-01-19 16:14:31 -0800104 op->data.metadata == channeld->method_get ||
105 op->data.metadata == channeld->method_post ||
ctillerd74729d2015-01-12 13:31:36 -0800106 op->data.metadata == channeld->http_scheme ||
107 op->data.metadata == channeld->https_scheme ||
108 op->data.metadata == channeld->grpc_scheme ||
109 op->data.metadata == channeld->content_type) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800110 /* swallow it */
Craig Tillerc923cd72015-01-19 16:14:31 -0800111 if (op->data.metadata == channeld->method_get) {
112 calld->seen_method = GET;
113 } else if (op->data.metadata == channeld->method_post) {
114 calld->seen_method = POST;
ctillerd74729d2015-01-12 13:31:36 -0800115 } else if (op->data.metadata->key == channeld->http_scheme->key) {
116 calld->seen_scheme = 1;
117 } else if (op->data.metadata == channeld->te_trailers) {
118 calld->seen_te_trailers = 1;
119 }
120 /* TODO(klempner): Track that we've seen all the headers we should
121 require */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800122 grpc_mdelem_unref(op->data.metadata);
123 op->done_cb(op->user_data, GRPC_OP_OK);
ctillerd74729d2015-01-12 13:31:36 -0800124 } else if (op->data.metadata->key == channeld->content_type->key) {
125 if (strncmp(grpc_mdstr_as_c_string(op->data.metadata->value),
126 "application/grpc+", 17) == 0) {
127 /* Although the C implementation doesn't (currently) generate them,
128 any
129 custom +-suffix is explicitly valid. */
130 /* TODO(klempner): We should consider preallocating common values such
131 as +proto or +json, or at least stashing them if we see them. */
132 /* TODO(klempner): Should we be surfacing this to application code? */
133 } else {
134 /* TODO(klempner): We're currently allowing this, but we shouldn't
135 see it without a proxy so log for now. */
136 gpr_log(GPR_INFO, "Unexpected content-type %s",
137 channeld->content_type->key);
138 }
139 grpc_mdelem_unref(op->data.metadata);
140 op->done_cb(op->user_data, GRPC_OP_OK);
141 } else if (op->data.metadata->key == channeld->te_trailers->key ||
Craig Tillerc923cd72015-01-19 16:14:31 -0800142 op->data.metadata->key == channeld->method_post->key ||
ctillerd74729d2015-01-12 13:31:36 -0800143 op->data.metadata->key == channeld->http_scheme->key ||
144 op->data.metadata->key == channeld->content_type->key) {
145 gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
146 grpc_mdstr_as_c_string(op->data.metadata->key),
ctiller03dc30e2014-12-01 17:13:04 -0800147 grpc_mdstr_as_c_string(op->data.metadata->value));
ctillerd74729d2015-01-12 13:31:36 -0800148 /* swallow it and error everything out. */
149 /* TODO(klempner): We ought to generate more descriptive error messages
150 on the wire here. */
ctiller03dc30e2014-12-01 17:13:04 -0800151 grpc_mdelem_unref(op->data.metadata);
152 op->done_cb(op->user_data, GRPC_OP_OK);
153 grpc_call_element_send_cancel(elem);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800154 } else if (op->data.metadata->key == channeld->path_key) {
155 if (calld->path != NULL) {
156 gpr_log(GPR_ERROR, "Received :path twice");
157 grpc_mdelem_unref(calld->path);
158 }
Craig Tiller12b533d2015-01-19 20:31:44 -0800159 calld->path = op->data.metadata;
Craig Tillerd2d0a112015-01-19 16:07:52 -0800160 op->done_cb(op->user_data, GRPC_OP_OK);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800161 } else {
162 /* pass the event up */
163 grpc_call_next_op(elem, op);
164 }
165 break;
ctillerd74729d2015-01-12 13:31:36 -0800166 case GRPC_RECV_END_OF_INITIAL_METADATA:
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) */
Craig Tillerc923cd72015-01-19 16:14:31 -0800170 if (calld->seen_method == POST && calld->seen_scheme && calld->seen_te_trailers &&
Craig Tillerd2d0a112015-01-19 16:07:52 -0800171 calld->path) {
172 grpc_call_element_recv_metadata(elem, calld->path);
173 calld->path = NULL;
ctillerd74729d2015-01-12 13:31:36 -0800174 grpc_call_next_op(elem, op);
Craig Tillerce6e3502015-01-19 18:04:42 -0800175 } else if (calld->seen_method == GET) {
176 handle_get(elem);
ctillerd74729d2015-01-12 13:31:36 -0800177 } else {
Craig Tillerc923cd72015-01-19 16:14:31 -0800178 if (calld->seen_method == NOT_RECEIVED) {
ctillerd74729d2015-01-12 13:31:36 -0800179 gpr_log(GPR_ERROR, "Missing :method header");
Craig Tillerc923cd72015-01-19 16:14:31 -0800180 }
181 if (!calld->seen_scheme) {
ctillerd74729d2015-01-12 13:31:36 -0800182 gpr_log(GPR_ERROR, "Missing :scheme header");
Craig Tillerc923cd72015-01-19 16:14:31 -0800183 }
184 if (!calld->seen_te_trailers) {
ctillerd74729d2015-01-12 13:31:36 -0800185 gpr_log(GPR_ERROR, "Missing te trailers header");
186 }
187 /* Error this call out */
188 op->done_cb(op->user_data, GRPC_OP_OK);
189 grpc_call_element_send_cancel(elem);
190 }
191 break;
klempner1e273282014-12-10 13:55:41 -0800192 case GRPC_SEND_START:
193 case GRPC_SEND_METADATA:
194 /* If we haven't sent status 200 yet, we need to so so because it needs to
195 come before any non : prefixed metadata. */
196 if (!calld->sent_status) {
197 calld->sent_status = 1;
ctillerd74729d2015-01-12 13:31:36 -0800198 /* status is reffed by grpc_call_element_send_metadata */
Craig Tiller12b533d2015-01-19 20:31:44 -0800199 grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->status));
klempner1e273282014-12-10 13:55:41 -0800200 }
201 grpc_call_next_op(elem, op);
202 break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800203 default:
204 /* pass control up or down the stack depending on op->dir */
205 grpc_call_next_op(elem, op);
206 break;
207 }
208}
209
210/* Called on special channel events, such as disconnection or new incoming
211 calls on the server */
ctillerf962f522014-12-10 15:28:27 -0800212static void channel_op(grpc_channel_element *elem,
213 grpc_channel_element *from_elem, grpc_channel_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800214 /* grab pointers to our data from the channel element */
215 channel_data *channeld = elem->channel_data;
216
217 ignore_unused(channeld);
218
219 switch (op->type) {
220 default:
221 /* pass control up or down the stack depending on op->dir */
222 grpc_channel_next_op(elem, op);
223 break;
224 }
225}
226
227/* Constructor for call_data */
228static void init_call_elem(grpc_call_element *elem,
229 const void *server_transport_data) {
230 /* grab pointers to our data from the call element */
231 call_data *calld = elem->call_data;
232 channel_data *channeld = elem->channel_data;
233
234 ignore_unused(channeld);
235
236 /* initialize members */
Craig Tillerd2d0a112015-01-19 16:07:52 -0800237 calld->path = NULL;
klempner1e273282014-12-10 13:55:41 -0800238 calld->sent_status = 0;
ctillerd74729d2015-01-12 13:31:36 -0800239 calld->seen_scheme = 0;
Craig Tillerc923cd72015-01-19 16:14:31 -0800240 calld->seen_method = NOT_RECEIVED;
ctillerd74729d2015-01-12 13:31:36 -0800241 calld->seen_te_trailers = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800242}
243
244/* Destructor for call_data */
245static void destroy_call_elem(grpc_call_element *elem) {
246 /* grab pointers to our data from the call element */
247 call_data *calld = elem->call_data;
248 channel_data *channeld = elem->channel_data;
249
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800250 ignore_unused(channeld);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800251
252 if (calld->path) {
253 grpc_mdelem_unref(calld->path);
254 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800255}
256
257/* Constructor for channel_data */
258static void init_channel_elem(grpc_channel_element *elem,
259 const grpc_channel_args *args, grpc_mdctx *mdctx,
260 int is_first, int is_last) {
Craig Tillerc50e3982015-01-20 16:30:54 -0800261 size_t i;
262 size_t gettable_capacity = 0;
263
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800264 /* grab pointers to our data from the channel element */
265 channel_data *channeld = elem->channel_data;
266
267 /* The first and the last filters tend to be implemented differently to
268 handle the case that there's no 'next' filter to call on the up or down
269 path */
270 GPR_ASSERT(!is_first);
271 GPR_ASSERT(!is_last);
272
273 /* initialize members */
274 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
ctillerd74729d2015-01-12 13:31:36 -0800275 channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
Craig Tillerc923cd72015-01-19 16:14:31 -0800276 channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST");
277 channeld->method_get = grpc_mdelem_from_strings(mdctx, ":method", "GET");
ctillerd74729d2015-01-12 13:31:36 -0800278 channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
279 channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
280 channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
Craig Tillerd2d0a112015-01-19 16:07:52 -0800281 channeld->path_key = grpc_mdstr_from_string(mdctx, ":path");
ctillerd74729d2015-01-12 13:31:36 -0800282 channeld->content_type =
283 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Craig Tillerc50e3982015-01-20 16:30:54 -0800284
285 for (i = 0; i < args->num_args; i++) {
286 if (0 == strcmp(args->args[i].key, GRPC_ARG_SERVE_OVER_HTTP)) {
287 gettable *g;
288 gpr_slice slice;
289 grpc_http_server_page *p = args->args[i].value.pointer.p;
290 if (channeld->gettable_count == gettable_capacity) {
291 gettable_capacity = GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1);
292 channeld->gettables = gpr_realloc(channeld->gettables, gettable_capacity);
293 }
294 g = &channeld->gettables[channeld->gettable_count++];
295 g->path = grpc_mdelem_from_strings(mdctx, ":path", p->path);
296 g->content_type = grpc_mdelem_from_strings(mdctx, "content-type", p->content_type);
297 slice = gpr_slice_from_copied_string(p->content);
298 g->content = grpc_byte_buffer_create(&slice, 1);
299 }
300 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800301}
302
303/* Destructor for channel data */
304static void destroy_channel_elem(grpc_channel_element *elem) {
305 /* grab pointers to our data from the channel element */
306 channel_data *channeld = elem->channel_data;
307
308 grpc_mdelem_unref(channeld->te_trailers);
ctillerd74729d2015-01-12 13:31:36 -0800309 grpc_mdelem_unref(channeld->status);
Craig Tillerc923cd72015-01-19 16:14:31 -0800310 grpc_mdelem_unref(channeld->method_post);
311 grpc_mdelem_unref(channeld->method_get);
ctillerd74729d2015-01-12 13:31:36 -0800312 grpc_mdelem_unref(channeld->http_scheme);
313 grpc_mdelem_unref(channeld->https_scheme);
314 grpc_mdelem_unref(channeld->grpc_scheme);
315 grpc_mdelem_unref(channeld->content_type);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800316 grpc_mdstr_unref(channeld->path_key);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800317}
318
319const grpc_channel_filter grpc_http_server_filter = {
Craig Tillerecd49342015-01-18 14:36:47 -0800320 call_op, channel_op,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800321
Craig Tillerecd49342015-01-18 14:36:47 -0800322 sizeof(call_data), init_call_elem, destroy_call_elem,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800323
324 sizeof(channel_data), init_channel_elem, destroy_channel_elem,
325
326 "http-server"};