blob: 6f5a9740ad480ca6649bc49d40e17f3af1a5043b [file] [log] [blame]
David Garcia Quintas55b4ea12015-06-16 14:27:32 -07001/*
2 *
Craig Tillera93a25f2016-01-28 13:55:49 -08003 * Copyright 2015-2016, Google Inc.
David Garcia Quintas55b4ea12015-06-16 14:27:32 -07004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
David Garcia Quintasd16af0e2015-06-22 22:39:21 -070034#include <assert.h>
David Garcia Quintas55b4ea12015-06-16 14:27:32 -070035#include <string.h>
36
David Garcia Quintas55b4ea12015-06-16 14:27:32 -070037#include <grpc/compression.h>
David Garcia Quintase091af82015-07-15 21:37:02 -070038#include <grpc/support/alloc.h>
David Garcia Quintas55b4ea12015-06-16 14:27:32 -070039#include <grpc/support/log.h>
40#include <grpc/support/slice_buffer.h>
41
David Garcia Quintas20afd462015-07-06 23:01:39 -070042#include "src/core/channel/channel_args.h"
yang-gd88e1d82015-12-02 13:23:33 -080043#include "src/core/channel/compress_filter.h"
Craig Tillerebdef9d2015-11-19 17:09:49 -080044#include "src/core/compression/algorithm_metadata.h"
David Garcia Quintas20afd462015-07-06 23:01:39 -070045#include "src/core/compression/message_compress.h"
yang-gd88e1d82015-12-02 13:23:33 -080046#include "src/core/profiling/timers.h"
David Garcia Quintase091af82015-07-15 21:37:02 -070047#include "src/core/support/string.h"
Craig Tillered43f512015-11-19 08:53:23 -080048#include "src/core/transport/static_metadata.h"
David Garcia Quintas20afd462015-07-06 23:01:39 -070049
Craig Tillera82950e2015-09-22 12:33:20 -070050typedef struct call_data {
David Garcia Quintas7c4fdb52015-07-17 14:21:15 -070051 gpr_slice_buffer slices; /**< Buffers up input slices to be compressed */
David Garcia Quintasd16af0e2015-06-22 22:39:21 -070052 grpc_linked_mdelem compression_algorithm_storage;
David Garcia Quintase091af82015-07-15 21:37:02 -070053 grpc_linked_mdelem accept_encoding_storage;
Craig Tiller7536af02015-12-22 13:49:30 -080054 uint32_t remaining_slice_bytes;
David Garcia Quintas7c4fdb52015-07-17 14:21:15 -070055 /** Compression algorithm we'll try to use. It may be given by incoming
56 * metadata, or by the channel's default compression settings. */
David Garcia Quintasd16af0e2015-06-22 22:39:21 -070057 grpc_compression_algorithm compression_algorithm;
Craig Tillerd6c98df2015-08-18 09:33:44 -070058 /** If true, contents of \a compression_algorithm are authoritative */
David Garcia Quintas7c4fdb52015-07-17 14:21:15 -070059 int has_compression_algorithm;
Craig Tiller577c9b22015-11-02 14:11:15 -080060
61 grpc_transport_stream_op send_op;
Craig Tiller7536af02015-12-22 13:49:30 -080062 uint32_t send_length;
63 uint32_t send_flags;
Craig Tiller577c9b22015-11-02 14:11:15 -080064 gpr_slice incoming_slice;
65 grpc_slice_buffer_stream replacement_stream;
66 grpc_closure *post_send;
67 grpc_closure send_done;
68 grpc_closure got_slice;
David Garcia Quintas55b4ea12015-06-16 14:27:32 -070069} call_data;
70
Craig Tillera82950e2015-09-22 12:33:20 -070071typedef struct channel_data {
David Garcia Quintas7c4fdb52015-07-17 14:21:15 -070072 /** The default, channel-level, compression algorithm */
David Garcia Quintasd16af0e2015-06-22 22:39:21 -070073 grpc_compression_algorithm default_compression_algorithm;
David Garcia Quintasbeac88c2015-08-10 13:39:52 -070074 /** Compression options for the channel */
75 grpc_compression_options compression_options;
Craig Tillerebdef9d2015-11-19 17:09:49 -080076 /** Supported compression algorithms */
Craig Tiller7536af02015-12-22 13:49:30 -080077 uint32_t supported_compression_algorithms;
David Garcia Quintas55b4ea12015-06-16 14:27:32 -070078} channel_data;
79
David Garcia Quintasd16af0e2015-06-22 22:39:21 -070080/** For each \a md element from the incoming metadata, filter out the entry for
David Garcia Quintasd7d9ce22015-06-30 23:29:03 -070081 * "grpc-encoding", using its value to populate the call data's
David Garcia Quintasd16af0e2015-06-22 22:39:21 -070082 * compression_algorithm field. */
Craig Tillera82950e2015-09-22 12:33:20 -070083static grpc_mdelem *compression_md_filter(void *user_data, grpc_mdelem *md) {
David Garcia Quintasd16af0e2015-06-22 22:39:21 -070084 grpc_call_element *elem = user_data;
85 call_data *calld = elem->call_data;
86 channel_data *channeld = elem->channel_data;
87
Craig Tillerebdef9d2015-11-19 17:09:49 -080088 if (md->key == GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST) {
Craig Tillera82950e2015-09-22 12:33:20 -070089 const char *md_c_str = grpc_mdstr_as_c_string(md->value);
90 if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str),
91 &calld->compression_algorithm)) {
92 gpr_log(GPR_ERROR,
93 "Invalid compression algorithm: '%s' (unknown). Ignoring.",
94 md_c_str);
95 calld->compression_algorithm = GRPC_COMPRESS_NONE;
David Garcia Quintasbeac88c2015-08-10 13:39:52 -070096 }
Craig Tillera82950e2015-09-22 12:33:20 -070097 if (grpc_compression_options_is_algorithm_enabled(
98 &channeld->compression_options, calld->compression_algorithm) ==
99 0) {
100 gpr_log(GPR_ERROR,
101 "Invalid compression algorithm: '%s' (previously disabled). "
102 "Ignoring.",
103 md_c_str);
104 calld->compression_algorithm = GRPC_COMPRESS_NONE;
105 }
106 calld->has_compression_algorithm = 1;
107 return NULL;
108 }
David Garcia Quintasd16af0e2015-06-22 22:39:21 -0700109
110 return md;
111}
112
Craig Tiller577c9b22015-11-02 14:11:15 -0800113static int skip_compression(grpc_call_element *elem) {
114 call_data *calld = elem->call_data;
115 channel_data *channeld = elem->channel_data;
Craig Tillera82950e2015-09-22 12:33:20 -0700116 if (calld->has_compression_algorithm) {
117 if (calld->compression_algorithm == GRPC_COMPRESS_NONE) {
118 return 1;
Craig Tillerd6c98df2015-08-18 09:33:44 -0700119 }
Craig Tillera82950e2015-09-22 12:33:20 -0700120 return 0; /* we have an actual call-specific algorithm */
121 }
David Garcia Quintasd16af0e2015-06-22 22:39:21 -0700122 /* no per-call compression override */
123 return channeld->default_compression_algorithm == GRPC_COMPRESS_NONE;
124}
125
Craig Tiller577c9b22015-11-02 14:11:15 -0800126/** Filter initial metadata */
127static void process_send_initial_metadata(
128 grpc_call_element *elem, grpc_metadata_batch *initial_metadata) {
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700129 call_data *calld = elem->call_data;
130 channel_data *channeld = elem->channel_data;
Craig Tiller577c9b22015-11-02 14:11:15 -0800131 /* Parse incoming request for compression. If any, it'll be available
132 * at calld->compression_algorithm */
133 grpc_metadata_batch_filter(initial_metadata, compression_md_filter, elem);
134 if (!calld->has_compression_algorithm) {
135 /* If no algorithm was found in the metadata and we aren't
136 * exceptionally skipping compression, fall back to the channel
137 * default */
138 calld->compression_algorithm = channeld->default_compression_algorithm;
139 calld->has_compression_algorithm = 1; /* GPR_TRUE */
Craig Tillera82950e2015-09-22 12:33:20 -0700140 }
Craig Tiller577c9b22015-11-02 14:11:15 -0800141 /* hint compression algorithm */
142 grpc_metadata_batch_add_tail(
143 initial_metadata, &calld->compression_algorithm_storage,
Craig Tillerebdef9d2015-11-19 17:09:49 -0800144 grpc_compression_encoding_mdelem(calld->compression_algorithm));
David Garcia Quintasd16af0e2015-06-22 22:39:21 -0700145
Craig Tiller577c9b22015-11-02 14:11:15 -0800146 /* convey supported compression algorithms */
Craig Tillerb2b42612015-11-20 12:02:17 -0800147 grpc_metadata_batch_add_tail(initial_metadata,
148 &calld->accept_encoding_storage,
149 GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(
150 channeld->supported_compression_algorithms));
Craig Tiller577c9b22015-11-02 14:11:15 -0800151}
152
153static void continue_send_message(grpc_exec_ctx *exec_ctx,
154 grpc_call_element *elem);
155
Craig Tiller6c396862016-01-28 13:53:40 -0800156static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, bool success) {
Craig Tiller577c9b22015-11-02 14:11:15 -0800157 grpc_call_element *elem = elemp;
158 call_data *calld = elem->call_data;
159 gpr_slice_buffer_reset_and_unref(&calld->slices);
160 calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, success);
161}
162
163static void finish_send_message(grpc_exec_ctx *exec_ctx,
164 grpc_call_element *elem) {
165 call_data *calld = elem->call_data;
166 int did_compress;
167 gpr_slice_buffer tmp;
168 gpr_slice_buffer_init(&tmp);
169 did_compress =
170 grpc_msg_compress(calld->compression_algorithm, &calld->slices, &tmp);
Craig Tillera82950e2015-09-22 12:33:20 -0700171 if (did_compress) {
Craig Tiller577c9b22015-11-02 14:11:15 -0800172 gpr_slice_buffer_swap(&calld->slices, &tmp);
173 calld->send_flags |= GRPC_WRITE_INTERNAL_COMPRESS;
174 }
175 gpr_slice_buffer_destroy(&tmp);
176
177 grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
178 calld->send_flags);
179 calld->send_op.send_message = &calld->replacement_stream.base;
180 calld->post_send = calld->send_op.on_complete;
181 calld->send_op.on_complete = &calld->send_done;
182
183 grpc_call_next_op(exec_ctx, elem, &calld->send_op);
184}
185
Craig Tiller6c396862016-01-28 13:53:40 -0800186static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, bool success) {
Craig Tiller577c9b22015-11-02 14:11:15 -0800187 grpc_call_element *elem = elemp;
188 call_data *calld = elem->call_data;
189 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
190 if (calld->send_length == calld->slices.length) {
191 finish_send_message(exec_ctx, elem);
192 } else {
193 continue_send_message(exec_ctx, elem);
Craig Tillera82950e2015-09-22 12:33:20 -0700194 }
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700195}
196
Craig Tiller577c9b22015-11-02 14:11:15 -0800197static void continue_send_message(grpc_exec_ctx *exec_ctx,
198 grpc_call_element *elem) {
199 call_data *calld = elem->call_data;
200 while (grpc_byte_stream_next(exec_ctx, calld->send_op.send_message,
201 &calld->incoming_slice, ~(size_t)0,
202 &calld->got_slice)) {
203 gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
204 if (calld->send_length == calld->slices.length) {
205 finish_send_message(exec_ctx, elem);
206 break;
207 }
208 }
209}
210
Craig Tillera82950e2015-09-22 12:33:20 -0700211static void compress_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
212 grpc_call_element *elem,
213 grpc_transport_stream_op *op) {
Craig Tiller577c9b22015-11-02 14:11:15 -0800214 call_data *calld = elem->call_data;
215
Craig Tiller0ba432d2015-10-09 16:57:11 -0700216 GPR_TIMER_BEGIN("compress_start_transport_stream_op", 0);
Craig Tiller1f41b6b2015-10-09 15:07:02 -0700217
Craig Tiller577c9b22015-11-02 14:11:15 -0800218 if (op->send_initial_metadata) {
219 process_send_initial_metadata(elem, op->send_initial_metadata);
220 }
221 if (op->send_message != NULL && !skip_compression(elem) &&
222 0 == (op->send_message->flags & GRPC_WRITE_NO_COMPRESS)) {
223 calld->send_op = *op;
224 calld->send_length = op->send_message->length;
225 calld->send_flags = op->send_message->flags;
226 continue_send_message(exec_ctx, elem);
227 } else {
228 /* pass control down the stack */
229 grpc_call_next_op(exec_ctx, elem, op);
Craig Tillera82950e2015-09-22 12:33:20 -0700230 }
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700231
Craig Tiller0ba432d2015-10-09 16:57:11 -0700232 GPR_TIMER_END("compress_start_transport_stream_op", 0);
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700233}
234
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700235/* Constructor for call_data */
Craig Tillera82950e2015-09-22 12:33:20 -0700236static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
Craig Tiller577c9b22015-11-02 14:11:15 -0800237 grpc_call_element_args *args) {
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700238 /* grab pointers to our data from the call element */
239 call_data *calld = elem->call_data;
240
241 /* initialize members */
Craig Tillera82950e2015-09-22 12:33:20 -0700242 gpr_slice_buffer_init(&calld->slices);
David Garcia Quintasd16af0e2015-06-22 22:39:21 -0700243 calld->has_compression_algorithm = 0;
Craig Tiller577c9b22015-11-02 14:11:15 -0800244 grpc_closure_init(&calld->got_slice, got_slice, elem);
245 grpc_closure_init(&calld->send_done, send_done, elem);
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700246}
247
248/* Destructor for call_data */
Craig Tillera82950e2015-09-22 12:33:20 -0700249static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
250 grpc_call_element *elem) {
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700251 /* grab pointers to our data from the call element */
252 call_data *calld = elem->call_data;
Craig Tillera82950e2015-09-22 12:33:20 -0700253 gpr_slice_buffer_destroy(&calld->slices);
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700254}
255
256/* Constructor for channel_data */
Craig Tillera82950e2015-09-22 12:33:20 -0700257static void init_channel_elem(grpc_exec_ctx *exec_ctx,
Craig Tiller577c9b22015-11-02 14:11:15 -0800258 grpc_channel_element *elem,
259 grpc_channel_element_args *args) {
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700260 channel_data *channeld = elem->channel_data;
David Garcia Quintasd16af0e2015-06-22 22:39:21 -0700261 grpc_compression_algorithm algo_idx;
David Garcia Quintasf74a49e2015-06-18 17:22:45 -0700262
Craig Tillera82950e2015-09-22 12:33:20 -0700263 grpc_compression_options_init(&channeld->compression_options);
264 channeld->compression_options.enabled_algorithms_bitset =
Craig Tiller7536af02015-12-22 13:49:30 -0800265 (uint32_t)grpc_channel_args_compression_algorithm_get_states(
Craig Tiller577c9b22015-11-02 14:11:15 -0800266 args->channel_args);
David Garcia Quintasbeac88c2015-08-10 13:39:52 -0700267
Craig Tillera82950e2015-09-22 12:33:20 -0700268 channeld->default_compression_algorithm =
Craig Tiller577c9b22015-11-02 14:11:15 -0800269 grpc_channel_args_get_compression_algorithm(args->channel_args);
David Garcia Quintasbeac88c2015-08-10 13:39:52 -0700270 /* Make sure the default isn't disabled. */
Craig Tillera82950e2015-09-22 12:33:20 -0700271 GPR_ASSERT(grpc_compression_options_is_algorithm_enabled(
272 &channeld->compression_options, channeld->default_compression_algorithm));
273 channeld->compression_options.default_compression_algorithm =
274 channeld->default_compression_algorithm;
David Garcia Quintasf74a49e2015-06-18 17:22:45 -0700275
Craig Tillerebdef9d2015-11-19 17:09:49 -0800276 channeld->supported_compression_algorithms = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700277 for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) {
Craig Tillera82950e2015-09-22 12:33:20 -0700278 /* skip disabled algorithms */
279 if (grpc_compression_options_is_algorithm_enabled(
280 &channeld->compression_options, algo_idx) == 0) {
281 continue;
David Garcia Quintasbeac88c2015-08-10 13:39:52 -0700282 }
Craig Tillerebdef9d2015-11-19 17:09:49 -0800283 channeld->supported_compression_algorithms |= 1u << algo_idx;
Craig Tillera82950e2015-09-22 12:33:20 -0700284 }
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700285
Craig Tiller577c9b22015-11-02 14:11:15 -0800286 GPR_ASSERT(!args->is_last);
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700287}
288
289/* Destructor for channel data */
Craig Tillera82950e2015-09-22 12:33:20 -0700290static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
Craig Tillerab33b482015-11-21 08:11:04 -0800291 grpc_channel_element *elem) {}
David Garcia Quintas55b4ea12015-06-16 14:27:32 -0700292
David Garcia Quintasd317e752015-07-15 00:09:27 -0700293const grpc_channel_filter grpc_compress_filter = {
Craig Tillerf40df232016-03-25 13:38:14 -0700294 compress_start_transport_stream_op,
295 grpc_channel_next_op,
296 sizeof(call_data),
297 init_call_elem,
298 grpc_call_stack_ignore_set_pollset,
299 destroy_call_elem,
300 sizeof(channel_data),
301 init_channel_elem,
302 destroy_channel_elem,
303 grpc_call_next_get_peer,
304 "compress"};