blob: 97c3c88752b6cb52735b4c9f2f0903d5c2b56cb4 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * 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 Tillerfd7d3ec2015-01-21 13:37:09 -080041typedef enum { NOT_RECEIVED, POST, GET } known_method_type;
Craig Tillerc923cd72015-01-19 16:14:31 -080042
Craig Tillerc50e3982015-01-20 16:30:54 -080043typedef struct {
44 grpc_mdelem *path;
45 grpc_mdelem *content_type;
46 grpc_byte_buffer *content;
47} gettable;
48
ctillerd74729d2015-01-12 13:31:36 -080049typedef struct call_data {
Craig Tillerc923cd72015-01-19 16:14:31 -080050 known_method_type seen_method;
Craig Tillerd2d0a112015-01-19 16:07:52 -080051 gpr_uint8 sent_status;
52 gpr_uint8 seen_scheme;
Craig Tillerd2d0a112015-01-19 16:07:52 -080053 gpr_uint8 seen_te_trailers;
54 grpc_mdelem *path;
ctillerd74729d2015-01-12 13:31:36 -080055} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080056
klempner1e273282014-12-10 13:55:41 -080057typedef struct channel_data {
58 grpc_mdelem *te_trailers;
Craig Tillerc923cd72015-01-19 16:14:31 -080059 grpc_mdelem *method_get;
60 grpc_mdelem *method_post;
ctillerd74729d2015-01-12 13:31:36 -080061 grpc_mdelem *http_scheme;
62 grpc_mdelem *https_scheme;
63 /* TODO(klempner): Remove this once we stop using it */
64 grpc_mdelem *grpc_scheme;
65 grpc_mdelem *content_type;
Craig Tillerfd7d3ec2015-01-21 13:37:09 -080066 grpc_mdelem *status_ok;
67 grpc_mdelem *status_not_found;
Craig Tillerd2d0a112015-01-19 16:07:52 -080068 grpc_mdstr *path_key;
Craig Tillerc50e3982015-01-20 16:30:54 -080069
70 size_t gettable_count;
71 gettable *gettables;
klempner1e273282014-12-10 13:55:41 -080072} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080073
74/* used to silence 'variable not used' warnings */
75static void ignore_unused(void *ignored) {}
76
Craig Tillerce6e3502015-01-19 18:04:42 -080077/* Handle 'GET': not technically grpc, so probably a web browser hitting
78 us */
Craig Tillerfd7d3ec2015-01-21 13:37:09 -080079static void payload_done(void *elem, grpc_op_error error) {
80 if (error == GRPC_OP_OK) {
81 grpc_call_element_send_finish(elem);
82 }
83}
84
Craig Tillerce6e3502015-01-19 18:04:42 -080085static void handle_get(grpc_call_element *elem) {
86 channel_data *channeld = elem->channel_data;
Craig Tillerfd7d3ec2015-01-21 13:37:09 -080087 call_data *calld = elem->call_data;
88 grpc_call_op op;
89 size_t i;
90
91 for (i = 0; i < channeld->gettable_count; i++) {
92 if (channeld->gettables[i].path == calld->path) {
93 grpc_call_element_send_metadata(elem,
94 grpc_mdelem_ref(channeld->status_ok));
95 grpc_call_element_send_metadata(
96 elem, grpc_mdelem_ref(channeld->gettables[i].content_type));
97 op.type = GRPC_SEND_PREFORMATTED_MESSAGE;
98 op.dir = GRPC_CALL_DOWN;
99 op.flags = 0;
100 op.data.message = channeld->gettables[i].content;
101 op.done_cb = payload_done;
102 op.user_data = elem;
103 grpc_call_next_op(elem, &op);
104 }
105 }
106 grpc_call_element_send_metadata(elem,
107 grpc_mdelem_ref(channeld->status_not_found));
Craig Tillerce6e3502015-01-19 18:04:42 -0800108 grpc_call_element_send_finish(elem);
109}
110
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800111/* Called either:
112 - in response to an API call (or similar) from above, to send something
113 - a network event (or similar) from below, to receive something
114 op contains type and call direction information, in addition to the data
115 that is being sent or received. */
ctillerf962f522014-12-10 15:28:27 -0800116static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
117 grpc_call_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800118 /* grab pointers to our data from the call element */
119 call_data *calld = elem->call_data;
120 channel_data *channeld = elem->channel_data;
121 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
122
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800123 switch (op->type) {
124 case GRPC_RECV_METADATA:
ctillerd74729d2015-01-12 13:31:36 -0800125 /* Check if it is one of the headers we care about. */
126 if (op->data.metadata == channeld->te_trailers ||
Craig Tillerc923cd72015-01-19 16:14:31 -0800127 op->data.metadata == channeld->method_get ||
128 op->data.metadata == channeld->method_post ||
ctillerd74729d2015-01-12 13:31:36 -0800129 op->data.metadata == channeld->http_scheme ||
130 op->data.metadata == channeld->https_scheme ||
131 op->data.metadata == channeld->grpc_scheme ||
132 op->data.metadata == channeld->content_type) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800133 /* swallow it */
Craig Tillerc923cd72015-01-19 16:14:31 -0800134 if (op->data.metadata == channeld->method_get) {
135 calld->seen_method = GET;
136 } else if (op->data.metadata == channeld->method_post) {
137 calld->seen_method = POST;
ctillerd74729d2015-01-12 13:31:36 -0800138 } else if (op->data.metadata->key == channeld->http_scheme->key) {
139 calld->seen_scheme = 1;
140 } else if (op->data.metadata == channeld->te_trailers) {
141 calld->seen_te_trailers = 1;
142 }
143 /* TODO(klempner): Track that we've seen all the headers we should
144 require */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800145 grpc_mdelem_unref(op->data.metadata);
146 op->done_cb(op->user_data, GRPC_OP_OK);
ctillerd74729d2015-01-12 13:31:36 -0800147 } else if (op->data.metadata->key == channeld->content_type->key) {
148 if (strncmp(grpc_mdstr_as_c_string(op->data.metadata->value),
149 "application/grpc+", 17) == 0) {
150 /* Although the C implementation doesn't (currently) generate them,
151 any
152 custom +-suffix is explicitly valid. */
153 /* TODO(klempner): We should consider preallocating common values such
154 as +proto or +json, or at least stashing them if we see them. */
155 /* TODO(klempner): Should we be surfacing this to application code? */
156 } else {
157 /* TODO(klempner): We're currently allowing this, but we shouldn't
158 see it without a proxy so log for now. */
159 gpr_log(GPR_INFO, "Unexpected content-type %s",
160 channeld->content_type->key);
161 }
162 grpc_mdelem_unref(op->data.metadata);
163 op->done_cb(op->user_data, GRPC_OP_OK);
164 } else if (op->data.metadata->key == channeld->te_trailers->key ||
Craig Tillerc923cd72015-01-19 16:14:31 -0800165 op->data.metadata->key == channeld->method_post->key ||
ctillerd74729d2015-01-12 13:31:36 -0800166 op->data.metadata->key == channeld->http_scheme->key ||
167 op->data.metadata->key == channeld->content_type->key) {
168 gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
169 grpc_mdstr_as_c_string(op->data.metadata->key),
ctiller03dc30e2014-12-01 17:13:04 -0800170 grpc_mdstr_as_c_string(op->data.metadata->value));
ctillerd74729d2015-01-12 13:31:36 -0800171 /* swallow it and error everything out. */
172 /* TODO(klempner): We ought to generate more descriptive error messages
173 on the wire here. */
ctiller03dc30e2014-12-01 17:13:04 -0800174 grpc_mdelem_unref(op->data.metadata);
175 op->done_cb(op->user_data, GRPC_OP_OK);
176 grpc_call_element_send_cancel(elem);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800177 } else if (op->data.metadata->key == channeld->path_key) {
178 if (calld->path != NULL) {
179 gpr_log(GPR_ERROR, "Received :path twice");
180 grpc_mdelem_unref(calld->path);
181 }
Craig Tiller12b533d2015-01-19 20:31:44 -0800182 calld->path = op->data.metadata;
Craig Tillerd2d0a112015-01-19 16:07:52 -0800183 op->done_cb(op->user_data, GRPC_OP_OK);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800184 } else {
185 /* pass the event up */
186 grpc_call_next_op(elem, op);
187 }
188 break;
ctillerd74729d2015-01-12 13:31:36 -0800189 case GRPC_RECV_END_OF_INITIAL_METADATA:
190 /* Have we seen the required http2 transport headers?
191 (:method, :scheme, content-type, with :path and :authority covered
192 at the channel level right now) */
Craig Tillerfd7d3ec2015-01-21 13:37:09 -0800193 if (calld->seen_method == POST && calld->seen_scheme &&
194 calld->seen_te_trailers && calld->path) {
Craig Tillerd2d0a112015-01-19 16:07:52 -0800195 grpc_call_element_recv_metadata(elem, calld->path);
196 calld->path = NULL;
ctillerd74729d2015-01-12 13:31:36 -0800197 grpc_call_next_op(elem, op);
Craig Tillerce6e3502015-01-19 18:04:42 -0800198 } else if (calld->seen_method == GET) {
199 handle_get(elem);
ctillerd74729d2015-01-12 13:31:36 -0800200 } else {
Craig Tillerc923cd72015-01-19 16:14:31 -0800201 if (calld->seen_method == NOT_RECEIVED) {
ctillerd74729d2015-01-12 13:31:36 -0800202 gpr_log(GPR_ERROR, "Missing :method header");
Craig Tillerc923cd72015-01-19 16:14:31 -0800203 }
204 if (!calld->seen_scheme) {
ctillerd74729d2015-01-12 13:31:36 -0800205 gpr_log(GPR_ERROR, "Missing :scheme header");
Craig Tillerc923cd72015-01-19 16:14:31 -0800206 }
207 if (!calld->seen_te_trailers) {
ctillerd74729d2015-01-12 13:31:36 -0800208 gpr_log(GPR_ERROR, "Missing te trailers header");
209 }
210 /* Error this call out */
211 op->done_cb(op->user_data, GRPC_OP_OK);
212 grpc_call_element_send_cancel(elem);
213 }
214 break;
klempner1e273282014-12-10 13:55:41 -0800215 case GRPC_SEND_START:
216 case GRPC_SEND_METADATA:
217 /* If we haven't sent status 200 yet, we need to so so because it needs to
218 come before any non : prefixed metadata. */
219 if (!calld->sent_status) {
220 calld->sent_status = 1;
ctillerd74729d2015-01-12 13:31:36 -0800221 /* status is reffed by grpc_call_element_send_metadata */
Craig Tillerfd7d3ec2015-01-21 13:37:09 -0800222 grpc_call_element_send_metadata(elem,
223 grpc_mdelem_ref(channeld->status_ok));
klempner1e273282014-12-10 13:55:41 -0800224 }
225 grpc_call_next_op(elem, op);
226 break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800227 default:
228 /* pass control up or down the stack depending on op->dir */
229 grpc_call_next_op(elem, op);
230 break;
231 }
232}
233
234/* Called on special channel events, such as disconnection or new incoming
235 calls on the server */
ctillerf962f522014-12-10 15:28:27 -0800236static void channel_op(grpc_channel_element *elem,
237 grpc_channel_element *from_elem, grpc_channel_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800238 /* grab pointers to our data from the channel element */
239 channel_data *channeld = elem->channel_data;
240
241 ignore_unused(channeld);
242
243 switch (op->type) {
244 default:
245 /* pass control up or down the stack depending on op->dir */
246 grpc_channel_next_op(elem, op);
247 break;
248 }
249}
250
251/* Constructor for call_data */
252static void init_call_elem(grpc_call_element *elem,
253 const void *server_transport_data) {
254 /* grab pointers to our data from the call element */
255 call_data *calld = elem->call_data;
256 channel_data *channeld = elem->channel_data;
257
258 ignore_unused(channeld);
259
260 /* initialize members */
Craig Tillerd2d0a112015-01-19 16:07:52 -0800261 calld->path = NULL;
klempner1e273282014-12-10 13:55:41 -0800262 calld->sent_status = 0;
ctillerd74729d2015-01-12 13:31:36 -0800263 calld->seen_scheme = 0;
Craig Tillerc923cd72015-01-19 16:14:31 -0800264 calld->seen_method = NOT_RECEIVED;
ctillerd74729d2015-01-12 13:31:36 -0800265 calld->seen_te_trailers = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800266}
267
268/* Destructor for call_data */
269static void destroy_call_elem(grpc_call_element *elem) {
270 /* grab pointers to our data from the call element */
271 call_data *calld = elem->call_data;
272 channel_data *channeld = elem->channel_data;
273
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800274 ignore_unused(channeld);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800275
276 if (calld->path) {
277 grpc_mdelem_unref(calld->path);
278 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800279}
280
281/* Constructor for channel_data */
282static void init_channel_elem(grpc_channel_element *elem,
283 const grpc_channel_args *args, grpc_mdctx *mdctx,
284 int is_first, int is_last) {
Craig Tillerc50e3982015-01-20 16:30:54 -0800285 size_t i;
286 size_t gettable_capacity = 0;
287
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800288 /* grab pointers to our data from the channel element */
289 channel_data *channeld = elem->channel_data;
290
291 /* The first and the last filters tend to be implemented differently to
292 handle the case that there's no 'next' filter to call on the up or down
293 path */
294 GPR_ASSERT(!is_first);
295 GPR_ASSERT(!is_last);
296
297 /* initialize members */
298 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
Craig Tillerfd7d3ec2015-01-21 13:37:09 -0800299 channeld->status_ok = grpc_mdelem_from_strings(mdctx, ":status", "200");
300 channeld->status_not_found =
301 grpc_mdelem_from_strings(mdctx, ":status", "404");
Craig Tillerc923cd72015-01-19 16:14:31 -0800302 channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST");
303 channeld->method_get = grpc_mdelem_from_strings(mdctx, ":method", "GET");
ctillerd74729d2015-01-12 13:31:36 -0800304 channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
305 channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
306 channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
Craig Tillerd2d0a112015-01-19 16:07:52 -0800307 channeld->path_key = grpc_mdstr_from_string(mdctx, ":path");
ctillerd74729d2015-01-12 13:31:36 -0800308 channeld->content_type =
309 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Craig Tillerfd7d3ec2015-01-21 13:37:09 -0800310
Craig Tiller53d5f9d2015-01-21 13:03:18 -0800311 /* initialize http download support */
312 channeld->gettable_count = 0;
313 channeld->gettables = NULL;
Craig Tillerc50e3982015-01-20 16:30:54 -0800314 for (i = 0; i < args->num_args; i++) {
315 if (0 == strcmp(args->args[i].key, GRPC_ARG_SERVE_OVER_HTTP)) {
316 gettable *g;
317 gpr_slice slice;
318 grpc_http_server_page *p = args->args[i].value.pointer.p;
319 if (channeld->gettable_count == gettable_capacity) {
Craig Tillerfd7d3ec2015-01-21 13:37:09 -0800320 gettable_capacity =
321 GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1);
Craig Tiller5a8a8072015-01-30 09:41:25 -0800322 channeld->gettables = gpr_realloc(channeld->gettables,
323 gettable_capacity * sizeof(gettable));
Craig Tillerc50e3982015-01-20 16:30:54 -0800324 }
325 g = &channeld->gettables[channeld->gettable_count++];
326 g->path = grpc_mdelem_from_strings(mdctx, ":path", p->path);
Craig Tillerfd7d3ec2015-01-21 13:37:09 -0800327 g->content_type =
328 grpc_mdelem_from_strings(mdctx, "content-type", p->content_type);
Craig Tillerc50e3982015-01-20 16:30:54 -0800329 slice = gpr_slice_from_copied_string(p->content);
330 g->content = grpc_byte_buffer_create(&slice, 1);
Craig Tillerd3756632015-01-30 09:45:50 -0800331 gpr_slice_unref(slice);
Craig Tillerc50e3982015-01-20 16:30:54 -0800332 }
333 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800334}
335
336/* Destructor for channel data */
337static void destroy_channel_elem(grpc_channel_element *elem) {
Craig Tiller5a8a8072015-01-30 09:41:25 -0800338 size_t i;
339
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800340 /* grab pointers to our data from the channel element */
341 channel_data *channeld = elem->channel_data;
342
Craig Tiller5a8a8072015-01-30 09:41:25 -0800343 for (i = 0; i < channeld->gettable_count; i++) {
344 grpc_mdelem_unref(channeld->gettables[i].path);
345 grpc_mdelem_unref(channeld->gettables[i].content_type);
Craig Tillerd3756632015-01-30 09:45:50 -0800346 grpc_byte_buffer_destroy(channeld->gettables[i].content);
Craig Tiller5a8a8072015-01-30 09:41:25 -0800347 }
348 gpr_free(channeld->gettables);
349
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800350 grpc_mdelem_unref(channeld->te_trailers);
Craig Tillerfd7d3ec2015-01-21 13:37:09 -0800351 grpc_mdelem_unref(channeld->status_ok);
352 grpc_mdelem_unref(channeld->status_not_found);
Craig Tillerc923cd72015-01-19 16:14:31 -0800353 grpc_mdelem_unref(channeld->method_post);
354 grpc_mdelem_unref(channeld->method_get);
ctillerd74729d2015-01-12 13:31:36 -0800355 grpc_mdelem_unref(channeld->http_scheme);
356 grpc_mdelem_unref(channeld->https_scheme);
357 grpc_mdelem_unref(channeld->grpc_scheme);
358 grpc_mdelem_unref(channeld->content_type);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800359 grpc_mdstr_unref(channeld->path_key);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800360}
361
362const grpc_channel_filter grpc_http_server_filter = {
Craig Tiller5a8a8072015-01-30 09:41:25 -0800363 call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
364 sizeof(channel_data), init_channel_elem, destroy_channel_elem,
Craig Tiller06059952015-02-18 08:34:56 -0800365 "http-server"};