blob: b5ab4dd5cac8f5f51029e5a60a146b2dce877a57 [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;
Craig Tiller1e6facb2015-06-11 22:47:11 -070070
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);
Mark D. Roth75d74782016-09-09 07:46:01 -0700106 grpc_call_element_send_close_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);
Makarand Dharmapurikarb94656d2016-08-31 15:42:44 -0700236
237 /* read slices of send_message and copy into payload_bytes */
238 calld->send_op = *op;
239 calld->send_length = op->send_message->length;
240 calld->send_flags = op->send_message->flags;
241 continue_send_message(exec_ctx, elem);
242
243 if (calld->send_message_blocked == false) {
244 /* when all the send_message data is available, then create a MDELEM and
245 append to headers */
246 grpc_mdelem *payload_bin = grpc_mdelem_from_metadata_strings(
247 GRPC_MDSTR_GRPC_PAYLOAD_BIN,
248 grpc_mdstr_from_buffer(calld->payload_bytes,
249 op->send_message->length));
250 grpc_metadata_batch_add_tail(op->send_initial_metadata,
251 &calld->payload_bin, payload_bin);
252 calld->on_complete = op->on_complete;
253 op->on_complete = &calld->hc_on_complete;
254 op->send_message = NULL;
255 } else {
256 /* Not all data is available. Fall back to POST. */
257 gpr_log(GPR_DEBUG,
258 "Request is marked Cacheable but not all data is available.\
259 Falling back to POST");
260 method = GRPC_MDELEM_METHOD_POST;
261 }
262 }
263
Craig Tiller577c9b22015-11-02 14:11:15 -0800264 grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter,
265 elem);
266 /* Send : prefixed headers, which have to be before any application
267 layer headers. */
Makdharma178f4bc2016-08-24 15:16:53 -0700268 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
269 method);
Craig Tiller577c9b22015-11-02 14:11:15 -0800270 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800271 channeld->static_scheme);
Craig Tiller577c9b22015-11-02 14:11:15 -0800272 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800273 GRPC_MDELEM_TE_TRAILERS);
274 grpc_metadata_batch_add_tail(
275 op->send_initial_metadata, &calld->content_type,
276 GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
Craig Tiller577c9b22015-11-02 14:11:15 -0800277 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
278 GRPC_MDELEM_REF(channeld->user_agent));
Craig Tillera82950e2015-09-22 12:33:20 -0700279 }
Craig Tiller83f88d92015-04-21 16:02:05 -0700280
Craig Tiller577c9b22015-11-02 14:11:15 -0800281 if (op->recv_initial_metadata != NULL) {
Craig Tillera82950e2015-09-22 12:33:20 -0700282 /* substitute our callback for the higher callback */
Craig Tiller577c9b22015-11-02 14:11:15 -0800283 calld->recv_initial_metadata = op->recv_initial_metadata;
Craig Tillera44cbfc2016-02-03 16:02:49 -0800284 calld->on_done_recv = op->recv_initial_metadata_ready;
285 op->recv_initial_metadata_ready = &calld->hc_on_recv;
Craig Tillera82950e2015-09-22 12:33:20 -0700286 }
Craig Tiller50d9db52015-04-23 10:52:14 -0700287}
Craig Tiller83f88d92015-04-21 16:02:05 -0700288
Craig Tillera82950e2015-09-22 12:33:20 -0700289static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
290 grpc_call_element *elem,
291 grpc_transport_stream_op *op) {
Craig Tiller0ba432d2015-10-09 16:57:11 -0700292 GPR_TIMER_BEGIN("hc_start_transport_op", 0);
Craig Tillera82950e2015-09-22 12:33:20 -0700293 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700294 hc_mutate_op(exec_ctx, elem, op);
Craig Tiller0ba432d2015-10-09 16:57:11 -0700295 GPR_TIMER_END("hc_start_transport_op", 0);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700296 call_data *calld = elem->call_data;
297 if (op->send_message != NULL && calld->send_message_blocked) {
298 /* Don't forward the op. send_message contains slices that aren't ready
299 yet. The call will be forwarded by the op_complete of slice read call.
300 */
301 } else {
302 grpc_call_next_op(exec_ctx, elem, op);
303 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800304}
305
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800306/* Constructor for call_data */
Mark D. Roth76d24422016-06-23 13:22:10 -0700307static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
Mark D. Roth0badbe82016-06-23 10:15:12 -0700308 grpc_call_element *elem,
309 grpc_call_element_args *args) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700310 call_data *calld = elem->call_data;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700311 calld->on_done_recv = NULL;
Makdharma7f0abf32016-08-26 13:34:54 -0700312 calld->on_complete = NULL;
313 calld->payload_bytes = NULL;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700314 gpr_slice_buffer_init(&calld->slices);
Craig Tillera82950e2015-09-22 12:33:20 -0700315 grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
Makdharma7f0abf32016-08-26 13:34:54 -0700316 grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700317 grpc_closure_init(&calld->got_slice, got_slice, elem);
318 grpc_closure_init(&calld->send_done, send_done, elem);
Mark D. Roth0badbe82016-06-23 10:15:12 -0700319 return GRPC_ERROR_NONE;
Craig Tiller83f88d92015-04-21 16:02:05 -0700320}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800321
322/* Destructor for call_data */
Craig Tiller2c8063c2016-03-22 22:12:15 -0700323static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
David Garcia Quintas5dde14c2016-07-28 17:29:27 -0700324 const grpc_call_final_info *final_info,
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700325 void *ignored) {
326 call_data *calld = elem->call_data;
327 gpr_slice_buffer_destroy(&calld->slices);
328}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800329
Craig Tillerebdef9d2015-11-19 17:09:49 -0800330static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100331 unsigned i;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800332 size_t j;
333 grpc_mdelem *valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
334 GRPC_MDELEM_SCHEME_HTTPS};
Craig Tillera82950e2015-09-22 12:33:20 -0700335 if (args != NULL) {
336 for (i = 0; i < args->num_args; ++i) {
337 if (args->args[i].type == GRPC_ARG_STRING &&
338 strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
Craig Tillerebdef9d2015-11-19 17:09:49 -0800339 for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
340 if (0 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value),
341 args->args[i].value.string)) {
342 return valid_schemes[j];
343 }
344 }
Craig Tillera82950e2015-09-22 12:33:20 -0700345 }
David Klempnera1e86932015-01-13 18:13:59 -0800346 }
Craig Tillera82950e2015-09-22 12:33:20 -0700347 }
Craig Tillerebdef9d2015-11-19 17:09:49 -0800348 return GRPC_MDELEM_SCHEME_HTTP;
David Klempnera1e86932015-01-13 18:13:59 -0800349}
350
Makdharma178f4bc2016-08-24 15:16:53 -0700351static size_t max_payload_size_from_args(const grpc_channel_args *args) {
352 if (args != NULL) {
353 for (size_t i = 0; i < args->num_args; ++i) {
354 if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) {
355 if (args->args[i].type != GRPC_ARG_INTEGER) {
356 gpr_log(GPR_ERROR, "%s: must be an integer",
357 GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET);
358 } else {
Makdharma6a523f02016-08-26 10:02:00 -0700359 return (size_t)args->args[i].value.integer;
Makdharma178f4bc2016-08-24 15:16:53 -0700360 }
361 }
362 }
363 }
364 return kMaxPayloadSizeForGet;
365}
366
Craig Tiller9d69e802016-06-06 11:37:50 -0700367static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
368 const char *transport_name) {
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700369 gpr_strvec v;
370 size_t i;
371 int is_first = 1;
372 char *tmp;
373 grpc_mdstr *result;
374
Craig Tillera82950e2015-09-22 12:33:20 -0700375 gpr_strvec_init(&v);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700376
Craig Tillera82950e2015-09-22 12:33:20 -0700377 for (i = 0; args && i < args->num_args; i++) {
378 if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
379 if (args->args[i].type != GRPC_ARG_STRING) {
380 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
381 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
382 } else {
383 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
384 is_first = 0;
385 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
386 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700387 }
Craig Tillera82950e2015-09-22 12:33:20 -0700388 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700389
Craig Tiller8dfdb7e2016-08-29 11:25:56 -0700390 gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s; %s)", is_first ? "" : " ",
391 grpc_version_string(), GPR_PLATFORM_STRING, transport_name,
392 grpc_g_stands_for());
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,
Mark D. Rothb2d24882016-10-27 15:44:07 -0700451 grpc_channel_next_get_info,
Craig Tillerf40df232016-03-25 13:38:14 -0700452 "http-client"};