blob: 26d6dcce781c79d7e57b981799b70186cd94c908 [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
Craig Tillera82950e2015-09-22 12:33:20 -070046typedef struct call_data {
Craig Tiller6902ad22015-04-16 08:01:49 -070047 grpc_linked_mdelem method;
48 grpc_linked_mdelem scheme;
Craig Tiller26b37142015-07-26 12:53:27 -070049 grpc_linked_mdelem authority;
Craig Tiller6902ad22015-04-16 08:01:49 -070050 grpc_linked_mdelem te_trailers;
51 grpc_linked_mdelem content_type;
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070052 grpc_linked_mdelem user_agent;
Makdharma178f4bc2016-08-24 15:16:53 -070053 grpc_linked_mdelem payload_bin;
Craig Tiller83f88d92015-04-21 16:02:05 -070054
Craig Tiller577c9b22015-11-02 14:11:15 -080055 grpc_metadata_batch *recv_initial_metadata;
Makdharma6a523f02016-08-26 10:02:00 -070056 uint8_t *payload_bytes;
Craig Tiller1e6facb2015-06-11 22:47:11 -070057
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -070058 /* Vars to read data off of send_message */
59 grpc_transport_stream_op send_op;
60 uint32_t send_length;
61 uint32_t send_flags;
62 gpr_slice incoming_slice;
63 grpc_slice_buffer_stream replacement_stream;
64 gpr_slice_buffer slices;
65 /* flag that indicates that all slices of send_messages aren't availble */
66 bool send_message_blocked;
67
Craig Tiller98bf7e62015-06-24 08:47:07 -070068 /** Closure to call when finished with the hc_on_recv hook */
Craig Tiller33825112015-09-18 07:44:19 -070069 grpc_closure *on_done_recv;
Makdharma7f0abf32016-08-26 13:34:54 -070070 grpc_closure *on_complete;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -070071 grpc_closure *post_send;
72
Craig Tiller98bf7e62015-06-24 08:47:07 -070073 /** Receive closures are chained: we inject this closure as the on_done_recv
74 up-call on transport_op, and remember to call our on_done_recv member
75 after handling it. */
Craig Tiller33825112015-09-18 07:44:19 -070076 grpc_closure hc_on_recv;
Makdharma7f0abf32016-08-26 13:34:54 -070077 grpc_closure hc_on_complete;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -070078 grpc_closure got_slice;
79 grpc_closure send_done;
Yang Gao5fd0d292015-01-26 00:19:48 -080080} call_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080081
Craig Tillera82950e2015-09-22 12:33:20 -070082typedef struct channel_data {
Craig Tillerb2b42612015-11-20 12:02:17 -080083 grpc_mdelem *static_scheme;
Craig Tiller0dc5e6c2015-07-10 10:07:53 -070084 grpc_mdelem *user_agent;
Makdharma178f4bc2016-08-24 15:16:53 -070085 size_t max_payload_size_for_get;
klempnerc463f742014-12-19 13:03:35 -080086} channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080087
Craig Tillera82950e2015-09-22 12:33:20 -070088typedef struct {
Craig Tillerd1bec032015-09-18 17:29:00 -070089 grpc_call_element *elem;
Craig Tiller8af4c332015-09-22 12:32:31 -070090 grpc_exec_ctx *exec_ctx;
Craig Tiller10ee2742015-09-22 09:25:57 -070091} client_recv_filter_args;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080092
Craig Tillera82950e2015-09-22 12:33:20 -070093static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
Craig Tiller10ee2742015-09-22 09:25:57 -070094 client_recv_filter_args *a = user_data;
Craig Tillerebdef9d2015-11-19 17:09:49 -080095 if (md == GRPC_MDELEM_STATUS_200) {
Craig Tillera82950e2015-09-22 12:33:20 -070096 return NULL;
Craig Tillerebdef9d2015-11-19 17:09:49 -080097 } else if (md->key == GRPC_MDSTR_STATUS) {
Yuchen Zengec066b32016-06-13 18:10:23 -070098 char *message_string;
99 gpr_asprintf(&message_string, "Received http2 header with status: %s",
100 grpc_mdstr_as_c_string(md->value));
101 gpr_slice message = gpr_slice_from_copied_string(message_string);
102 gpr_free(message_string);
103 grpc_call_element_send_cancel_with_message(a->exec_ctx, a->elem,
104 GRPC_STATUS_CANCELLED, &message);
Craig Tillera82950e2015-09-22 12:33:20 -0700105 return NULL;
David Garcia Quintas8ba60db2016-05-20 13:53:14 -0700106 } else if (md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
107 return NULL;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800108 } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
David Garcia Quintas8ba60db2016-05-20 13:53:14 -0700109 const char *value_str = grpc_mdstr_as_c_string(md->value);
110 if (strncmp(value_str, EXPECTED_CONTENT_TYPE,
111 EXPECTED_CONTENT_TYPE_LENGTH) == 0 &&
112 (value_str[EXPECTED_CONTENT_TYPE_LENGTH] == '+' ||
113 value_str[EXPECTED_CONTENT_TYPE_LENGTH] == ';')) {
114 /* Although the C implementation doesn't (currently) generate them,
115 any custom +-suffix is explicitly valid. */
116 /* TODO(klempner): We should consider preallocating common values such
117 as +proto or +json, or at least stashing them if we see them. */
118 /* TODO(klempner): Should we be surfacing this to application code? */
119 } else {
120 /* TODO(klempner): We're currently allowing this, but we shouldn't
121 see it without a proxy so log for now. */
122 gpr_log(GPR_INFO, "Unexpected content-type '%s'", value_str);
123 }
Craig Tillera82950e2015-09-22 12:33:20 -0700124 return NULL;
125 }
Craig Tiller6902ad22015-04-16 08:01:49 -0700126 return md;
127}
128
Craig Tillerc027e772016-05-03 16:27:00 -0700129static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
130 grpc_error *error) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700131 grpc_call_element *elem = user_data;
132 call_data *calld = elem->call_data;
Craig Tiller577c9b22015-11-02 14:11:15 -0800133 client_recv_filter_args a;
134 a.elem = elem;
135 a.exec_ctx = exec_ctx;
136 grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter,
137 &a);
Craig Tillerc027e772016-05-03 16:27:00 -0700138 calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error);
Craig Tiller83f88d92015-04-21 16:02:05 -0700139}
140
Makdharma7f0abf32016-08-26 13:34:54 -0700141static void hc_on_complete(grpc_exec_ctx *exec_ctx, void *user_data,
Makdharmab3054522016-08-26 13:36:26 -0700142 grpc_error *error) {
Makdharma7f0abf32016-08-26 13:34:54 -0700143 grpc_call_element *elem = user_data;
144 call_data *calld = elem->call_data;
145 if (calld->payload_bytes) {
146 gpr_free(calld->payload_bytes);
147 calld->payload_bytes = NULL;
148 }
149 calld->on_complete->cb(exec_ctx, calld->on_complete->cb_arg, error);
150}
151
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700152static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
153 grpc_call_element *elem = elemp;
154 call_data *calld = elem->call_data;
155 gpr_slice_buffer_reset_and_unref(&calld->slices);
156 calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
157}
158
Craig Tillera82950e2015-09-22 12:33:20 -0700159static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
Craig Tillerd9ddc772015-07-10 13:00:05 -0700160 /* eat the things we'd like to set ourselves */
Craig Tillerebdef9d2015-11-19 17:09:49 -0800161 if (md->key == GRPC_MDSTR_METHOD) return NULL;
162 if (md->key == GRPC_MDSTR_SCHEME) return NULL;
163 if (md->key == GRPC_MDSTR_TE) return NULL;
164 if (md->key == GRPC_MDSTR_CONTENT_TYPE) return NULL;
165 if (md->key == GRPC_MDSTR_USER_AGENT) return NULL;
Craig Tillerd9ddc772015-07-10 13:00:05 -0700166 return md;
167}
168
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700169static void continue_send_message(grpc_exec_ctx *exec_ctx,
170 grpc_call_element *elem) {
171 call_data *calld = elem->call_data;
172 uint8_t *wrptr = calld->payload_bytes;
173 while (grpc_byte_stream_next(exec_ctx, calld->send_op.send_message,
174 &calld->incoming_slice, ~(size_t)0,
175 &calld->got_slice)) {
176 memcpy(wrptr, GPR_SLICE_START_PTR(calld->incoming_slice), GPR_SLICE_LENGTH(calld->incoming_slice));
177 wrptr += GPR_SLICE_LENGTH(calld->incoming_slice);
178 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
179 if (calld->send_length == calld->slices.length) {
180 calld->send_message_blocked = false;
181 break;
182 }
183 }
184}
185
186static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
187 grpc_call_element *elem = elemp;
188 call_data *calld = elem->call_data;
189 calld->send_message_blocked = false;
190 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
191 if (calld->send_length == calld->slices.length) {
192 /* Pass down the original send_message op that was blocked.*/
193 grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
194 calld->send_flags);
195 calld->send_op.send_message = &calld->replacement_stream.base;
196 calld->post_send = calld->send_op.on_complete;
197 calld->send_op.on_complete = &calld->send_done;
198 grpc_call_next_op(exec_ctx, elem, &calld->send_op);
199 }
200 else {
201 continue_send_message(exec_ctx, elem);
202 }
203}
204
205static void hc_mutate_op(grpc_exec_ctx *exec_ctx,
206 grpc_call_element *elem,
Craig Tillera82950e2015-09-22 12:33:20 -0700207 grpc_transport_stream_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800208 /* grab pointers to our data from the call element */
209 call_data *calld = elem->call_data;
210 channel_data *channeld = elem->channel_data;
Makdharma178f4bc2016-08-24 15:16:53 -0700211
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700212 /* Decide which HTTP VERB to use. We use GET if the request is marked
213 cacheable, and the operation contains both initial metadata and send message,
214 and the payload is below the size threshold, and all the data
215 for this request is immediately available. */
Makdharma178f4bc2016-08-24 15:16:53 -0700216 grpc_mdelem *method = GRPC_MDELEM_METHOD_POST;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700217 calld->send_message_blocked = false;
Makarand Dharmapurikarf44946b2016-08-29 15:47:43 -0700218 if (op->send_initial_metadata != NULL && (op->send_initial_metadata_flags &
Makdharma178f4bc2016-08-24 15:16:53 -0700219 GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) &&
220 op->send_message != NULL &&
221 op->send_message->length < channeld->max_payload_size_for_get) {
222 method = GRPC_MDELEM_METHOD_GET;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700223 calld->send_message_blocked = true;
Makdharma178f4bc2016-08-24 15:16:53 -0700224 } else if (op->send_initial_metadata_flags &
225 GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) {
226 method = GRPC_MDELEM_METHOD_PUT;
227 }
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700228 /* Attempt to read the data from send_message and create a header field. */
Makdharma178f4bc2016-08-24 15:16:53 -0700229 if (method == GRPC_MDELEM_METHOD_GET) {
Makdharma7f0abf32016-08-26 13:34:54 -0700230 /* allocate memory to hold the entire payload */
Makdharma6a523f02016-08-26 10:02:00 -0700231 calld->payload_bytes = gpr_malloc(op->send_message->length);
Makdharma7f0abf32016-08-26 13:34:54 -0700232 GPR_ASSERT(calld->payload_bytes);
Makdharma6a523f02016-08-26 10:02:00 -0700233
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700234 /* read slices of send_message and copy into payload_bytes */
235 calld->send_op = *op;
236 calld->send_length = op->send_message->length;
237 calld->send_flags = op->send_message->flags;
238 continue_send_message(exec_ctx, elem);
239
240 if (calld->send_message_blocked == false) {
241 /* when all the send_message data is available, then create a MDELEM and
242 append to headers */
243 grpc_mdelem *payload_bin = grpc_mdelem_from_metadata_strings(
244 GRPC_MDSTR_GRPC_PAYLOAD_BIN,
245 grpc_mdstr_from_buffer(calld->payload_bytes,
246 op->send_message->length));
247 grpc_metadata_batch_add_tail(op->send_initial_metadata,
248 &calld->payload_bin, payload_bin);
249 calld->on_complete = op->on_complete;
250 op->on_complete = &calld->hc_on_complete;
251 op->send_message = NULL;
252 } else {
253 /* Not all data is available. Fall back to POST. */
254 gpr_log(GPR_DEBUG, "Request is marked Cacheable but not all data is available.\
255 Falling back to POST");
256 method = GRPC_MDELEM_METHOD_POST;
Makdharma178f4bc2016-08-24 15:16:53 -0700257 }
Makdharma178f4bc2016-08-24 15:16:53 -0700258 }
259
Craig Tiller577c9b22015-11-02 14:11:15 -0800260 if (op->send_initial_metadata != NULL) {
261 grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter,
262 elem);
263 /* Send : prefixed headers, which have to be before any application
264 layer headers. */
Makdharma178f4bc2016-08-24 15:16:53 -0700265 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
266 method);
Craig Tiller577c9b22015-11-02 14:11:15 -0800267 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800268 channeld->static_scheme);
Craig Tiller577c9b22015-11-02 14:11:15 -0800269 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800270 GRPC_MDELEM_TE_TRAILERS);
271 grpc_metadata_batch_add_tail(
272 op->send_initial_metadata, &calld->content_type,
273 GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
Craig Tiller577c9b22015-11-02 14:11:15 -0800274 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
275 GRPC_MDELEM_REF(channeld->user_agent));
Craig Tillera82950e2015-09-22 12:33:20 -0700276 }
Craig Tiller83f88d92015-04-21 16:02:05 -0700277
Craig Tiller577c9b22015-11-02 14:11:15 -0800278 if (op->recv_initial_metadata != NULL) {
Craig Tillera82950e2015-09-22 12:33:20 -0700279 /* substitute our callback for the higher callback */
Craig Tiller577c9b22015-11-02 14:11:15 -0800280 calld->recv_initial_metadata = op->recv_initial_metadata;
Craig Tillera44cbfc2016-02-03 16:02:49 -0800281 calld->on_done_recv = op->recv_initial_metadata_ready;
282 op->recv_initial_metadata_ready = &calld->hc_on_recv;
Craig Tillera82950e2015-09-22 12:33:20 -0700283 }
Craig Tiller50d9db52015-04-23 10:52:14 -0700284}
Craig Tiller83f88d92015-04-21 16:02:05 -0700285
Craig Tillera82950e2015-09-22 12:33:20 -0700286static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
287 grpc_call_element *elem,
288 grpc_transport_stream_op *op) {
Craig Tiller0ba432d2015-10-09 16:57:11 -0700289 GPR_TIMER_BEGIN("hc_start_transport_op", 0);
Craig Tillera82950e2015-09-22 12:33:20 -0700290 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700291 hc_mutate_op(exec_ctx, elem, op);
Craig Tiller0ba432d2015-10-09 16:57:11 -0700292 GPR_TIMER_END("hc_start_transport_op", 0);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700293 call_data *calld = elem->call_data;
294 if (op->send_message != NULL && calld->send_message_blocked) {
295 /* Don't forward the op. send_message contains slices that aren't ready
296 yet. The call will be forwarded by the op_complete of slice read call.
297 */
298 } else {
299 grpc_call_next_op(exec_ctx, elem, op);
300 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800301}
302
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800303/* Constructor for call_data */
Mark D. Roth76d24422016-06-23 13:22:10 -0700304static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
Mark D. Roth0badbe82016-06-23 10:15:12 -0700305 grpc_call_element *elem,
306 grpc_call_element_args *args) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700307 call_data *calld = elem->call_data;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700308 calld->on_done_recv = NULL;
Makdharma7f0abf32016-08-26 13:34:54 -0700309 calld->on_complete = NULL;
310 calld->payload_bytes = NULL;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700311 gpr_slice_buffer_init(&calld->slices);
Craig Tillera82950e2015-09-22 12:33:20 -0700312 grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
Makdharma7f0abf32016-08-26 13:34:54 -0700313 grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700314 grpc_closure_init(&calld->got_slice, got_slice, elem);
315 grpc_closure_init(&calld->send_done, send_done, elem);
Mark D. Roth0badbe82016-06-23 10:15:12 -0700316 return GRPC_ERROR_NONE;
Craig Tiller83f88d92015-04-21 16:02:05 -0700317}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800318
319/* Destructor for call_data */
Craig Tiller2c8063c2016-03-22 22:12:15 -0700320static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
David Garcia Quintas5dde14c2016-07-28 17:29:27 -0700321 const grpc_call_final_info *final_info,
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700322 void *ignored) {
323 call_data *calld = elem->call_data;
324 gpr_slice_buffer_destroy(&calld->slices);
325}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800326
Craig Tillerebdef9d2015-11-19 17:09:49 -0800327static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100328 unsigned i;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800329 size_t j;
330 grpc_mdelem *valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
331 GRPC_MDELEM_SCHEME_HTTPS};
Craig Tillera82950e2015-09-22 12:33:20 -0700332 if (args != NULL) {
333 for (i = 0; i < args->num_args; ++i) {
334 if (args->args[i].type == GRPC_ARG_STRING &&
335 strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
Craig Tillerebdef9d2015-11-19 17:09:49 -0800336 for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
337 if (0 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value),
338 args->args[i].value.string)) {
339 return valid_schemes[j];
340 }
341 }
Craig Tillera82950e2015-09-22 12:33:20 -0700342 }
David Klempnera1e86932015-01-13 18:13:59 -0800343 }
Craig Tillera82950e2015-09-22 12:33:20 -0700344 }
Craig Tillerebdef9d2015-11-19 17:09:49 -0800345 return GRPC_MDELEM_SCHEME_HTTP;
David Klempnera1e86932015-01-13 18:13:59 -0800346}
347
Makdharma178f4bc2016-08-24 15:16:53 -0700348static size_t max_payload_size_from_args(const grpc_channel_args *args) {
349 if (args != NULL) {
350 for (size_t i = 0; i < args->num_args; ++i) {
351 if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) {
352 if (args->args[i].type != GRPC_ARG_INTEGER) {
353 gpr_log(GPR_ERROR, "%s: must be an integer",
354 GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET);
355 } else {
Makdharma6a523f02016-08-26 10:02:00 -0700356 return (size_t)args->args[i].value.integer;
Makdharma178f4bc2016-08-24 15:16:53 -0700357 }
358 }
359 }
360 }
361 return kMaxPayloadSizeForGet;
362}
363
Craig Tiller9d69e802016-06-06 11:37:50 -0700364static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
365 const char *transport_name) {
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700366 gpr_strvec v;
367 size_t i;
368 int is_first = 1;
369 char *tmp;
370 grpc_mdstr *result;
371
Craig Tillera82950e2015-09-22 12:33:20 -0700372 gpr_strvec_init(&v);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700373
Craig Tillera82950e2015-09-22 12:33:20 -0700374 for (i = 0; args && i < args->num_args; i++) {
375 if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
376 if (args->args[i].type != GRPC_ARG_STRING) {
377 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
378 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
379 } else {
380 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
381 is_first = 0;
382 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
383 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700384 }
Craig Tillera82950e2015-09-22 12:33:20 -0700385 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700386
Craig Tiller9d69e802016-06-06 11:37:50 -0700387 gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s)", is_first ? "" : " ",
388 grpc_version_string(), GPR_PLATFORM_STRING, transport_name);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700389 is_first = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700390 gpr_strvec_add(&v, tmp);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700391
Craig Tillera82950e2015-09-22 12:33:20 -0700392 for (i = 0; args && i < args->num_args; i++) {
393 if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
394 if (args->args[i].type != GRPC_ARG_STRING) {
395 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
396 GRPC_ARG_SECONDARY_USER_AGENT_STRING);
397 } else {
398 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
399 is_first = 0;
400 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
401 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700402 }
Craig Tillera82950e2015-09-22 12:33:20 -0700403 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700404
Craig Tillera82950e2015-09-22 12:33:20 -0700405 tmp = gpr_strvec_flatten(&v, NULL);
406 gpr_strvec_destroy(&v);
Craig Tillerb2b42612015-11-20 12:02:17 -0800407 result = grpc_mdstr_from_string(tmp);
Craig Tillera82950e2015-09-22 12:33:20 -0700408 gpr_free(tmp);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700409
410 return result;
411}
412
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800413/* Constructor for channel_data */
Craig Tillera82950e2015-09-22 12:33:20 -0700414static void init_channel_elem(grpc_exec_ctx *exec_ctx,
Craig Tiller577c9b22015-11-02 14:11:15 -0800415 grpc_channel_element *elem,
416 grpc_channel_element_args *args) {
Craig Tillerebdef9d2015-11-19 17:09:49 -0800417 channel_data *chand = elem->channel_data;
Craig Tiller577c9b22015-11-02 14:11:15 -0800418 GPR_ASSERT(!args->is_last);
Craig Tiller9d69e802016-06-06 11:37:50 -0700419 GPR_ASSERT(args->optional_transport != NULL);
Craig Tillerebdef9d2015-11-19 17:09:49 -0800420 chand->static_scheme = scheme_from_args(args->channel_args);
Makdharma178f4bc2016-08-24 15:16:53 -0700421 chand->max_payload_size_for_get =
422 max_payload_size_from_args(args->channel_args);
Craig Tillerb2b42612015-11-20 12:02:17 -0800423 chand->user_agent = grpc_mdelem_from_metadata_strings(
Craig Tiller9d69e802016-06-06 11:37:50 -0700424 GRPC_MDSTR_USER_AGENT,
425 user_agent_from_args(args->channel_args,
426 args->optional_transport->vtable->name));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800427}
428
429/* Destructor for channel data */
Craig Tillera82950e2015-09-22 12:33:20 -0700430static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
431 grpc_channel_element *elem) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800432 channel_data *chand = elem->channel_data;
433 GRPC_MDELEM_UNREF(chand->user_agent);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800434}
435
436const grpc_channel_filter grpc_http_client_filter = {
Craig Tillerf40df232016-03-25 13:38:14 -0700437 hc_start_transport_op,
438 grpc_channel_next_op,
439 sizeof(call_data),
440 init_call_elem,
David Garcia Quintas4afce7e2016-04-18 16:25:17 -0700441 grpc_call_stack_ignore_set_pollset_or_pollset_set,
Craig Tillerf40df232016-03-25 13:38:14 -0700442 destroy_call_elem,
443 sizeof(channel_data),
444 init_channel_elem,
445 destroy_channel_elem,
446 grpc_call_next_get_peer,
447 "http-client"};