blob: cffb8ffe29c8f2923c3da0382bc93d670ef5afa4 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
Craig Tiller6169d5f2016-03-31 07:46:18 -07002 * 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
Craig Tiller9533d042016-03-25 17:11:06 -070033#include "src/core/lib/channel/http_client_filter.h"
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070034#include <grpc/support/alloc.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080035#include <grpc/support/log.h>
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070036#include <grpc/support/string_util.h>
yang-gd88e1d82015-12-02 13:23:33 -080037#include <string.h>
Craig Tiller9533d042016-03-25 17:11:06 -070038#include "src/core/lib/profiling/timers.h"
39#include "src/core/lib/support/string.h"
40#include "src/core/lib/transport/static_metadata.h"
Craig Tiller9d69e802016-06-06 11:37:50 -070041#include "src/core/lib/transport/transport_impl.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080042
David Garcia Quintas8ba60db2016-05-20 13:53:14 -070043#define EXPECTED_CONTENT_TYPE "application/grpc"
44#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
45
Makarand Dharmapurikar3f374382016-08-31 17:34:56 -070046/* default maximum size of payload eligable for GET request */
47static const size_t kMaxPayloadSizeForGet = 2048;
48
Craig Tillera82950e2015-09-22 12:33:20 -070049typedef struct call_data {
Craig Tiller6902ad22015-04-16 08:01:49 -070050 grpc_linked_mdelem method;
51 grpc_linked_mdelem scheme;
Craig Tiller26b37142015-07-26 12:53:27 -070052 grpc_linked_mdelem authority;
Craig Tiller6902ad22015-04-16 08:01:49 -070053 grpc_linked_mdelem te_trailers;
54 grpc_linked_mdelem content_type;
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070055 grpc_linked_mdelem user_agent;
Makdharma178f4bc2016-08-24 15:16:53 -070056 grpc_linked_mdelem payload_bin;
Craig Tiller83f88d92015-04-21 16:02:05 -070057
Craig Tiller577c9b22015-11-02 14:11:15 -080058 grpc_metadata_batch *recv_initial_metadata;
Makdharma6a523f02016-08-26 10:02:00 -070059 uint8_t *payload_bytes;
Craig Tiller1e6facb2015-06-11 22:47:11 -070060
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -070061 /* Vars to read data off of send_message */
62 grpc_transport_stream_op send_op;
63 uint32_t send_length;
64 uint32_t send_flags;
65 gpr_slice incoming_slice;
66 grpc_slice_buffer_stream replacement_stream;
67 gpr_slice_buffer slices;
68 /* flag that indicates that all slices of send_messages aren't availble */
69 bool send_message_blocked;
70
Craig Tiller98bf7e62015-06-24 08:47:07 -070071 /** Closure to call when finished with the hc_on_recv hook */
Craig Tiller33825112015-09-18 07:44:19 -070072 grpc_closure *on_done_recv;
Makdharma7f0abf32016-08-26 13:34:54 -070073 grpc_closure *on_complete;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -070074 grpc_closure *post_send;
75
Craig Tiller98bf7e62015-06-24 08:47:07 -070076 /** Receive closures are chained: we inject this closure as the on_done_recv
77 up-call on transport_op, and remember to call our on_done_recv member
78 after handling it. */
Craig Tiller33825112015-09-18 07:44:19 -070079 grpc_closure hc_on_recv;
Makdharma7f0abf32016-08-26 13:34:54 -070080 grpc_closure hc_on_complete;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -070081 grpc_closure got_slice;
82 grpc_closure send_done;
Yang Gao5fd0d292015-01-26 00:19:48 -080083} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080084
Craig Tillera82950e2015-09-22 12:33:20 -070085typedef struct channel_data {
Craig Tillerb2b42612015-11-20 12:02:17 -080086 grpc_mdelem *static_scheme;
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070087 grpc_mdelem *user_agent;
Makdharma178f4bc2016-08-24 15:16:53 -070088 size_t max_payload_size_for_get;
klempnerc463f742014-12-19 13:03:35 -080089} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080090
Craig Tillera82950e2015-09-22 12:33:20 -070091typedef struct {
Craig Tillerd1bec032015-09-18 17:29:00 -070092 grpc_call_element *elem;
Craig Tiller8af4c332015-09-22 12:32:31 -070093 grpc_exec_ctx *exec_ctx;
Craig Tiller10ee2742015-09-22 09:25:57 -070094} client_recv_filter_args;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080095
Craig Tillera82950e2015-09-22 12:33:20 -070096static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
Craig Tiller10ee2742015-09-22 09:25:57 -070097 client_recv_filter_args *a = user_data;
Craig Tillerebdef9d2015-11-19 17:09:49 -080098 if (md == GRPC_MDELEM_STATUS_200) {
Craig Tillera82950e2015-09-22 12:33:20 -070099 return NULL;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800100 } else if (md->key == GRPC_MDSTR_STATUS) {
Yuchen Zengec066b32016-06-13 18:10:23 -0700101 char *message_string;
102 gpr_asprintf(&message_string, "Received http2 header with status: %s",
103 grpc_mdstr_as_c_string(md->value));
104 gpr_slice message = gpr_slice_from_copied_string(message_string);
105 gpr_free(message_string);
106 grpc_call_element_send_cancel_with_message(a->exec_ctx, a->elem,
107 GRPC_STATUS_CANCELLED, &message);
Craig Tillera82950e2015-09-22 12:33:20 -0700108 return NULL;
David Garcia Quintas8ba60db2016-05-20 13:53:14 -0700109 } else if (md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
110 return NULL;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800111 } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
David Garcia Quintas8ba60db2016-05-20 13:53:14 -0700112 const char *value_str = grpc_mdstr_as_c_string(md->value);
113 if (strncmp(value_str, EXPECTED_CONTENT_TYPE,
114 EXPECTED_CONTENT_TYPE_LENGTH) == 0 &&
115 (value_str[EXPECTED_CONTENT_TYPE_LENGTH] == '+' ||
116 value_str[EXPECTED_CONTENT_TYPE_LENGTH] == ';')) {
117 /* Although the C implementation doesn't (currently) generate them,
118 any custom +-suffix is explicitly valid. */
119 /* TODO(klempner): We should consider preallocating common values such
120 as +proto or +json, or at least stashing them if we see them. */
121 /* TODO(klempner): Should we be surfacing this to application code? */
122 } else {
123 /* TODO(klempner): We're currently allowing this, but we shouldn't
124 see it without a proxy so log for now. */
125 gpr_log(GPR_INFO, "Unexpected content-type '%s'", value_str);
126 }
Craig Tillera82950e2015-09-22 12:33:20 -0700127 return NULL;
128 }
Craig Tiller6902ad22015-04-16 08:01:49 -0700129 return md;
130}
131
Craig Tillerc027e772016-05-03 16:27:00 -0700132static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
133 grpc_error *error) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700134 grpc_call_element *elem = user_data;
135 call_data *calld = elem->call_data;
Craig Tiller577c9b22015-11-02 14:11:15 -0800136 client_recv_filter_args a;
137 a.elem = elem;
138 a.exec_ctx = exec_ctx;
139 grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter,
140 &a);
Craig Tillerc027e772016-05-03 16:27:00 -0700141 calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error);
Craig Tiller83f88d92015-04-21 16:02:05 -0700142}
143
Makdharma7f0abf32016-08-26 13:34:54 -0700144static void hc_on_complete(grpc_exec_ctx *exec_ctx, void *user_data,
Makdharmab3054522016-08-26 13:36:26 -0700145 grpc_error *error) {
Makdharma7f0abf32016-08-26 13:34:54 -0700146 grpc_call_element *elem = user_data;
147 call_data *calld = elem->call_data;
148 if (calld->payload_bytes) {
149 gpr_free(calld->payload_bytes);
150 calld->payload_bytes = NULL;
151 }
152 calld->on_complete->cb(exec_ctx, calld->on_complete->cb_arg, error);
153}
154
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700155static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
156 grpc_call_element *elem = elemp;
157 call_data *calld = elem->call_data;
158 gpr_slice_buffer_reset_and_unref(&calld->slices);
159 calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
160}
161
Craig Tillera82950e2015-09-22 12:33:20 -0700162static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
Craig Tillerd9ddc772015-07-10 13:00:05 -0700163 /* eat the things we'd like to set ourselves */
Craig Tillerebdef9d2015-11-19 17:09:49 -0800164 if (md->key == GRPC_MDSTR_METHOD) return NULL;
165 if (md->key == GRPC_MDSTR_SCHEME) return NULL;
166 if (md->key == GRPC_MDSTR_TE) return NULL;
167 if (md->key == GRPC_MDSTR_CONTENT_TYPE) return NULL;
168 if (md->key == GRPC_MDSTR_USER_AGENT) return NULL;
Craig Tillerd9ddc772015-07-10 13:00:05 -0700169 return md;
170}
171
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700172static void continue_send_message(grpc_exec_ctx *exec_ctx,
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700173 grpc_call_element *elem) {
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700174 call_data *calld = elem->call_data;
175 uint8_t *wrptr = calld->payload_bytes;
176 while (grpc_byte_stream_next(exec_ctx, calld->send_op.send_message,
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700177 &calld->incoming_slice, ~(size_t)0,
178 &calld->got_slice)) {
179 memcpy(wrptr, GPR_SLICE_START_PTR(calld->incoming_slice),
180 GPR_SLICE_LENGTH(calld->incoming_slice));
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700181 wrptr += GPR_SLICE_LENGTH(calld->incoming_slice);
182 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
183 if (calld->send_length == calld->slices.length) {
184 calld->send_message_blocked = false;
185 break;
186 }
187 }
188}
189
190static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
191 grpc_call_element *elem = elemp;
192 call_data *calld = elem->call_data;
193 calld->send_message_blocked = false;
194 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
195 if (calld->send_length == calld->slices.length) {
196 /* Pass down the original send_message op that was blocked.*/
197 grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700198 calld->send_flags);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700199 calld->send_op.send_message = &calld->replacement_stream.base;
200 calld->post_send = calld->send_op.on_complete;
201 calld->send_op.on_complete = &calld->send_done;
202 grpc_call_next_op(exec_ctx, elem, &calld->send_op);
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700203 } else {
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700204 continue_send_message(exec_ctx, elem);
205 }
206}
207
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700208static void hc_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
Craig Tillera82950e2015-09-22 12:33:20 -0700209 grpc_transport_stream_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800210 /* grab pointers to our data from the call element */
211 call_data *calld = elem->call_data;
212 channel_data *channeld = elem->channel_data;
Makdharma178f4bc2016-08-24 15:16:53 -0700213
Craig Tiller577c9b22015-11-02 14:11:15 -0800214 if (op->send_initial_metadata != NULL) {
Makarand Dharmapurikarb94656d2016-08-31 15:42:44 -0700215 /* Decide which HTTP VERB to use. We use GET if the request is marked
216 cacheable, and the operation contains both initial metadata and send
217 message, and the payload is below the size threshold, and all the data
218 for this request is immediately available. */
219 grpc_mdelem *method = GRPC_MDELEM_METHOD_POST;
220 calld->send_message_blocked = false;
221 if ((op->send_initial_metadata_flags &
222 GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) &&
223 op->send_message != NULL &&
224 op->send_message->length < channeld->max_payload_size_for_get) {
225 method = GRPC_MDELEM_METHOD_GET;
226 calld->send_message_blocked = true;
227 } else if (op->send_initial_metadata_flags &
228 GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) {
229 method = GRPC_MDELEM_METHOD_PUT;
230 }
231
232 /* Attempt to read the data from send_message and create a header field. */
233 if (method == GRPC_MDELEM_METHOD_GET) {
234 /* allocate memory to hold the entire payload */
235 calld->payload_bytes = gpr_malloc(op->send_message->length);
236 GPR_ASSERT(calld->payload_bytes);
237
238 /* read slices of send_message and copy into payload_bytes */
239 calld->send_op = *op;
240 calld->send_length = op->send_message->length;
241 calld->send_flags = op->send_message->flags;
242 continue_send_message(exec_ctx, elem);
243
244 if (calld->send_message_blocked == false) {
245 /* when all the send_message data is available, then create a MDELEM and
246 append to headers */
247 grpc_mdelem *payload_bin = grpc_mdelem_from_metadata_strings(
248 GRPC_MDSTR_GRPC_PAYLOAD_BIN,
249 grpc_mdstr_from_buffer(calld->payload_bytes,
250 op->send_message->length));
251 grpc_metadata_batch_add_tail(op->send_initial_metadata,
252 &calld->payload_bin, payload_bin);
253 calld->on_complete = op->on_complete;
254 op->on_complete = &calld->hc_on_complete;
255 op->send_message = NULL;
256 } else {
257 /* Not all data is available. Fall back to POST. */
258 gpr_log(GPR_DEBUG,
259 "Request is marked Cacheable but not all data is available.\
260 Falling back to POST");
261 method = GRPC_MDELEM_METHOD_POST;
262 }
263 }
264
Craig Tiller577c9b22015-11-02 14:11:15 -0800265 grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter,
266 elem);
267 /* Send : prefixed headers, which have to be before any application
268 layer headers. */
Makdharma178f4bc2016-08-24 15:16:53 -0700269 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
270 method);
Craig Tiller577c9b22015-11-02 14:11:15 -0800271 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800272 channeld->static_scheme);
Craig Tiller577c9b22015-11-02 14:11:15 -0800273 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800274 GRPC_MDELEM_TE_TRAILERS);
275 grpc_metadata_batch_add_tail(
276 op->send_initial_metadata, &calld->content_type,
277 GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
Craig Tiller577c9b22015-11-02 14:11:15 -0800278 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
279 GRPC_MDELEM_REF(channeld->user_agent));
Craig Tillera82950e2015-09-22 12:33:20 -0700280 }
Craig Tiller83f88d92015-04-21 16:02:05 -0700281
Craig Tiller577c9b22015-11-02 14:11:15 -0800282 if (op->recv_initial_metadata != NULL) {
Craig Tillera82950e2015-09-22 12:33:20 -0700283 /* substitute our callback for the higher callback */
Craig Tiller577c9b22015-11-02 14:11:15 -0800284 calld->recv_initial_metadata = op->recv_initial_metadata;
Craig Tillera44cbfc2016-02-03 16:02:49 -0800285 calld->on_done_recv = op->recv_initial_metadata_ready;
286 op->recv_initial_metadata_ready = &calld->hc_on_recv;
Craig Tillera82950e2015-09-22 12:33:20 -0700287 }
Craig Tiller50d9db52015-04-23 10:52:14 -0700288}
Craig Tiller83f88d92015-04-21 16:02:05 -0700289
Craig Tillera82950e2015-09-22 12:33:20 -0700290static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
291 grpc_call_element *elem,
292 grpc_transport_stream_op *op) {
Craig Tiller0ba432d2015-10-09 16:57:11 -0700293 GPR_TIMER_BEGIN("hc_start_transport_op", 0);
Craig Tillera82950e2015-09-22 12:33:20 -0700294 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700295 hc_mutate_op(exec_ctx, elem, op);
Craig Tiller0ba432d2015-10-09 16:57:11 -0700296 GPR_TIMER_END("hc_start_transport_op", 0);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700297 call_data *calld = elem->call_data;
298 if (op->send_message != NULL && calld->send_message_blocked) {
299 /* Don't forward the op. send_message contains slices that aren't ready
300 yet. The call will be forwarded by the op_complete of slice read call.
301 */
302 } else {
303 grpc_call_next_op(exec_ctx, elem, op);
304 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800305}
306
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800307/* Constructor for call_data */
Mark D. Roth76d24422016-06-23 13:22:10 -0700308static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
Mark D. Roth0badbe82016-06-23 10:15:12 -0700309 grpc_call_element *elem,
310 grpc_call_element_args *args) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700311 call_data *calld = elem->call_data;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700312 calld->on_done_recv = NULL;
Makdharma7f0abf32016-08-26 13:34:54 -0700313 calld->on_complete = NULL;
314 calld->payload_bytes = NULL;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700315 gpr_slice_buffer_init(&calld->slices);
Craig Tillera82950e2015-09-22 12:33:20 -0700316 grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
Makdharma7f0abf32016-08-26 13:34:54 -0700317 grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700318 grpc_closure_init(&calld->got_slice, got_slice, elem);
319 grpc_closure_init(&calld->send_done, send_done, elem);
Mark D. Roth0badbe82016-06-23 10:15:12 -0700320 return GRPC_ERROR_NONE;
Craig Tiller83f88d92015-04-21 16:02:05 -0700321}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800322
323/* Destructor for call_data */
Craig Tiller2c8063c2016-03-22 22:12:15 -0700324static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
David Garcia Quintas5dde14c2016-07-28 17:29:27 -0700325 const grpc_call_final_info *final_info,
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700326 void *ignored) {
327 call_data *calld = elem->call_data;
328 gpr_slice_buffer_destroy(&calld->slices);
329}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800330
Craig Tillerebdef9d2015-11-19 17:09:49 -0800331static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100332 unsigned i;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800333 size_t j;
334 grpc_mdelem *valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
335 GRPC_MDELEM_SCHEME_HTTPS};
Craig Tillera82950e2015-09-22 12:33:20 -0700336 if (args != NULL) {
337 for (i = 0; i < args->num_args; ++i) {
338 if (args->args[i].type == GRPC_ARG_STRING &&
339 strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
Craig Tillerebdef9d2015-11-19 17:09:49 -0800340 for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
341 if (0 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value),
342 args->args[i].value.string)) {
343 return valid_schemes[j];
344 }
345 }
Craig Tillera82950e2015-09-22 12:33:20 -0700346 }
David Klempnera1e86932015-01-13 18:13:59 -0800347 }
Craig Tillera82950e2015-09-22 12:33:20 -0700348 }
Craig Tillerebdef9d2015-11-19 17:09:49 -0800349 return GRPC_MDELEM_SCHEME_HTTP;
David Klempnera1e86932015-01-13 18:13:59 -0800350}
351
Makdharma178f4bc2016-08-24 15:16:53 -0700352static size_t max_payload_size_from_args(const grpc_channel_args *args) {
353 if (args != NULL) {
354 for (size_t i = 0; i < args->num_args; ++i) {
355 if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) {
356 if (args->args[i].type != GRPC_ARG_INTEGER) {
357 gpr_log(GPR_ERROR, "%s: must be an integer",
358 GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET);
359 } else {
Makdharma6a523f02016-08-26 10:02:00 -0700360 return (size_t)args->args[i].value.integer;
Makdharma178f4bc2016-08-24 15:16:53 -0700361 }
362 }
363 }
364 }
365 return kMaxPayloadSizeForGet;
366}
367
Craig Tiller9d69e802016-06-06 11:37:50 -0700368static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
369 const char *transport_name) {
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700370 gpr_strvec v;
371 size_t i;
372 int is_first = 1;
373 char *tmp;
374 grpc_mdstr *result;
375
Craig Tillera82950e2015-09-22 12:33:20 -0700376 gpr_strvec_init(&v);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700377
Craig Tillera82950e2015-09-22 12:33:20 -0700378 for (i = 0; args && i < args->num_args; i++) {
379 if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
380 if (args->args[i].type != GRPC_ARG_STRING) {
381 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
382 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
383 } else {
384 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
385 is_first = 0;
386 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
387 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700388 }
Craig Tillera82950e2015-09-22 12:33:20 -0700389 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700390
Craig Tiller9d69e802016-06-06 11:37:50 -0700391 gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s)", is_first ? "" : " ",
392 grpc_version_string(), GPR_PLATFORM_STRING, transport_name);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700393 is_first = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700394 gpr_strvec_add(&v, tmp);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700395
Craig Tillera82950e2015-09-22 12:33:20 -0700396 for (i = 0; args && i < args->num_args; i++) {
397 if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
398 if (args->args[i].type != GRPC_ARG_STRING) {
399 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
400 GRPC_ARG_SECONDARY_USER_AGENT_STRING);
401 } else {
402 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
403 is_first = 0;
404 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
405 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700406 }
Craig Tillera82950e2015-09-22 12:33:20 -0700407 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700408
Craig Tillera82950e2015-09-22 12:33:20 -0700409 tmp = gpr_strvec_flatten(&v, NULL);
410 gpr_strvec_destroy(&v);
Craig Tillerb2b42612015-11-20 12:02:17 -0800411 result = grpc_mdstr_from_string(tmp);
Craig Tillera82950e2015-09-22 12:33:20 -0700412 gpr_free(tmp);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700413
414 return result;
415}
416
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800417/* Constructor for channel_data */
Craig Tillera82950e2015-09-22 12:33:20 -0700418static void init_channel_elem(grpc_exec_ctx *exec_ctx,
Craig Tiller577c9b22015-11-02 14:11:15 -0800419 grpc_channel_element *elem,
420 grpc_channel_element_args *args) {
Craig Tillerebdef9d2015-11-19 17:09:49 -0800421 channel_data *chand = elem->channel_data;
Craig Tiller577c9b22015-11-02 14:11:15 -0800422 GPR_ASSERT(!args->is_last);
Craig Tiller9d69e802016-06-06 11:37:50 -0700423 GPR_ASSERT(args->optional_transport != NULL);
Craig Tillerebdef9d2015-11-19 17:09:49 -0800424 chand->static_scheme = scheme_from_args(args->channel_args);
Makdharma178f4bc2016-08-24 15:16:53 -0700425 chand->max_payload_size_for_get =
426 max_payload_size_from_args(args->channel_args);
Craig Tillerb2b42612015-11-20 12:02:17 -0800427 chand->user_agent = grpc_mdelem_from_metadata_strings(
Craig Tiller9d69e802016-06-06 11:37:50 -0700428 GRPC_MDSTR_USER_AGENT,
429 user_agent_from_args(args->channel_args,
430 args->optional_transport->vtable->name));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800431}
432
433/* Destructor for channel data */
Craig Tillera82950e2015-09-22 12:33:20 -0700434static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
435 grpc_channel_element *elem) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800436 channel_data *chand = elem->channel_data;
437 GRPC_MDELEM_UNREF(chand->user_agent);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800438}
439
440const grpc_channel_filter grpc_http_client_filter = {
Craig Tillerf40df232016-03-25 13:38:14 -0700441 hc_start_transport_op,
442 grpc_channel_next_op,
443 sizeof(call_data),
444 init_call_elem,
David Garcia Quintas4afce7e2016-04-18 16:25:17 -0700445 grpc_call_stack_ignore_set_pollset_or_pollset_set,
Craig Tillerf40df232016-03-25 13:38:14 -0700446 destroy_call_elem,
447 sizeof(channel_data),
448 init_channel_elem,
449 destroy_channel_elem,
450 grpc_call_next_get_peer,
451 "http-client"};