blob: 08a2c0df3c02cab87bde64c8823e5e8e33038f55 [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>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080035#include <grpc/support/log.h>
36
Yang Gao5fd0d292015-01-26 00:19:48 -080037typedef struct call_data {
Craig Tiller6902ad22015-04-16 08:01:49 -070038 grpc_linked_mdelem method;
39 grpc_linked_mdelem scheme;
40 grpc_linked_mdelem te_trailers;
41 grpc_linked_mdelem content_type;
Craig Tiller83f88d92015-04-21 16:02:05 -070042 int sent_initial_metadata;
43
44 int got_initial_metadata;
45 grpc_stream_op_buffer *recv_ops;
Craig Tiller1e6facb2015-06-11 22:47:11 -070046
Craig Tiller98bf7e62015-06-24 08:47:07 -070047 /** Closure to call when finished with the hc_on_recv hook */
48 grpc_iomgr_closure *on_done_recv;
49 /** Receive closures are chained: we inject this closure as the on_done_recv
50 up-call on transport_op, and remember to call our on_done_recv member
51 after handling it. */
Craig Tiller1e6facb2015-06-11 22:47:11 -070052 grpc_iomgr_closure hc_on_recv;
Yang Gao5fd0d292015-01-26 00:19:48 -080053} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080054
klempnerc463f742014-12-19 13:03:35 -080055typedef struct channel_data {
56 grpc_mdelem *te_trailers;
57 grpc_mdelem *method;
58 grpc_mdelem *scheme;
59 grpc_mdelem *content_type;
Craig Tiller5b9efed2015-02-03 20:13:06 -080060 grpc_mdelem *status;
klempnerc463f742014-12-19 13:03:35 -080061} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080062
63/* used to silence 'variable not used' warnings */
64static void ignore_unused(void *ignored) {}
65
Craig Tiller6902ad22015-04-16 08:01:49 -070066static grpc_mdelem *client_filter(void *user_data, grpc_mdelem *md) {
67 grpc_call_element *elem = user_data;
68 channel_data *channeld = elem->channel_data;
69 if (md == channeld->status) {
70 return NULL;
71 } else if (md->key == channeld->status->key) {
72 grpc_call_element_send_cancel(elem);
73 return NULL;
74 }
75 return md;
76}
77
Craig Tiller83f88d92015-04-21 16:02:05 -070078static void hc_on_recv(void *user_data, int success) {
79 grpc_call_element *elem = user_data;
80 call_data *calld = elem->call_data;
81 if (success) {
82 size_t i;
83 size_t nops = calld->recv_ops->nops;
84 grpc_stream_op *ops = calld->recv_ops->ops;
85 for (i = 0; i < nops; i++) {
86 grpc_stream_op *op = &ops[i];
87 if (op->type != GRPC_OP_METADATA) continue;
88 calld->got_initial_metadata = 1;
89 grpc_metadata_batch_filter(&op->data.metadata, client_filter, elem);
90 }
91 }
Craig Tiller1e6facb2015-06-11 22:47:11 -070092 calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success);
Craig Tiller83f88d92015-04-21 16:02:05 -070093}
94
Craig Tiller50d9db52015-04-23 10:52:14 -070095static void hc_mutate_op(grpc_call_element *elem, grpc_transport_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080096 /* grab pointers to our data from the call element */
97 call_data *calld = elem->call_data;
98 channel_data *channeld = elem->channel_data;
Craig Tiller83f88d92015-04-21 16:02:05 -070099 size_t i;
Craig Tiller83f88d92015-04-21 16:02:05 -0700100 if (op->send_ops && !calld->sent_initial_metadata) {
101 size_t nops = op->send_ops->nops;
102 grpc_stream_op *ops = op->send_ops->ops;
103 for (i = 0; i < nops; i++) {
104 grpc_stream_op *op = &ops[i];
105 if (op->type != GRPC_OP_METADATA) continue;
106 calld->sent_initial_metadata = 1;
Craig Tiller6902ad22015-04-16 08:01:49 -0700107 /* Send : prefixed headers, which have to be before any application
Craig Tiller83f88d92015-04-21 16:02:05 -0700108 layer headers. */
Craig Tiller205aee12015-04-16 14:46:41 -0700109 grpc_metadata_batch_add_head(&op->data.metadata, &calld->method,
Craig Tiller1b5062c2015-04-21 11:53:02 -0700110 grpc_mdelem_ref(channeld->method));
Craig Tiller205aee12015-04-16 14:46:41 -0700111 grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme,
Craig Tiller1b5062c2015-04-21 11:53:02 -0700112 grpc_mdelem_ref(channeld->scheme));
Craig Tiller205aee12015-04-16 14:46:41 -0700113 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers,
Craig Tiller1b5062c2015-04-21 11:53:02 -0700114 grpc_mdelem_ref(channeld->te_trailers));
Craig Tiller205aee12015-04-16 14:46:41 -0700115 grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
Craig Tiller1b5062c2015-04-21 11:53:02 -0700116 grpc_mdelem_ref(channeld->content_type));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800117 break;
Craig Tiller83f88d92015-04-21 16:02:05 -0700118 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800119 }
Craig Tiller83f88d92015-04-21 16:02:05 -0700120
121 if (op->recv_ops && !calld->got_initial_metadata) {
122 /* substitute our callback for the higher callback */
123 calld->recv_ops = op->recv_ops;
124 calld->on_done_recv = op->on_done_recv;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700125 op->on_done_recv = &calld->hc_on_recv;
Craig Tiller83f88d92015-04-21 16:02:05 -0700126 }
Craig Tiller50d9db52015-04-23 10:52:14 -0700127}
Craig Tiller83f88d92015-04-21 16:02:05 -0700128
Craig Tiller06aeea72015-04-23 10:54:45 -0700129static void hc_start_transport_op(grpc_call_element *elem,
130 grpc_transport_op *op) {
Craig Tiller50d9db52015-04-23 10:52:14 -0700131 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
132 hc_mutate_op(elem, op);
Craig Tiller83f88d92015-04-21 16:02:05 -0700133 grpc_call_next_op(elem, op);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800134}
135
136/* Called on special channel events, such as disconnection or new incoming
137 calls on the server */
ctillerf962f522014-12-10 15:28:27 -0800138static void channel_op(grpc_channel_element *elem,
139 grpc_channel_element *from_elem, grpc_channel_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800140 /* grab pointers to our data from the channel element */
141 channel_data *channeld = elem->channel_data;
142
143 ignore_unused(channeld);
144
145 switch (op->type) {
146 default:
147 /* pass control up or down the stack depending on op->dir */
148 grpc_channel_next_op(elem, op);
149 break;
150 }
151}
152
153/* Constructor for call_data */
154static void init_call_elem(grpc_call_element *elem,
Craig Tiller50d9db52015-04-23 10:52:14 -0700155 const void *server_transport_data,
156 grpc_transport_op *initial_op) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700157 call_data *calld = elem->call_data;
158 calld->sent_initial_metadata = 0;
159 calld->got_initial_metadata = 0;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700160 calld->on_done_recv = NULL;
161 grpc_iomgr_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
Craig Tiller50d9db52015-04-23 10:52:14 -0700162 if (initial_op) hc_mutate_op(elem, initial_op);
Craig Tiller83f88d92015-04-21 16:02:05 -0700163}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800164
165/* Destructor for call_data */
166static void destroy_call_elem(grpc_call_element *elem) {
167 /* grab pointers to our data from the call element */
168 call_data *calld = elem->call_data;
169 channel_data *channeld = elem->channel_data;
170
171 ignore_unused(calld);
172 ignore_unused(channeld);
173}
174
David Klempnered0cbc82015-01-14 14:46:10 -0800175static const char *scheme_from_args(const grpc_channel_args *args) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100176 unsigned i;
David Klempnera1e86932015-01-13 18:13:59 -0800177 if (args != NULL) {
178 for (i = 0; i < args->num_args; ++i) {
179 if (args->args[i].type == GRPC_ARG_STRING &&
David Klempnered0cbc82015-01-14 14:46:10 -0800180 strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
David Klempnera1e86932015-01-13 18:13:59 -0800181 return args->args[i].value.string;
182 }
183 }
184 }
185 return "http";
186}
187
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800188/* Constructor for channel_data */
189static void init_channel_elem(grpc_channel_element *elem,
190 const grpc_channel_args *args, grpc_mdctx *mdctx,
191 int is_first, int is_last) {
192 /* grab pointers to our data from the channel element */
193 channel_data *channeld = elem->channel_data;
194
195 /* The first and the last filters tend to be implemented differently to
196 handle the case that there's no 'next' filter to call on the up or down
197 path */
198 GPR_ASSERT(!is_first);
199 GPR_ASSERT(!is_last);
200
201 /* initialize members */
202 channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
klempnerc463f742014-12-19 13:03:35 -0800203 channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
David Klempnera1e86932015-01-13 18:13:59 -0800204 channeld->scheme =
205 grpc_mdelem_from_strings(mdctx, ":scheme", scheme_from_args(args));
klempnerc463f742014-12-19 13:03:35 -0800206 channeld->content_type =
207 grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
Craig Tiller5b9efed2015-02-03 20:13:06 -0800208 channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800209}
210
211/* Destructor for channel data */
212static void destroy_channel_elem(grpc_channel_element *elem) {
213 /* grab pointers to our data from the channel element */
214 channel_data *channeld = elem->channel_data;
215
216 grpc_mdelem_unref(channeld->te_trailers);
klempnerc463f742014-12-19 13:03:35 -0800217 grpc_mdelem_unref(channeld->method);
218 grpc_mdelem_unref(channeld->scheme);
219 grpc_mdelem_unref(channeld->content_type);
Craig Tiller5b9efed2015-02-03 20:13:06 -0800220 grpc_mdelem_unref(channeld->status);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800221}
222
223const grpc_channel_filter grpc_http_client_filter = {
Craig Tiller06aeea72015-04-23 10:54:45 -0700224 hc_start_transport_op, channel_op, sizeof(call_data), init_call_elem,
225 destroy_call_elem, sizeof(channel_data), init_channel_elem,
226 destroy_channel_elem, "http-client"};