@ -18,10 +18,13 @@
# include "src/core/ext/filters/message_size/message_size_filter.h"
# include <inttypes.h>
# include <functional>
# include <initializer_list>
# include <new>
# include <string>
# include <utility>
# include "absl/status/status.h"
# include "absl/strings/str_format.h"
# include <grpc/grpc.h>
@ -32,21 +35,22 @@
# include "src/core/lib/channel/channel_stack.h"
# include "src/core/lib/channel/channel_stack_builder.h"
# include "src/core/lib/config/core_configuration.h"
# include "src/core/lib/gprpp/debug_location.h"
# include "src/core/lib/gprpp/status_helper.h"
# include "src/core/lib/iomgr/call_combiner.h"
# include "src/core/lib/iomgr/closure.h"
# include "src/core/lib/iomgr/error.h"
# include "src/core/lib/debug/trace.h"
# include "src/core/lib/promise/activity.h"
# include "src/core/lib/promise/context.h"
# include "src/core/lib/promise/latch.h"
# include "src/core/lib/promise/poll.h"
# include "src/core/lib/promise/race.h"
# include "src/core/lib/resource_quota/arena.h"
# include "src/core/lib/service_config/service_config_call_data.h"
# include "src/core/lib/slice/slice.h"
# include "src/core/lib/slice/slice_buffer.h"
# include "src/core/lib/surface/call_trace.h"
# include "src/core/lib/surface/channel_init.h"
# include "src/core/lib/surface/channel_stack_type.h"
# include "src/core/lib/transport/metadata_batch.h"
# include "src/core/lib/transport/transport.h"
static void recv_message_ready ( void * user_data , grpc_error_handle error ) ;
static void recv_trailing_metadata_ready ( void * user_data ,
grpc_error_handle error ) ;
namespace grpc_core {
//
@ -124,251 +128,164 @@ size_t MessageSizeParser::ParserIndex() {
parser_name ( ) ) ;
}
} // namespace grpc_core
namespace {
struct channel_data {
grpc_core : : MessageSizeParsedConfig limits ;
const size_t service_config_parser_index {
grpc_core : : MessageSizeParser : : ParserIndex ( ) } ;
} ;
//
// MessageSizeFilter
//
struct call_data {
call_data ( grpc_call_element * elem , const channel_data & chand ,
const grpc_call_element_args & args )
: call_combiner ( args . call_combiner ) , limits ( chand . limits ) {
GRPC_CLOSURE_INIT ( & recv_message_ready , : : recv_message_ready , elem ,
grpc_schedule_on_exec_ctx ) ;
GRPC_CLOSURE_INIT ( & recv_trailing_metadata_ready ,
: : recv_trailing_metadata_ready , elem ,
grpc_schedule_on_exec_ctx ) ;
// Get max sizes from channel data, then merge in per-method config values.
// Note: Per-method config is only available on the client, so we
// apply the max request size to the send limit and the max response
// size to the receive limit.
const grpc_core : : MessageSizeParsedConfig * config_from_call_context =
grpc_core : : MessageSizeParsedConfig : : GetFromCallContext (
args . context , chand . service_config_parser_index ) ;
if ( config_from_call_context ! = nullptr ) {
absl : : optional < uint32_t > max_send_size = limits . max_send_size ( ) ;
absl : : optional < uint32_t > max_recv_size = limits . max_recv_size ( ) ;
if ( config_from_call_context - > max_send_size ( ) . has_value ( ) & &
( ! max_send_size . has_value ( ) | |
* config_from_call_context - > max_send_size ( ) < * max_send_size ) ) {
max_send_size = * config_from_call_context - > max_send_size ( ) ;
const grpc_channel_filter ClientMessageSizeFilter : : kFilter =
MakePromiseBasedFilter < ClientMessageSizeFilter , FilterEndpoint : : kClient ,
kFilterExaminesOutboundMessages |
kFilterExaminesInboundMessages > ( " message_size " ) ;
const grpc_channel_filter ServerMessageSizeFilter : : kFilter =
MakePromiseBasedFilter < ServerMessageSizeFilter , FilterEndpoint : : kServer ,
kFilterExaminesOutboundMessages |
kFilterExaminesInboundMessages > ( " message_size " ) ;
class MessageSizeFilter : : CallBuilder {
private :
auto Interceptor ( uint32_t max_length , bool is_send ) {
return [ max_length , is_send ,
err = err_ ] ( MessageHandle msg ) - > absl : : optional < MessageHandle > {
if ( grpc_call_trace . enabled ( ) ) {
gpr_log ( GPR_INFO , " %s[message_size] %s len:% " PRIdPTR " max:%d " ,
Activity : : current ( ) - > DebugTag ( ) . c_str ( ) ,
is_send ? " send " : " recv " , msg - > payload ( ) - > Length ( ) ,
max_length ) ;
}
if ( config_from_call_context - > max_recv_size ( ) . has_value ( ) & &
( ! max_recv_size . has_value ( ) | |
* config_from_call_context - > max_recv_size ( ) < * max_recv_size ) ) {
max_recv_size = * config_from_call_context - > max_recv_size ( ) ;
if ( msg - > payload ( ) - > Length ( ) > max_length ) {
if ( err - > is_set ( ) ) return std : : move ( msg ) ;
auto r = GetContext < Arena > ( ) - > MakePooled < ServerMetadata > (
GetContext < Arena > ( ) ) ;
r - > Set ( GrpcStatusMetadata ( ) , GRPC_STATUS_RESOURCE_EXHAUSTED ) ;
r - > Set ( GrpcMessageMetadata ( ) ,
Slice : : FromCopiedString (
absl : : StrFormat ( " %s message larger than max (%u vs. %d) " ,
is_send ? " Sent " : " Received " ,
msg - > payload ( ) - > Length ( ) , max_length ) ) ) ;
err - > Set ( std : : move ( r ) ) ;
return absl : : nullopt ;
}
limits = grpc_core : : MessageSizeParsedConfig ( max_send_size , max_recv_size ) ;
}
return std : : move ( msg ) ;
} ;
}
~ call_data ( ) { }
grpc_core : : CallCombiner * call_combiner ;
grpc_core : : MessageSizeParsedConfig limits ;
// Receive closures are chained: we inject this closure as the
// recv_message_ready up-call on transport_stream_op, and remember to
// call our next_recv_message_ready member after handling it.
grpc_closure recv_message_ready ;
grpc_closure recv_trailing_metadata_ready ;
// The error caused by a message that is too large, or absl::OkStatus()
grpc_error_handle error ;
// Used by recv_message_ready.
absl : : optional < grpc_core : : SliceBuffer > * recv_message = nullptr ;
// Original recv_message_ready callback, invoked after our own.
grpc_closure * next_recv_message_ready = nullptr ;
// Original recv_trailing_metadata callback, invoked after our own.
grpc_closure * original_recv_trailing_metadata_ready ;
bool seen_recv_trailing_metadata = false ;
grpc_error_handle recv_trailing_metadata_error ;
} ;
} // namespace
public :
explicit CallBuilder ( const MessageSizeParsedConfig & limits )
: limits_ ( limits ) { }
// Callback invoked when we receive a message. Here we check the max
// receive message size.
static void recv_message_ready ( void * user_data , grpc_error_handle error ) {
grpc_call_element * elem = static_cast < grpc_call_element * > ( user_data ) ;
call_data * calld = static_cast < call_data * > ( elem - > call_data ) ;
if ( calld - > recv_message - > has_value ( ) & &
calld - > limits . max_recv_size ( ) . has_value ( ) & &
( * calld - > recv_message ) - > Length ( ) >
static_cast < size_t > ( * calld - > limits . max_recv_size ( ) ) ) {
grpc_error_handle new_error = grpc_error_set_int (
GRPC_ERROR_CREATE ( absl : : StrFormat (
" Received message larger than max (%u vs. %d) " ,
( * calld - > recv_message ) - > Length ( ) , * calld - > limits . max_recv_size ( ) ) ) ,
grpc_core : : StatusIntProperty : : kRpcStatus ,
GRPC_STATUS_RESOURCE_EXHAUSTED ) ;
error = grpc_error_add_child ( error , new_error ) ;
calld - > error = error ;
template < typename T >
void AddSend ( T * pipe_end ) {
if ( ! limits_ . max_send_size ( ) . has_value ( ) ) return ;
pipe_end - > InterceptAndMap ( Interceptor ( * limits_ . max_send_size ( ) , true ) ) ;
}
// Invoke the next callback.
grpc_closure * closure = calld - > next_recv_message_ready ;
calld - > next_recv_message_ready = nullptr ;
if ( calld - > seen_recv_trailing_metadata ) {
// We might potentially see another RECV_MESSAGE op. In that case, we do not
// want to run the recv_trailing_metadata_ready closure again. The newer
// RECV_MESSAGE op cannot cause any errors since the transport has already
// invoked the recv_trailing_metadata_ready closure and all further
// RECV_MESSAGE ops will get null payloads.
calld - > seen_recv_trailing_metadata = false ;
GRPC_CALL_COMBINER_START ( calld - > call_combiner ,
& calld - > recv_trailing_metadata_ready ,
calld - > recv_trailing_metadata_error ,
" continue recv_trailing_metadata_ready " ) ;
template < typename T >
void AddRecv ( T * pipe_end ) {
if ( ! limits_ . max_recv_size ( ) . has_value ( ) ) return ;
pipe_end - > InterceptAndMap ( Interceptor ( * limits_ . max_recv_size ( ) , false ) ) ;
}
grpc_core : : Closure : : Run ( DEBUG_LOCATION , closure , error ) ;
}
// Callback invoked on completion of recv_trailing_metadata
// Notifies the recv_trailing_metadata batch of any message size failures
static void recv_trailing_metadata_ready ( void * user_data ,
grpc_error_handle error ) {
grpc_call_element * elem = static_cast < grpc_call_element * > ( user_data ) ;
call_data * calld = static_cast < call_data * > ( elem - > call_data ) ;
if ( calld - > next_recv_message_ready ! = nullptr ) {
calld - > seen_recv_trailing_metadata = true ;
calld - > recv_trailing_metadata_error = error ;
GRPC_CALL_COMBINER_STOP ( calld - > call_combiner ,
" deferring recv_trailing_metadata_ready until "
" after recv_message_ready " ) ;
return ;
ArenaPromise < ServerMetadataHandle > Run (
CallArgs call_args , NextPromiseFactory next_promise_factory ) {
return Race ( err_ - > Wait ( ) , next_promise_factory ( std : : move ( call_args ) ) ) ;
}
error = grpc_error_add_child ( error , calld - > error ) ;
// Invoke the next callback.
grpc_core : : Closure : : Run ( DEBUG_LOCATION ,
calld - > original_recv_trailing_metadata_ready , error ) ;
}
// Start transport stream op.
static void message_size_start_transport_stream_op_batch (
grpc_call_element * elem , grpc_transport_stream_op_batch * op ) {
call_data * calld = static_cast < call_data * > ( elem - > call_data ) ;
// Check max send message size.
if ( op - > send_message & & calld - > limits . max_send_size ( ) . has_value ( ) & &
op - > payload - > send_message . send_message - > Length ( ) >
static_cast < size_t > ( * calld - > limits . max_send_size ( ) ) ) {
grpc_transport_stream_op_batch_finish_with_failure (
op ,
grpc_error_set_int ( GRPC_ERROR_CREATE ( absl : : StrFormat (
" Sent message larger than max (%u vs. %d) " ,
op - > payload - > send_message . send_message - > Length ( ) ,
* calld - > limits . max_send_size ( ) ) ) ,
grpc_core : : StatusIntProperty : : kRpcStatus ,
GRPC_STATUS_RESOURCE_EXHAUSTED ) ,
calld - > call_combiner ) ;
return ;
}
// Inject callback for receiving a message.
if ( op - > recv_message ) {
calld - > next_recv_message_ready =
op - > payload - > recv_message . recv_message_ready ;
calld - > recv_message = op - > payload - > recv_message . recv_message ;
op - > payload - > recv_message . recv_message_ready = & calld - > recv_message_ready ;
}
// Inject callback for receiving trailing metadata.
if ( op - > recv_trailing_metadata ) {
calld - > original_recv_trailing_metadata_ready =
op - > payload - > recv_trailing_metadata . recv_trailing_metadata_ready ;
op - > payload - > recv_trailing_metadata . recv_trailing_metadata_ready =
& calld - > recv_trailing_metadata_ready ;
}
// Chain to the next filter.
grpc_call_next_op ( elem , op ) ;
}
private :
Latch < ServerMetadataHandle > * const err_ =
GetContext < Arena > ( ) - > New < Latch < ServerMetadataHandle > > ( ) ;
MessageSizeParsedConfig limits_ ;
} ;
// Constructor for call_data.
static grpc_error_handle message_size_init_call_elem (
grpc_call_element * elem , const grpc_call_element_args * args ) {
channel_data * chand = static_cast < channel_data * > ( elem - > channel_data ) ;
new ( elem - > call_data ) call_data ( elem , * chand , * args ) ;
return absl : : OkStatus ( ) ;
absl : : StatusOr < ClientMessageSizeFilter > ClientMessageSizeFilter : : Create (
const ChannelArgs & args , ChannelFilter : : Args ) {
return ClientMessageSizeFilter ( args ) ;
}
// Destructor for call_data.
static void message_size_destroy_call_elem (
grpc_call_element * elem , const grpc_call_final_info * /*final_info*/ ,
grpc_closure * /*ignored*/ ) {
call_data * calld = static_cast < call_data * > ( elem - > call_data ) ;
calld - > ~ call_data ( ) ;
absl : : StatusOr < ServerMessageSizeFilter > ServerMessageSizeFilter : : Create (
const ChannelArgs & args , ChannelFilter : : Args ) {
return ServerMessageSizeFilter ( args ) ;
}
// Constructor for channel_data.
static grpc_error_handle message_size_init_channel_elem (
grpc_channel_element * elem , grpc_channel_element_args * args ) {
GPR_ASSERT ( ! args - > is_last ) ;
channel_data * chand = static_cast < channel_data * > ( elem - > channel_data ) ;
new ( chand ) channel_data ( ) ;
chand - > limits = grpc_core : : MessageSizeParsedConfig : : GetFromChannelArgs (
args - > channel_args ) ;
return absl : : OkStatus ( ) ;
}
ArenaPromise < ServerMetadataHandle > ClientMessageSizeFilter : : MakeCallPromise (
CallArgs call_args , NextPromiseFactory next_promise_factory ) {
// Get max sizes from channel data, then merge in per-method config values.
// Note: Per-method config is only available on the client, so we
// apply the max request size to the send limit and the max response
// size to the receive limit.
MessageSizeParsedConfig limits = this - > limits ( ) ;
const MessageSizeParsedConfig * config_from_call_context =
MessageSizeParsedConfig : : GetFromCallContext (
GetContext < grpc_call_context_element > ( ) ,
service_config_parser_index_ ) ;
if ( config_from_call_context ! = nullptr ) {
absl : : optional < uint32_t > max_send_size = limits . max_send_size ( ) ;
absl : : optional < uint32_t > max_recv_size = limits . max_recv_size ( ) ;
if ( config_from_call_context - > max_send_size ( ) . has_value ( ) & &
( ! max_send_size . has_value ( ) | |
* config_from_call_context - > max_send_size ( ) < * max_send_size ) ) {
max_send_size = * config_from_call_context - > max_send_size ( ) ;
}
if ( config_from_call_context - > max_recv_size ( ) . has_value ( ) & &
( ! max_recv_size . has_value ( ) | |
* config_from_call_context - > max_recv_size ( ) < * max_recv_size ) ) {
max_recv_size = * config_from_call_context - > max_recv_size ( ) ;
}
limits = MessageSizeParsedConfig ( max_send_size , max_recv_size ) ;
}
// Destructor for channel_data.
static void message_size_destroy_channel_elem ( grpc_channel_element * elem ) {
channel_data * chand = static_cast < channel_data * > ( elem - > channel_data ) ;
chand - > ~ channel_data ( ) ;
CallBuilder b ( limits ) ;
b . AddSend ( call_args . client_to_server_messages ) ;
b . AddRecv ( call_args . server_to_client_messages ) ;
return b . Run ( std : : move ( call_args ) , std : : move ( next_promise_factory ) ) ;
}
const grpc_channel_filter grpc_message_size_filter = {
message_size_start_transport_stream_op_batch ,
nullptr ,
grpc_channel_next_op ,
sizeof ( call_data ) ,
message_size_init_call_elem ,
grpc_call_stack_ignore_set_pollset_or_pollset_set ,
message_size_destroy_call_elem ,
sizeof ( channel_data ) ,
message_size_init_channel_elem ,
grpc_channel_stack_no_post_init ,
message_size_destroy_channel_elem ,
grpc_channel_next_get_info ,
" message_size " } ;
ArenaPromise < ServerMetadataHandle > ServerMessageSizeFilter : : MakeCallPromise (
CallArgs call_args , NextPromiseFactory next_promise_factory ) {
CallBuilder b ( limits ( ) ) ;
b . AddSend ( call_args . server_to_client_messages ) ;
b . AddRecv ( call_args . client_to_server_messages ) ;
return b . Run ( std : : move ( call_args ) , std : : move ( next_promise_factory ) ) ;
}
namespace {
// Used for GRPC_CLIENT_SUBCHANNEL
static bool maybe_add_message_size_filter_subchannel (
grpc_core : : ChannelStackBuilder * builder ) {
bool MaybeAddMessageSizeFilterToSubchannel ( ChannelStackBuilder * builder ) {
if ( builder - > channel_args ( ) . WantMinimalStack ( ) ) {
return true ;
}
builder - > PrependFilter ( & grpc_message_size_f ilter) ;
builder - > PrependFilter ( & ClientMessageSizeFilter : : kF ilter) ;
return true ;
}
// Used for GRPC_CLIENT_DIRECT_CHANNEL and GRPC_SERVER_CHANNEL. Adds the filter
// only if message size limits or service config is specified.
static bool maybe_add_message_size_filter (
grpc_core : : ChannelStackBuilder * builder ) {
auto channel_args = builder - > channel_args ( ) ;
if ( channel_args . WantMinimalStack ( ) ) {
// Used for GRPC_CLIENT_DIRECT_CHANNEL and GRPC_SERVER_CHANNEL. Adds the
// filter only if message size limits or service config is specified.
auto MaybeAddMessageSizeFilter ( const grpc_channel_filter * filter ) {
return [ filter ] ( ChannelStackBuilder * builder ) {
auto channel_args = builder - > channel_args ( ) ;
if ( channel_args . WantMinimalStack ( ) ) {
return true ;
}
MessageSizeParsedConfig limits =
MessageSizeParsedConfig : : GetFromChannelArgs ( channel_args ) ;
const bool enable =
limits . max_send_size ( ) . has_value ( ) | |
limits . max_recv_size ( ) . has_value ( ) | |
channel_args . GetString ( GRPC_ARG_SERVICE_CONFIG ) . has_value ( ) ;
if ( enable ) builder - > PrependFilter ( filter ) ;
return true ;
}
grpc_core : : MessageSizeParsedConfig limits =
grpc_core : : MessageSizeParsedConfig : : GetFromChannelArgs ( channel_args ) ;
const bool enable =
limits . max_send_size ( ) . has_value ( ) | |
limits . max_recv_size ( ) . has_value ( ) | |
channel_args . GetString ( GRPC_ARG_SERVICE_CONFIG ) . has_value ( ) ;
if ( enable ) builder - > PrependFilter ( & grpc_message_size_filter ) ;
return true ;
} ;
}
namespace grpc_core {
} // namespace
void RegisterMessageSizeFilter ( CoreConfiguration : : Builder * builder ) {
MessageSizeParser : : Register ( builder ) ;
builder - > channel_init ( ) - > RegisterStage (
GRPC_CLIENT_SUBCHANNEL , GRPC_CHANNEL_INIT_BUILTIN_PRIORITY ,
maybe_add_message_size_filter_subchannel ) ;
builder - > channel_init ( ) - > RegisterStage ( GRPC_CLIENT_DIRECT_CHANNEL ,
GRPC_CHANNEL_INIT_BUILTIN_PRIORITY ,
maybe_add_message_size_filter ) ;
builder - > channel_init ( ) - > RegisterStage ( GRPC_SERVER_CHANNEL ,
builder - > channel_init ( ) - > RegisterStage ( GRPC_CLIENT_SUBCHANNEL ,
GRPC_CHANNEL_INIT_BUILTIN_PRIORITY ,
maybe_add_message_size_filter ) ;
MaybeAddMessageSizeFilterToSubchannel ) ;
builder - > channel_init ( ) - > RegisterStage (
GRPC_CLIENT_DIRECT_CHANNEL , GRPC_CHANNEL_INIT_BUILTIN_PRIORITY ,
MaybeAddMessageSizeFilter ( & ClientMessageSizeFilter : : kFilter ) ) ;
builder - > channel_init ( ) - > RegisterStage (
GRPC_SERVER_CHANNEL , GRPC_CHANNEL_INIT_BUILTIN_PRIORITY ,
MaybeAddMessageSizeFilter ( & ServerMessageSizeFilter : : kFilter ) ) ;
}
} // namespace grpc_core