void grpc_chttp2_cleanup_writing( grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_writing *transport_writing) { grpc_chttp2_stream_writing *stream_writing; grpc_chttp2_stream_global *stream_global; while (grpc_chttp2_list_pop_written_stream( transport_global, transport_writing, &stream_global, &stream_writing)) { GPR_ASSERT(stream_global->writing_now != 0); if (stream_writing->send_closed != GRPC_DONT_SEND_CLOSED) { stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; if (!transport_global->is_client) { stream_global->read_closed = 1; } } if (stream_global->writing_now & GRPC_CHTTP2_WRITING_DATA) { if (stream_global->outgoing_sopb != NULL && stream_global->outgoing_sopb->nops == 0) { GPR_ASSERT(stream_global->write_state != GRPC_WRITE_STATE_QUEUED_CLOSE); stream_global->outgoing_sopb = NULL; grpc_chttp2_schedule_closure(transport_global, stream_global->send_done_closure, 1); } } stream_global->writing_now = 0; grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } transport_writing->outbuf.count = 0; transport_writing->outbuf.length = 0; }
void grpc_chttp2_cleanup_writing( grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_writing *transport_writing) { grpc_chttp2_stream_writing *stream_writing; grpc_chttp2_stream_global *stream_global; while (grpc_chttp2_list_pop_written_stream( transport_global, transport_writing, &stream_global, &stream_writing)) { GPR_ASSERT(stream_global->writing_now != 0); if (stream_writing->send_closed != GRPC_DONT_SEND_CLOSED) { stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; if (!transport_global->is_client) { stream_global->read_closed = 1; } } if (stream_global->writing_now & GRPC_CHTTP2_WRITING_DATA) { if (stream_global->outgoing_sopb != NULL && stream_global->outgoing_sopb->nops == 0) { GPR_ASSERT(stream_global->write_state != GRPC_WRITE_STATE_QUEUED_CLOSE); stream_global->outgoing_sopb = NULL; grpc_exec_ctx_enqueue(exec_ctx, stream_global->send_done_closure, 1); } } stream_global->writing_now = 0; grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } gpr_slice_buffer_reset_and_unref(&transport_writing->outbuf); }
static void perform_op_locked(grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global, grpc_transport_op *op) { if (op->cancel_with_status != GRPC_STATUS_OK) { cancel_from_api(transport_global, stream_global, op->cancel_with_status); } if (op->send_ops) { GPR_ASSERT(stream_global->outgoing_sopb == NULL); stream_global->send_done_closure = op->on_done_send; if (!stream_global->cancelled) { stream_global->outgoing_sopb = op->send_ops; if (op->is_last_send && stream_global->write_state == GRPC_WRITE_STATE_OPEN) { stream_global->write_state = GRPC_WRITE_STATE_QUEUED_CLOSE; } if (stream_global->id == 0) { GRPC_CHTTP2_IF_TRACING(gpr_log( GPR_DEBUG, "HTTP:%s: New grpc_chttp2_stream %p waiting for concurrency", transport_global->is_client ? "CLI" : "SVR", stream_global)); grpc_chttp2_list_add_waiting_for_concurrency(transport_global, stream_global); maybe_start_some_streams(transport_global); } else if (stream_global->outgoing_window > 0) { grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } } else { grpc_sopb_reset(op->send_ops); grpc_chttp2_schedule_closure(transport_global, stream_global->send_done_closure, 0); } } if (op->recv_ops) { GPR_ASSERT(stream_global->publish_sopb == NULL); GPR_ASSERT(stream_global->published_state != GRPC_STREAM_CLOSED); stream_global->recv_done_closure = op->on_done_recv; stream_global->publish_sopb = op->recv_ops; stream_global->publish_sopb->nops = 0; stream_global->publish_state = op->recv_state; grpc_chttp2_incoming_metadata_live_op_buffer_end( &stream_global->outstanding_metadata); grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); grpc_chttp2_list_add_writable_window_update_stream(transport_global, stream_global); } if (op->bind_pollset) { add_to_pollset_locked(TRANSPORT_FROM_GLOBAL(transport_global), op->bind_pollset); } if (op->on_consumed) { grpc_chttp2_schedule_closure(transport_global, op->on_consumed, 1); } }
static void cancel_from_api(grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global, grpc_status_code status) { stream_global->cancelled = 1; stream_global->cancelled_status = status; if (stream_global->id != 0) { gpr_slice_buffer_add(&transport_global->qbuf, grpc_chttp2_rst_stream_create( stream_global->id, grpc_chttp2_grpc_status_to_http2_error(status))); } grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); }
static void unlock_check_read_write_state(grpc_chttp2_transport *t) { grpc_chttp2_transport_global *transport_global = &t->global; grpc_chttp2_stream_global *stream_global; grpc_stream_state state; if (!t->parsing_active) { /* if a stream is in the stream map, and gets cancelled, we need to ensure we are not parsing before continuing the cancellation to keep things in a sane state */ while (grpc_chttp2_list_pop_closed_waiting_for_parsing(transport_global, &stream_global)) { GPR_ASSERT(stream_global->in_stream_map); GPR_ASSERT(stream_global->write_state != GRPC_WRITE_STATE_OPEN); GPR_ASSERT(stream_global->read_closed); remove_stream(t, stream_global->id); grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } } while (grpc_chttp2_list_pop_read_write_state_changed(transport_global, &stream_global)) { if (stream_global->cancelled) { stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; stream_global->read_closed = 1; if (!stream_global->published_cancelled) { char buffer[GPR_LTOA_MIN_BUFSIZE]; gpr_ltoa(stream_global->cancelled_status, buffer); grpc_chttp2_incoming_metadata_buffer_add(&stream_global->incoming_metadata, grpc_mdelem_from_strings(t->metadata_context, "grpc-status", buffer)); grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( &stream_global->incoming_metadata, &stream_global->incoming_sopb); stream_global->published_cancelled = 1; } } if (stream_global->write_state == GRPC_WRITE_STATE_SENT_CLOSE && stream_global->read_closed && stream_global->in_stream_map) { if (t->parsing_active) { grpc_chttp2_list_add_closed_waiting_for_parsing(transport_global, stream_global); } else { remove_stream(t, stream_global->id); } } if (!stream_global->publish_sopb) { continue; } /* FIXME(ctiller): we include in_stream_map in our computation of whether the stream is write-closed. This is completely bogus, but has the effect of delaying stream-closed until the stream is indeed evicted from the stream map, making it safe to delete. To fix this will require having an edge after stream-closed indicating that the stream is closed AND safe to delete. */ state = compute_state( stream_global->write_state == GRPC_WRITE_STATE_SENT_CLOSE && !stream_global->in_stream_map, stream_global->read_closed); if (stream_global->incoming_sopb.nops == 0 && state == stream_global->published_state) { continue; } grpc_chttp2_incoming_metadata_buffer_postprocess_sopb_and_begin_live_op( &stream_global->incoming_metadata, &stream_global->incoming_sopb, &stream_global->outstanding_metadata); grpc_sopb_swap(stream_global->publish_sopb, &stream_global->incoming_sopb); stream_global->published_state = *stream_global->publish_state = state; grpc_chttp2_schedule_closure(transport_global, stream_global->recv_done_closure, 1); stream_global->recv_done_closure = NULL; stream_global->publish_sopb = NULL; stream_global->publish_state = NULL; } }
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); } } }
static void close_from_api(grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global, grpc_status_code status, gpr_slice *optional_message) { gpr_slice hdr; gpr_slice status_hdr; gpr_slice message_pfx; gpr_uint8 *p; gpr_uint32 len = 0; GPR_ASSERT(status >= 0 && (int)status < 100); stream_global->cancelled = 1; stream_global->cancelled_status = status; GPR_ASSERT(stream_global->id != 0); GPR_ASSERT(!stream_global->written_anything); /* Hand roll a header block. This is unnecessarily ugly - at some point we should find a more elegant solution. It's complicated by the fact that our send machinery would be dead by the 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)); p = GPR_SLICE_START_PTR(status_hdr); *p++ = 0x40; /* literal header */ *p++ = 11; /* len(grpc-status) */ *p++ = 'g'; *p++ = 'r'; *p++ = 'p'; *p++ = 'c'; *p++ = '-'; *p++ = 's'; *p++ = 't'; *p++ = 'a'; *p++ = 't'; *p++ = 'u'; *p++ = 's'; if (status < 10) { *p++ = 1; *p++ = (gpr_uint8)('0' + status); } else { *p++ = 2; *p++ = (gpr_uint8)('0' + (status / 10)); *p++ = (gpr_uint8)('0' + (status % 10)); } GPR_ASSERT(p == GPR_SLICE_END_PTR(status_hdr)); len += (gpr_uint32)GPR_SLICE_LENGTH(status_hdr); if (optional_message) { GPR_ASSERT(GPR_SLICE_LENGTH(*optional_message) < 127); message_pfx = gpr_slice_malloc(15); p = GPR_SLICE_START_PTR(message_pfx); *p++ = 0x40; *p++ = 12; /* len(grpc-message) */ *p++ = 'g'; *p++ = 'r'; *p++ = 'p'; *p++ = 'c'; *p++ = '-'; *p++ = 'm'; *p++ = 'e'; *p++ = 's'; *p++ = 's'; *p++ = 'a'; *p++ = 'g'; *p++ = 'e'; *p++ = (gpr_uint8)GPR_SLICE_LENGTH(*optional_message); GPR_ASSERT(p == GPR_SLICE_END_PTR(message_pfx)); len += (gpr_uint32)GPR_SLICE_LENGTH(message_pfx); len += (gpr_uint32)GPR_SLICE_LENGTH(*optional_message); } hdr = gpr_slice_malloc(9); p = GPR_SLICE_START_PTR(hdr); *p++ = (gpr_uint8)(len >> 16); *p++ = (gpr_uint8)(len >> 8); *p++ = (gpr_uint8)(len); *p++ = GRPC_CHTTP2_FRAME_HEADER; *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; *p++ = (gpr_uint8)(stream_global->id >> 24); *p++ = (gpr_uint8)(stream_global->id >> 16); *p++ = (gpr_uint8)(stream_global->id >> 8); *p++ = (gpr_uint8)(stream_global->id); GPR_ASSERT(p == GPR_SLICE_END_PTR(hdr)); gpr_slice_buffer_add(&transport_global->qbuf, hdr); gpr_slice_buffer_add(&transport_global->qbuf, status_hdr); 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_buffer_add( &transport_global->qbuf, grpc_chttp2_rst_stream_create(stream_global->id, GRPC_CHTTP2_NO_ERROR)); grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); }
static void perform_stream_op_locked( grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) { if (op->cancel_with_status != GRPC_STATUS_OK) { cancel_from_api(transport_global, stream_global, op->cancel_with_status); } if (op->close_with_status != GRPC_STATUS_OK) { close_from_api(transport_global, stream_global, op->close_with_status, op->optional_close_message); } if (op->send_ops) { GPR_ASSERT(stream_global->outgoing_sopb == NULL); stream_global->send_done_closure = op->on_done_send; if (!stream_global->cancelled) { stream_global->written_anything = 1; stream_global->outgoing_sopb = op->send_ops; if (op->is_last_send && stream_global->write_state == GRPC_WRITE_STATE_OPEN) { stream_global->write_state = GRPC_WRITE_STATE_QUEUED_CLOSE; } if (stream_global->id == 0) { GRPC_CHTTP2_IF_TRACING(gpr_log( GPR_DEBUG, "HTTP:%s: New grpc_chttp2_stream %p waiting for concurrency", transport_global->is_client ? "CLI" : "SVR", stream_global)); grpc_chttp2_list_add_waiting_for_concurrency(transport_global, stream_global); maybe_start_some_streams(exec_ctx, transport_global); } else if (stream_global->outgoing_window > 0) { grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } } else { grpc_sopb_reset(op->send_ops); grpc_exec_ctx_enqueue(exec_ctx, stream_global->send_done_closure, 0); } } if (op->recv_ops) { GPR_ASSERT(stream_global->publish_sopb == NULL); GPR_ASSERT(stream_global->published_state != GRPC_STREAM_CLOSED); stream_global->recv_done_closure = op->on_done_recv; stream_global->publish_sopb = op->recv_ops; stream_global->publish_sopb->nops = 0; stream_global->publish_state = op->recv_state; /* clamp max recv bytes */ op->max_recv_bytes = GPR_MIN(op->max_recv_bytes, GPR_UINT32_MAX); if (stream_global->max_recv_bytes < op->max_recv_bytes) { GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "op", transport_global, stream_global, max_recv_bytes, op->max_recv_bytes - stream_global->max_recv_bytes); GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "op", transport_global, stream_global, unannounced_incoming_window, op->max_recv_bytes - stream_global->max_recv_bytes); stream_global->unannounced_incoming_window += (gpr_uint32)op->max_recv_bytes - stream_global->max_recv_bytes; stream_global->max_recv_bytes = (gpr_uint32)op->max_recv_bytes; } grpc_chttp2_incoming_metadata_live_op_buffer_end( &stream_global->outstanding_metadata); grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); if (stream_global->id != 0) { grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } } if (op->bind_pollset) { add_to_pollset_locked(exec_ctx, TRANSPORT_FROM_GLOBAL(transport_global), op->bind_pollset); } grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1); }