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 <grpc/support/alloc.h>
35 : #include <grpc/support/log.h>
36 : #include <grpc/support/string_util.h>
37 : #include <string.h>
38 : #include "src/core/profiling/timers.h"
39 : #include "src/core/support/string.h"
40 : #include "src/core/transport/static_metadata.h"
41 :
42 : typedef struct call_data {
43 : grpc_linked_mdelem method;
44 : grpc_linked_mdelem scheme;
45 : grpc_linked_mdelem authority;
46 : grpc_linked_mdelem te_trailers;
47 : grpc_linked_mdelem content_type;
48 : grpc_linked_mdelem user_agent;
49 :
50 : grpc_metadata_batch *recv_initial_metadata;
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 *static_scheme;
62 : grpc_mdelem *user_agent;
63 : } channel_data;
64 :
65 : typedef struct {
66 : grpc_call_element *elem;
67 : grpc_exec_ctx *exec_ctx;
68 : } client_recv_filter_args;
69 :
70 8874948 : static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
71 8874615 : client_recv_filter_args *a = user_data;
72 8874948 : if (md == GRPC_MDELEM_STATUS_200) {
73 2271433 : return NULL;
74 6603441 : } else if (md->key == GRPC_MDSTR_STATUS) {
75 0 : grpc_call_element_send_cancel(a->exec_ctx, a->elem);
76 0 : return NULL;
77 6603441 : } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
78 2270757 : return NULL;
79 : }
80 4332610 : return md;
81 : }
82 :
83 2272069 : static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) {
84 2271982 : grpc_call_element *elem = user_data;
85 2272069 : call_data *calld = elem->call_data;
86 : client_recv_filter_args a;
87 2272069 : a.elem = elem;
88 2272069 : a.exec_ctx = exec_ctx;
89 2272069 : grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter,
90 : &a);
91 2271440 : calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
92 2272032 : }
93 :
94 10441062 : static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
95 : /* eat the things we'd like to set ourselves */
96 10441062 : if (md->key == GRPC_MDSTR_METHOD) return NULL;
97 10441062 : if (md->key == GRPC_MDSTR_SCHEME) return NULL;
98 10441062 : if (md->key == GRPC_MDSTR_TE) return NULL;
99 10441062 : if (md->key == GRPC_MDSTR_CONTENT_TYPE) return NULL;
100 10441062 : if (md->key == GRPC_MDSTR_USER_AGENT) return NULL;
101 10440793 : return md;
102 : }
103 :
104 4276400 : static void hc_mutate_op(grpc_call_element *elem,
105 : grpc_transport_stream_op *op) {
106 : /* grab pointers to our data from the call element */
107 4276400 : call_data *calld = elem->call_data;
108 4276400 : channel_data *channeld = elem->channel_data;
109 4276400 : if (op->send_initial_metadata != NULL) {
110 2272216 : grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter,
111 : elem);
112 : /* Send : prefixed headers, which have to be before any application
113 : layer headers. */
114 2272064 : grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
115 : GRPC_MDELEM_METHOD_POST);
116 2271844 : grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
117 : channeld->static_scheme);
118 2271652 : grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
119 : GRPC_MDELEM_TE_TRAILERS);
120 2271535 : grpc_metadata_batch_add_tail(
121 : op->send_initial_metadata, &calld->content_type,
122 : GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
123 2271582 : grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
124 : GRPC_MDELEM_REF(channeld->user_agent));
125 : }
126 :
127 4278055 : if (op->recv_initial_metadata != NULL) {
128 : /* substitute our callback for the higher callback */
129 2271781 : calld->recv_initial_metadata = op->recv_initial_metadata;
130 2271781 : calld->on_done_recv = op->on_complete;
131 2271781 : op->on_complete = &calld->hc_on_recv;
132 : }
133 4278055 : }
134 :
135 4276802 : static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
136 : grpc_call_element *elem,
137 : grpc_transport_stream_op *op) {
138 : GPR_TIMER_BEGIN("hc_start_transport_op", 0);
139 4276802 : GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
140 4276802 : hc_mutate_op(elem, op);
141 : GPR_TIMER_END("hc_start_transport_op", 0);
142 4275430 : grpc_call_next_op(exec_ctx, elem, op);
143 4278559 : }
144 :
145 : /* Constructor for call_data */
146 2271453 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
147 : grpc_call_element_args *args) {
148 2271453 : call_data *calld = elem->call_data;
149 2271453 : calld->on_done_recv = NULL;
150 2271453 : grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
151 2271709 : }
152 :
153 : /* Destructor for call_data */
154 2272067 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
155 2272067 : grpc_call_element *elem) {}
156 :
157 2982 : static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
158 : unsigned i;
159 : size_t j;
160 2982 : grpc_mdelem *valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
161 : GRPC_MDELEM_SCHEME_HTTPS};
162 2982 : if (args != NULL) {
163 4592 : for (i = 0; i < args->num_args; ++i) {
164 3888 : if (args->args[i].type == GRPC_ARG_STRING &&
165 1293 : strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
166 940 : for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
167 956 : if (0 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value),
168 922 : args->args[i].value.string)) {
169 478 : return valid_schemes[j];
170 : }
171 : }
172 : }
173 : }
174 : }
175 2481 : return GRPC_MDELEM_SCHEME_HTTP;
176 : }
177 :
178 2983 : static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args) {
179 : gpr_strvec v;
180 : size_t i;
181 2942 : int is_first = 1;
182 : char *tmp;
183 : grpc_mdstr *result;
184 :
185 2983 : gpr_strvec_init(&v);
186 :
187 6056 : for (i = 0; args && i < args->num_args; i++) {
188 3073 : if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
189 245 : if (args->args[i].type != GRPC_ARG_STRING) {
190 0 : gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
191 : GRPC_ARG_PRIMARY_USER_AGENT_STRING);
192 : } else {
193 245 : if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
194 206 : is_first = 0;
195 245 : gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
196 : }
197 : }
198 : }
199 :
200 2983 : gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
201 : grpc_version_string(), GPR_PLATFORM_STRING);
202 2942 : is_first = 0;
203 2983 : gpr_strvec_add(&v, tmp);
204 :
205 6057 : for (i = 0; args && i < args->num_args; i++) {
206 3074 : if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
207 72 : if (args->args[i].type != GRPC_ARG_STRING) {
208 0 : gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
209 : GRPC_ARG_SECONDARY_USER_AGENT_STRING);
210 : } else {
211 72 : if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
212 72 : is_first = 0;
213 72 : gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
214 : }
215 : }
216 : }
217 :
218 2983 : tmp = gpr_strvec_flatten(&v, NULL);
219 2983 : gpr_strvec_destroy(&v);
220 2983 : result = grpc_mdstr_from_string(tmp);
221 2983 : gpr_free(tmp);
222 :
223 2983 : return result;
224 : }
225 :
226 : /* Constructor for channel_data */
227 2983 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
228 : grpc_channel_element *elem,
229 : grpc_channel_element_args *args) {
230 2983 : channel_data *chand = elem->channel_data;
231 2983 : GPR_ASSERT(!args->is_last);
232 2983 : chand->static_scheme = scheme_from_args(args->channel_args);
233 2983 : chand->user_agent = grpc_mdelem_from_metadata_strings(
234 : GRPC_MDSTR_USER_AGENT, user_agent_from_args(args->channel_args));
235 2983 : }
236 :
237 : /* Destructor for channel data */
238 2940 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
239 : grpc_channel_element *elem) {
240 2940 : channel_data *chand = elem->channel_data;
241 2940 : GRPC_MDELEM_UNREF(chand->user_agent);
242 2940 : }
243 :
244 : const grpc_channel_filter grpc_http_client_filter = {
245 : hc_start_transport_op, grpc_channel_next_op, sizeof(call_data),
246 : init_call_elem, grpc_call_stack_ignore_set_pollset, destroy_call_elem,
247 : sizeof(channel_data), init_channel_elem, destroy_channel_elem,
248 : grpc_call_next_get_peer, "http-client"};
|