blob: 8290ead8a789063aa25f7144abaec268eccdf128 [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 {
Craig Tillerd2d0a112015-01-19 16:07:52 -080040 gpr_uint8 sent_status;
41 gpr_uint8 seen_scheme;
42 gpr_uint8 seen_method;
43 gpr_uint8 seen_te_trailers;
44 grpc_mdelem *path;
ctillerd74729d2015-01-12 13:31:36 -080045} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080046
klempner1e273282014-12-10 13:55:41 -080047typedef struct channel_data {
48 grpc_mdelem *te_trailers;
ctillerd74729d2015-01-12 13:31:36 -080049 grpc_mdelem *method;
50 grpc_mdelem *http_scheme;
51 grpc_mdelem *https_scheme;
52 /* TODO(klempner): Remove this once we stop using it */
53 grpc_mdelem *grpc_scheme;
54 grpc_mdelem *content_type;
55 grpc_mdelem *status;
Craig Tillerd2d0a112015-01-19 16:07:52 -080056 grpc_mdstr *path_key;
klempner1e273282014-12-10 13:55:41 -080057} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080058
59/* used to silence 'variable not used' warnings */
60static void ignore_unused(void *ignored) {}
61
62/* Called either:
63 - in response to an API call (or similar) from above, to send something
64 - a network event (or similar) from below, to receive something
65 op contains type and call direction information, in addition to the data
66 that is being sent or received. */
ctillerf962f522014-12-10 15:28:27 -080067static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
68 grpc_call_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080069 /* grab pointers to our data from the call element */
70 call_data *calld = elem->call_data;
71 channel_data *channeld = elem->channel_data;
72 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
73
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080074 switch (op->type) {
75 case GRPC_RECV_METADATA:
ctillerd74729d2015-01-12 13:31:36 -080076 /* Check if it is one of the headers we care about. */
77 if (op->data.metadata == channeld->te_trailers ||
78 op->data.metadata == channeld->method ||
79 op->data.metadata == channeld->http_scheme ||
80 op->data.metadata == channeld->https_scheme ||
81 op->data.metadata == channeld->grpc_scheme ||
82 op->data.metadata == channeld->content_type) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080083 /* swallow it */
ctillerd74729d2015-01-12 13:31:36 -080084 if (op->data.metadata == channeld->method) {
85 calld->seen_method = 1;
86 } else if (op->data.metadata->key == channeld->http_scheme->key) {
87 calld->seen_scheme = 1;
88 } else if (op->data.metadata == channeld->te_trailers) {
89 calld->seen_te_trailers = 1;
90 }
91 /* TODO(klempner): Track that we've seen all the headers we should
92 require */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080093 grpc_mdelem_unref(op->data.metadata);
94 op->done_cb(op->user_data, GRPC_OP_OK);
ctillerd74729d2015-01-12 13:31:36 -080095 } else if (op->data.metadata->key == channeld->content_type->key) {
96 if (strncmp(grpc_mdstr_as_c_string(op->data.metadata->value),
97 "application/grpc+", 17) == 0) {
98 /* Although the C implementation doesn't (currently) generate them,
99 any
100 custom +-suffix is explicitly valid. */
101 /* TODO(klempner): We should consider preallocating common values such
102 as +proto or +json, or at least stashing them if we see them. */
103 /* TODO(klempner): Should we be surfacing this to application code? */
104 } else {
105 /* TODO(klempner): We're currently allowing this, but we shouldn't
106 see it without a proxy so log for now. */
107 gpr_log(GPR_INFO, "Unexpected content-type %s",
108 channeld->content_type->key);
109 }
110 grpc_mdelem_unref(op->data.metadata);
111 op->done_cb(op->user_data, GRPC_OP_OK);
112 } else if (op->data.metadata->key == channeld->te_trailers->key ||
113 op->data.metadata->key == channeld->method->key ||
114 op->data.metadata->key == channeld->http_scheme->key ||
115 op->data.metadata->key == channeld->content_type->key) {
116 gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
117 grpc_mdstr_as_c_string(op->data.metadata->key),
ctiller03dc30e2014-12-01 17:13:04 -0800118 grpc_mdstr_as_c_string(op->data.metadata->value));
ctillerd74729d2015-01-12 13:31:36 -0800119 /* swallow it and error everything out. */
120 /* TODO(klempner): We ought to generate more descriptive error messages
121 on the wire here. */
ctiller03dc30e2014-12-01 17:13:04 -0800122 grpc_mdelem_unref(op->data.metadata);
123 op->done_cb(op->user_data, GRPC_OP_OK);
124 grpc_call_element_send_cancel(elem);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800125 } else if (op->data.metadata->key == channeld->path_key) {
126 if (calld->path != NULL) {
127 gpr_log(GPR_ERROR, "Received :path twice");
128 grpc_mdelem_unref(calld->path);
129 }
130 calld->path = grpc_mdelem_ref(op->data.metadata);
131 op->done_cb(op->user_data, GRPC_OP_OK);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800132 } else {
133 /* pass the event up */
134 grpc_call_next_op(elem, op);
135 }
136 break;
ctillerd74729d2015-01-12 13:31:36 -0800137 case GRPC_RECV_END_OF_INITIAL_METADATA:
138 /* Have we seen the required http2 transport headers?
139 (:method, :scheme, content-type, with :path and :authority covered
140 at the channel level right now) */
Craig Tillerd2d0a112015-01-19 16:07:52 -0800141 if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers &&
142 calld->path) {
143 grpc_call_element_recv_metadata(elem, calld->path);
144 calld->path = NULL;
ctillerd74729d2015-01-12 13:31:36 -0800145 grpc_call_next_op(elem, op);
146 } else {
147 if (!calld->seen_method) {
148 gpr_log(GPR_ERROR, "Missing :method header");
149 } else if (!calld->seen_scheme) {
150 gpr_log(GPR_ERROR, "Missing :scheme header");
151 } else if (!calld->seen_te_trailers) {
152 gpr_log(GPR_ERROR, "Missing te trailers header");
153 }
154 /* Error this call out */
155 op->done_cb(op->user_data, GRPC_OP_OK);
156 grpc_call_element_send_cancel(elem);
157 }
158 break;
klempner1e273282014-12-10 13:55:41 -0800159 case GRPC_SEND_START:
160 case GRPC_SEND_METADATA:
161 /* If we haven't sent status 200 yet, we need to so so because it needs to
162 come before any non : prefixed metadata. */
163 if (!calld->sent_status) {
164 calld->sent_status = 1;
ctillerd74729d2015-01-12 13:31:36 -0800165 /* status is reffed by grpc_call_element_send_metadata */
166 grpc_call_element_send_metadata(elem, channeld->status);
klempner1e273282014-12-10 13:55:41 -0800167 }
168 grpc_call_next_op(elem, op);
169 break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800170 default:
171 /* pass control up or down the stack depending on op->dir */
172 grpc_call_next_op(elem, op);
173 break;
174 }
175}
176
177/* Called on special channel events, such as disconnection or new incoming
178 calls on the server */
ctillerf962f522014-12-10 15:28:27 -0800179static void channel_op(grpc_channel_element *elem,
180 grpc_channel_element *from_elem, grpc_channel_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800181 /* grab pointers to our data from the channel element */
182 channel_data *channeld = elem->channel_data;
183
184 ignore_unused(channeld);
185
186 switch (op->type) {
187 default:
188 /* pass control up or down the stack depending on op->dir */
189 grpc_channel_next_op(elem, op);
190 break;
191 }
192}
193
194/* Constructor for call_data */
195static void init_call_elem(grpc_call_element *elem,
196 const void *server_transport_data) {
197 /* grab pointers to our data from the call element */
198 call_data *calld = elem->call_data;
199 channel_data *channeld = elem->channel_data;
200
201 ignore_unused(channeld);
202
203 /* initialize members */
Craig Tillerd2d0a112015-01-19 16:07:52 -0800204 calld->path = NULL;
klempner1e273282014-12-10 13:55:41 -0800205 calld->sent_status = 0;
ctillerd74729d2015-01-12 13:31:36 -0800206 calld->seen_scheme = 0;
207 calld->seen_method = 0;
208 calld->seen_te_trailers = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800209}
210
211/* Destructor for call_data */
212static void destroy_call_elem(grpc_call_element *elem) {
213 /* grab pointers to our data from the call element */
214 call_data *calld = elem->call_data;
215 channel_data *channeld = elem->channel_data;
216
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800217 ignore_unused(channeld);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800218
219 if (calld->path) {
220 grpc_mdelem_unref(calld->path);
221 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800222}
223
224/* Constructor for channel_data */
225static void init_channel_elem(grpc_channel_element *elem,
226 const grpc_channel_args *args, grpc_mdctx *mdctx,
227 int is_first, int is_last) {
228 /* grab pointers to our data from the channel element */
229 channel_data *channeld = elem->channel_data;
230
231 /* The first and the last filters tend to be implemented differently to
232 handle the case that there's no 'next' filter to call on the up or down
233 path */
234 GPR_ASSERT(!is_first);
235 GPR_ASSERT(!is_last);
236
237 /* initialize members */
238 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
ctillerd74729d2015-01-12 13:31:36 -0800239 channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
240 channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
241 channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
242 channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
243 channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
Craig Tillerd2d0a112015-01-19 16:07:52 -0800244 channeld->path_key = grpc_mdstr_from_string(mdctx, ":path");
ctillerd74729d2015-01-12 13:31:36 -0800245 channeld->content_type =
246 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800247}
248
249/* Destructor for channel data */
250static void destroy_channel_elem(grpc_channel_element *elem) {
251 /* grab pointers to our data from the channel element */
252 channel_data *channeld = elem->channel_data;
253
254 grpc_mdelem_unref(channeld->te_trailers);
ctillerd74729d2015-01-12 13:31:36 -0800255 grpc_mdelem_unref(channeld->status);
256 grpc_mdelem_unref(channeld->method);
257 grpc_mdelem_unref(channeld->http_scheme);
258 grpc_mdelem_unref(channeld->https_scheme);
259 grpc_mdelem_unref(channeld->grpc_scheme);
260 grpc_mdelem_unref(channeld->content_type);
Craig Tillerd2d0a112015-01-19 16:07:52 -0800261 grpc_mdstr_unref(channeld->path_key);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800262}
263
264const grpc_channel_filter grpc_http_server_filter = {
Craig Tillerecd49342015-01-18 14:36:47 -0800265 call_op, channel_op,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800266
Craig Tillerecd49342015-01-18 14:36:47 -0800267 sizeof(call_data), init_call_elem, destroy_call_elem,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800268
269 sizeof(channel_data), init_channel_elem, destroy_channel_elem,
270
271 "http-server"};