blob: b424c0d2acbb55e7b79dd47d0270ee8267f67015 [file] [log] [blame]
Mark D. Rotha6f1b982016-08-24 08:16:16 -07001//
2// Copyright 2016, Google Inc.
3// 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//
Mark D. Rothaf00d8b2016-08-23 12:48:16 -070031
32#include "src/core/lib/channel/message_size_filter.h"
33
Mark D. Rothc3c6faf2016-08-26 09:20:49 -070034#include <limits.h>
Mark D. Rothaf00d8b2016-08-23 12:48:16 -070035#include <string.h>
36
yang-ge46de3d2016-11-17 15:20:02 -080037#include <grpc/impl/codegen/grpc_types.h>
Mark D. Rothaf00d8b2016-08-23 12:48:16 -070038#include <grpc/support/alloc.h>
39#include <grpc/support/log.h>
40#include <grpc/support/string_util.h>
41
Mark D. Rothafa8c102016-09-30 10:56:53 -070042#include "src/core/lib/channel/channel_args.h"
Mark D. Rothc968e602016-11-02 14:07:36 -070043#include "src/core/lib/support/string.h"
Mark D. Rothea846a02016-11-03 11:32:54 -070044#include "src/core/lib/transport/service_config.h"
Mark D. Rothc3c6faf2016-08-26 09:20:49 -070045
Mark D. Roth29a7f402016-10-19 13:46:38 -070046typedef struct message_size_limits {
47 int max_send_size;
48 int max_recv_size;
49} message_size_limits;
50
51static void* message_size_limits_copy(void* value) {
52 void* new_value = gpr_malloc(sizeof(message_size_limits));
53 memcpy(new_value, value, sizeof(message_size_limits));
54 return new_value;
55}
56
Craig Tillerb28c7e82016-11-18 10:29:04 -080057static void message_size_limits_free(grpc_exec_ctx* exec_ctx, void* value) {
58 gpr_free(value);
Mark D. Roth29a7f402016-10-19 13:46:38 -070059}
60
Craig Tiller7c70b6c2017-01-23 07:48:42 -080061static const grpc_slice_hash_table_vtable message_size_limits_vtable = {
Craig Tillerb28c7e82016-11-18 10:29:04 -080062 message_size_limits_free, message_size_limits_copy};
Mark D. Roth29a7f402016-10-19 13:46:38 -070063
Mark D. Rothe30baeb2016-11-03 08:16:19 -070064static void* message_size_limits_create_from_json(const grpc_json* json) {
Mark D. Rothc968e602016-11-02 14:07:36 -070065 int max_request_message_bytes = -1;
66 int max_response_message_bytes = -1;
67 for (grpc_json* field = json->child; field != NULL; field = field->next) {
68 if (field->key == NULL) continue;
Mark D. Roth84c8a022016-11-10 09:39:34 -080069 if (strcmp(field->key, "maxRequestMessageBytes") == 0) {
Mark D. Rothc968e602016-11-02 14:07:36 -070070 if (max_request_message_bytes >= 0) return NULL; // Duplicate.
Mark D. Roth40b4c782017-01-26 14:36:43 -080071 if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
72 return NULL;
73 }
Mark D. Roth84c8a022016-11-10 09:39:34 -080074 max_request_message_bytes = gpr_parse_nonnegative_int(field->value);
Mark D. Rothc968e602016-11-02 14:07:36 -070075 if (max_request_message_bytes == -1) return NULL;
Mark D. Roth84c8a022016-11-10 09:39:34 -080076 } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) {
Mark D. Rothc968e602016-11-02 14:07:36 -070077 if (max_response_message_bytes >= 0) return NULL; // Duplicate.
Mark D. Roth40b4c782017-01-26 14:36:43 -080078 if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
79 return NULL;
80 }
Mark D. Roth84c8a022016-11-10 09:39:34 -080081 max_response_message_bytes = gpr_parse_nonnegative_int(field->value);
Mark D. Rothc968e602016-11-02 14:07:36 -070082 if (max_response_message_bytes == -1) return NULL;
83 }
84 }
Mark D. Roth29a7f402016-10-19 13:46:38 -070085 message_size_limits* value = gpr_malloc(sizeof(message_size_limits));
Mark D. Rothc968e602016-11-02 14:07:36 -070086 value->max_send_size = max_request_message_bytes;
87 value->max_recv_size = max_response_message_bytes;
Mark D. Roth29a7f402016-10-19 13:46:38 -070088 return value;
89}
90
Mark D. Rothaf00d8b2016-08-23 12:48:16 -070091typedef struct call_data {
Mark D. Roth996e95b2016-09-27 10:55:37 -070092 int max_send_size;
93 int max_recv_size;
Mark D. Rothaf00d8b2016-08-23 12:48:16 -070094 // Receive closures are chained: we inject this closure as the
95 // recv_message_ready up-call on transport_stream_op, and remember to
96 // call our next_recv_message_ready member after handling it.
97 grpc_closure recv_message_ready;
98 // Used by recv_message_ready.
99 grpc_byte_stream** recv_message;
100 // Original recv_message_ready callback, invoked after our own.
101 grpc_closure* next_recv_message_ready;
102} call_data;
103
104typedef struct channel_data {
Mark D. Roth996e95b2016-09-27 10:55:37 -0700105 int max_send_size;
106 int max_recv_size;
Mark D. Roth29a7f402016-10-19 13:46:38 -0700107 // Maps path names to message_size_limits structs.
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800108 grpc_slice_hash_table* method_limit_table;
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700109} channel_data;
110
111// Callback invoked when we receive a message. Here we check the max
112// receive message size.
113static void recv_message_ready(grpc_exec_ctx* exec_ctx, void* user_data,
114 grpc_error* error) {
115 grpc_call_element* elem = user_data;
116 call_data* calld = elem->call_data;
Mark D. Roth996e95b2016-09-27 10:55:37 -0700117 if (*calld->recv_message != NULL && calld->max_recv_size >= 0 &&
118 (*calld->recv_message)->length > (size_t)calld->max_recv_size) {
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700119 char* message_string;
Mark D. Rothe425ce62016-09-23 09:29:12 -0700120 gpr_asprintf(&message_string,
121 "Received message larger than max (%u vs. %d)",
Mark D. Roth56e470e2016-09-28 13:20:15 -0700122 (*calld->recv_message)->length, calld->max_recv_size);
Mark D. Roth274c8ed2016-10-04 09:21:42 -0700123 grpc_error* new_error = grpc_error_set_int(
124 GRPC_ERROR_CREATE(message_string), GRPC_ERROR_INT_GRPC_STATUS,
125 GRPC_STATUS_INVALID_ARGUMENT);
126 if (error == GRPC_ERROR_NONE) {
127 error = new_error;
128 } else {
129 error = grpc_error_add_child(error, new_error);
130 GRPC_ERROR_UNREF(new_error);
131 }
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700132 gpr_free(message_string);
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700133 }
134 // Invoke the next callback.
Craig Tiller91031da2016-12-28 15:44:25 -0800135 grpc_closure_sched(exec_ctx, calld->next_recv_message_ready, error);
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700136}
137
Mark D. Rothec393342016-10-03 13:14:56 -0700138// Start transport stream op.
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700139static void start_transport_stream_op(grpc_exec_ctx* exec_ctx,
140 grpc_call_element* elem,
141 grpc_transport_stream_op* op) {
142 call_data* calld = elem->call_data;
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700143 // Check max send message size.
Mark D. Roth996e95b2016-09-27 10:55:37 -0700144 if (op->send_message != NULL && calld->max_send_size >= 0 &&
145 op->send_message->length > (size_t)calld->max_send_size) {
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700146 char* message_string;
Mark D. Roth996e95b2016-09-27 10:55:37 -0700147 gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)",
148 op->send_message->length, calld->max_send_size);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800149 grpc_transport_stream_op_finish_with_failure(
150 exec_ctx, op, grpc_error_set_int(GRPC_ERROR_CREATE(message_string),
151 GRPC_ERROR_INT_GRPC_STATUS,
152 GRPC_STATUS_INVALID_ARGUMENT));
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700153 gpr_free(message_string);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800154 return;
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700155 }
156 // Inject callback for receiving a message.
Mark D. Rotha6f1b982016-08-24 08:16:16 -0700157 if (op->recv_message_ready != NULL) {
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700158 calld->next_recv_message_ready = op->recv_message_ready;
159 calld->recv_message = op->recv_message;
160 op->recv_message_ready = &calld->recv_message_ready;
161 }
162 // Chain to the next filter.
163 grpc_call_next_op(exec_ctx, elem, op);
164}
165
166// Constructor for call_data.
167static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx,
168 grpc_call_element* elem,
Craig Tillerc52ba3a2017-02-15 22:57:43 -0800169 const grpc_call_element_args* args) {
Mark D. Roth996e95b2016-09-27 10:55:37 -0700170 channel_data* chand = elem->channel_data;
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700171 call_data* calld = elem->call_data;
172 calld->next_recv_message_ready = NULL;
Craig Tiller91031da2016-12-28 15:44:25 -0800173 grpc_closure_init(&calld->recv_message_ready, recv_message_ready, elem,
174 grpc_schedule_on_exec_ctx);
Mark D. Roth996e95b2016-09-27 10:55:37 -0700175 // Get max sizes from channel data, then merge in per-method config values.
176 // Note: Per-method config is only available on the client, so we
177 // apply the max request size to the send limit and the max response
178 // size to the receive limit.
179 calld->max_send_size = chand->max_send_size;
180 calld->max_recv_size = chand->max_recv_size;
Mark D. Roth29a7f402016-10-19 13:46:38 -0700181 if (chand->method_limit_table != NULL) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700182 message_size_limits* limits = grpc_method_config_table_get(
183 exec_ctx, chand->method_limit_table, args->path);
Mark D. Roth29a7f402016-10-19 13:46:38 -0700184 if (limits != NULL) {
185 if (limits->max_send_size >= 0 &&
186 (limits->max_send_size < calld->max_send_size ||
Mark D. Roth996e95b2016-09-27 10:55:37 -0700187 calld->max_send_size < 0)) {
Mark D. Roth29a7f402016-10-19 13:46:38 -0700188 calld->max_send_size = limits->max_send_size;
Mark D. Roth996e95b2016-09-27 10:55:37 -0700189 }
Mark D. Roth29a7f402016-10-19 13:46:38 -0700190 if (limits->max_recv_size >= 0 &&
191 (limits->max_recv_size < calld->max_recv_size ||
Mark D. Roth996e95b2016-09-27 10:55:37 -0700192 calld->max_recv_size < 0)) {
Mark D. Roth29a7f402016-10-19 13:46:38 -0700193 calld->max_recv_size = limits->max_recv_size;
Mark D. Roth996e95b2016-09-27 10:55:37 -0700194 }
195 }
196 }
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700197 return GRPC_ERROR_NONE;
198}
199
200// Destructor for call_data.
201static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
202 const grpc_call_final_info* final_info,
203 void* ignored) {}
204
205// Constructor for channel_data.
Mark D. Roth5e2566e2016-11-18 10:53:13 -0800206static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx,
207 grpc_channel_element* elem,
208 grpc_channel_element_args* args) {
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700209 GPR_ASSERT(!args->is_last);
210 channel_data* chand = elem->channel_data;
yang-ge46de3d2016-11-17 15:20:02 -0800211 chand->max_send_size = GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH;
212 chand->max_recv_size = GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH;
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700213 for (size_t i = 0; i < args->channel_args->num_args; ++i) {
214 if (strcmp(args->channel_args->args[i].key,
215 GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) == 0) {
yang-ge46de3d2016-11-17 15:20:02 -0800216 const grpc_integer_options options = {
217 GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH, 0, INT_MAX};
Mark D. Rothe425ce62016-09-23 09:29:12 -0700218 chand->max_send_size =
219 grpc_channel_arg_get_integer(&args->channel_args->args[i], options);
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700220 }
221 if (strcmp(args->channel_args->args[i].key,
222 GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) {
yang-ge46de3d2016-11-17 15:20:02 -0800223 const grpc_integer_options options = {
224 GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH, 0, INT_MAX};
Mark D. Rothe425ce62016-09-23 09:29:12 -0700225 chand->max_recv_size =
226 grpc_channel_arg_get_integer(&args->channel_args->args[i], options);
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700227 }
228 }
Mark D. Roth996e95b2016-09-27 10:55:37 -0700229 // Get method config table from channel args.
Mark D. Rothafa8c102016-09-30 10:56:53 -0700230 const grpc_arg* channel_arg =
231 grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG);
Mark D. Roth996e95b2016-09-27 10:55:37 -0700232 if (channel_arg != NULL) {
Mark D. Roth9ec28af2016-11-03 12:32:39 -0700233 GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING);
234 grpc_service_config* service_config =
235 grpc_service_config_create(channel_arg->value.string);
Mark D. Rothbdc58b22016-11-04 09:25:57 -0700236 if (service_config != NULL) {
237 chand->method_limit_table =
238 grpc_service_config_create_method_config_table(
Craig Tillerb28c7e82016-11-18 10:29:04 -0800239 exec_ctx, service_config, message_size_limits_create_from_json,
Mark D. Rothbdc58b22016-11-04 09:25:57 -0700240 &message_size_limits_vtable);
241 grpc_service_config_destroy(service_config);
242 }
Mark D. Roth996e95b2016-09-27 10:55:37 -0700243 }
Mark D. Roth5e2566e2016-11-18 10:53:13 -0800244 return GRPC_ERROR_NONE;
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700245}
246
247// Destructor for channel_data.
248static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
Mark D. Roth996e95b2016-09-27 10:55:37 -0700249 grpc_channel_element* elem) {
250 channel_data* chand = elem->channel_data;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800251 grpc_slice_hash_table_unref(exec_ctx, chand->method_limit_table);
Mark D. Roth996e95b2016-09-27 10:55:37 -0700252}
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700253
254const grpc_channel_filter grpc_message_size_filter = {
255 start_transport_stream_op,
256 grpc_channel_next_op,
257 sizeof(call_data),
258 init_call_elem,
259 grpc_call_stack_ignore_set_pollset_or_pollset_set,
260 destroy_call_elem,
261 sizeof(channel_data),
262 init_channel_elem,
263 destroy_channel_elem,
264 grpc_call_next_get_peer,
Mark D. Rothb2d24882016-10-27 15:44:07 -0700265 grpc_channel_next_get_info,
Mark D. Rothaf00d8b2016-08-23 12:48:16 -0700266 "message_size"};