void grpc_error_get_status(grpc_error *error, gpr_timespec deadline, grpc_status_code *code, grpc_slice *slice, grpc_http2_error_code *http_error) { // Start with the parent error and recurse through the tree of children // until we find the first one that has a status code. grpc_error *found_error = recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS); if (found_error == NULL) { /// If no grpc-status exists, retry through the tree to find a http2 error /// code found_error = recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR); } // If we found an error with a status code above, use that; otherwise, // fall back to using the parent error. if (found_error == NULL) found_error = error; grpc_status_code status = GRPC_STATUS_UNKNOWN; intptr_t integer; if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) { status = (grpc_status_code)integer; } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) { status = grpc_http2_error_to_grpc_status((grpc_http2_error_code)integer, deadline); } if (code != NULL) *code = status; if (http_error != NULL) { if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) { *http_error = (grpc_http2_error_code)integer; } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) { *http_error = grpc_status_to_http2_error((grpc_status_code)integer); } else { *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR : GRPC_HTTP2_INTERNAL_ERROR; } } // If the error has a status message, use it. Otherwise, fall back to // the error description. if (slice != NULL) { if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE, slice)) { if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION, slice)) { *slice = grpc_slice_from_static_string("unknown error"); } } } if (found_error == NULL) found_error = error; }
static grpc_error *parse_frame_slice(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_slice slice, int is_last) { grpc_chttp2_stream *s = t->incoming_stream; grpc_error *err = t->parser(exec_ctx, t->parser_data, t, s, slice, is_last); if (err == GRPC_ERROR_NONE) { return err; } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { if (GRPC_TRACER_ON(grpc_http_trace)) { const char *msg = grpc_error_string(err); gpr_log(GPR_ERROR, "%s", msg); } grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); if (s) { s->forced_close_error = err; grpc_slice_buffer_add( &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, GRPC_HTTP2_PROTOCOL_ERROR, &s->stats.outgoing)); } else { GRPC_ERROR_UNREF(err); } } return err; }
static grpc_error *parse_frame_slice( grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, gpr_slice slice, int is_last) { grpc_chttp2_stream_parsing *stream_parsing = transport_parsing->incoming_stream; grpc_error *err = transport_parsing->parser( exec_ctx, transport_parsing->parser_data, transport_parsing, stream_parsing, slice, is_last); if (err == GRPC_ERROR_NONE) { if (stream_parsing) { grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, stream_parsing); } return GRPC_ERROR_NONE; } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { if (grpc_http_trace) { const char *msg = grpc_error_string(err); gpr_log(GPR_ERROR, "%s", msg); grpc_error_free_string(msg); } grpc_chttp2_parsing_become_skip_parser(exec_ctx, transport_parsing); if (stream_parsing) { stream_parsing->forced_close_error = err; gpr_slice_buffer_add( &transport_parsing->qbuf, grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id, GRPC_CHTTP2_PROTOCOL_ERROR, &stream_parsing->stats.outgoing)); } else { GRPC_ERROR_UNREF(err); } } return err; }
static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { grpc_chttp2_stream *s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); grpc_error *err = GRPC_ERROR_NONE; err = grpc_chttp2_flowctl_recv_data(&t->flow_control, s == NULL ? NULL : &s->flow_control, t->incoming_frame_size); grpc_chttp2_act_on_flowctl_action( exec_ctx, grpc_chttp2_flowctl_get_action( &t->flow_control, s == NULL ? NULL : &s->flow_control), t, s); if (err != GRPC_ERROR_NONE) { goto error_handler; } if (s == NULL) { return init_skip_frame_parser(exec_ctx, t, 0); } s->received_bytes += t->incoming_frame_size; s->stats.incoming.framing_bytes += 9; if (err == GRPC_ERROR_NONE && s->read_closed) { return init_skip_frame_parser(exec_ctx, t, 0); } if (err == GRPC_ERROR_NONE) { err = grpc_chttp2_data_parser_begin_frame( &s->data_parser, t->incoming_frame_flags, s->id, s); } error_handler: if (err == GRPC_ERROR_NONE) { t->incoming_stream = s; /* t->parser = grpc_chttp2_data_parser_parse;*/ t->parser = grpc_chttp2_data_parser_parse; t->parser_data = &s->data_parser; t->ping_state.pings_before_data_required = t->ping_policy.max_pings_without_data; t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC); return GRPC_ERROR_NONE; } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { /* handle stream errors by closing the stream */ if (s != NULL) { grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, err); } grpc_slice_buffer_add( &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, GRPC_HTTP2_PROTOCOL_ERROR, &s->stats.outgoing)); return init_skip_frame_parser(exec_ctx, t, 0); } else { return err; } }
bool grpc_error_has_clear_grpc_status(grpc_error *error) { if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) { return true; } uint8_t slot = error->first_err; while (slot != UINT8_MAX) { grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot); if (grpc_error_has_clear_grpc_status(lerr->err)) { return true; } slot = lerr->next; } return false; }
static grpc_error *recursively_find_error_with_field(grpc_error *error, grpc_error_ints which) { // If the error itself has a status code, return it. if (grpc_error_get_int(error, which, NULL)) { return error; } if (grpc_error_is_special(error)) return NULL; // Otherwise, search through its children. uint8_t slot = error->first_err; while (slot != UINT8_MAX) { grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot); grpc_error *result = recursively_find_error_with_field(lerr->err, which); if (result) return result; slot = lerr->next; } return NULL; }
static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { grpc_chttp2_stream *s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); grpc_error *err = GRPC_ERROR_NONE; err = update_incoming_window(exec_ctx, t, s); if (err != GRPC_ERROR_NONE) { goto error_handler; } if (s == NULL) { return init_skip_frame_parser(exec_ctx, t, 0); } s->stats.incoming.framing_bytes += 9; if (err == GRPC_ERROR_NONE && s->read_closed) { return init_skip_frame_parser(exec_ctx, t, 0); } if (err == GRPC_ERROR_NONE) { err = grpc_chttp2_data_parser_begin_frame( &s->data_parser, t->incoming_frame_flags, s->id, s); } error_handler: if (err == GRPC_ERROR_NONE) { t->incoming_stream = s; /* t->parser = grpc_chttp2_data_parser_parse;*/ t->parser = grpc_chttp2_data_parser_parse; t->parser_data = &s->data_parser; return GRPC_ERROR_NONE; } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { /* handle stream errors by closing the stream */ if (s != NULL) { grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, err); } grpc_slice_buffer_add( &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, GRPC_HTTP2_PROTOCOL_ERROR, &s->stats.outgoing)); return init_skip_frame_parser(exec_ctx, t, 0); } else { return err; } }
static grpc_error *init_data_frame_parser( grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) { grpc_chttp2_stream_parsing *stream_parsing = grpc_chttp2_parsing_lookup_stream(transport_parsing, transport_parsing->incoming_stream_id); grpc_error *err = GRPC_ERROR_NONE; if (stream_parsing == NULL) { return init_skip_frame_parser(exec_ctx, transport_parsing, 0); } stream_parsing->stats.incoming.framing_bytes += 9; if (stream_parsing->received_close) { return init_skip_frame_parser(exec_ctx, transport_parsing, 0); } if (err == GRPC_ERROR_NONE) { err = update_incoming_window(exec_ctx, transport_parsing, stream_parsing); } if (err == GRPC_ERROR_NONE) { err = grpc_chttp2_data_parser_begin_frame( &stream_parsing->data_parser, transport_parsing->incoming_frame_flags, stream_parsing->id); } if (err == GRPC_ERROR_NONE) { transport_parsing->incoming_stream = stream_parsing; transport_parsing->parser = grpc_chttp2_data_parser_parse; transport_parsing->parser_data = &stream_parsing->data_parser; return GRPC_ERROR_NONE; } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { /* handle stream errors by closing the stream */ stream_parsing->received_close = 1; stream_parsing->forced_close_error = err; gpr_slice_buffer_add( &transport_parsing->qbuf, grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id, GRPC_CHTTP2_PROTOCOL_ERROR, &stream_parsing->stats.outgoing)); return init_skip_frame_parser(exec_ctx, transport_parsing, 0); } else { return err; } }
void grpc_chttp2_publish_reads( grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_parsing *transport_parsing) { grpc_chttp2_stream_global *stream_global; grpc_chttp2_stream_parsing *stream_parsing; int was_zero; int is_zero; /* transport_parsing->last_incoming_stream_id is used as last-grpc_chttp2_stream-id when sending GOAWAY frame. https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-6.8 says that last-grpc_chttp2_stream-id is peer-initiated grpc_chttp2_stream ID. So, since we don't have server pushed streams, client should send GOAWAY last-grpc_chttp2_stream-id=0 in this case. */ if (!transport_parsing->is_client) { transport_global->last_incoming_stream_id = transport_parsing->last_incoming_stream_id; } /* update global settings */ if (transport_parsing->settings_updated) { memcpy(transport_global->settings[GRPC_PEER_SETTINGS], transport_parsing->settings, sizeof(transport_parsing->settings)); transport_parsing->settings_updated = 0; } /* update settings based on ack if received */ if (transport_parsing->settings_ack_received) { memcpy(transport_global->settings[GRPC_ACKED_SETTINGS], transport_global->settings[GRPC_SENT_SETTINGS], GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); transport_parsing->settings_ack_received = 0; transport_global->sent_local_settings = 0; } /* move goaway to the global state if we received one (it will be published later */ if (transport_parsing->goaway_received) { grpc_chttp2_add_incoming_goaway(exec_ctx, transport_global, (uint32_t)transport_parsing->goaway_error, transport_parsing->goaway_text); transport_parsing->goaway_text = gpr_empty_slice(); transport_parsing->goaway_received = 0; } /* propagate flow control tokens to global state */ was_zero = transport_global->outgoing_window <= 0; GRPC_CHTTP2_FLOW_MOVE_TRANSPORT("parsed", transport_global, outgoing_window, transport_parsing, outgoing_window); is_zero = transport_global->outgoing_window <= 0; if (was_zero && !is_zero) { while (grpc_chttp2_list_pop_stalled_by_transport(transport_global, &stream_global)) { grpc_chttp2_become_writable(transport_global, stream_global); } } if (transport_parsing->incoming_window < transport_global->connection_window_target * 3 / 4) { int64_t announce_bytes = transport_global->connection_window_target - transport_parsing->incoming_window; GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parsed", transport_global, announce_incoming_window, announce_bytes); GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parsed", transport_parsing, incoming_window, announce_bytes); } /* for each stream that saw an update, fixup global state */ while (grpc_chttp2_list_pop_parsing_seen_stream( transport_global, transport_parsing, &stream_global, &stream_parsing)) { if (stream_parsing->seen_error) { stream_global->seen_error = true; stream_global->exceeded_metadata_size = stream_parsing->exceeded_metadata_size; grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); } /* flush stats to global stream state */ grpc_transport_move_stats(&stream_parsing->stats, &stream_global->stats); /* update outgoing flow control window */ was_zero = stream_global->outgoing_window <= 0; GRPC_CHTTP2_FLOW_MOVE_STREAM("parsed", transport_global, stream_global, outgoing_window, stream_parsing, outgoing_window); is_zero = stream_global->outgoing_window <= 0; if (was_zero && !is_zero) { grpc_chttp2_become_writable(transport_global, stream_global); } stream_global->max_recv_bytes -= (uint32_t)GPR_MIN( stream_global->max_recv_bytes, stream_parsing->received_bytes); stream_parsing->received_bytes = 0; /* publish incoming stream ops */ if (stream_global->incoming_frames.tail != NULL) { stream_global->incoming_frames.tail->is_tail = 0; } if (stream_parsing->data_parser.incoming_frames.head != NULL) { grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); } grpc_chttp2_incoming_frame_queue_merge( &stream_global->incoming_frames, &stream_parsing->data_parser.incoming_frames); if (stream_global->incoming_frames.tail != NULL) { stream_global->incoming_frames.tail->is_tail = 1; } if (!stream_global->published_initial_metadata && stream_parsing->got_metadata_on_parse[0]) { stream_parsing->got_metadata_on_parse[0] = 0; stream_global->published_initial_metadata = 1; GPR_SWAP(grpc_chttp2_incoming_metadata_buffer, stream_parsing->metadata_buffer[0], stream_global->received_initial_metadata); grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); } if (!stream_global->published_trailing_metadata && stream_parsing->got_metadata_on_parse[1]) { stream_parsing->got_metadata_on_parse[1] = 0; stream_global->published_trailing_metadata = 1; GPR_SWAP(grpc_chttp2_incoming_metadata_buffer, stream_parsing->metadata_buffer[1], stream_global->received_trailing_metadata); grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); } if (stream_parsing->forced_close_error != GRPC_ERROR_NONE) { intptr_t reason; bool has_reason = grpc_error_get_int(stream_parsing->forced_close_error, GRPC_ERROR_INT_HTTP2_ERROR, &reason); if (has_reason && reason != GRPC_CHTTP2_NO_ERROR) { grpc_status_code status_code = has_reason ? grpc_chttp2_http2_error_to_grpc_status( (grpc_chttp2_error_code)reason, stream_global->deadline) : GRPC_STATUS_INTERNAL; const char *status_details = grpc_error_string(stream_parsing->forced_close_error); gpr_slice slice_details = gpr_slice_from_copied_string(status_details); grpc_error_free_string(status_details); grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, status_code, &slice_details); } grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1, 1, stream_parsing->forced_close_error); } if (stream_parsing->received_close) { grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1, 0, GRPC_ERROR_NONE); } } }