@ -106,14 +106,12 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
static void cancel_from_api ( grpc_exec_ctx * exec_ctx ,
grpc_chttp2_transport_global * transport_global ,
grpc_chttp2_stream_global * stream_global ,
grpc_status_code status ,
gpr_slice * optional_message ) ;
grpc_error * error ) ;
static void close_from_api ( grpc_exec_ctx * exec_ctx ,
grpc_chttp2_transport_global * transport_global ,
grpc_chttp2_stream_global * stream_global ,
grpc_status_code status ,
gpr_slice * optional_message ) ;
grpc_error * error ) ;
/** Add endpoint from this transport to pollset */
static void add_to_pollset_locked ( grpc_exec_ctx * exec_ctx ,
@ -163,8 +161,6 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
GPR_ASSERT ( t - > ep = = NULL ) ;
gpr_slice_unref ( t - > optional_drop_message ) ;
gpr_slice_buffer_destroy ( & t - > global . qbuf ) ;
gpr_slice_buffer_destroy ( & t - > writing . outbuf ) ;
@ -266,7 +262,6 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0 ;
t - > parsing . is_first_frame = true ;
t - > writing . is_client = is_client ;
t - > optional_drop_message = gpr_empty_slice ( ) ;
grpc_connectivity_state_init (
& t - > channel_callback . state_tracker , GRPC_CHANNEL_READY ,
is_client ? " client_transport " : " server_transport " ) ;
@ -876,7 +871,9 @@ static void maybe_start_some_streams(
grpc_chttp2_list_pop_waiting_for_concurrency ( transport_global ,
& stream_global ) ) {
cancel_from_api ( exec_ctx , transport_global , stream_global ,
GRPC_STATUS_UNAVAILABLE , NULL ) ;
grpc_error_set_int (
GRPC_ERROR_CREATE ( " Stream IDs exhausted " ) ,
GRPC_ERROR_INT_GRPC_STATUS , GRPC_STATUS_UNAVAILABLE ) ) ;
}
}
@ -958,14 +955,14 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
on_complete - > next_data . scratch | = CLOSURE_BARRIER_STATS_BIT ;
}
if ( op - > cancel_with_status ! = GRPC_STATUS_OK ) {
if ( op - > cancel_error ! = GRPC_ERROR_NONE ) {
cancel_from_api ( exec_ctx , transport_global , stream_global ,
op - > cancel_with_status , op - > optional_close_message ) ;
GRPC_ERROR_REF ( op - > cancel_error ) ) ;
}
if ( op - > close_with_status ! = GRPC_STATUS_OK ) {
if ( op - > close_error ! = GRPC_ERROR_NONE ) {
close_from_api ( exec_ctx , transport_global , stream_global ,
op - > close_with_status , op - > optional_close_message ) ;
GRPC_ERROR_REF ( op - > close_error ) ) ;
}
if ( op - > send_initial_metadata ! = NULL ) {
@ -979,12 +976,16 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
transport_global - > settings [ GRPC_PEER_SETTINGS ]
[ GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE ] ;
if ( metadata_size > metadata_peer_limit ) {
gpr_log ( GPR_DEBUG ,
" to-be-sent initial metadata size exceeds peer limit "
" (% " PRIuPTR " vs. % " PRIuPTR " ) " ,
metadata_size , metadata_peer_limit ) ;
cancel_from_api ( exec_ctx , transport_global , stream_global ,
GRPC_STATUS_RESOURCE_EXHAUSTED , NULL ) ;
cancel_from_api (
exec_ctx , transport_global , stream_global ,
grpc_error_set_int (
grpc_error_set_int (
grpc_error_set_int (
GRPC_ERROR_CREATE ( " to-be-sent initial metadata size "
" exceeds peer limit " ) ,
GRPC_ERROR_INT_SIZE , ( intptr_t ) metadata_size ) ,
GRPC_ERROR_INT_LIMIT , ( intptr_t ) metadata_peer_limit ) ,
GRPC_ERROR_INT_GRPC_STATUS , GRPC_STATUS_RESOURCE_EXHAUSTED ) ) ;
} else {
if ( contains_non_ok_status ( transport_global , op - > send_initial_metadata ) ) {
stream_global - > seen_error = true ;
@ -1038,12 +1039,16 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
transport_global - > settings [ GRPC_PEER_SETTINGS ]
[ GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE ] ;
if ( metadata_size > metadata_peer_limit ) {
gpr_log ( GPR_DEBUG ,
" to-be-sent trailing metadata size exceeds peer limit "
" (% " PRIuPTR " vs. % " PRIuPTR " ) " ,
metadata_size , metadata_peer_limit ) ;
cancel_from_api ( exec_ctx , transport_global , stream_global ,
GRPC_STATUS_RESOURCE_EXHAUSTED , NULL ) ;
cancel_from_api (
exec_ctx , transport_global , stream_global ,
grpc_error_set_int (
grpc_error_set_int (
grpc_error_set_int (
GRPC_ERROR_CREATE ( " to-be-sent trailing metadata size "
" exceeds peer limit " ) ,
GRPC_ERROR_INT_SIZE , ( intptr_t ) metadata_size ) ,
GRPC_ERROR_INT_LIMIT , ( intptr_t ) metadata_peer_limit ) ,
GRPC_ERROR_INT_GRPC_STATUS , GRPC_STATUS_RESOURCE_EXHAUSTED ) ) ;
} else {
if ( contains_non_ok_status ( transport_global ,
op - > send_trailing_metadata ) ) {
@ -1235,8 +1240,12 @@ static void check_read_ops(grpc_exec_ctx *exec_ctx,
incoming_byte_stream_destroy_locked ( exec_ctx , NULL , NULL , bs ) ;
}
if ( stream_global - > exceeded_metadata_size ) {
cancel_from_api ( exec_ctx , transport_global , stream_global ,
GRPC_STATUS_RESOURCE_EXHAUSTED , NULL ) ;
cancel_from_api (
exec_ctx , transport_global , stream_global ,
grpc_error_set_int (
GRPC_ERROR_CREATE (
" received initial metadata size exceeds limit " ) ,
GRPC_ERROR_INT_GRPC_STATUS , GRPC_STATUS_RESOURCE_EXHAUSTED ) ) ;
}
}
grpc_chttp2_incoming_metadata_buffer_publish (
@ -1275,8 +1284,12 @@ static void check_read_ops(grpc_exec_ctx *exec_ctx,
incoming_byte_stream_destroy_locked ( exec_ctx , NULL , NULL , bs ) ;
}
if ( stream_global - > exceeded_metadata_size ) {
cancel_from_api ( exec_ctx , transport_global , stream_global ,
GRPC_STATUS_RESOURCE_EXHAUSTED , NULL ) ;
cancel_from_api (
exec_ctx , transport_global , stream_global ,
grpc_error_set_int (
GRPC_ERROR_CREATE (
" received trailing metadata size exceeds limit " ) ,
GRPC_ERROR_INT_GRPC_STATUS , GRPC_STATUS_RESOURCE_EXHAUSTED ) ) ;
}
}
if ( stream_global - > all_incoming_byte_streams_finished ) {
@ -1340,35 +1353,67 @@ static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
GRPC_ERROR_UNREF ( error ) ;
}
static void status_codes_from_error ( grpc_error * error ,
grpc_chttp2_error_code * http2_error ,
grpc_status_code * grpc_status ) {
intptr_t ip_http ;
intptr_t ip_grpc ;
bool have_http =
grpc_error_get_int ( error , GRPC_ERROR_INT_HTTP2_ERROR , & ip_http ) ;
bool have_grpc =
grpc_error_get_int ( error , GRPC_ERROR_INT_GRPC_STATUS , & ip_grpc ) ;
if ( have_http ) {
* http2_error = ( grpc_chttp2_error_code ) ip_http ;
} else if ( have_grpc ) {
* http2_error =
grpc_chttp2_grpc_status_to_http2_error ( ( grpc_status_code ) ip_grpc ) ;
} else {
* http2_error = GRPC_CHTTP2_INTERNAL_ERROR ;
}
if ( have_grpc ) {
* grpc_status = ( grpc_status_code ) ip_grpc ;
} else if ( have_http ) {
* grpc_status =
grpc_chttp2_http2_error_to_grpc_status ( ( grpc_chttp2_error_code ) ip_http ) ;
} else {
* grpc_status = GRPC_STATUS_INTERNAL ;
}
}
static void cancel_from_api ( grpc_exec_ctx * exec_ctx ,
grpc_chttp2_transport_global * transport_global ,
grpc_chttp2_stream_global * stream_global ,
grpc_status_code status ,
gpr_slice * optional_message ) {
grpc_error * due_to_error ) {
if ( ! stream_global - > read_closed | | ! stream_global - > write_closed ) {
grpc_status_code grpc_status ;
grpc_chttp2_error_code http_error ;
status_codes_from_error ( due_to_error , & http_error , & grpc_status ) ;
if ( stream_global - > id ! = 0 ) {
gpr_slice_buffer_add (
& transport_global - > qbuf ,
grpc_chttp2_rst_stream_create (
stream_global - > id ,
( uint32_t ) grpc_chttp2_grpc_status_to_http2_error ( status ) ,
& stream_global - > stats . outgoing ) ) ;
grpc_chttp2_rst_stream_create ( stream_global - > id , ( uint32_t ) http_error ,
& stream_global - > stats . outgoing ) ) ;
}
if ( optional_message ) {
gpr_slice_ref ( * optional_message ) ;
const char * msg =
grpc_error_get_str ( due_to_error , GRPC_ERROR_STR_GRPC_MESSAGE ) ;
bool free_msg = false ;
if ( msg = = NULL ) {
free_msg = true ;
msg = grpc_error_string ( due_to_error ) ;
}
grpc_chttp2_fake_status ( exec_ctx , transport_global , stream_global , status ,
optional_message ) ;
gpr_slice msg_slice = gpr_slice_from_copied_string ( msg ) ;
grpc_chttp2_fake_status ( exec_ctx , transport_global , stream_global ,
grpc_status , & msg_slice ) ;
if ( free_msg ) grpc_error_free_string ( msg ) ;
}
if ( status ! = GRPC_STATUS_OK & & ! stream_global - > seen_error ) {
if ( due_to_error ! = GRPC_ERROR_NONE & & ! stream_global - > seen_error ) {
stream_global - > seen_error = true ;
grpc_chttp2_list_add_check_read_ops ( transport_global , stream_global ) ;
}
grpc_chttp2_mark_stream_closed (
exec_ctx , transport_global , stream_global , 1 , 1 ,
grpc_error_set_int ( GRPC_ERROR_CREATE ( " Cancelled " ) ,
GRPC_ERROR_INT_GRPC_STATUS , status ) ) ;
grpc_chttp2_mark_stream_closed ( exec_ctx , transport_global , stream_global , 1 ,
1 , due_to_error ) ;
}
void grpc_chttp2_fake_status ( grpc_exec_ctx * exec_ctx ,
@ -1469,15 +1514,17 @@ void grpc_chttp2_mark_stream_closed(
static void close_from_api ( grpc_exec_ctx * exec_ctx ,
grpc_chttp2_transport_global * transport_global ,
grpc_chttp2_stream_global * stream_global ,
grpc_status_code status ,
gpr_slice * optional_message ) {
grpc_error * error ) {
gpr_slice hdr ;
gpr_slice status_hdr ;
gpr_slice message_pfx ;
uint8_t * p ;
uint32_t len = 0 ;
grpc_status_code grpc_status ;
grpc_chttp2_error_code http_error ;
status_codes_from_error ( error , & http_error , & grpc_status ) ;
GPR_ASSERT ( status > = 0 & & ( int ) status < 100 ) ;
GPR_ASSERT ( grpc_ status > = 0 & & ( int ) grpc_ status < 100 ) ;
if ( stream_global - > id ! = 0 & & ! transport_global - > is_client ) {
/* Hand roll a header block.
@ -1487,7 +1534,7 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
time we got around to sending this , so instead we ignore HPACK
compression
and just write the uncompressed bytes onto the wire . */
status_hdr = gpr_slice_malloc ( 15 + ( status > = 10 ) ) ;
status_hdr = gpr_slice_malloc ( 15 + ( grpc_ status > = 10 ) ) ;
p = GPR_SLICE_START_PTR ( status_hdr ) ;
* p + + = 0x40 ; /* literal header */
* p + + = 11 ; /* len(grpc-status) */
@ -1502,19 +1549,23 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
* p + + = ' t ' ;
* p + + = ' u ' ;
* p + + = ' s ' ;
if ( status < 10 ) {
if ( grpc_ status < 10 ) {
* p + + = 1 ;
* p + + = ( uint8_t ) ( ' 0 ' + status ) ;
* p + + = ( uint8_t ) ( ' 0 ' + grpc_ status) ;
} else {
* p + + = 2 ;
* p + + = ( uint8_t ) ( ' 0 ' + ( status / 10 ) ) ;
* p + + = ( uint8_t ) ( ' 0 ' + ( status % 10 ) ) ;
* p + + = ( uint8_t ) ( ' 0 ' + ( grpc_ status / 10 ) ) ;
* p + + = ( uint8_t ) ( ' 0 ' + ( grpc_ status % 10 ) ) ;
}
GPR_ASSERT ( p = = GPR_SLICE_END_PTR ( status_hdr ) ) ;
len + = ( uint32_t ) GPR_SLICE_LENGTH ( status_hdr ) ;
if ( optional_message ) {
GPR_ASSERT ( GPR_SLICE_LENGTH ( * optional_message ) < 127 ) ;
const char * optional_message =
grpc_error_get_str ( error , GRPC_ERROR_STR_GRPC_MESSAGE ) ;
if ( optional_message ! = NULL ) {
size_t msg_len = strlen ( optional_message ) ;
GPR_ASSERT ( msg_len < 127 ) ;
message_pfx = gpr_slice_malloc ( 15 ) ;
p = GPR_SLICE_START_PTR ( message_pfx ) ;
* p + + = 0x40 ;
@ -1531,10 +1582,10 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
* p + + = ' a ' ;
* p + + = ' g ' ;
* p + + = ' e ' ;
* p + + = ( uint8_t ) GPR_SLICE_LENGTH ( * optional_ me ssa ge) ;
* p + + = ( uint8_t ) msg_l en ;
GPR_ASSERT ( p = = GPR_SLICE_END_PTR ( message_pfx ) ) ;
len + = ( uint32_t ) GPR_SLICE_LENGTH ( message_pfx ) ;
len + = ( uint32_t ) GPR_SLICE_LENGTH ( * optional_ me ssa ge) ;
len + = ( uint32_t ) msg_l en ;
}
hdr = gpr_slice_malloc ( 9 ) ;
@ -1555,53 +1606,54 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
if ( optional_message ) {
gpr_slice_buffer_add ( & transport_global - > qbuf , message_pfx ) ;
gpr_slice_buffer_add ( & transport_global - > qbuf ,
gpr_slice_ref ( * optional_message ) ) ;
gpr_slice_from_copied_string ( optional_message ) ) ;
}
gpr_slice_buffer_add (
& transport_global - > qbuf ,
grpc_chttp2_rst_stream_create ( stream_global - > id , GRPC_CHTTP2_NO_ERROR ,
& stream_global - > stats . outgoing ) ) ;
if ( optional_message ) {
gpr_slice_ref ( * optional_message ) ;
}
}
grpc_chttp2_fake_status ( exec_ctx , transport_global , stream_global , status ,
optional_message ) ;
grpc_error * err = GRPC_ERROR_CREATE ( " Stream closed " ) ;
err = grpc_error_set_int ( err , GRPC_ERROR_INT_GRPC_STATUS , status ) ;
if ( optional_message ) {
char * str =
gpr_dump_slice ( * optional_message , GPR_DUMP_HEX | GPR_DUMP_ASCII ) ;
err = grpc_error_set_str ( err , GRPC_ERROR_STR_GRPC_MESSAGE , str ) ;
gpr_free ( str ) ;
const char * msg = grpc_error_get_str ( error , GRPC_ERROR_STR_GRPC_MESSAGE ) ;
bool free_msg = false ;
if ( msg = = NULL ) {
free_msg = true ;
msg = grpc_error_string ( error ) ;
}
gpr_slice msg_slice = gpr_slice_from_copied_string ( msg ) ;
grpc_chttp2_fake_status ( exec_ctx , transport_global , stream_global ,
grpc_status , & msg_slice ) ;
if ( free_msg ) grpc_error_free_string ( msg ) ;
grpc_chttp2_mark_stream_closed ( exec_ctx , transport_global , stream_global , 1 ,
1 , err ) ;
1 , error ) ;
}
typedef struct {
grpc_exec_ctx * exec_ctx ;
grpc_error * error ;
} cancel_stream_cb_args ;
static void cancel_stream_cb ( grpc_chttp2_transport_global * transport_global ,
void * user_data ,
grpc_chttp2_stream_global * stream_global ) {
grpc_chttp2_transport * transport = TRANSPORT_FROM_GLOBAL ( transport_global ) ;
cancel_from_api ( user_data , transport_global , stream_global ,
GRPC_STATUS_UNAVAILABLE ,
GPR_SLICE_IS_EMPTY ( transport - > optional_drop_message )
? NULL
: & transport - > optional_drop_message ) ;
cancel_stream_cb_args * args = user_data ;
cancel_from_api ( args - > exec_ctx , transport_global , stream_global ,
GRPC_ERROR_REF ( args - > error ) ) ;
}
static void end_all_the_calls ( grpc_exec_ctx * exec_ctx ,
grpc_chttp2_transport * t ) {
grpc_chttp2_for_all_streams ( & t - > global , exec_ctx , cancel_stream_cb ) ;
static void end_all_the_calls ( grpc_exec_ctx * exec_ctx , grpc_chttp2_transport * t ,
grpc_error * error ) {
cancel_stream_cb_args args = { exec_ctx , error } ;
grpc_chttp2_for_all_streams ( & t - > global , & args , cancel_stream_cb ) ;
GRPC_ERROR_UNREF ( error ) ;
}
static void drop_connection ( grpc_exec_ctx * exec_ctx , grpc_chttp2_transport * t ,
grpc_error * error ) {
close_transport_locked ( exec_ctx , t , error ) ;
end_all_the_calls ( exec_ctx , t ) ;
close_transport_locked ( exec_ctx , t , GRPC_ERROR_REF ( error ) ) ;
end_all_the_calls ( exec_ctx , t , error ) ;
}
/** update window from a settings change */
@ -1708,15 +1760,7 @@ static void parsing_action(grpc_exec_ctx *exec_ctx, void *arg,
t - > read_buffer . slices [ i ] ) ;
} ;
if ( i ! = t - > read_buffer . count ) {
gpr_slice_unref ( t - > optional_drop_message ) ;
errors [ 2 ] = try_http_parsing ( exec_ctx , t ) ;
if ( errors [ 2 ] ! = GRPC_ERROR_NONE ) {
t - > optional_drop_message = gpr_slice_from_copied_string (
" Connection dropped: received http1.x response " ) ;
} else {
t - > optional_drop_message = gpr_slice_from_copied_string (
" Connection dropped: received unparseable response " ) ;
}
}
grpc_error * err =
errors [ 0 ] = = GRPC_ERROR_NONE & & errors [ 1 ] = = GRPC_ERROR_NONE & &
@ -1784,6 +1828,10 @@ static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
error = GRPC_ERROR_CREATE ( " Transport closed " ) ;
}
if ( error ! = GRPC_ERROR_NONE ) {
if ( ! grpc_error_get_int ( error , GRPC_ERROR_INT_GRPC_STATUS , NULL ) ) {
error = grpc_error_set_int ( error , GRPC_ERROR_INT_GRPC_STATUS ,
GRPC_STATUS_UNAVAILABLE ) ;
}
drop_connection ( exec_ctx , t , GRPC_ERROR_REF ( error ) ) ;
t - > endpoint_reading = 0 ;
if ( ! t - > executor . writing_active & & t - > ep ) {
@ -1798,6 +1846,7 @@ static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
prevent_endpoint_shutdown ( t ) ;
}
gpr_slice_buffer_reset_and_unref ( & t - > read_buffer ) ;
GRPC_ERROR_UNREF ( error ) ;
if ( keep_reading ) {
grpc_endpoint_read ( exec_ctx , t - > ep , & t - > read_buffer , & t - > reading_action ) ;
@ -1806,8 +1855,6 @@ static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
} else {
UNREF_TRANSPORT ( exec_ctx , t , " reading_action " ) ;
}
GRPC_LOG_IF_ERROR ( " close_transport " , error ) ;
}
/*******************************************************************************