static void maybe_start_some_streams( grpc_chttp2_transport_global *transport_global) { grpc_chttp2_stream_global *stream_global; /* start streams where we have free grpc_chttp2_stream ids and free * concurrency */ while (transport_global->next_stream_id <= MAX_CLIENT_STREAM_ID && transport_global->concurrent_stream_count < transport_global ->settings[GRPC_PEER_SETTINGS] [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] && grpc_chttp2_list_pop_waiting_for_concurrency(transport_global, &stream_global)) { GRPC_CHTTP2_IF_TRACING(gpr_log( GPR_DEBUG, "HTTP:%s: Allocating new grpc_chttp2_stream %p to id %d", transport_global->is_client ? "CLI" : "SVR", stream_global, transport_global->next_stream_id)); GPR_ASSERT(stream_global->id == 0); stream_global->id = transport_global->next_stream_id; transport_global->next_stream_id += 2; if (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID) { grpc_chttp2_add_incoming_goaway( transport_global, GRPC_CHTTP2_NO_ERROR, gpr_slice_from_copied_string("Exceeded sequence number limit")); } stream_global->outgoing_window = transport_global->settings[GRPC_PEER_SETTINGS] [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; stream_global->incoming_window = transport_global->settings[GRPC_SENT_SETTINGS] [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; grpc_chttp2_stream_map_add( &TRANSPORT_FROM_GLOBAL(transport_global)->new_stream_map, stream_global->id, STREAM_FROM_GLOBAL(stream_global)); stream_global->in_stream_map = 1; transport_global->concurrent_stream_count++; grpc_chttp2_list_add_incoming_window_updated(transport_global, stream_global); grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } /* cancel out streams that will never be started */ while (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID && grpc_chttp2_list_pop_waiting_for_concurrency(transport_global, &stream_global)) { cancel_from_api(transport_global, stream_global, GRPC_STATUS_UNAVAILABLE); } }
void grpc_chttp2_publish_reads( grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_parsing *transport_parsing) { grpc_chttp2_stream_global *stream_global; grpc_chttp2_stream_parsing *stream_parsing; /* 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->incoming_stream_id; } /* copy parsing qbuf to global qbuf */ gpr_slice_buffer_move_into(&transport_parsing->qbuf, &transport_global->qbuf); /* 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(gpr_uint32)); transport_parsing->settings_ack_received = 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(transport_global, 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 */ if (transport_parsing->outgoing_window_update) { GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( "parsed", transport_global, outgoing_window, transport_parsing->outgoing_window_update); GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( "parsed", transport_parsing, outgoing_window_update, -(gpr_int64)transport_parsing->outgoing_window_update); transport_global->outgoing_window += transport_parsing->outgoing_window_update; transport_parsing->outgoing_window_update = 0; } if (transport_parsing->incoming_window_delta) { GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( "parsed", transport_global, incoming_window, -(gpr_int64)transport_parsing->incoming_window_delta); GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( "parsed", transport_parsing, incoming_window_delta, -(gpr_int64)transport_parsing->incoming_window_delta); transport_global->incoming_window -= transport_parsing->incoming_window_delta; transport_parsing->incoming_window_delta = 0; } /* 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)) { /* update incoming flow control window */ if (stream_parsing->incoming_window_delta) { GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "parsed", transport_parsing, stream_global, incoming_window, -(gpr_int64)stream_parsing->incoming_window_delta); GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "parsed", transport_parsing, stream_parsing, incoming_window_delta, -(gpr_int64)stream_parsing->incoming_window_delta); GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "parsed", transport_parsing, stream_global, max_recv_bytes, -(gpr_int64)stream_parsing->incoming_window_delta); stream_global->incoming_window -= stream_parsing->incoming_window_delta; GPR_ASSERT(stream_global->max_recv_bytes >= stream_parsing->incoming_window_delta); stream_global->max_recv_bytes -= stream_parsing->incoming_window_delta; stream_parsing->incoming_window_delta = 0; grpc_chttp2_list_add_writable_window_update_stream(transport_global, stream_global); } /* update outgoing flow control window */ if (stream_parsing->outgoing_window_update) { int was_zero = stream_global->outgoing_window <= 0; int is_zero; GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("parsed", transport_parsing, stream_global, outgoing_window, stream_parsing->outgoing_window_update); GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "parsed", transport_parsing, stream_parsing, outgoing_window_update, -(gpr_int64)stream_parsing->outgoing_window_update); stream_global->outgoing_window += stream_parsing->outgoing_window_update; stream_parsing->outgoing_window_update = 0; is_zero = stream_global->outgoing_window <= 0; if (was_zero && !is_zero) { grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } } /* updating closed status */ if (stream_parsing->received_close) { stream_global->read_closed = 1; grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } if (stream_parsing->saw_rst_stream) { stream_global->cancelled = 1; stream_global->cancelled_status = grpc_chttp2_http2_error_to_grpc_status( stream_parsing->rst_stream_reason); if (stream_parsing->rst_stream_reason == GRPC_CHTTP2_NO_ERROR) { stream_global->published_cancelled = 1; } grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } /* publish incoming stream ops */ if (stream_parsing->data_parser.incoming_sopb.nops > 0) { grpc_incoming_metadata_buffer_move_to_referencing_sopb( &stream_parsing->incoming_metadata, &stream_global->incoming_metadata, &stream_parsing->data_parser.incoming_sopb); grpc_sopb_move_to(&stream_parsing->data_parser.incoming_sopb, &stream_global->incoming_sopb); grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } } }
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); } } }
grpc_error *grpc_chttp2_goaway_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, grpc_chttp2_transport *t, grpc_chttp2_stream *s, gpr_slice slice, int is_last) { uint8_t *const beg = GPR_SLICE_START_PTR(slice); uint8_t *const end = GPR_SLICE_END_PTR(slice); uint8_t *cur = beg; grpc_chttp2_goaway_parser *p = parser; switch (p->state) { case GRPC_CHTTP2_GOAWAY_LSI0: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_LSI0; return GRPC_ERROR_NONE; } p->last_stream_id = ((uint32_t)*cur) << 24; ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_LSI1: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_LSI1; return GRPC_ERROR_NONE; } p->last_stream_id |= ((uint32_t)*cur) << 16; ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_LSI2: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_LSI2; return GRPC_ERROR_NONE; } p->last_stream_id |= ((uint32_t)*cur) << 8; ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_LSI3: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_LSI3; return GRPC_ERROR_NONE; } p->last_stream_id |= ((uint32_t)*cur); ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_ERR0: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_ERR0; return GRPC_ERROR_NONE; } p->error_code = ((uint32_t)*cur) << 24; ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_ERR1: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_ERR1; return GRPC_ERROR_NONE; } p->error_code |= ((uint32_t)*cur) << 16; ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_ERR2: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_ERR2; return GRPC_ERROR_NONE; } p->error_code |= ((uint32_t)*cur) << 8; ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_ERR3: if (cur == end) { p->state = GRPC_CHTTP2_GOAWAY_ERR3; return GRPC_ERROR_NONE; } p->error_code |= ((uint32_t)*cur); ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_DEBUG: if (end != cur) memcpy(p->debug_data + p->debug_pos, cur, (size_t)(end - cur)); GPR_ASSERT((size_t)(end - cur) < UINT32_MAX - p->debug_pos); p->debug_pos += (uint32_t)(end - cur); p->state = GRPC_CHTTP2_GOAWAY_DEBUG; if (is_last) { grpc_chttp2_add_incoming_goaway( exec_ctx, t, (uint32_t)p->error_code, gpr_slice_new(p->debug_data, p->debug_length, gpr_free)); p->debug_data = NULL; } return GRPC_ERROR_NONE; } GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); }