Line data Source code
1 : /*
2 : * Copyright 2015, Google Inc.
3 : * 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"
34 : #include <string.h>
35 : #include <grpc/support/alloc.h>
36 : #include <grpc/support/log.h>
37 : #include <grpc/support/string_util.h>
38 : #include "src/core/support/string.h"
39 :
40 : typedef struct call_data {
41 : grpc_linked_mdelem method;
42 : grpc_linked_mdelem scheme;
43 : grpc_linked_mdelem authority;
44 : grpc_linked_mdelem te_trailers;
45 : grpc_linked_mdelem content_type;
46 : grpc_linked_mdelem user_agent;
47 : int sent_initial_metadata;
48 :
49 : int got_initial_metadata;
50 : grpc_stream_op_buffer *recv_ops;
51 :
52 : /** Closure to call when finished with the hc_on_recv hook */
53 : grpc_closure *on_done_recv;
54 : /** 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. */
57 : grpc_closure hc_on_recv;
58 : } call_data;
59 :
60 : typedef struct channel_data {
61 : grpc_mdelem *te_trailers;
62 : grpc_mdelem *method;
63 : grpc_mdelem *scheme;
64 : grpc_mdelem *content_type;
65 : grpc_mdelem *status;
66 : /** complete user agent mdelem */
67 : grpc_mdelem *user_agent;
68 : } channel_data;
69 :
70 : typedef struct {
71 : grpc_call_element *elem;
72 : grpc_exec_ctx *exec_ctx;
73 : } client_recv_filter_args;
74 :
75 7105320 : static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
76 7105320 : client_recv_filter_args *a = user_data;
77 7105320 : grpc_call_element *elem = a->elem;
78 7105320 : channel_data *channeld = elem->channel_data;
79 7105320 : if (md == channeld->status) {
80 1402442 : return NULL;
81 5702878 : } else if (md->key == channeld->status->key) {
82 0 : grpc_call_element_send_cancel(a->exec_ctx, elem);
83 0 : return NULL;
84 5702878 : } else if (md->key == channeld->content_type->key) {
85 1402164 : return NULL;
86 : }
87 4300714 : return md;
88 : }
89 :
90 1404004 : static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) {
91 1404004 : grpc_call_element *elem = user_data;
92 1404004 : call_data *calld = elem->call_data;
93 : size_t i;
94 1404004 : size_t nops = calld->recv_ops->nops;
95 1404004 : grpc_stream_op *ops = calld->recv_ops->ops;
96 6991989 : for (i = 0; i < nops; i++) {
97 5589360 : grpc_stream_op *op = &ops[i];
98 : client_recv_filter_args a;
99 5589360 : if (op->type != GRPC_OP_METADATA) continue;
100 2798837 : calld->got_initial_metadata = 1;
101 2798837 : a.elem = elem;
102 2798837 : a.exec_ctx = exec_ctx;
103 2798837 : grpc_metadata_batch_filter(&op->data.metadata, client_recv_filter, &a);
104 : }
105 1402629 : calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
106 1402984 : }
107 :
108 5607818 : static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
109 5607818 : grpc_call_element *elem = user_data;
110 5607818 : channel_data *channeld = elem->channel_data;
111 : /* eat the things we'd like to set ourselves */
112 5607818 : if (md->key == channeld->method->key) return NULL;
113 5607818 : if (md->key == channeld->scheme->key) return NULL;
114 5607818 : if (md->key == channeld->te_trailers->key) return NULL;
115 5607818 : if (md->key == channeld->content_type->key) return NULL;
116 5607818 : if (md->key == channeld->user_agent->key) return NULL;
117 5607532 : return md;
118 : }
119 :
120 3348454 : static void hc_mutate_op(grpc_call_element *elem,
121 : grpc_transport_stream_op *op) {
122 : /* grab pointers to our data from the call element */
123 3348454 : call_data *calld = elem->call_data;
124 3348454 : channel_data *channeld = elem->channel_data;
125 : size_t i;
126 3348454 : if (op->send_ops && !calld->sent_initial_metadata) {
127 1402925 : size_t nops = op->send_ops->nops;
128 1402925 : grpc_stream_op *ops = op->send_ops->ops;
129 2805850 : for (i = 0; i < nops; i++) {
130 1401593 : grpc_stream_op *stream_op = &ops[i];
131 1401593 : if (stream_op->type != GRPC_OP_METADATA) continue;
132 1401593 : calld->sent_initial_metadata = 1;
133 1401593 : grpc_metadata_batch_filter(&stream_op->data.metadata, client_strip_filter,
134 : elem);
135 : /* Send : prefixed headers, which have to be before any application
136 : layer headers. */
137 1402693 : grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->method,
138 : GRPC_MDELEM_REF(channeld->method));
139 1402731 : grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->scheme,
140 : GRPC_MDELEM_REF(channeld->scheme));
141 1402575 : grpc_metadata_batch_add_tail(&stream_op->data.metadata,
142 : &calld->te_trailers,
143 : GRPC_MDELEM_REF(channeld->te_trailers));
144 1402580 : grpc_metadata_batch_add_tail(&stream_op->data.metadata,
145 : &calld->content_type,
146 : GRPC_MDELEM_REF(channeld->content_type));
147 1402604 : grpc_metadata_batch_add_tail(&stream_op->data.metadata,
148 : &calld->user_agent,
149 : GRPC_MDELEM_REF(channeld->user_agent));
150 1402662 : break;
151 : }
152 : }
153 :
154 3349523 : if (op->recv_ops && !calld->got_initial_metadata) {
155 : /* substitute our callback for the higher callback */
156 1402877 : calld->recv_ops = op->recv_ops;
157 1402877 : calld->on_done_recv = op->on_done_recv;
158 1402877 : op->on_done_recv = &calld->hc_on_recv;
159 : }
160 3349523 : }
161 :
162 3346935 : static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
163 : grpc_call_element *elem,
164 : grpc_transport_stream_op *op) {
165 3346935 : GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
166 3346935 : hc_mutate_op(elem, op);
167 3347174 : grpc_call_next_op(exec_ctx, elem, op);
168 3349154 : }
169 :
170 : /* Constructor for call_data */
171 1402142 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
172 : const void *server_transport_data,
173 : grpc_transport_stream_op *initial_op) {
174 1402142 : call_data *calld = elem->call_data;
175 1402142 : calld->sent_initial_metadata = 0;
176 1402142 : calld->got_initial_metadata = 0;
177 1402142 : calld->on_done_recv = NULL;
178 1402142 : grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
179 1402632 : if (initial_op) hc_mutate_op(elem, initial_op);
180 1402632 : }
181 :
182 : /* Destructor for call_data */
183 1402624 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
184 1402624 : grpc_call_element *elem) {}
185 :
186 1992 : static const char *scheme_from_args(const grpc_channel_args *args) {
187 : unsigned i;
188 1992 : if (args != NULL) {
189 2753 : for (i = 0; i < args->num_args; ++i) {
190 2493 : if (args->args[i].type == GRPC_ARG_STRING &&
191 971 : strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
192 365 : return args->args[i].value.string;
193 : }
194 : }
195 : }
196 1627 : return "http";
197 : }
198 :
199 1992 : static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
200 : const grpc_channel_args *args) {
201 : gpr_strvec v;
202 : size_t i;
203 1992 : int is_first = 1;
204 : char *tmp;
205 : grpc_mdstr *result;
206 :
207 1992 : gpr_strvec_init(&v);
208 :
209 3877 : for (i = 0; args && i < args->num_args; i++) {
210 1887 : if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
211 169 : if (args->args[i].type != GRPC_ARG_STRING) {
212 0 : gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
213 : GRPC_ARG_PRIMARY_USER_AGENT_STRING);
214 : } else {
215 169 : if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
216 169 : is_first = 0;
217 169 : gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
218 : }
219 : }
220 : }
221 :
222 1990 : gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
223 : grpc_version_string(), GPR_PLATFORM_STRING);
224 1992 : is_first = 0;
225 1992 : gpr_strvec_add(&v, tmp);
226 :
227 3879 : for (i = 0; args && i < args->num_args; i++) {
228 1887 : if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
229 72 : if (args->args[i].type != GRPC_ARG_STRING) {
230 0 : gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
231 : GRPC_ARG_SECONDARY_USER_AGENT_STRING);
232 : } else {
233 72 : if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
234 72 : is_first = 0;
235 72 : gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
236 : }
237 : }
238 : }
239 :
240 1992 : tmp = gpr_strvec_flatten(&v, NULL);
241 1992 : gpr_strvec_destroy(&v);
242 1992 : result = grpc_mdstr_from_string(mdctx, tmp);
243 1992 : gpr_free(tmp);
244 :
245 1992 : return result;
246 : }
247 :
248 : /* Constructor for channel_data */
249 1990 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
250 : grpc_channel_element *elem, grpc_channel *master,
251 : const grpc_channel_args *channel_args,
252 : grpc_mdctx *mdctx, int is_first, int is_last) {
253 : /* grab pointers to our data from the channel element */
254 1990 : channel_data *channeld = elem->channel_data;
255 :
256 : /* The first and the last filters tend to be implemented differently to
257 : handle the case that there's no 'next' filter to call on the up or down
258 : path */
259 1990 : GPR_ASSERT(!is_last);
260 :
261 : /* initialize members */
262 1990 : channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
263 1992 : channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
264 1992 : channeld->scheme = grpc_mdelem_from_strings(mdctx, ":scheme",
265 : scheme_from_args(channel_args));
266 1992 : channeld->content_type =
267 1992 : grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
268 1992 : channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
269 1992 : channeld->user_agent = grpc_mdelem_from_metadata_strings(
270 : mdctx, grpc_mdstr_from_string(mdctx, "user-agent"),
271 : user_agent_from_args(mdctx, channel_args));
272 1992 : }
273 :
274 : /* Destructor for channel data */
275 1992 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
276 : grpc_channel_element *elem) {
277 : /* grab pointers to our data from the channel element */
278 1992 : channel_data *channeld = elem->channel_data;
279 :
280 1992 : GRPC_MDELEM_UNREF(channeld->te_trailers);
281 1992 : GRPC_MDELEM_UNREF(channeld->method);
282 1992 : GRPC_MDELEM_UNREF(channeld->scheme);
283 1992 : GRPC_MDELEM_UNREF(channeld->content_type);
284 1992 : GRPC_MDELEM_UNREF(channeld->status);
285 1992 : GRPC_MDELEM_UNREF(channeld->user_agent);
286 1992 : }
287 :
288 : const grpc_channel_filter grpc_http_client_filter = {
289 : hc_start_transport_op, grpc_channel_next_op, sizeof(call_data),
290 : init_call_elem, destroy_call_elem, sizeof(channel_data),
291 : init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer,
292 : "http-client"};
|