blob: dff03dc6078f9c5d4fea112f9be79e70924ae7bc [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,
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700170 grpc_call_element *elem) {
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700171 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,
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700174 &calld->incoming_slice, ~(size_t)0,
175 &calld->got_slice)) {
176 memcpy(wrptr, GPR_SLICE_START_PTR(calld->incoming_slice),
177 GPR_SLICE_LENGTH(calld->incoming_slice));
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700178 wrptr += GPR_SLICE_LENGTH(calld->incoming_slice);
179 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
180 if (calld->send_length == calld->slices.length) {
181 calld->send_message_blocked = false;
182 break;
183 }
184 }
185}
186
187static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
188 grpc_call_element *elem = elemp;
189 call_data *calld = elem->call_data;
190 calld->send_message_blocked = false;
191 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
192 if (calld->send_length == calld->slices.length) {
193 /* Pass down the original send_message op that was blocked.*/
194 grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700195 calld->send_flags);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700196 calld->send_op.send_message = &calld->replacement_stream.base;
197 calld->post_send = calld->send_op.on_complete;
198 calld->send_op.on_complete = &calld->send_done;
199 grpc_call_next_op(exec_ctx, elem, &calld->send_op);
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700200 } else {
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700201 continue_send_message(exec_ctx, elem);
202 }
203}
204
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700205static void hc_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
Craig Tillera82950e2015-09-22 12:33:20 -0700206 grpc_transport_stream_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800207 /* grab pointers to our data from the call element */
208 call_data *calld = elem->call_data;
209 channel_data *channeld = elem->channel_data;
Makdharma178f4bc2016-08-24 15:16:53 -0700210
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700211 /* Decide which HTTP VERB to use. We use GET if the request is marked
212 cacheable, and the operation contains both initial metadata and send message,
213 and the payload is below the size threshold, and all the data
214 for this request is immediately available. */
Makdharma178f4bc2016-08-24 15:16:53 -0700215 grpc_mdelem *method = GRPC_MDELEM_METHOD_POST;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700216 calld->send_message_blocked = false;
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700217 if (op->send_initial_metadata != NULL &&
218 (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(
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700244 GRPC_MDSTR_GRPC_PAYLOAD_BIN,
245 grpc_mdstr_from_buffer(calld->payload_bytes,
246 op->send_message->length));
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700247 grpc_metadata_batch_add_tail(op->send_initial_metadata,
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700248 &calld->payload_bin, payload_bin);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700249 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. */
Makarand Dharmapurikar432d1db2016-08-31 11:46:23 -0700254 gpr_log(GPR_DEBUG,
255 "Request is marked Cacheable but not all data is available.\
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700256 Falling back to POST");
257 method = GRPC_MDELEM_METHOD_POST;
Makdharma178f4bc2016-08-24 15:16:53 -0700258 }
Makdharma178f4bc2016-08-24 15:16:53 -0700259 }
260
Craig Tiller577c9b22015-11-02 14:11:15 -0800261 if (op->send_initial_metadata != NULL) {
262 grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter,
263 elem);
264 /* Send : prefixed headers, which have to be before any application
265 layer headers. */
Makdharma178f4bc2016-08-24 15:16:53 -0700266 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
267 method);
Craig Tiller577c9b22015-11-02 14:11:15 -0800268 grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800269 channeld->static_scheme);
Craig Tiller577c9b22015-11-02 14:11:15 -0800270 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800271 GRPC_MDELEM_TE_TRAILERS);
272 grpc_metadata_batch_add_tail(
273 op->send_initial_metadata, &calld->content_type,
274 GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
Craig Tiller577c9b22015-11-02 14:11:15 -0800275 grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
276 GRPC_MDELEM_REF(channeld->user_agent));
Craig Tillera82950e2015-09-22 12:33:20 -0700277 }
Craig Tiller83f88d92015-04-21 16:02:05 -0700278
Craig Tiller577c9b22015-11-02 14:11:15 -0800279 if (op->recv_initial_metadata != NULL) {
Craig Tillera82950e2015-09-22 12:33:20 -0700280 /* substitute our callback for the higher callback */
Craig Tiller577c9b22015-11-02 14:11:15 -0800281 calld->recv_initial_metadata = op->recv_initial_metadata;
Craig Tillera44cbfc2016-02-03 16:02:49 -0800282 calld->on_done_recv = op->recv_initial_metadata_ready;
283 op->recv_initial_metadata_ready = &calld->hc_on_recv;
Craig Tillera82950e2015-09-22 12:33:20 -0700284 }
Craig Tiller50d9db52015-04-23 10:52:14 -0700285}
Craig Tiller83f88d92015-04-21 16:02:05 -0700286
Craig Tillera82950e2015-09-22 12:33:20 -0700287static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
288 grpc_call_element *elem,
289 grpc_transport_stream_op *op) {
Craig Tiller0ba432d2015-10-09 16:57:11 -0700290 GPR_TIMER_BEGIN("hc_start_transport_op", 0);
Craig Tillera82950e2015-09-22 12:33:20 -0700291 GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700292 hc_mutate_op(exec_ctx, elem, op);
Craig Tiller0ba432d2015-10-09 16:57:11 -0700293 GPR_TIMER_END("hc_start_transport_op", 0);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700294 call_data *calld = elem->call_data;
295 if (op->send_message != NULL && calld->send_message_blocked) {
296 /* Don't forward the op. send_message contains slices that aren't ready
297 yet. The call will be forwarded by the op_complete of slice read call.
298 */
299 } else {
300 grpc_call_next_op(exec_ctx, elem, op);
301 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800302}
303
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800304/* Constructor for call_data */
Mark D. Roth76d24422016-06-23 13:22:10 -0700305static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
Mark D. Roth0badbe82016-06-23 10:15:12 -0700306 grpc_call_element *elem,
307 grpc_call_element_args *args) {
Craig Tiller83f88d92015-04-21 16:02:05 -0700308 call_data *calld = elem->call_data;
Craig Tiller1e6facb2015-06-11 22:47:11 -0700309 calld->on_done_recv = NULL;
Makdharma7f0abf32016-08-26 13:34:54 -0700310 calld->on_complete = NULL;
311 calld->payload_bytes = NULL;
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700312 gpr_slice_buffer_init(&calld->slices);
Craig Tillera82950e2015-09-22 12:33:20 -0700313 grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
Makdharma7f0abf32016-08-26 13:34:54 -0700314 grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem);
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700315 grpc_closure_init(&calld->got_slice, got_slice, elem);
316 grpc_closure_init(&calld->send_done, send_done, elem);
Mark D. Roth0badbe82016-06-23 10:15:12 -0700317 return GRPC_ERROR_NONE;
Craig Tiller83f88d92015-04-21 16:02:05 -0700318}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800319
320/* Destructor for call_data */
Craig Tiller2c8063c2016-03-22 22:12:15 -0700321static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
David Garcia Quintas5dde14c2016-07-28 17:29:27 -0700322 const grpc_call_final_info *final_info,
Makarand Dharmapurikara6babb02016-08-30 16:59:26 -0700323 void *ignored) {
324 call_data *calld = elem->call_data;
325 gpr_slice_buffer_destroy(&calld->slices);
326}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800327
Craig Tillerebdef9d2015-11-19 17:09:49 -0800328static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100329 unsigned i;
Craig Tillerebdef9d2015-11-19 17:09:49 -0800330 size_t j;
331 grpc_mdelem *valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
332 GRPC_MDELEM_SCHEME_HTTPS};
Craig Tillera82950e2015-09-22 12:33:20 -0700333 if (args != NULL) {
334 for (i = 0; i < args->num_args; ++i) {
335 if (args->args[i].type == GRPC_ARG_STRING &&
336 strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
Craig Tillerebdef9d2015-11-19 17:09:49 -0800337 for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
338 if (0 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value),
339 args->args[i].value.string)) {
340 return valid_schemes[j];
341 }
342 }
Craig Tillera82950e2015-09-22 12:33:20 -0700343 }
David Klempnera1e86932015-01-13 18:13:59 -0800344 }
Craig Tillera82950e2015-09-22 12:33:20 -0700345 }
Craig Tillerebdef9d2015-11-19 17:09:49 -0800346 return GRPC_MDELEM_SCHEME_HTTP;
David Klempnera1e86932015-01-13 18:13:59 -0800347}
348
Makdharma178f4bc2016-08-24 15:16:53 -0700349static size_t max_payload_size_from_args(const grpc_channel_args *args) {
350 if (args != NULL) {
351 for (size_t i = 0; i < args->num_args; ++i) {
352 if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) {
353 if (args->args[i].type != GRPC_ARG_INTEGER) {
354 gpr_log(GPR_ERROR, "%s: must be an integer",
355 GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET);
356 } else {
Makdharma6a523f02016-08-26 10:02:00 -0700357 return (size_t)args->args[i].value.integer;
Makdharma178f4bc2016-08-24 15:16:53 -0700358 }
359 }
360 }
361 }
362 return kMaxPayloadSizeForGet;
363}
364
Craig Tiller9d69e802016-06-06 11:37:50 -0700365static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
366 const char *transport_name) {
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700367 gpr_strvec v;
368 size_t i;
369 int is_first = 1;
370 char *tmp;
371 grpc_mdstr *result;
372
Craig Tillera82950e2015-09-22 12:33:20 -0700373 gpr_strvec_init(&v);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700374
Craig Tillera82950e2015-09-22 12:33:20 -0700375 for (i = 0; args && i < args->num_args; i++) {
376 if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
377 if (args->args[i].type != GRPC_ARG_STRING) {
378 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
379 GRPC_ARG_PRIMARY_USER_AGENT_STRING);
380 } else {
381 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
382 is_first = 0;
383 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
384 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700385 }
Craig Tillera82950e2015-09-22 12:33:20 -0700386 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700387
Craig Tiller9d69e802016-06-06 11:37:50 -0700388 gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s)", is_first ? "" : " ",
389 grpc_version_string(), GPR_PLATFORM_STRING, transport_name);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700390 is_first = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700391 gpr_strvec_add(&v, tmp);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700392
Craig Tillera82950e2015-09-22 12:33:20 -0700393 for (i = 0; args && i < args->num_args; i++) {
394 if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
395 if (args->args[i].type != GRPC_ARG_STRING) {
396 gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
397 GRPC_ARG_SECONDARY_USER_AGENT_STRING);
398 } else {
399 if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
400 is_first = 0;
401 gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
402 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700403 }
Craig Tillera82950e2015-09-22 12:33:20 -0700404 }
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700405
Craig Tillera82950e2015-09-22 12:33:20 -0700406 tmp = gpr_strvec_flatten(&v, NULL);
407 gpr_strvec_destroy(&v);
Craig Tillerb2b42612015-11-20 12:02:17 -0800408 result = grpc_mdstr_from_string(tmp);
Craig Tillera82950e2015-09-22 12:33:20 -0700409 gpr_free(tmp);
Craig Tiller0dc5e6c2015-07-10 10:07:53 -0700410
411 return result;
412}
413
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800414/* Constructor for channel_data */
Craig Tillera82950e2015-09-22 12:33:20 -0700415static void init_channel_elem(grpc_exec_ctx *exec_ctx,
Craig Tiller577c9b22015-11-02 14:11:15 -0800416 grpc_channel_element *elem,
417 grpc_channel_element_args *args) {
Craig Tillerebdef9d2015-11-19 17:09:49 -0800418 channel_data *chand = elem->channel_data;
Craig Tiller577c9b22015-11-02 14:11:15 -0800419 GPR_ASSERT(!args->is_last);
Craig Tiller9d69e802016-06-06 11:37:50 -0700420 GPR_ASSERT(args->optional_transport != NULL);
Craig Tillerebdef9d2015-11-19 17:09:49 -0800421 chand->static_scheme = scheme_from_args(args->channel_args);
Makdharma178f4bc2016-08-24 15:16:53 -0700422 chand->max_payload_size_for_get =
423 max_payload_size_from_args(args->channel_args);
Craig Tillerb2b42612015-11-20 12:02:17 -0800424 chand->user_agent = grpc_mdelem_from_metadata_strings(
Craig Tiller9d69e802016-06-06 11:37:50 -0700425 GRPC_MDSTR_USER_AGENT,
426 user_agent_from_args(args->channel_args,
427 args->optional_transport->vtable->name));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800428}
429
430/* Destructor for channel data */
Craig Tillera82950e2015-09-22 12:33:20 -0700431static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
432 grpc_channel_element *elem) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800433 channel_data *chand = elem->channel_data;
434 GRPC_MDELEM_UNREF(chand->user_agent);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800435}
436
437const grpc_channel_filter grpc_http_client_filter = {
Craig Tillerf40df232016-03-25 13:38:14 -0700438 hc_start_transport_op,
439 grpc_channel_next_op,
440 sizeof(call_data),
441 init_call_elem,
David Garcia Quintas4afce7e2016-04-18 16:25:17 -0700442 grpc_call_stack_ignore_set_pollset_or_pollset_set,
Craig Tillerf40df232016-03-25 13:38:14 -0700443 destroy_call_elem,
444 sizeof(channel_data),
445 init_channel_elem,
446 destroy_channel_elem,
447 grpc_call_next_get_peer,
448 "http-client"};