blob: 0aa708694df2dfc312dc88bc22b6aa16e0d8a2e1 [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;
48
49 int got_initial_metadata;
50 grpc_stream_op_buffer *recv_ops;
Craig Tiller1e6facb2015-06-11 22:47:11 -070051
Craig Tiller98bf7e62015-06-24 08:47:07 -070052 /** Closure to call when finished with the hc_on_recv hook */
Craig Tiller33825112015-09-18 07:44:19 -070053 grpc_closure *on_done_recv;
Craig Tiller98bf7e62015-06-24 08:47:07 -070054 /** Receive closures are chained: we inject this closure as the on_done_recv
55 up-call on transport_op, and remember to call our on_done_recv member
56 after handling it. */
Craig Tiller33825112015-09-18 07:44:19 -070057 grpc_closure hc_on_recv;
Yang Gao5fd0d292015-01-26 00:19:48 -080058} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080059
klempnerc463f742014-12-19 13:03:35 -080060typedef struct channel_data {
61 grpc_mdelem *te_trailers;
62 grpc_mdelem *method;
63 grpc_mdelem *scheme;
64 grpc_mdelem *content_type;
Craig Tiller5b9efed2015-02-03 20:13:06 -080065 grpc_mdelem *status;
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070066 /** complete user agent mdelem */
67 grpc_mdelem *user_agent;
klempnerc463f742014-12-19 13:03:35 -080068} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080069
Craig Tillerd1bec032015-09-18 17:29:00 -070070typedef struct {
71 grpc_call_element *elem;
72 grpc_call_list *call_list;
Craig Tiller10ee2742015-09-22 09:25:57 -070073} client_recv_filter_args;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080074
David Garcia Quintas13a71122015-09-18 12:43:52 -070075static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
Craig Tiller10ee2742015-09-22 09:25:57 -070076 client_recv_filter_args *a = user_data;
Craig Tillerd1bec032015-09-18 17:29:00 -070077 grpc_call_element *elem = a->elem;
Craig Tiller6902ad22015-04-16 08:01:49 -070078 channel_data *channeld = elem->channel_data;
79 if (md == channeld->status) {
80 return NULL;
81 } else if (md->key == channeld->status->key) {
Craig Tillerd1bec032015-09-18 17:29:00 -070082 grpc_call_element_send_cancel(elem, a->call_list);
Craig Tiller6902ad22015-04-16 08:01:49 -070083 return NULL;
David Garcia Quintas13a71122015-09-18 12:43:52 -070084 } else if (md->key == channeld->content_type->key) {
85 return NULL;
Craig Tiller6902ad22015-04-16 08:01:49 -070086 }
87 return md;
88}
89
Craig Tillerd1bec032015-09-18 17:29:00 -070090static void hc_on_recv(void *user_data, int success,
91 grpc_call_list *call_list) {
Craig Tiller83f88d92015-04-21 16:02:05 -070092 grpc_call_element *elem = user_data;
93 call_data *calld = elem->call_data;
Craig Tiller37b00442015-09-04 08:39:14 -070094 size_t i;
95 size_t nops = calld->recv_ops->nops;
96 grpc_stream_op *ops = calld->recv_ops->ops;
97 for (i = 0; i < nops; i++) {
98 grpc_stream_op *op = &ops[i];
Craig Tiller10ee2742015-09-22 09:25:57 -070099 client_recv_filter_args a;
Craig Tiller37b00442015-09-04 08:39:14 -0700100 if (op->type != GRPC_OP_METADATA) continue;
101 calld->got_initial_metadata = 1;
Craig Tillerd1bec032015-09-18 17:29:00 -0700102 a.elem = elem;
103 a.call_list = call_list;
Craig Tiller10ee2742015-09-22 09:25:57 -0700104 grpc_metadata_batch_filter(&op->data.metadata, client_recv_filter, &a);
Craig Tiller83f88d92015-04-21 16:02:05 -0700105 }
Craig Tillerd1bec032015-09-18 17:29:00 -0700106 calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success, call_list);
Craig Tiller83f88d92015-04-21 16:02:05 -0700107}
108
Craig Tillerd9ddc772015-07-10 13:00:05 -0700109static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
110 grpc_call_element *elem = user_data;
111 channel_data *channeld = elem->channel_data;
112 /* eat the things we'd like to set ourselves */
113 if (md->key == channeld->method->key) return NULL;
114 if (md->key == channeld->scheme->key) return NULL;
115 if (md->key == channeld->te_trailers->key) return NULL;
116 if (md->key == channeld->content_type->key) return NULL;
117 if (md->key == channeld->user_agent->key) return NULL;
118 return md;
119}
120
Craig Tillerb7959a02015-06-25 08:50:54 -0700121static void hc_mutate_op(grpc_call_element *elem,
122 grpc_transport_stream_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800123 /* grab pointers to our data from the call element */
124 call_data *calld = elem->call_data;
125 channel_data *channeld = elem->channel_data;
Craig Tiller83f88d92015-04-21 16:02:05 -0700126 size_t i;
Craig Tiller83f88d92015-04-21 16:02:05 -0700127 if (op->send_ops && !calld->sent_initial_metadata) {
128 size_t nops = op->send_ops->nops;
129 grpc_stream_op *ops = op->send_ops->ops;
130 for (i = 0; i < nops; i++) {
131 grpc_stream_op *op = &ops[i];
132 if (op->type != GRPC_OP_METADATA) continue;
133 calld->sent_initial_metadata = 1;
Craig Tillerd9ddc772015-07-10 13:00:05 -0700134 grpc_metadata_batch_filter(&op->data.metadata, client_strip_filter, elem);
Craig Tiller6902ad22015-04-16 08:01:49 -0700135 /* Send : prefixed headers, which have to be before any application
Craig Tiller83f88d92015-04-21 16:02:05 -0700136 layer headers. */
Craig Tiller205aee12015-04-16 14:46:41 -0700137 grpc_metadata_batch_add_head(&op->data.metadata, &calld->method,
Craig Tiller1a65a232015-07-06 10:22:32 -0700138 GRPC_MDELEM_REF(channeld->method));
Craig Tiller205aee12015-04-16 14:46:41 -0700139 grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme,
Craig Tiller1a65a232015-07-06 10:22:32 -0700140 GRPC_MDELEM_REF(channeld->scheme));
Craig Tiller205aee12015-04-16 14:46:41 -0700141 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers,
Craig Tiller1a65a232015-07-06 10:22:32 -0700142 GRPC_MDELEM_REF(channeld->te_trailers));
Craig Tiller205aee12015-04-16 14:46:41 -0700143 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
Craig Tiller1a65a232015-07-06 10:22:32 -0700144 GRPC_MDELEM_REF(channeld->content_type));
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700145 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->user_agent,
146 GRPC_MDELEM_REF(channeld->user_agent));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800147 break;
Craig Tiller83f88d92015-04-21 16:02:05 -0700148 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800149 }
Craig Tiller83f88d92015-04-21 16:02:05 -0700150
151 if (op->recv_ops && !calld->got_initial_metadata) {
152 /* substitute our callback for the higher callback */
153 calld->recv_ops = op->recv_ops;
154 calld->on_done_recv = op->on_done_recv;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700155 op->on_done_recv = &calld->hc_on_recv;
Craig Tiller83f88d92015-04-21 16:02:05 -0700156 }
Craig Tiller50d9db52015-04-23 10:52:14 -0700157}
Craig Tiller83f88d92015-04-21 16:02:05 -0700158
Craig Tiller06aeea72015-04-23 10:54:45 -0700159static void hc_start_transport_op(grpc_call_element *elem,
Craig Tillerd1bec032015-09-18 17:29:00 -0700160 grpc_transport_stream_op *op,
161 grpc_call_list *call_list) {
Craig Tiller50d9db52015-04-23 10:52:14 -0700162 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
163 hc_mutate_op(elem, op);
Craig Tillerd1bec032015-09-18 17:29:00 -0700164 grpc_call_next_op(elem, op, call_list);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800165}
166
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800167/* Constructor for call_data */
168static void init_call_elem(grpc_call_element *elem,
Craig Tiller50d9db52015-04-23 10:52:14 -0700169 const void *server_transport_data,
Craig Tillerdfff1b82015-09-21 14:39:57 -0700170 grpc_transport_stream_op *initial_op,
171 grpc_call_list *call_list) {
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 Tiller1e6facb2015-06-11 22:47:11 -0700175 calld->on_done_recv = NULL;
Craig Tiller33825112015-09-18 07:44:19 -0700176 grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
Craig Tiller50d9db52015-04-23 10:52:14 -0700177 if (initial_op) hc_mutate_op(elem, initial_op);
Craig Tiller83f88d92015-04-21 16:02:05 -0700178}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800179
180/* Destructor for call_data */
Craig Tillerd1bec032015-09-18 17:29:00 -0700181static void destroy_call_elem(grpc_call_element *elem,
182 grpc_call_list *call_list) {}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800183
David Klempnered0cbc82015-01-14 14:46:10 -0800184static const char *scheme_from_args(const grpc_channel_args *args) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100185 unsigned i;
David Klempnera1e86932015-01-13 18:13:59 -0800186 if (args != NULL) {
187 for (i = 0; i < args->num_args; ++i) {
188 if (args->args[i].type == GRPC_ARG_STRING &&
David Klempnered0cbc82015-01-14 14:46:10 -0800189 strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
David Klempnera1e86932015-01-13 18:13:59 -0800190 return args->args[i].value.string;
191 }
192 }
193 }
194 return "http";
195}
196
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700197static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
198 const grpc_channel_args *args) {
199 gpr_strvec v;
200 size_t i;
201 int is_first = 1;
202 char *tmp;
203 grpc_mdstr *result;
204
205 gpr_strvec_init(&v);
206
207 for (i = 0; args && i < args->num_args; i++) {
208 if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
209 if (args->args[i].type != GRPC_ARG_STRING) {
210 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
211 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
212 } else {
213 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
214 is_first = 0;
215 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
216 }
217 }
218 }
219
220 gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
221 grpc_version_string(), GPR_PLATFORM_STRING);
222 is_first = 0;
223 gpr_strvec_add(&v, tmp);
224
225 for (i = 0; args && i < args->num_args; i++) {
226 if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
227 if (args->args[i].type != GRPC_ARG_STRING) {
228 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
229 GRPC_ARG_SECONDARY_USER_AGENT_STRING);
230 } else {
231 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
232 is_first = 0;
233 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
234 }
235 }
236 }
237
238 tmp = gpr_strvec_flatten(&v, NULL);
239 gpr_strvec_destroy(&v);
Craig Tillerf7e7d082015-07-23 10:52:23 -0700240 result = grpc_mdstr_from_string(mdctx, tmp, 0);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700241 gpr_free(tmp);
242
243 return result;
244}
245
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800246/* Constructor for channel_data */
Craig Tiller079a11b2015-06-30 10:07:15 -0700247static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
Craig Tiller5666a202015-07-31 17:25:22 -0700248 const grpc_channel_args *channel_args,
Craig Tillerd1bec032015-09-18 17:29:00 -0700249 grpc_mdctx *mdctx, int is_first, int is_last,
250 grpc_call_list *call_list) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800251 /* grab pointers to our data from the channel element */
252 channel_data *channeld = elem->channel_data;
253
254 /* The first and the last filters tend to be implemented differently to
255 handle the case that there's no 'next' filter to call on the up or down
256 path */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800257 GPR_ASSERT(!is_last);
258
259 /* initialize members */
260 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
klempnerc463f742014-12-19 13:03:35 -0800261 channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
Craig Tiller5666a202015-07-31 17:25:22 -0700262 channeld->scheme = grpc_mdelem_from_strings(mdctx, ":scheme",
263 scheme_from_args(channel_args));
klempnerc463f742014-12-19 13:03:35 -0800264 channeld->content_type =
265 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Craig Tiller5b9efed2015-02-03 20:13:06 -0800266 channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700267 channeld->user_agent = grpc_mdelem_from_metadata_strings(
Craig Tillerf7e7d082015-07-23 10:52:23 -0700268 mdctx, grpc_mdstr_from_string(mdctx, "user-agent", 0),
Craig Tiller26b37142015-07-26 12:53:27 -0700269 user_agent_from_args(mdctx, channel_args));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800270}
271
272/* Destructor for channel data */
Craig Tillerd1bec032015-09-18 17:29:00 -0700273static void destroy_channel_elem(grpc_channel_element *elem,
274 grpc_call_list *call_list) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800275 /* grab pointers to our data from the channel element */
276 channel_data *channeld = elem->channel_data;
277
Craig Tiller1a65a232015-07-06 10:22:32 -0700278 GRPC_MDELEM_UNREF(channeld->te_trailers);
279 GRPC_MDELEM_UNREF(channeld->method);
280 GRPC_MDELEM_UNREF(channeld->scheme);
281 GRPC_MDELEM_UNREF(channeld->content_type);
282 GRPC_MDELEM_UNREF(channeld->status);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700283 GRPC_MDELEM_UNREF(channeld->user_agent);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800284}
285
286const grpc_channel_filter grpc_http_client_filter = {
Craig Tillere039f032015-06-25 12:54:23 -0700287 hc_start_transport_op, grpc_channel_next_op, sizeof(call_data),
288 init_call_elem, destroy_call_elem, sizeof(channel_data),
Craig Tiller1b22b9d2015-07-20 13:42:22 -0700289 init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer,
290 "http-client"};