|
|
|
@ -44,7 +44,9 @@ |
|
|
|
|
#include <grpc/support/string_util.h> |
|
|
|
|
#include <grpc/support/useful.h> |
|
|
|
|
|
|
|
|
|
#include "src/core/ext/transport/chttp2/transport/http2_errors.h" |
|
|
|
|
#include "src/core/ext/transport/chttp2/transport/internal.h" |
|
|
|
|
#include "src/core/ext/transport/chttp2/transport/status_conversion.h" |
|
|
|
|
#include "src/core/ext/transport/chttp2/transport/varint.h" |
|
|
|
|
#include "src/core/lib/channel/channel_args.h" |
|
|
|
|
#include "src/core/lib/http/parser.h" |
|
|
|
@ -53,10 +55,7 @@ |
|
|
|
|
#include "src/core/lib/slice/slice_internal.h" |
|
|
|
|
#include "src/core/lib/slice/slice_string_helpers.h" |
|
|
|
|
#include "src/core/lib/support/string.h" |
|
|
|
|
#include "src/core/lib/transport/error_utils.h" |
|
|
|
|
#include "src/core/lib/transport/http2_errors.h" |
|
|
|
|
#include "src/core/lib/transport/static_metadata.h" |
|
|
|
|
#include "src/core/lib/transport/status_conversion.h" |
|
|
|
|
#include "src/core/lib/transport/timeout_encoding.h" |
|
|
|
|
#include "src/core/lib/transport/transport_impl.h" |
|
|
|
|
|
|
|
|
@ -410,7 +409,7 @@ static void close_transport_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_error_add_child(t->close_transport_on_writes_finished, error); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (!grpc_error_has_clear_grpc_status(error)) { |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
@ -867,6 +866,7 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx, |
|
|
|
|
(int)(closure->next_data.scratch / CLOSURE_BARRIER_FIRST_REF_BIT), |
|
|
|
|
(int)(closure->next_data.scratch % CLOSURE_BARRIER_FIRST_REF_BIT), |
|
|
|
|
desc, errstr); |
|
|
|
|
grpc_error_free_string(errstr); |
|
|
|
|
} |
|
|
|
|
if (error != GRPC_ERROR_NONE) { |
|
|
|
|
if (closure->error_data.error == GRPC_ERROR_NONE) { |
|
|
|
@ -895,9 +895,12 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool contains_non_ok_status(grpc_metadata_batch *batch) { |
|
|
|
|
if (batch->idx.named.grpc_status != NULL) { |
|
|
|
|
return !grpc_mdelem_eq(batch->idx.named.grpc_status->md, |
|
|
|
|
GRPC_MDELEM_GRPC_STATUS_0); |
|
|
|
|
grpc_linked_mdelem *l; |
|
|
|
|
for (l = batch->list.head; l; l = l->next) { |
|
|
|
|
if (l->md->key == GRPC_MDSTR_GRPC_STATUS && |
|
|
|
|
l->md != GRPC_MDELEM_GRPC_STATUS_0) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
@ -977,12 +980,9 @@ static void log_metadata(const grpc_metadata_batch *md_batch, uint32_t id, |
|
|
|
|
bool is_client, bool is_initial) { |
|
|
|
|
for (grpc_linked_mdelem *md = md_batch->list.head; md != md_batch->list.tail; |
|
|
|
|
md = md->next) { |
|
|
|
|
char *key = grpc_slice_to_c_string(GRPC_MDKEY(md->md)); |
|
|
|
|
char *value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md)); |
|
|
|
|
gpr_log(GPR_INFO, "HTTP:%d:%s:%s: %s: %s", id, is_initial ? "HDR" : "TRL", |
|
|
|
|
is_client ? "CLI" : "SVR", key, value); |
|
|
|
|
gpr_free(key); |
|
|
|
|
gpr_free(value); |
|
|
|
|
is_client ? "CLI" : "SVR", grpc_mdstr_as_c_string(md->md->key), |
|
|
|
|
grpc_mdstr_as_c_string(md->md->value)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1025,7 +1025,11 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op->cancel_error != GRPC_ERROR_NONE) { |
|
|
|
|
grpc_chttp2_cancel_stream(exec_ctx, t, s, op->cancel_error); |
|
|
|
|
grpc_chttp2_cancel_stream(exec_ctx, t, s, GRPC_ERROR_REF(op->cancel_error)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op->close_error != GRPC_ERROR_NONE) { |
|
|
|
|
close_from_api(exec_ctx, t, s, GRPC_ERROR_REF(op->close_error)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op->send_initial_metadata != NULL) { |
|
|
|
@ -1076,9 +1080,8 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op, |
|
|
|
|
s->send_initial_metadata = NULL; |
|
|
|
|
grpc_chttp2_complete_closure_step( |
|
|
|
|
exec_ctx, t, s, &s->send_initial_metadata_finished, |
|
|
|
|
GRPC_ERROR_CREATE_REFERENCING( |
|
|
|
|
"Attempt to send initial metadata after stream was closed", |
|
|
|
|
&s->write_closed_error, 1), |
|
|
|
|
GRPC_ERROR_CREATE( |
|
|
|
|
"Attempt to send initial metadata after stream was closed"), |
|
|
|
|
"send_initial_metadata_finished"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1090,9 +1093,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op, |
|
|
|
|
if (s->write_closed) { |
|
|
|
|
grpc_chttp2_complete_closure_step( |
|
|
|
|
exec_ctx, t, s, &s->fetching_send_message_finished, |
|
|
|
|
GRPC_ERROR_CREATE_REFERENCING( |
|
|
|
|
"Attempt to send message after stream was closed", |
|
|
|
|
&s->write_closed_error, 1), |
|
|
|
|
GRPC_ERROR_CREATE("Attempt to send message after stream was closed"), |
|
|
|
|
"fetching_send_message_finished"); |
|
|
|
|
} else { |
|
|
|
|
GPR_ASSERT(s->fetching_send_message == NULL); |
|
|
|
@ -1264,16 +1265,11 @@ void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void send_goaway(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
grpc_error *error) { |
|
|
|
|
grpc_chttp2_error_code error, grpc_slice data) { |
|
|
|
|
t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED; |
|
|
|
|
grpc_http2_error_code http_error; |
|
|
|
|
const char *msg; |
|
|
|
|
grpc_error_get_status(error, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL, &msg, |
|
|
|
|
&http_error); |
|
|
|
|
grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)http_error, |
|
|
|
|
grpc_slice_from_copied_string(msg), &t->qbuf); |
|
|
|
|
grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)error, data, |
|
|
|
|
&t->qbuf); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, t, false, "goaway_sent"); |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
@ -1289,8 +1285,10 @@ static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
op->on_connectivity_state_change); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op->goaway_error) { |
|
|
|
|
send_goaway(exec_ctx, t, op->goaway_error); |
|
|
|
|
if (op->send_goaway) { |
|
|
|
|
send_goaway(exec_ctx, t, |
|
|
|
|
grpc_chttp2_grpc_status_to_http2_error(op->goaway_status), |
|
|
|
|
grpc_slice_ref_internal(*op->goaway_message)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op->set_accept_stream) { |
|
|
|
@ -1350,8 +1348,8 @@ void grpc_chttp2_maybe_complete_recv_initial_metadata(grpc_exec_ctx *exec_ctx, |
|
|
|
|
incoming_byte_stream_destroy_locked(exec_ctx, bs, GRPC_ERROR_NONE); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_publish( |
|
|
|
|
exec_ctx, &s->metadata_buffer[0], s->recv_initial_metadata); |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[0], |
|
|
|
|
s->recv_initial_metadata); |
|
|
|
|
null_then_run_closure(exec_ctx, &s->recv_initial_metadata_ready, |
|
|
|
|
GRPC_ERROR_NONE); |
|
|
|
|
} |
|
|
|
@ -1394,8 +1392,8 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} |
|
|
|
|
if (s->all_incoming_byte_streams_finished && |
|
|
|
|
s->recv_trailing_metadata_finished != NULL) { |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_publish( |
|
|
|
|
exec_ctx, &s->metadata_buffer[1], s->recv_trailing_metadata); |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[1], |
|
|
|
|
s->recv_trailing_metadata); |
|
|
|
|
grpc_chttp2_complete_closure_step( |
|
|
|
|
exec_ctx, t, s, &s->recv_trailing_metadata_finished, GRPC_ERROR_NONE, |
|
|
|
|
"recv_trailing_metadata_finished"); |
|
|
|
@ -1443,37 +1441,70 @@ static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
maybe_start_some_streams(exec_ctx, t); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void status_codes_from_error(grpc_error *error, gpr_timespec deadline, |
|
|
|
|
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, deadline); |
|
|
|
|
} else { |
|
|
|
|
*grpc_status = GRPC_STATUS_INTERNAL; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_chttp2_cancel_stream(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport *t, grpc_chttp2_stream *s, |
|
|
|
|
grpc_error *due_to_error) { |
|
|
|
|
if (!t->is_client && !s->sent_trailing_metadata && |
|
|
|
|
grpc_error_has_clear_grpc_status(due_to_error)) { |
|
|
|
|
close_from_api(exec_ctx, t, s, due_to_error); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!s->read_closed || !s->write_closed) { |
|
|
|
|
grpc_status_code grpc_status; |
|
|
|
|
grpc_chttp2_error_code http_error; |
|
|
|
|
status_codes_from_error(due_to_error, s->deadline, &http_error, |
|
|
|
|
&grpc_status); |
|
|
|
|
|
|
|
|
|
if (s->id != 0) { |
|
|
|
|
grpc_http2_error_code http_error; |
|
|
|
|
grpc_error_get_status(due_to_error, s->deadline, NULL, NULL, &http_error); |
|
|
|
|
grpc_slice_buffer_add( |
|
|
|
|
&t->qbuf, grpc_chttp2_rst_stream_create(s->id, (uint32_t)http_error, |
|
|
|
|
&s->stats.outgoing)); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, t, false, "rst_stream"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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_slice msg_slice = grpc_slice_from_copied_string(msg); |
|
|
|
|
grpc_chttp2_fake_status(exec_ctx, t, s, grpc_status, &msg_slice); |
|
|
|
|
if (free_msg) grpc_error_free_string(msg); |
|
|
|
|
} |
|
|
|
|
if (due_to_error != GRPC_ERROR_NONE && !s->seen_error) { |
|
|
|
|
s->seen_error = true; |
|
|
|
|
grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); |
|
|
|
|
} |
|
|
|
|
grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, due_to_error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
grpc_chttp2_stream *s, grpc_error *error) { |
|
|
|
|
grpc_status_code status; |
|
|
|
|
const char *msg; |
|
|
|
|
grpc_error_get_status(error, s->deadline, &status, &msg, NULL); |
|
|
|
|
|
|
|
|
|
grpc_chttp2_stream *s, grpc_status_code status, |
|
|
|
|
grpc_slice *slice) { |
|
|
|
|
if (status != GRPC_STATUS_OK) { |
|
|
|
|
s->seen_error = true; |
|
|
|
|
} |
|
|
|
@ -1487,21 +1518,24 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
s->recv_trailing_metadata_finished != NULL) { |
|
|
|
|
char status_string[GPR_LTOA_MIN_BUFSIZE]; |
|
|
|
|
gpr_ltoa(status, status_string); |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_replace_or_add( |
|
|
|
|
exec_ctx, &s->metadata_buffer[1], |
|
|
|
|
grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_STATUS, |
|
|
|
|
grpc_slice_from_copied_string(status_string))); |
|
|
|
|
if (msg != NULL) { |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_replace_or_add( |
|
|
|
|
exec_ctx, &s->metadata_buffer[1], |
|
|
|
|
grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_MESSAGE, |
|
|
|
|
grpc_slice_from_copied_string(msg))); |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_add( |
|
|
|
|
&s->metadata_buffer[1], grpc_mdelem_from_metadata_strings( |
|
|
|
|
exec_ctx, GRPC_MDSTR_GRPC_STATUS, |
|
|
|
|
grpc_mdstr_from_string(status_string))); |
|
|
|
|
if (slice) { |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_add( |
|
|
|
|
&s->metadata_buffer[1], |
|
|
|
|
grpc_mdelem_from_metadata_strings( |
|
|
|
|
exec_ctx, GRPC_MDSTR_GRPC_MESSAGE, |
|
|
|
|
grpc_mdstr_from_slice(exec_ctx, |
|
|
|
|
grpc_slice_ref_internal(*slice)))); |
|
|
|
|
} |
|
|
|
|
s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE; |
|
|
|
|
grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
if (slice) { |
|
|
|
|
grpc_slice_unref_internal(exec_ctx, *slice); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void add_error(grpc_error *error, grpc_error **refs, size_t *nrefs) { |
|
|
|
@ -1567,48 +1601,36 @@ void grpc_chttp2_mark_stream_closed(grpc_exec_ctx *exec_ctx, |
|
|
|
|
int close_writes, grpc_error *error) { |
|
|
|
|
if (s->read_closed && s->write_closed) { |
|
|
|
|
/* already closed */ |
|
|
|
|
grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
bool closed_read = false; |
|
|
|
|
bool became_closed = false; |
|
|
|
|
if (close_reads && !s->read_closed) { |
|
|
|
|
s->read_closed_error = GRPC_ERROR_REF(error); |
|
|
|
|
s->read_closed = true; |
|
|
|
|
closed_read = true; |
|
|
|
|
for (int i = 0; i < 2; i++) { |
|
|
|
|
if (s->published_metadata[i] == GRPC_METADATA_NOT_PUBLISHED) { |
|
|
|
|
s->published_metadata[i] = GPRC_METADATA_PUBLISHED_AT_CLOSE; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
decrement_active_streams_locked(exec_ctx, t, s); |
|
|
|
|
grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s); |
|
|
|
|
grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); |
|
|
|
|
grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); |
|
|
|
|
} |
|
|
|
|
if (close_writes && !s->write_closed) { |
|
|
|
|
s->write_closed_error = GRPC_ERROR_REF(error); |
|
|
|
|
s->write_closed = true; |
|
|
|
|
grpc_chttp2_fail_pending_writes(exec_ctx, t, s, GRPC_ERROR_REF(error)); |
|
|
|
|
grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); |
|
|
|
|
} |
|
|
|
|
if (s->read_closed && s->write_closed) { |
|
|
|
|
became_closed = true; |
|
|
|
|
grpc_error *overall_error = |
|
|
|
|
removal_error(GRPC_ERROR_REF(error), s, "Stream removed"); |
|
|
|
|
if (s->id != 0) { |
|
|
|
|
remove_stream(exec_ctx, t, s->id, GRPC_ERROR_REF(overall_error)); |
|
|
|
|
remove_stream(exec_ctx, t, s->id, |
|
|
|
|
removal_error(GRPC_ERROR_REF(error), s, "Stream removed")); |
|
|
|
|
} else { |
|
|
|
|
/* Purge streams waiting on concurrency still waiting for id assignment */ |
|
|
|
|
grpc_chttp2_list_remove_waiting_for_concurrency(t, s); |
|
|
|
|
} |
|
|
|
|
if (overall_error != GRPC_ERROR_NONE) { |
|
|
|
|
grpc_chttp2_fake_status(exec_ctx, t, s, overall_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (closed_read) { |
|
|
|
|
for (int i = 0; i < 2; i++) { |
|
|
|
|
if (s->published_metadata[i] == GRPC_METADATA_NOT_PUBLISHED) { |
|
|
|
|
s->published_metadata[i] = GPRC_METADATA_PUBLISHED_AT_CLOSE; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
decrement_active_streams_locked(exec_ctx, t, s); |
|
|
|
|
grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s); |
|
|
|
|
grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); |
|
|
|
|
} |
|
|
|
|
if (became_closed) { |
|
|
|
|
grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); |
|
|
|
|
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2"); |
|
|
|
|
} |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
@ -1622,92 +1644,112 @@ static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
uint8_t *p; |
|
|
|
|
uint32_t len = 0; |
|
|
|
|
grpc_status_code grpc_status; |
|
|
|
|
const char *msg; |
|
|
|
|
grpc_error_get_status(error, s->deadline, &grpc_status, &msg, NULL); |
|
|
|
|
grpc_chttp2_error_code http_error; |
|
|
|
|
status_codes_from_error(error, s->deadline, &http_error, &grpc_status); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100); |
|
|
|
|
|
|
|
|
|
/* 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 = grpc_slice_malloc(15 + (grpc_status >= 10)); |
|
|
|
|
p = GRPC_SLICE_START_PTR(status_hdr); |
|
|
|
|
*p++ = 0x00; /* literal header, not indexed */ |
|
|
|
|
*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 (grpc_status < 10) { |
|
|
|
|
*p++ = 1; |
|
|
|
|
*p++ = (uint8_t)('0' + grpc_status); |
|
|
|
|
} else { |
|
|
|
|
*p++ = 2; |
|
|
|
|
*p++ = (uint8_t)('0' + (grpc_status / 10)); |
|
|
|
|
*p++ = (uint8_t)('0' + (grpc_status % 10)); |
|
|
|
|
} |
|
|
|
|
GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr)); |
|
|
|
|
len += (uint32_t)GRPC_SLICE_LENGTH(status_hdr); |
|
|
|
|
|
|
|
|
|
if (msg != NULL) { |
|
|
|
|
size_t msg_len = strlen(msg); |
|
|
|
|
GPR_ASSERT(msg_len <= UINT32_MAX); |
|
|
|
|
uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 0); |
|
|
|
|
message_pfx = grpc_slice_malloc(14 + msg_len_len); |
|
|
|
|
p = GRPC_SLICE_START_PTR(message_pfx); |
|
|
|
|
*p++ = 0x00; /* literal header, not indexed */ |
|
|
|
|
*p++ = 12; /* len(grpc-message) */ |
|
|
|
|
if (s->id != 0 && !t->is_client) { |
|
|
|
|
/* 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 = grpc_slice_malloc(15 + (grpc_status >= 10)); |
|
|
|
|
p = GRPC_SLICE_START_PTR(status_hdr); |
|
|
|
|
*p++ = 0x40; /* literal header */ |
|
|
|
|
*p++ = 11; /* len(grpc-status) */ |
|
|
|
|
*p++ = 'g'; |
|
|
|
|
*p++ = 'r'; |
|
|
|
|
*p++ = 'p'; |
|
|
|
|
*p++ = 'c'; |
|
|
|
|
*p++ = '-'; |
|
|
|
|
*p++ = 'm'; |
|
|
|
|
*p++ = 'e'; |
|
|
|
|
*p++ = 's'; |
|
|
|
|
*p++ = 's'; |
|
|
|
|
*p++ = 't'; |
|
|
|
|
*p++ = 'a'; |
|
|
|
|
*p++ = 'g'; |
|
|
|
|
*p++ = 'e'; |
|
|
|
|
GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 0, 0, p, (uint32_t)msg_len_len); |
|
|
|
|
p += msg_len_len; |
|
|
|
|
GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx)); |
|
|
|
|
len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx); |
|
|
|
|
len += (uint32_t)msg_len; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hdr = grpc_slice_malloc(9); |
|
|
|
|
p = GRPC_SLICE_START_PTR(hdr); |
|
|
|
|
*p++ = (uint8_t)(len >> 16); |
|
|
|
|
*p++ = (uint8_t)(len >> 8); |
|
|
|
|
*p++ = (uint8_t)(len); |
|
|
|
|
*p++ = GRPC_CHTTP2_FRAME_HEADER; |
|
|
|
|
*p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; |
|
|
|
|
*p++ = (uint8_t)(s->id >> 24); |
|
|
|
|
*p++ = (uint8_t)(s->id >> 16); |
|
|
|
|
*p++ = (uint8_t)(s->id >> 8); |
|
|
|
|
*p++ = (uint8_t)(s->id); |
|
|
|
|
GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr)); |
|
|
|
|
|
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, hdr); |
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, status_hdr); |
|
|
|
|
if (msg != NULL) { |
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, message_pfx); |
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, grpc_slice_from_copied_string(msg)); |
|
|
|
|
} |
|
|
|
|
grpc_slice_buffer_add( |
|
|
|
|
&t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR, |
|
|
|
|
&s->stats.outgoing)); |
|
|
|
|
*p++ = 't'; |
|
|
|
|
*p++ = 'u'; |
|
|
|
|
*p++ = 's'; |
|
|
|
|
if (grpc_status < 10) { |
|
|
|
|
*p++ = 1; |
|
|
|
|
*p++ = (uint8_t)('0' + grpc_status); |
|
|
|
|
} else { |
|
|
|
|
*p++ = 2; |
|
|
|
|
*p++ = (uint8_t)('0' + (grpc_status / 10)); |
|
|
|
|
*p++ = (uint8_t)('0' + (grpc_status % 10)); |
|
|
|
|
} |
|
|
|
|
GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr)); |
|
|
|
|
len += (uint32_t)GRPC_SLICE_LENGTH(status_hdr); |
|
|
|
|
|
|
|
|
|
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 <= UINT32_MAX); |
|
|
|
|
uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 0); |
|
|
|
|
message_pfx = grpc_slice_malloc(14 + msg_len_len); |
|
|
|
|
p = GRPC_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'; |
|
|
|
|
GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 0, 0, p, |
|
|
|
|
(uint32_t)msg_len_len); |
|
|
|
|
p += msg_len_len; |
|
|
|
|
GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx)); |
|
|
|
|
len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx); |
|
|
|
|
len += (uint32_t)msg_len; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hdr = grpc_slice_malloc(9); |
|
|
|
|
p = GRPC_SLICE_START_PTR(hdr); |
|
|
|
|
*p++ = (uint8_t)(len >> 16); |
|
|
|
|
*p++ = (uint8_t)(len >> 8); |
|
|
|
|
*p++ = (uint8_t)(len); |
|
|
|
|
*p++ = GRPC_CHTTP2_FRAME_HEADER; |
|
|
|
|
*p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; |
|
|
|
|
*p++ = (uint8_t)(s->id >> 24); |
|
|
|
|
*p++ = (uint8_t)(s->id >> 16); |
|
|
|
|
*p++ = (uint8_t)(s->id >> 8); |
|
|
|
|
*p++ = (uint8_t)(s->id); |
|
|
|
|
GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr)); |
|
|
|
|
|
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, hdr); |
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, status_hdr); |
|
|
|
|
if (optional_message) { |
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, message_pfx); |
|
|
|
|
grpc_slice_buffer_add(&t->qbuf, |
|
|
|
|
grpc_slice_from_copied_string(optional_message)); |
|
|
|
|
} |
|
|
|
|
grpc_slice_buffer_add( |
|
|
|
|
&t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_CHTTP2_NO_ERROR, |
|
|
|
|
&s->stats.outgoing)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
grpc_slice msg_slice = grpc_slice_from_copied_string(msg); |
|
|
|
|
grpc_chttp2_fake_status(exec_ctx, t, s, grpc_status, &msg_slice); |
|
|
|
|
if (free_msg) grpc_error_free_string(msg); |
|
|
|
|
|
|
|
|
|
grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, error); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, t, false, "close_from_api"); |
|
|
|
@ -1785,10 +1827,8 @@ static grpc_error *try_http_parsing(grpc_exec_ctx *exec_ctx, |
|
|
|
|
if (parse_error == GRPC_ERROR_NONE && |
|
|
|
|
(parse_error = grpc_http_parser_eof(&parser)) == GRPC_ERROR_NONE) { |
|
|
|
|
error = grpc_error_set_int( |
|
|
|
|
grpc_error_set_int( |
|
|
|
|
GRPC_ERROR_CREATE("Trying to connect an http1.x server"), |
|
|
|
|
GRPC_ERROR_INT_HTTP_STATUS, response.status), |
|
|
|
|
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); |
|
|
|
|
GRPC_ERROR_CREATE("Trying to connect an http1.x server"), |
|
|
|
|
GRPC_ERROR_INT_HTTP_STATUS, response.status); |
|
|
|
|
} |
|
|
|
|
GRPC_ERROR_UNREF(parse_error); |
|
|
|
|
|
|
|
|
@ -2049,8 +2089,6 @@ static void incoming_byte_stream_publish_error( |
|
|
|
|
grpc_closure_sched(exec_ctx, bs->on_next, GRPC_ERROR_REF(error)); |
|
|
|
|
bs->on_next = NULL; |
|
|
|
|
GRPC_ERROR_UNREF(bs->error); |
|
|
|
|
grpc_chttp2_cancel_stream(exec_ctx, bs->transport, bs->stream, |
|
|
|
|
GRPC_ERROR_REF(error)); |
|
|
|
|
bs->error = error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2159,10 +2197,8 @@ static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory", |
|
|
|
|
t->peer_string); |
|
|
|
|
} |
|
|
|
|
send_goaway(exec_ctx, t, |
|
|
|
|
grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"), |
|
|
|
|
GRPC_ERROR_INT_HTTP2_ERROR, |
|
|
|
|
GRPC_HTTP2_ENHANCE_YOUR_CALM)); |
|
|
|
|
send_goaway(exec_ctx, t, GRPC_CHTTP2_ENHANCE_YOUR_CALM, |
|
|
|
|
grpc_slice_from_static_string("Buffers full")); |
|
|
|
|
} else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace) { |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR |
|
|
|
@ -2191,7 +2227,7 @@ static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
grpc_chttp2_cancel_stream( |
|
|
|
|
exec_ctx, t, s, grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"), |
|
|
|
|
GRPC_ERROR_INT_HTTP2_ERROR, |
|
|
|
|
GRPC_HTTP2_ENHANCE_YOUR_CALM)); |
|
|
|
|
GRPC_CHTTP2_ENHANCE_YOUR_CALM)); |
|
|
|
|
if (n > 1) { |
|
|
|
|
/* Since we cancel one stream per destructive reclamation, if
|
|
|
|
|
there are more streams left, we can immediately post a new |
|
|
|
|