blob: 59a35d1e35009183f6f4c8e0a787b7502c0081ce [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
Craig Tiller06059952015-02-18 08:34:56 -08002 * Copyright 2015, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08003 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33#include "src/core/channel/http_client_filter.h"
David Klempnera1e86932015-01-13 18:13:59 -080034#include <string.h>
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070035#include <grpc/support/alloc.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080036#include <grpc/support/log.h>
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070037#include <grpc/support/string_util.h>
38#include "src/core/support/string.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080039
Yang Gao5fd0d292015-01-26 00:19:48 -080040typedef struct call_data {
Craig Tiller6902ad22015-04-16 08:01:49 -070041 grpc_linked_mdelem method;
42 grpc_linked_mdelem scheme;
Craig Tiller26b37142015-07-26 12:53:27 -070043 grpc_linked_mdelem authority;
Craig Tiller6902ad22015-04-16 08:01:49 -070044 grpc_linked_mdelem te_trailers;
45 grpc_linked_mdelem content_type;
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070046 grpc_linked_mdelem user_agent;
Craig Tiller83f88d92015-04-21 16:02:05 -070047 int sent_initial_metadata;
Craig Tiller26b37142015-07-26 12:53:27 -070048 int sent_authority;
Craig Tiller83f88d92015-04-21 16:02:05 -070049
50 int got_initial_metadata;
51 grpc_stream_op_buffer *recv_ops;
Craig Tiller1e6facb2015-06-11 22:47:11 -070052
Craig Tiller98bf7e62015-06-24 08:47:07 -070053 /** Closure to call when finished with the hc_on_recv hook */
54 grpc_iomgr_closure *on_done_recv;
55 /** Receive closures are chained: we inject this closure as the on_done_recv
56 up-call on transport_op, and remember to call our on_done_recv member
57 after handling it. */
Craig Tiller1e6facb2015-06-11 22:47:11 -070058 grpc_iomgr_closure hc_on_recv;
Yang Gao5fd0d292015-01-26 00:19:48 -080059} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080060
klempnerc463f742014-12-19 13:03:35 -080061typedef struct channel_data {
62 grpc_mdelem *te_trailers;
63 grpc_mdelem *method;
64 grpc_mdelem *scheme;
65 grpc_mdelem *content_type;
Craig Tiller5b9efed2015-02-03 20:13:06 -080066 grpc_mdelem *status;
Craig Tiller26b37142015-07-26 12:53:27 -070067 grpc_mdelem *default_authority;
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070068 /** complete user agent mdelem */
69 grpc_mdelem *user_agent;
klempnerc463f742014-12-19 13:03:35 -080070} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080071
72/* used to silence 'variable not used' warnings */
73static void ignore_unused(void *ignored) {}
74
Craig Tiller6902ad22015-04-16 08:01:49 -070075static grpc_mdelem *client_filter(void *user_data, grpc_mdelem *md) {
76 grpc_call_element *elem = user_data;
77 channel_data *channeld = elem->channel_data;
78 if (md == channeld->status) {
79 return NULL;
80 } else if (md->key == channeld->status->key) {
81 grpc_call_element_send_cancel(elem);
82 return NULL;
83 }
84 return md;
85}
86
Craig Tiller83f88d92015-04-21 16:02:05 -070087static void hc_on_recv(void *user_data, int success) {
88 grpc_call_element *elem = user_data;
89 call_data *calld = elem->call_data;
90 if (success) {
91 size_t i;
92 size_t nops = calld->recv_ops->nops;
93 grpc_stream_op *ops = calld->recv_ops->ops;
94 for (i = 0; i < nops; i++) {
95 grpc_stream_op *op = &ops[i];
96 if (op->type != GRPC_OP_METADATA) continue;
97 calld->got_initial_metadata = 1;
98 grpc_metadata_batch_filter(&op->data.metadata, client_filter, elem);
99 }
100 }
Craig Tiller1e6facb2015-06-11 22:47:11 -0700101 calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success);
Craig Tiller83f88d92015-04-21 16:02:05 -0700102}
103
Craig Tillerd9ddc772015-07-10 13:00:05 -0700104static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
105 grpc_call_element *elem = user_data;
Craig Tiller26b37142015-07-26 12:53:27 -0700106 call_data *calld = elem->call_data;
Craig Tillerd9ddc772015-07-10 13:00:05 -0700107 channel_data *channeld = elem->channel_data;
108 /* eat the things we'd like to set ourselves */
109 if (md->key == channeld->method->key) return NULL;
110 if (md->key == channeld->scheme->key) return NULL;
111 if (md->key == channeld->te_trailers->key) return NULL;
112 if (md->key == channeld->content_type->key) return NULL;
113 if (md->key == channeld->user_agent->key) return NULL;
Craig Tiller26b37142015-07-26 12:53:27 -0700114 if (channeld->default_authority && channeld->default_authority->key == md->key) {
115 calld->sent_authority = 1;
116 }
Craig Tillerd9ddc772015-07-10 13:00:05 -0700117 return md;
118}
119
Craig Tillerb7959a02015-06-25 08:50:54 -0700120static void hc_mutate_op(grpc_call_element *elem,
121 grpc_transport_stream_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800122 /* grab pointers to our data from the call element */
123 call_data *calld = elem->call_data;
124 channel_data *channeld = elem->channel_data;
Craig Tiller83f88d92015-04-21 16:02:05 -0700125 size_t i;
Craig Tiller83f88d92015-04-21 16:02:05 -0700126 if (op->send_ops && !calld->sent_initial_metadata) {
127 size_t nops = op->send_ops->nops;
128 grpc_stream_op *ops = op->send_ops->ops;
129 for (i = 0; i < nops; i++) {
130 grpc_stream_op *op = &ops[i];
131 if (op->type != GRPC_OP_METADATA) continue;
132 calld->sent_initial_metadata = 1;
Craig Tillerd9ddc772015-07-10 13:00:05 -0700133 grpc_metadata_batch_filter(&op->data.metadata, client_strip_filter, elem);
Craig Tiller6902ad22015-04-16 08:01:49 -0700134 /* Send : prefixed headers, which have to be before any application
Craig Tiller83f88d92015-04-21 16:02:05 -0700135 layer headers. */
Craig Tiller205aee12015-04-16 14:46:41 -0700136 grpc_metadata_batch_add_head(&op->data.metadata, &calld->method,
Craig Tiller1a65a232015-07-06 10:22:32 -0700137 GRPC_MDELEM_REF(channeld->method));
Craig Tiller205aee12015-04-16 14:46:41 -0700138 grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme,
Craig Tiller1a65a232015-07-06 10:22:32 -0700139 GRPC_MDELEM_REF(channeld->scheme));
Craig Tiller26b37142015-07-26 12:53:27 -0700140 if (channeld->default_authority && !calld->sent_authority) {
141 grpc_metadata_batch_add_head(&op->data.metadata, &calld->authority, GRPC_MDELEM_REF(channeld->default_authority));
142 }
Craig Tiller205aee12015-04-16 14:46:41 -0700143 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers,
Craig Tiller1a65a232015-07-06 10:22:32 -0700144 GRPC_MDELEM_REF(channeld->te_trailers));
Craig Tiller205aee12015-04-16 14:46:41 -0700145 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
Craig Tiller1a65a232015-07-06 10:22:32 -0700146 GRPC_MDELEM_REF(channeld->content_type));
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700147 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->user_agent,
148 GRPC_MDELEM_REF(channeld->user_agent));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800149 break;
Craig Tiller83f88d92015-04-21 16:02:05 -0700150 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800151 }
Craig Tiller83f88d92015-04-21 16:02:05 -0700152
153 if (op->recv_ops && !calld->got_initial_metadata) {
154 /* substitute our callback for the higher callback */
155 calld->recv_ops = op->recv_ops;
156 calld->on_done_recv = op->on_done_recv;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700157 op->on_done_recv = &calld->hc_on_recv;
Craig Tiller83f88d92015-04-21 16:02:05 -0700158 }
Craig Tiller50d9db52015-04-23 10:52:14 -0700159}
Craig Tiller83f88d92015-04-21 16:02:05 -0700160
Craig Tiller06aeea72015-04-23 10:54:45 -0700161static void hc_start_transport_op(grpc_call_element *elem,
Craig Tillerb7959a02015-06-25 08:50:54 -0700162 grpc_transport_stream_op *op) {
Craig Tiller50d9db52015-04-23 10:52:14 -0700163 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
164 hc_mutate_op(elem, op);
Craig Tiller83f88d92015-04-21 16:02:05 -0700165 grpc_call_next_op(elem, op);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800166}
167
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800168/* Constructor for call_data */
169static void init_call_elem(grpc_call_element *elem,
Craig Tiller50d9db52015-04-23 10:52:14 -0700170 const void *server_transport_data,
Craig Tillerb7959a02015-06-25 08:50:54 -0700171 grpc_transport_stream_op *initial_op) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700172 call_data *calld = elem->call_data;
173 calld->sent_initial_metadata = 0;
174 calld->got_initial_metadata = 0;
Craig Tiller26b37142015-07-26 12:53:27 -0700175 calld->sent_authority = 0;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700176 calld->on_done_recv = NULL;
177 grpc_iomgr_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
Craig Tiller50d9db52015-04-23 10:52:14 -0700178 if (initial_op) hc_mutate_op(elem, initial_op);
Craig Tiller83f88d92015-04-21 16:02:05 -0700179}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800180
181/* Destructor for call_data */
182static void destroy_call_elem(grpc_call_element *elem) {
183 /* grab pointers to our data from the call element */
184 call_data *calld = elem->call_data;
185 channel_data *channeld = elem->channel_data;
186
187 ignore_unused(calld);
188 ignore_unused(channeld);
189}
190
David Klempnered0cbc82015-01-14 14:46:10 -0800191static const char *scheme_from_args(const grpc_channel_args *args) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100192 unsigned i;
David Klempnera1e86932015-01-13 18:13:59 -0800193 if (args != NULL) {
194 for (i = 0; i < args->num_args; ++i) {
195 if (args->args[i].type == GRPC_ARG_STRING &&
David Klempnered0cbc82015-01-14 14:46:10 -0800196 strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
David Klempnera1e86932015-01-13 18:13:59 -0800197 return args->args[i].value.string;
198 }
199 }
200 }
201 return "http";
202}
203
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700204static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
205 const grpc_channel_args *args) {
206 gpr_strvec v;
207 size_t i;
208 int is_first = 1;
209 char *tmp;
210 grpc_mdstr *result;
211
212 gpr_strvec_init(&v);
213
214 for (i = 0; args && i < args->num_args; i++) {
215 if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
216 if (args->args[i].type != GRPC_ARG_STRING) {
217 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
218 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
219 } else {
220 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
221 is_first = 0;
222 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
223 }
224 }
225 }
226
227 gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
228 grpc_version_string(), GPR_PLATFORM_STRING);
229 is_first = 0;
230 gpr_strvec_add(&v, tmp);
231
232 for (i = 0; args && i < args->num_args; i++) {
233 if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
234 if (args->args[i].type != GRPC_ARG_STRING) {
235 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
236 GRPC_ARG_SECONDARY_USER_AGENT_STRING);
237 } else {
238 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
239 is_first = 0;
240 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
241 }
242 }
243 }
244
245 tmp = gpr_strvec_flatten(&v, NULL);
246 gpr_strvec_destroy(&v);
247 result = grpc_mdstr_from_string(mdctx, tmp);
248 gpr_free(tmp);
249
250 return result;
251}
252
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800253/* Constructor for channel_data */
Craig Tiller079a11b2015-06-30 10:07:15 -0700254static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
Craig Tiller26b37142015-07-26 12:53:27 -0700255 const grpc_channel_args *channel_args, grpc_mdctx *mdctx,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800256 int is_first, int is_last) {
Craig Tiller26b37142015-07-26 12:53:27 -0700257 size_t i;
258
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800259 /* grab pointers to our data from the channel element */
260 channel_data *channeld = elem->channel_data;
261
262 /* The first and the last filters tend to be implemented differently to
263 handle the case that there's no 'next' filter to call on the up or down
264 path */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800265 GPR_ASSERT(!is_last);
266
Craig Tiller26b37142015-07-26 12:53:27 -0700267 channeld->default_authority = NULL;
268 if (channel_args) {
269 for (i = 0; i < channel_args->num_args; i++) {
270 if (0 ==
271 strcmp(channel_args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
272 if (channel_args->args[i].type != GRPC_ARG_STRING) {
273 gpr_log(GPR_ERROR, "%s: must be an string",
274 GRPC_ARG_MAX_CONCURRENT_STREAMS);
275 } else {
276 channeld->default_authority = grpc_mdelem_from_strings(mdctx, ":authority", channel_args->args[i].value.string);
277 }
278 }
279 }
280 }
281
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800282 /* initialize members */
283 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
klempnerc463f742014-12-19 13:03:35 -0800284 channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
David Klempnera1e86932015-01-13 18:13:59 -0800285 channeld->scheme =
Craig Tiller26b37142015-07-26 12:53:27 -0700286 grpc_mdelem_from_strings(mdctx, ":scheme", scheme_from_args(channel_args));
klempnerc463f742014-12-19 13:03:35 -0800287 channeld->content_type =
288 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Craig Tiller5b9efed2015-02-03 20:13:06 -0800289 channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700290 channeld->user_agent = grpc_mdelem_from_metadata_strings(
291 mdctx, grpc_mdstr_from_string(mdctx, "user-agent"),
Craig Tiller26b37142015-07-26 12:53:27 -0700292 user_agent_from_args(mdctx, channel_args));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800293}
294
295/* Destructor for channel data */
296static void destroy_channel_elem(grpc_channel_element *elem) {
297 /* grab pointers to our data from the channel element */
298 channel_data *channeld = elem->channel_data;
299
Craig Tiller1a65a232015-07-06 10:22:32 -0700300 GRPC_MDELEM_UNREF(channeld->te_trailers);
301 GRPC_MDELEM_UNREF(channeld->method);
302 GRPC_MDELEM_UNREF(channeld->scheme);
303 GRPC_MDELEM_UNREF(channeld->content_type);
304 GRPC_MDELEM_UNREF(channeld->status);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700305 GRPC_MDELEM_UNREF(channeld->user_agent);
Craig Tiller26b37142015-07-26 12:53:27 -0700306 if (channeld->default_authority) {
307 GRPC_MDELEM_UNREF(channeld->default_authority);
308 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800309}
310
311const grpc_channel_filter grpc_http_client_filter = {
Craig Tillere039f032015-06-25 12:54:23 -0700312 hc_start_transport_op, grpc_channel_next_op, sizeof(call_data),
313 init_call_elem, destroy_call_elem, sizeof(channel_data),
Craig Tiller1b22b9d2015-07-20 13:42:22 -0700314 init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer,
315 "http-client"};