Encapsulate metadata aggregates (#27262)

* Encapsulate metadata aggregates

* Automated change: Fix sanity tests

* add const

* Automated change: Fix sanity tests

* add a comment about an awful api

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
reviewable/pr27347/r3
Craig Tiller 3 years ago committed by GitHub
parent 8d5b93eacc
commit fd233193ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      src/core/ext/filters/client_channel/client_channel.cc
  2. 6
      src/core/ext/filters/client_channel/health/health_check_client.cc
  3. 21
      src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
  4. 44
      src/core/ext/filters/client_channel/retry_filter.cc
  5. 4
      src/core/ext/filters/client_channel/subchannel.cc
  6. 2
      src/core/ext/filters/deadline/deadline_filter.cc
  7. 29
      src/core/ext/filters/fault_injection/fault_injection_filter.cc
  8. 56
      src/core/ext/filters/http/client/http_client_filter.cc
  9. 5
      src/core/ext/filters/http/client_authority_filter.cc
  10. 11
      src/core/ext/filters/http/message_compress/message_compress_filter.cc
  11. 2
      src/core/ext/filters/http/message_compress/message_decompress_filter.cc
  12. 78
      src/core/ext/filters/http/server/http_server_filter.cc
  13. 4
      src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc
  14. 26
      src/core/ext/transport/binder/transport/binder_transport.cc
  15. 44
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  16. 7
      src/core/ext/transport/chttp2/transport/hpack_parser.cc
  17. 18
      src/core/ext/transport/chttp2/transport/incoming_metadata.cc
  18. 6
      src/core/ext/transport/chttp2/transport/incoming_metadata.h
  19. 18
      src/core/ext/transport/chttp2/transport/writing.cc
  20. 54
      src/core/ext/transport/cronet/transport/cronet_transport.cc
  21. 30
      src/core/ext/transport/inproc/inproc_transport.cc
  22. 24
      src/core/lib/security/authorization/evaluate_args.cc
  23. 10
      src/core/lib/security/transport/client_auth_filter.cc
  24. 6
      src/core/lib/security/transport/server_auth_filter.cc
  25. 64
      src/core/lib/surface/call.cc
  26. 21
      src/core/lib/surface/server.cc
  27. 309
      src/core/lib/transport/metadata_batch.cc
  28. 275
      src/core/lib/transport/metadata_batch.h
  29. 17
      src/core/lib/transport/transport_op_string.cc
  30. 12
      src/cpp/ext/filters/census/client_filter.cc
  31. 17
      src/cpp/ext/filters/census/server_filter.cc
  32. 5
      test/core/end2end/tests/filter_status_code.cc
  33. 18
      test/core/transport/binder/binder_transport_test.cc
  34. 25
      test/core/transport/chttp2/hpack_encoder_test.cc
  35. 6
      test/cpp/microbenchmarks/bm_chttp2_hpack.cc
  36. 6
      test/cpp/microbenchmarks/bm_chttp2_transport.cc

@ -2502,14 +2502,14 @@ class ClientChannel::LoadBalancedCall::Metadata
std::vector<std::pair<std::string, std::string>> TestOnlyCopyToVector()
override {
std::vector<std::pair<std::string, std::string>> result;
for (grpc_linked_mdelem* entry = batch_->list.head; entry != nullptr;
entry = entry->next) {
if (batch_->idx.named.path != entry) {
result.push_back(std::make_pair(
std::string(StringViewFromSlice(GRPC_MDKEY(entry->md))),
std::string(StringViewFromSlice(GRPC_MDVALUE(entry->md)))));
(*batch_)->ForEach([&](grpc_mdelem md) {
auto key = std::string(StringViewFromSlice(GRPC_MDKEY(md)));
if (key != ":path") {
result.push_back(
std::make_pair(std::move(key),
std::string(StringViewFromSlice(GRPC_MDVALUE(md)))));
}
}
});
return result;
}
@ -2537,8 +2537,9 @@ class ClientChannel::LoadBalancedCall::LbCallState
const LoadBalancingPolicy::BackendMetricData* GetBackendMetricData()
override {
if (lb_call_->backend_metric_data_ == nullptr) {
grpc_linked_mdelem* md = lb_call_->recv_trailing_metadata_->idx.named
.x_endpoint_load_metrics_bin;
grpc_linked_mdelem* md = (*lb_call_->recv_trailing_metadata_)
->legacy_index()
->named.x_endpoint_load_metrics_bin;
if (md != nullptr) {
lb_call_->backend_metric_data_ =
ParseBackendMetricData(GRPC_MDVALUE(md->md), lb_call_->arena_);
@ -2918,7 +2919,8 @@ void ClientChannel::LoadBalancedCall::RecvTrailingMetadataReady(
StringViewFromSlice(message));
} else {
// Get status from headers.
const auto& fields = self->recv_trailing_metadata_->idx.named;
const auto& fields =
(*self->recv_trailing_metadata_)->legacy_index()->named;
GPR_ASSERT(fields.grpc_status != nullptr);
grpc_status_code code =
grpc_get_status_code_from_metadata(fields.grpc_status->md);

@ -558,9 +558,11 @@ void HealthCheckClient::CallState::RecvTrailingMetadataReady(
grpc_error_get_status(error, GRPC_MILLIS_INF_FUTURE, &status,
nullptr /* slice */, nullptr /* http_error */,
nullptr /* error_string */);
} else if (self->recv_trailing_metadata_.idx.named.grpc_status != nullptr) {
} else if ((*self->recv_trailing_metadata_)
.legacy_index()
->named.grpc_status != nullptr) {
status = grpc_get_status_code_from_metadata(
self->recv_trailing_metadata_.idx.named.grpc_status->md);
(*self->recv_trailing_metadata_).legacy_index()->named.grpc_status->md);
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
gpr_log(GPR_INFO,

@ -101,20 +101,15 @@ static void clr_start_transport_stream_op_batch(
// Handle send_initial_metadata.
if (batch->send_initial_metadata) {
// Grab client stats object from metadata.
grpc_linked_mdelem* client_stats_md =
batch->payload->send_initial_metadata.send_initial_metadata->list.head;
for (; client_stats_md != nullptr;
client_stats_md = client_stats_md->next) {
if (GRPC_SLICE_START_PTR(GRPC_MDKEY(client_stats_md->md)) ==
static_cast<const void*>(grpc_core::kGrpcLbClientStatsMetadataKey)) {
break;
}
}
if (client_stats_md != nullptr) {
auto client_stats_md =
(*batch->payload->send_initial_metadata.send_initial_metadata)
->Remove(grpc_slice_from_static_string(
grpc_core::kGrpcLbClientStatsMetadataKey));
if (client_stats_md.has_value()) {
grpc_core::GrpcLbClientStats* client_stats =
const_cast<grpc_core::GrpcLbClientStats*>(
reinterpret_cast<const grpc_core::GrpcLbClientStats*>(
GRPC_SLICE_START_PTR(GRPC_MDVALUE(client_stats_md->md))));
GRPC_SLICE_START_PTR(*client_stats_md)));
if (client_stats != nullptr) {
calld->client_stats.reset(client_stats);
// Intercept completion.
@ -123,10 +118,6 @@ static void clr_start_transport_stream_op_batch(
calld, grpc_schedule_on_exec_ctx);
batch->on_complete = &calld->on_complete_for_send;
}
// Remove metadata so it doesn't go out on the wire.
grpc_metadata_batch_remove(
batch->payload->send_initial_metadata.send_initial_metadata,
client_stats_md);
}
}
// Intercept completion of recv_initial_metadata.

@ -1565,11 +1565,12 @@ void GetCallStatus(grpc_millis deadline, grpc_metadata_batch* md_batch,
*is_lb_drop = true;
}
} else {
GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr);
*status =
grpc_get_status_code_from_metadata(md_batch->idx.named.grpc_status->md);
if (md_batch->idx.named.grpc_retry_pushback_ms != nullptr) {
*server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md;
GPR_ASSERT((*md_batch)->legacy_index()->named.grpc_status != nullptr);
*status = grpc_get_status_code_from_metadata(
(*md_batch)->legacy_index()->named.grpc_status->md);
if ((*md_batch)->legacy_index()->named.grpc_retry_pushback_ms != nullptr) {
*server_pushback_md =
&(*md_batch)->legacy_index()->named.grpc_retry_pushback_ms->md;
}
}
GRPC_ERROR_UNREF(error);
@ -1921,15 +1922,16 @@ void RetryFilter::CallData::CallAttempt::BatchData::
// If we've already completed one or more attempts, add the
// grpc-retry-attempts header.
call_attempt_->send_initial_metadata_storage_ =
static_cast<grpc_linked_mdelem*>(
calld->arena_->Alloc(sizeof(grpc_linked_mdelem) *
(calld->send_initial_metadata_.list.count +
(calld->num_attempts_completed_ > 0))));
static_cast<grpc_linked_mdelem*>(calld->arena_->Alloc(
sizeof(grpc_linked_mdelem) *
(calld->send_initial_metadata_->non_deadline_count() +
(calld->num_attempts_completed_ > 0))));
grpc_metadata_batch_copy(&calld->send_initial_metadata_,
&call_attempt_->send_initial_metadata_,
call_attempt_->send_initial_metadata_storage_);
if (GPR_UNLIKELY(call_attempt_->send_initial_metadata_.idx.named
.grpc_previous_rpc_attempts != nullptr)) {
if (GPR_UNLIKELY((*call_attempt_->send_initial_metadata_)
.legacy_index()
->named.grpc_previous_rpc_attempts != nullptr)) {
grpc_metadata_batch_remove(&call_attempt_->send_initial_metadata_,
GRPC_BATCH_GRPC_PREVIOUS_RPC_ATTEMPTS);
}
@ -1940,7 +1942,7 @@ void RetryFilter::CallData::CallAttempt::BatchData::
grpc_error_handle error = grpc_metadata_batch_add_tail(
&call_attempt_->send_initial_metadata_,
&call_attempt_->send_initial_metadata_storage_
[calld->send_initial_metadata_.list.count],
[calld->send_initial_metadata_->non_deadline_count()],
retry_md, GRPC_BATCH_GRPC_PREVIOUS_RPC_ATTEMPTS);
if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
gpr_log(GPR_ERROR, "error adding retry metadata: %s",
@ -1984,9 +1986,9 @@ void RetryFilter::CallData::CallAttempt::BatchData::
// the filters in the subchannel stack may modify this batch, and we don't
// want those modifications to be passed forward to subsequent attempts.
call_attempt_->send_trailing_metadata_storage_ =
static_cast<grpc_linked_mdelem*>(
calld->arena_->Alloc(sizeof(grpc_linked_mdelem) *
calld->send_trailing_metadata_.list.count));
static_cast<grpc_linked_mdelem*>(calld->arena_->Alloc(
sizeof(grpc_linked_mdelem) *
calld->send_trailing_metadata_->non_deadline_count()));
grpc_metadata_batch_copy(&calld->send_trailing_metadata_,
&call_attempt_->send_trailing_metadata_,
call_attempt_->send_trailing_metadata_storage_);
@ -2304,9 +2306,9 @@ void RetryFilter::CallData::MaybeCacheSendOpsForBatch(PendingBatch* pending) {
GPR_ASSERT(send_initial_metadata_storage_ == nullptr);
grpc_metadata_batch* send_initial_metadata =
batch->payload->send_initial_metadata.send_initial_metadata;
send_initial_metadata_storage_ =
static_cast<grpc_linked_mdelem*>(arena_->Alloc(
sizeof(grpc_linked_mdelem) * send_initial_metadata->list.count));
send_initial_metadata_storage_ = static_cast<grpc_linked_mdelem*>(
arena_->Alloc(sizeof(grpc_linked_mdelem) *
(*send_initial_metadata)->non_deadline_count()));
grpc_metadata_batch_copy(send_initial_metadata, &send_initial_metadata_,
send_initial_metadata_storage_);
send_initial_metadata_flags_ =
@ -2325,9 +2327,9 @@ void RetryFilter::CallData::MaybeCacheSendOpsForBatch(PendingBatch* pending) {
GPR_ASSERT(send_trailing_metadata_storage_ == nullptr);
grpc_metadata_batch* send_trailing_metadata =
batch->payload->send_trailing_metadata.send_trailing_metadata;
send_trailing_metadata_storage_ =
static_cast<grpc_linked_mdelem*>(arena_->Alloc(
sizeof(grpc_linked_mdelem) * send_trailing_metadata->list.count));
send_trailing_metadata_storage_ = static_cast<grpc_linked_mdelem*>(
arena_->Alloc(sizeof(grpc_linked_mdelem) *
(*send_trailing_metadata)->non_deadline_count()));
grpc_metadata_batch_copy(send_trailing_metadata, &send_trailing_metadata_,
send_trailing_metadata_storage_);
}

@ -255,9 +255,9 @@ void GetCallStatus(grpc_status_code* status, grpc_millis deadline,
if (error != GRPC_ERROR_NONE) {
grpc_error_get_status(error, deadline, status, nullptr, nullptr, nullptr);
} else {
if (md_batch->idx.named.grpc_status != nullptr) {
if ((*md_batch)->legacy_index()->named.grpc_status != nullptr) {
*status = grpc_get_status_code_from_metadata(
md_batch->idx.named.grpc_status->md);
(*md_batch)->legacy_index()->named.grpc_status->md);
} else {
*status = GRPC_STATUS_UNKNOWN;
}

@ -295,7 +295,7 @@ static void deadline_client_start_transport_stream_op_batch(
static void recv_initial_metadata_ready(void* arg, grpc_error_handle error) {
grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
server_call_data* calld = static_cast<server_call_data*>(elem->call_data);
start_timer_if_needed(elem, calld->recv_initial_metadata->deadline);
start_timer_if_needed(elem, (*calld->recv_initial_metadata)->deadline());
// Invoke the next callback.
grpc_core::Closure::Run(DEBUG_LOCATION,
calld->next_recv_initial_metadata_ready,

@ -46,27 +46,27 @@ static_assert(
std::is_trivially_destructible<std::atomic<uint32_t>>::value,
"the active fault counter needs to have a trivially destructible type");
inline int GetLinkedMetadatumValueInt(grpc_linked_mdelem* md) {
inline int GetMetadatumValueInt(grpc_mdelem md) {
int res;
if (absl::SimpleAtoi(StringViewFromSlice(GRPC_MDVALUE(md->md)), &res)) {
if (absl::SimpleAtoi(StringViewFromSlice(GRPC_MDVALUE(md)), &res)) {
return res;
} else {
return -1;
}
}
inline uint32_t GetLinkedMetadatumValueUnsignedInt(grpc_linked_mdelem* md) {
inline uint32_t GetMetadatumValueUnsignedInt(grpc_mdelem md) {
uint32_t res;
if (absl::SimpleAtoi(StringViewFromSlice(GRPC_MDVALUE(md->md)), &res)) {
if (absl::SimpleAtoi(StringViewFromSlice(GRPC_MDVALUE(md)), &res)) {
return res;
} else {
return -1;
}
}
inline int64_t GetLinkedMetadatumValueInt64(grpc_linked_mdelem* md) {
inline int64_t GetMetadatumValueInt64(grpc_mdelem md) {
int64_t res;
if (absl::SimpleAtoi(StringViewFromSlice(GRPC_MDVALUE(md->md)), &res)) {
if (absl::SimpleAtoi(StringViewFromSlice(GRPC_MDVALUE(md)), &res)) {
return res;
} else {
return -1;
@ -347,9 +347,8 @@ void CallData::DecideWhetherToInjectFaults(
*fi_policy_);
}
};
for (grpc_linked_mdelem* md = initial_metadata->list.head; md != nullptr;
md = md->next) {
absl::string_view key = StringViewFromSlice(GRPC_MDKEY(md->md));
(*initial_metadata)->ForEach([&](grpc_mdelem md) {
absl::string_view key = StringViewFromSlice(GRPC_MDKEY(md));
// Only perform string comparison if:
// 1. Needs to check this header;
// 2. The value is not been filled before.
@ -358,31 +357,31 @@ void CallData::DecideWhetherToInjectFaults(
copied_policy->abort_code == GRPC_STATUS_OK) &&
key == fi_policy_->abort_code_header) {
maybe_copy_policy_func();
grpc_status_code_from_int(GetLinkedMetadatumValueInt(md),
grpc_status_code_from_int(GetMetadatumValueInt(md),
&copied_policy->abort_code);
}
if (!fi_policy_->abort_percentage_header.empty() &&
key == fi_policy_->abort_percentage_header) {
maybe_copy_policy_func();
copied_policy->abort_percentage_numerator =
GPR_MIN(GetLinkedMetadatumValueUnsignedInt(md),
GPR_MIN(GetMetadatumValueUnsignedInt(md),
fi_policy_->abort_percentage_numerator);
}
if (!fi_policy_->delay_header.empty() &&
(copied_policy == nullptr || copied_policy->delay == 0) &&
key == fi_policy_->delay_header) {
maybe_copy_policy_func();
copied_policy->delay = static_cast<grpc_millis>(
GPR_MAX(GetLinkedMetadatumValueInt64(md), 0));
copied_policy->delay =
static_cast<grpc_millis>(GPR_MAX(GetMetadatumValueInt64(md), 0));
}
if (!fi_policy_->delay_percentage_header.empty() &&
key == fi_policy_->delay_percentage_header) {
maybe_copy_policy_func();
copied_policy->delay_percentage_numerator =
GPR_MIN(GetLinkedMetadatumValueUnsignedInt(md),
GPR_MIN(GetMetadatumValueUnsignedInt(md),
fi_policy_->delay_percentage_numerator);
}
}
});
if (copied_policy != nullptr) fi_policy_ = copied_policy;
}
// Roll the dice

@ -113,18 +113,18 @@ struct channel_data {
static grpc_error_handle client_filter_incoming_metadata(
grpc_metadata_batch* b) {
if (b->idx.named.status != nullptr) {
if ((*b)->legacy_index()->named.status != nullptr) {
/* If both gRPC status and HTTP status are provided in the response, we
* should prefer the gRPC status code, as mentioned in
* https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
*/
if (b->idx.named.grpc_status != nullptr ||
grpc_mdelem_static_value_eq(b->idx.named.status->md,
if ((*b)->legacy_index()->named.grpc_status != nullptr ||
grpc_mdelem_static_value_eq((*b)->legacy_index()->named.status->md,
GRPC_MDELEM_STATUS_200)) {
grpc_metadata_batch_remove(b, GRPC_BATCH_STATUS);
} else {
char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.status->md),
GPR_DUMP_ASCII);
char* val = grpc_dump_slice(
GRPC_MDVALUE((*b)->legacy_index()->named.status->md), GPR_DUMP_ASCII);
std::string msg =
absl::StrCat("Received http2 header with status: ", val);
grpc_error_handle e = grpc_error_set_str(
@ -142,29 +142,33 @@ static grpc_error_handle client_filter_incoming_metadata(
}
}
if (b->idx.named.grpc_message != nullptr) {
if ((*b)->legacy_index()->named.grpc_message != nullptr) {
grpc_slice pct_decoded_msg = grpc_core::PermissivePercentDecodeSlice(
GRPC_MDVALUE(b->idx.named.grpc_message->md));
if (grpc_slice_is_equivalent(pct_decoded_msg,
GRPC_MDVALUE(b->idx.named.grpc_message->md))) {
GRPC_MDVALUE((*b)->legacy_index()->named.grpc_message->md));
if (grpc_slice_is_equivalent(
pct_decoded_msg,
GRPC_MDVALUE((*b)->legacy_index()->named.grpc_message->md))) {
grpc_slice_unref_internal(pct_decoded_msg);
} else {
grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_decoded_msg);
grpc_metadata_batch_set_value((*b)->legacy_index()->named.grpc_message,
pct_decoded_msg);
}
}
if (b->idx.named.content_type != nullptr) {
if ((*b)->legacy_index()->named.content_type != nullptr) {
if (!grpc_mdelem_static_value_eq(
b->idx.named.content_type->md,
(*b)->legacy_index()->named.content_type->md,
GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
EXPECTED_CONTENT_TYPE,
EXPECTED_CONTENT_TYPE_LENGTH) &&
if (grpc_slice_buf_start_eq(
GRPC_MDVALUE((*b)->legacy_index()->named.content_type->md),
EXPECTED_CONTENT_TYPE, EXPECTED_CONTENT_TYPE_LENGTH) &&
(GRPC_SLICE_START_PTR(GRPC_MDVALUE(
b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
(*b)->legacy_index()
->named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
'+' ||
GRPC_SLICE_START_PTR(GRPC_MDVALUE(
b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
(*b)->legacy_index()
->named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
';')) {
/* Although the C implementation doesn't (currently) generate them,
any custom +-suffix is explicitly valid. */
@ -174,8 +178,9 @@ static grpc_error_handle client_filter_incoming_metadata(
} else {
/* TODO(klempner): We're currently allowing this, but we shouldn't
see it without a proxy so log for now. */
char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md),
GPR_DUMP_ASCII);
char* val = grpc_dump_slice(
GRPC_MDVALUE((*b)->legacy_index()->named.content_type->md),
GPR_DUMP_ASCII);
gpr_log(GPR_INFO, "Unexpected content-type '%s'", val);
gpr_free(val);
}
@ -315,9 +320,10 @@ static char* slice_buffer_to_string(grpc_slice_buffer* slice_buffer) {
static grpc_error_handle update_path_for_get(
grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
call_data* calld = static_cast<call_data*>(elem->call_data);
grpc_slice path_slice =
GRPC_MDVALUE(batch->payload->send_initial_metadata.send_initial_metadata
->idx.named.path->md);
grpc_slice path_slice = GRPC_MDVALUE(
(*batch->payload->send_initial_metadata.send_initial_metadata)
->legacy_index()
->named.path->md);
/* sum up individual component's lengths and allocate enough memory to
* hold combined path+query */
size_t estimated_len = GRPC_SLICE_LENGTH(path_slice);
@ -350,15 +356,13 @@ static grpc_error_handle update_path_for_get(
grpc_mdelem_from_slices(GRPC_MDSTR_PATH, path_with_query_slice);
grpc_metadata_batch* b =
batch->payload->send_initial_metadata.send_initial_metadata;
return grpc_metadata_batch_substitute(b, b->idx.named.path,
return grpc_metadata_batch_substitute(b, (*b)->legacy_index()->named.path,
mdelem_path_and_query);
}
static void remove_if_present(grpc_metadata_batch* batch,
grpc_metadata_batch_callouts_index idx) {
if (batch->idx.array[idx] != nullptr) {
grpc_metadata_batch_remove(batch, idx);
}
grpc_metadata_batch_remove(batch, idx);
}
static void http_client_start_transport_stream_op_batch(

@ -56,8 +56,9 @@ void client_authority_start_transport_stream_op_batch(
// Handle send_initial_metadata.
// If the initial metadata doesn't already contain :authority, add it.
if (batch->send_initial_metadata &&
batch->payload->send_initial_metadata.send_initial_metadata->idx.named
.authority == nullptr) {
(*batch->payload->send_initial_metadata.send_initial_metadata)
->legacy_index()
->named.authority == nullptr) {
grpc_error_handle error = grpc_metadata_batch_add_head(
batch->payload->send_initial_metadata.send_initial_metadata,
&calld->authority_storage,

@ -191,13 +191,16 @@ bool CallData::SkipMessageCompression() {
// channel's default setting.
grpc_compression_algorithm FindCompressionAlgorithm(
grpc_metadata_batch* initial_metadata, ChannelData* channeld) {
if (initial_metadata->idx.named.grpc_internal_encoding_request == nullptr) {
if ((*initial_metadata)
->legacy_index()
->named.grpc_internal_encoding_request == nullptr) {
return channeld->default_compression_algorithm();
}
grpc_compression_algorithm compression_algorithm;
// Parse the compression algorithm from the initial metadata.
grpc_mdelem md =
initial_metadata->idx.named.grpc_internal_encoding_request->md;
grpc_mdelem md = (*initial_metadata)
->legacy_index()
->named.grpc_internal_encoding_request->md;
GPR_ASSERT(grpc_compression_algorithm_parse(GRPC_MDVALUE(md),
&compression_algorithm));
// Remove this metadata since it's an internal one (i.e., it won't be
@ -272,7 +275,7 @@ grpc_error_handle CallData::ProcessSendInitialMetadata(
if (error != GRPC_ERROR_NONE) return error;
// Do not overwrite accept-encoding header if it already presents (e.g., added
// by some proxy).
if (!initial_metadata->idx.named.accept_encoding) {
if (!(*initial_metadata)->legacy_index()->named.accept_encoding) {
error = grpc_metadata_batch_add_tail(
initial_metadata, &accept_stream_encoding_storage_,
GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(

@ -153,7 +153,7 @@ void CallData::OnRecvInitialMetadataReady(void* arg, grpc_error_handle error) {
CallData* calld = static_cast<CallData*>(arg);
if (error == GRPC_ERROR_NONE) {
grpc_linked_mdelem* grpc_encoding =
calld->recv_initial_metadata_->idx.named.grpc_encoding;
(*calld->recv_initial_metadata_)->legacy_index()->named.grpc_encoding;
if (grpc_encoding != nullptr) {
calld->algorithm_ = DecodeMessageCompressionAlgorithm(grpc_encoding->md);
}

@ -104,15 +104,17 @@ struct channel_data {
} // namespace
static grpc_error_handle hs_filter_outgoing_metadata(grpc_metadata_batch* b) {
if (b->idx.named.grpc_message != nullptr) {
if ((*b)->legacy_index()->named.grpc_message != nullptr) {
grpc_slice pct_encoded_msg = grpc_core::PercentEncodeSlice(
GRPC_MDVALUE(b->idx.named.grpc_message->md),
GRPC_MDVALUE((*b)->legacy_index()->named.grpc_message->md),
grpc_core::PercentEncodingType::Compatible);
if (grpc_slice_is_equivalent(pct_encoded_msg,
GRPC_MDVALUE(b->idx.named.grpc_message->md))) {
if (grpc_slice_is_equivalent(
pct_encoded_msg,
GRPC_MDVALUE((*b)->legacy_index()->named.grpc_message->md))) {
grpc_slice_unref_internal(pct_encoded_msg);
} else {
grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_encoded_msg);
grpc_metadata_batch_set_value((*b)->legacy_index()->named.grpc_message,
pct_encoded_msg);
}
}
return GRPC_ERROR_NONE;
@ -159,18 +161,19 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
grpc_error_handle error = GRPC_ERROR_NONE;
static const char* error_name = "Failed processing incoming headers";
if (b->idx.named.method != nullptr) {
if (md_strict_equal(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) {
if ((*b)->legacy_index()->named.method != nullptr) {
if (md_strict_equal((*b)->legacy_index()->named.method->md,
GRPC_MDELEM_METHOD_POST)) {
*calld->recv_initial_metadata_flags &=
~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST |
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST);
} else if (md_strict_equal(b->idx.named.method->md,
} else if (md_strict_equal((*b)->legacy_index()->named.method->md,
GRPC_MDELEM_METHOD_PUT)) {
*calld->recv_initial_metadata_flags &=
~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
*calld->recv_initial_metadata_flags |=
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
} else if (md_strict_equal(b->idx.named.method->md,
} else if (md_strict_equal((*b)->legacy_index()->named.method->md,
GRPC_MDELEM_METHOD_GET)) {
*calld->recv_initial_metadata_flags |=
GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
@ -180,7 +183,7 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
hs_add_error(error_name, &error,
grpc_attach_md_to_error(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
b->idx.named.method->md));
(*b)->legacy_index()->named.method->md));
}
grpc_metadata_batch_remove(b, GRPC_BATCH_METHOD);
} else {
@ -191,13 +194,13 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":method")));
}
if (b->idx.named.te != nullptr) {
if (!grpc_mdelem_static_value_eq(b->idx.named.te->md,
if ((*b)->legacy_index()->named.te != nullptr) {
if (!grpc_mdelem_static_value_eq((*b)->legacy_index()->named.te->md,
GRPC_MDELEM_TE_TRAILERS)) {
hs_add_error(error_name, &error,
grpc_attach_md_to_error(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
b->idx.named.te->md));
(*b)->legacy_index()->named.te->md));
}
grpc_metadata_batch_remove(b, GRPC_BATCH_TE);
} else {
@ -207,15 +210,17 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
GRPC_ERROR_STR_KEY, grpc_slice_from_static_string("te")));
}
if (b->idx.named.scheme != nullptr) {
if (!md_strict_equal(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTP) &&
!md_strict_equal(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTPS) &&
!grpc_mdelem_static_value_eq(b->idx.named.scheme->md,
if ((*b)->legacy_index()->named.scheme != nullptr) {
if (!md_strict_equal((*b)->legacy_index()->named.scheme->md,
GRPC_MDELEM_SCHEME_HTTP) &&
!md_strict_equal((*b)->legacy_index()->named.scheme->md,
GRPC_MDELEM_SCHEME_HTTPS) &&
!grpc_mdelem_static_value_eq((*b)->legacy_index()->named.scheme->md,
GRPC_MDELEM_SCHEME_GRPC)) {
hs_add_error(error_name, &error,
grpc_attach_md_to_error(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
b->idx.named.scheme->md));
(*b)->legacy_index()->named.scheme->md));
}
grpc_metadata_batch_remove(b, GRPC_BATCH_SCHEME);
} else {
@ -226,18 +231,20 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":scheme")));
}
if (b->idx.named.content_type != nullptr) {
if ((*b)->legacy_index()->named.content_type != nullptr) {
if (!grpc_mdelem_static_value_eq(
b->idx.named.content_type->md,
(*b)->legacy_index()->named.content_type->md,
GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
EXPECTED_CONTENT_TYPE,
EXPECTED_CONTENT_TYPE_LENGTH) &&
if (grpc_slice_buf_start_eq(
GRPC_MDVALUE((*b)->legacy_index()->named.content_type->md),
EXPECTED_CONTENT_TYPE, EXPECTED_CONTENT_TYPE_LENGTH) &&
(GRPC_SLICE_START_PTR(GRPC_MDVALUE(
b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
(*b)->legacy_index()
->named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
'+' ||
GRPC_SLICE_START_PTR(GRPC_MDVALUE(
b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
(*b)->legacy_index()
->named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
';')) {
/* Although the C implementation doesn't (currently) generate them,
any custom +-suffix is explicitly valid. */
@ -247,8 +254,9 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
} else {
/* TODO(klempner): We're currently allowing this, but we shouldn't
see it without a proxy so log for now. */
char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md),
GPR_DUMP_ASCII);
char* val = grpc_dump_slice(
GRPC_MDVALUE((*b)->legacy_index()->named.content_type->md),
GPR_DUMP_ASCII);
gpr_log(GPR_INFO, "Unexpected content-type '%s'", val);
gpr_free(val);
}
@ -256,7 +264,7 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
grpc_metadata_batch_remove(b, GRPC_BATCH_CONTENT_TYPE);
}
if (b->idx.named.path == nullptr) {
if ((*b)->legacy_index()->named.path == nullptr) {
hs_add_error(
error_name, &error,
grpc_error_set_str(
@ -267,7 +275,7 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
/* We have a cacheable request made with GET verb. The path contains the
* query parameter which is base64 encoded request payload. */
const char k_query_separator = '?';
grpc_slice path_slice = GRPC_MDVALUE(b->idx.named.path->md);
grpc_slice path_slice = GRPC_MDVALUE((*b)->legacy_index()->named.path->md);
uint8_t* path_ptr = GRPC_SLICE_START_PTR(path_slice);
size_t path_length = GRPC_SLICE_LENGTH(path_slice);
/* offset of the character '?' */
@ -283,7 +291,7 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
grpc_mdelem mdelem_path_without_query = grpc_mdelem_from_slices(
GRPC_MDSTR_PATH, grpc_slice_sub(path_slice, 0, offset));
grpc_metadata_batch_substitute(b, b->idx.named.path,
grpc_metadata_batch_substitute(b, (*b)->legacy_index()->named.path,
mdelem_path_without_query);
/* decode payload from query and add to the slice buffer to be returned */
@ -304,8 +312,9 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
}
}
if (b->idx.named.host != nullptr && b->idx.named.authority == nullptr) {
grpc_linked_mdelem* el = b->idx.named.host;
if ((*b)->legacy_index()->named.host != nullptr &&
(*b)->legacy_index()->named.authority == nullptr) {
grpc_linked_mdelem* el = (*b)->legacy_index()->named.host;
grpc_mdelem md = GRPC_MDELEM_REF(el->md);
grpc_metadata_batch_remove(b, el);
hs_add_error(
@ -318,7 +327,7 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
GRPC_MDELEM_UNREF(md);
}
if (b->idx.named.authority == nullptr) {
if ((*b)->legacy_index()->named.authority == nullptr) {
hs_add_error(
error_name, &error,
grpc_error_set_str(
@ -327,7 +336,8 @@ static grpc_error_handle hs_filter_incoming_metadata(grpc_call_element* elem,
}
channel_data* chand = static_cast<channel_data*>(elem->channel_data);
if (!chand->surface_user_agent && b->idx.named.user_agent != nullptr) {
if (!chand->surface_user_agent &&
(*b)->legacy_index()->named.user_agent != nullptr) {
grpc_metadata_batch_remove(b, GRPC_BATCH_USER_AGENT);
}

@ -46,8 +46,8 @@ struct call_data {
// Find the user agent metadata element in the batch
static bool get_user_agent_mdelem(const grpc_metadata_batch* batch,
grpc_mdelem* md) {
if (batch->idx.named.user_agent != nullptr) {
*md = batch->idx.named.user_agent->md;
if ((*batch)->legacy_index()->named.user_agent != nullptr) {
*md = (*batch)->legacy_index()->named.user_agent->md;
return true;
}
return false;

@ -375,15 +375,13 @@ static void perform_stream_op_locked(void* stream_op,
grpc_binder::Metadata init_md;
auto batch = op->payload->send_initial_metadata.send_initial_metadata;
for (grpc_linked_mdelem* md = batch->list.head; md != nullptr;
md = md->next) {
absl::string_view key =
grpc_core::StringViewFromSlice(GRPC_MDKEY(md->md));
(*batch)->ForEach([&](grpc_mdelem md) {
absl::string_view key = grpc_core::StringViewFromSlice(GRPC_MDKEY(md));
absl::string_view value =
grpc_core::StringViewFromSlice(GRPC_MDVALUE(md->md));
grpc_core::StringViewFromSlice(GRPC_MDVALUE(md));
gpr_log(GPR_INFO, "send initial metatday key-value %s",
absl::StrCat(key, " ", value).c_str());
if (grpc_slice_eq(GRPC_MDKEY(md->md), GRPC_MDSTR_PATH)) {
if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) {
// TODO(b/192208403): Figure out if it is correct to simply drop '/'
// prefix and treat it as rpc method name
GPR_ASSERT(value[0] == '/');
@ -395,7 +393,7 @@ static void perform_stream_op_locked(void* stream_op,
} else {
init_md.emplace_back(std::string(key), std::string(value));
}
}
});
tx.SetPrefix(init_md);
}
if (op->send_message) {
@ -429,25 +427,23 @@ static void perform_stream_op_locked(void* stream_op,
auto batch = op->payload->send_trailing_metadata.send_trailing_metadata;
grpc_binder::Metadata trailing_metadata;
for (grpc_linked_mdelem* md = batch->list.head; md != nullptr;
md = md->next) {
(*batch)->ForEach([&](grpc_mdelem md) {
// Client will not send trailing metadata.
GPR_ASSERT(!gbt->is_client);
if (grpc_slice_eq(GRPC_MDKEY(md->md), GRPC_MDSTR_GRPC_STATUS)) {
int status = grpc_get_status_code_from_metadata(md->md);
if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS)) {
int status = grpc_get_status_code_from_metadata(md);
gpr_log(GPR_INFO, "send trailing metadata status = %d", status);
tx.SetStatus(status);
} else {
absl::string_view key =
grpc_core::StringViewFromSlice(GRPC_MDKEY(md->md));
absl::string_view key = grpc_core::StringViewFromSlice(GRPC_MDKEY(md));
absl::string_view value =
grpc_core::StringViewFromSlice(GRPC_MDVALUE(md->md));
grpc_core::StringViewFromSlice(GRPC_MDVALUE(md));
gpr_log(GPR_INFO, "send trailing metatday key-value %s",
absl::StrCat(key, " ", value).c_str());
trailing_metadata.emplace_back(std::string(key), std::string(value));
}
}
});
// TODO(mingcl): Will we ever has key-value pair here? According to
// wireformat client suffix data is always empty.
tx.SetSuffix(trailing_metadata);

@ -1252,9 +1252,10 @@ void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
}
static bool contains_non_ok_status(grpc_metadata_batch* batch) {
if (batch->idx.named.grpc_status != nullptr) {
return !grpc_mdelem_static_value_eq(batch->idx.named.grpc_status->md,
GRPC_MDELEM_GRPC_STATUS_0);
if ((*batch)->legacy_index()->named.grpc_status != nullptr) {
return !grpc_mdelem_static_value_eq(
(*batch)->legacy_index()->named.grpc_status->md,
GRPC_MDELEM_GRPC_STATUS_0);
}
return false;
}
@ -1349,15 +1350,14 @@ static void complete_fetch_locked(void* gs, grpc_error_handle error) {
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 != nullptr;
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));
(*md_batch)->ForEach([=](grpc_mdelem md) {
char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
char* value = grpc_slice_to_c_string(GRPC_MDVALUE(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);
}
});
}
static void perform_stream_op_locked(void* stream_op,
@ -1411,12 +1411,14 @@ static void perform_stream_op_locked(void* stream_op,
on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
// Identify stream compression
if (op_payload->send_initial_metadata.send_initial_metadata->idx.named
.content_encoding == nullptr ||
if ((*op_payload->send_initial_metadata.send_initial_metadata)
->legacy_index()
->named.content_encoding == nullptr ||
grpc_stream_compression_method_parse(
GRPC_MDVALUE(
op_payload->send_initial_metadata.send_initial_metadata->idx
.named.content_encoding->md),
(*op_payload->send_initial_metadata.send_initial_metadata)
->legacy_index()
->named.content_encoding->md),
true, &s->stream_compression_method) == 0) {
s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
}
@ -1430,7 +1432,8 @@ static void perform_stream_op_locked(void* stream_op,
s->send_initial_metadata =
op_payload->send_initial_metadata.send_initial_metadata;
if (t->is_client) {
s->deadline = GPR_MIN(s->deadline, s->send_initial_metadata->deadline);
s->deadline =
GPR_MIN(s->deadline, (*s->send_initial_metadata)->deadline());
}
if (contains_non_ok_status(s->send_initial_metadata)) {
s->seen_error = true;
@ -1624,12 +1627,14 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
if (!t->is_client) {
if (op->send_initial_metadata) {
grpc_millis deadline =
op->payload->send_initial_metadata.send_initial_metadata->deadline;
(*op->payload->send_initial_metadata.send_initial_metadata)
->deadline();
GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
}
if (op->send_trailing_metadata) {
grpc_millis deadline =
op->payload->send_trailing_metadata.send_trailing_metadata->deadline;
(*op->payload->send_trailing_metadata.send_trailing_metadata)
->deadline();
GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
}
}
@ -2104,16 +2109,13 @@ void grpc_chttp2_fake_status(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
gpr_ltoa(status, status_string);
GRPC_LOG_IF_ERROR("add_status",
grpc_chttp2_incoming_metadata_buffer_replace_or_add(
&s->metadata_buffer[1],
grpc_mdelem_from_slices(
GRPC_MDSTR_GRPC_STATUS,
grpc_core::UnmanagedMemorySlice(status_string))));
&s->metadata_buffer[1], GRPC_MDSTR_GRPC_STATUS,
grpc_core::UnmanagedMemorySlice(status_string)));
if (!GRPC_SLICE_IS_EMPTY(slice)) {
GRPC_LOG_IF_ERROR(
"add_status_message",
grpc_chttp2_incoming_metadata_buffer_replace_or_add(
&s->metadata_buffer[1],
grpc_mdelem_create(GRPC_MDSTR_GRPC_MESSAGE, slice, nullptr)));
&s->metadata_buffer[1], GRPC_MDSTR_GRPC_MESSAGE, slice));
}
s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE;
grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);

@ -1348,10 +1348,11 @@ static void force_client_rst_stream(void* sp, grpc_error_handle /*error*/) {
static void parse_stream_compression_md(grpc_chttp2_transport* /*t*/,
grpc_chttp2_stream* s,
grpc_metadata_batch* initial_metadata) {
if (initial_metadata->idx.named.content_encoding == nullptr ||
if ((*initial_metadata)->legacy_index()->named.content_encoding == nullptr ||
grpc_stream_compression_method_parse(
GRPC_MDVALUE(initial_metadata->idx.named.content_encoding->md), false,
&s->stream_decompression_method) == 0) {
GRPC_MDVALUE(
(*initial_metadata)->legacy_index()->named.content_encoding->md),
false, &s->stream_decompression_method) == 0) {
s->stream_decompression_method =
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
}

@ -43,21 +43,17 @@ grpc_error_handle grpc_chttp2_incoming_metadata_buffer_add(
}
grpc_error_handle grpc_chttp2_incoming_metadata_buffer_replace_or_add(
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_mdelem elem) {
for (grpc_linked_mdelem* l = buffer->batch.list.head; l != nullptr;
l = l->next) {
if (grpc_slice_eq(GRPC_MDKEY(l->md), GRPC_MDKEY(elem))) {
GRPC_MDELEM_UNREF(l->md);
l->md = elem;
return GRPC_ERROR_NONE;
}
}
return grpc_chttp2_incoming_metadata_buffer_add(buffer, elem);
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_slice key,
grpc_slice value) {
if (buffer->batch->ReplaceIfExists(key, value)) return GRPC_ERROR_NONE;
return grpc_chttp2_incoming_metadata_buffer_add(
buffer, grpc_mdelem_from_slices(grpc_slice_ref_internal(key),
grpc_slice_ref_internal(value)));
}
void grpc_chttp2_incoming_metadata_buffer_set_deadline(
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_millis deadline) {
buffer->batch.deadline = deadline;
buffer->batch->SetDeadline(deadline);
}
void grpc_chttp2_incoming_metadata_buffer_publish(

@ -27,7 +27,7 @@ struct grpc_chttp2_incoming_metadata_buffer {
explicit grpc_chttp2_incoming_metadata_buffer(grpc_core::Arena* arena)
: arena(arena) {
grpc_metadata_batch_init(&batch);
batch.deadline = GRPC_MILLIS_INF_FUTURE;
batch->ClearDeadline();
}
~grpc_chttp2_incoming_metadata_buffer() {
grpc_metadata_batch_destroy(&batch);
@ -50,8 +50,8 @@ grpc_error_handle grpc_chttp2_incoming_metadata_buffer_add(
grpc_chttp2_incoming_metadata_buffer* buffer,
grpc_mdelem elem) GRPC_MUST_USE_RESULT;
grpc_error_handle grpc_chttp2_incoming_metadata_buffer_replace_or_add(
grpc_chttp2_incoming_metadata_buffer* buffer,
grpc_mdelem elem) GRPC_MUST_USE_RESULT;
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_slice key,
grpc_slice value) GRPC_MUST_USE_RESULT;
void grpc_chttp2_incoming_metadata_buffer_set_deadline(
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_millis deadline);

@ -193,7 +193,8 @@ static uint32_t target_write_size(grpc_chttp2_transport* /*t*/) {
// Returns true if initial_metadata contains only default headers.
static bool is_default_initial_metadata(grpc_metadata_batch* initial_metadata) {
return initial_metadata->list.default_count == initial_metadata->list.count;
return (*initial_metadata)->default_count() ==
(*initial_metadata)->non_deadline_count();
}
namespace {
@ -468,7 +469,7 @@ class StreamWriteContext {
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], // max_frame_size
&s_->stats.outgoing // stats
},
*s_->send_initial_metadata, &t_->outbuf);
**s_->send_initial_metadata, &t_->outbuf);
grpc_chttp2_reset_ping_clock(t_);
write_context_->IncInitialMetadataWrites();
}
@ -579,7 +580,7 @@ class StreamWriteContext {
grpc_core::MetadataArray(
extra_headers_for_trailing_metadata_,
num_extra_headers_for_trailing_metadata_),
*s_->send_trailing_metadata),
**s_->send_trailing_metadata),
&t_->outbuf);
}
write_context_->IncTrailingMetadataWrites();
@ -600,15 +601,18 @@ class StreamWriteContext {
gpr_log(GPR_INFO, "not sending initial_metadata (Trailers-Only)"));
// When sending Trailers-Only, we need to move the :status and
// content-type headers to the trailers.
if (s_->send_initial_metadata->idx.named.status != nullptr) {
if ((*s_->send_initial_metadata)->legacy_index()->named.status != nullptr) {
extra_headers_for_trailing_metadata_
[num_extra_headers_for_trailing_metadata_++] =
&s_->send_initial_metadata->idx.named.status->md;
&(*s_->send_initial_metadata)->legacy_index()->named.status->md;
}
if (s_->send_initial_metadata->idx.named.content_type != nullptr) {
if ((*s_->send_initial_metadata)->legacy_index()->named.content_type !=
nullptr) {
extra_headers_for_trailing_metadata_
[num_extra_headers_for_trailing_metadata_++] =
&s_->send_initial_metadata->idx.named.content_type->md;
&(*s_->send_initial_metadata)
->legacy_index()
->named.content_type->md;
}
}

@ -704,17 +704,8 @@ static void convert_metadata_to_cronet_headers(
grpc_metadata_batch* metadata, const char* host, std::string* pp_url,
bidirectional_stream_header** pp_headers, size_t* p_num_headers,
const char** method) {
grpc_linked_mdelem* curr = metadata->list.head;
/* Walk the linked list and get number of header fields */
size_t num_headers_available = 0;
while (curr != nullptr) {
curr = curr->next;
num_headers_available++;
}
grpc_millis deadline = metadata->deadline;
if (deadline != GRPC_MILLIS_INF_FUTURE) {
num_headers_available++;
}
/* Get number of header fields */
size_t num_headers_available = (*metadata)->count();
/* Allocate enough memory. It is freed in the on_stream_ready callback
*/
bidirectional_stream_header* headers =
@ -727,11 +718,8 @@ static void convert_metadata_to_cronet_headers(
are not used for cronet.
TODO (makdharma): Eliminate need to traverse the LL second time for perf.
*/
curr = metadata->list.head;
size_t num_headers = 0;
while (num_headers < num_headers_available) {
grpc_mdelem mdelem = curr->md;
curr = curr->next;
(*metadata)->ForEach([&](grpc_mdelem mdelem) {
char* key = grpc_slice_to_c_string(GRPC_MDKEY(mdelem));
char* value;
if (grpc_is_binary_header_internal(GRPC_MDKEY(mdelem))) {
@ -747,7 +735,7 @@ static void convert_metadata_to_cronet_headers(
/* Cronet populates these fields on its own */
gpr_free(key);
gpr_free(value);
continue;
return;
}
if (grpc_slice_eq_static_interned(GRPC_MDKEY(mdelem), GRPC_MDSTR_METHOD)) {
if (grpc_slice_eq_static_interned(GRPC_MDVALUE(mdelem), GRPC_MDSTR_PUT)) {
@ -758,29 +746,26 @@ static void convert_metadata_to_cronet_headers(
}
gpr_free(key);
gpr_free(value);
continue;
return;
}
if (grpc_slice_eq_static_interned(GRPC_MDKEY(mdelem), GRPC_MDSTR_PATH)) {
/* Create URL by appending :path value to the hostname */
*pp_url = absl::StrCat("https://", host, value);
gpr_free(key);
gpr_free(value);
continue;
return;
}
CRONET_LOG(GPR_DEBUG, "header %s = %s", key, value);
headers[num_headers].key = key;
headers[num_headers].value = value;
num_headers++;
if (curr == nullptr) {
break;
}
}
if (deadline != GRPC_MILLIS_INF_FUTURE) {
});
if ((*metadata)->deadline() != GRPC_MILLIS_INF_FUTURE) {
char* key = grpc_slice_to_c_string(GRPC_MDSTR_GRPC_TIMEOUT);
char* value =
static_cast<char*>(gpr_malloc(GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE));
grpc_http2_encode_timeout(deadline - grpc_core::ExecCtx::Get()->Now(),
value);
grpc_http2_encode_timeout(
(*metadata)->deadline() - grpc_core::ExecCtx::Get()->Now(), value);
headers[num_headers].key = key;
headers[num_headers].value = value;
@ -802,15 +787,14 @@ static void parse_grpc_header(const uint8_t* data, int* length,
*length |= (*p++);
}
static bool header_has_authority(grpc_linked_mdelem* head) {
while (head != nullptr) {
if (grpc_slice_eq_static_interned(GRPC_MDKEY(head->md),
GRPC_MDSTR_AUTHORITY)) {
return true;
static bool header_has_authority(const grpc_metadata_batch* b) {
bool found = false;
(*b)->ForEach([&](grpc_mdelem elem) {
if (grpc_slice_eq_static_interned(GRPC_MDKEY(elem), GRPC_MDSTR_AUTHORITY)) {
found = true;
}
head = head->next;
}
return false;
});
return found;
}
/*
@ -1438,8 +1422,8 @@ static void perform_stream_op(grpc_transport* /*gt*/, grpc_stream* gs,
grpc_transport_stream_op_batch* op) {
CRONET_LOG(GPR_DEBUG, "perform_stream_op");
if (op->send_initial_metadata &&
header_has_authority(op->payload->send_initial_metadata
.send_initial_metadata->list.head)) {
header_has_authority(
op->payload->send_initial_metadata.send_initial_metadata)) {
/* Cronet does not support :authority header field. We cancel the call when
this field is present in metadata */
if (op->recv_initial_metadata) {

@ -285,15 +285,14 @@ struct inproc_stream {
void log_metadata(const grpc_metadata_batch* md_batch, bool is_client,
bool is_initial) {
for (grpc_linked_mdelem* md = md_batch->list.head; md != nullptr;
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));
(*md_batch)->ForEach([=](grpc_mdelem md) {
char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
char* value = grpc_slice_to_c_string(GRPC_MDVALUE(md));
gpr_log(GPR_INFO, "INPROC:%s:%s: %s: %s", is_initial ? "HDR" : "TRL",
is_client ? "CLI" : "SVR", key, value);
gpr_free(key);
gpr_free(value);
}
});
}
grpc_error_handle fill_in_metadata(inproc_stream* s,
@ -311,16 +310,15 @@ grpc_error_handle fill_in_metadata(inproc_stream* s,
*markfilled = true;
}
grpc_error_handle error = GRPC_ERROR_NONE;
for (grpc_linked_mdelem* elem = metadata->list.head;
(elem != nullptr) && (error == GRPC_ERROR_NONE); elem = elem->next) {
(*metadata)->ForEach([&](grpc_mdelem md) {
if (error != GRPC_ERROR_NONE) return;
grpc_linked_mdelem* nelem =
static_cast<grpc_linked_mdelem*>(s->arena->Alloc(sizeof(*nelem)));
nelem->md =
grpc_mdelem_from_slices(grpc_slice_intern(GRPC_MDKEY(elem->md)),
grpc_slice_intern(GRPC_MDVALUE(elem->md)));
nelem->md = grpc_mdelem_from_slices(grpc_slice_intern(GRPC_MDKEY(md)),
grpc_slice_intern(GRPC_MDVALUE(md)));
error = grpc_metadata_batch_link_tail(out_md, nelem);
}
});
return error;
}
@ -709,8 +707,9 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
.recv_initial_metadata,
s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags,
nullptr);
s->recv_initial_md_op->payload->recv_initial_metadata
.recv_initial_metadata->deadline = s->deadline;
(*s->recv_initial_md_op->payload->recv_initial_metadata
.recv_initial_metadata)
->SetDeadline(s->deadline);
if (s->recv_initial_md_op->payload->recv_initial_metadata
.trailing_metadata_available != nullptr) {
*s->recv_initial_md_op->payload->recv_initial_metadata
@ -1033,8 +1032,9 @@ void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
if (s->t->is_client) {
grpc_millis* dl =
(other == nullptr) ? &s->write_buffer_deadline : &other->deadline;
*dl = GPR_MIN(*dl, op->payload->send_initial_metadata
.send_initial_metadata->deadline);
*dl = GPR_MIN(
*dl, (*op->payload->send_initial_metadata.send_initial_metadata)
->deadline());
s->initial_md_sent = true;
}
}

@ -80,8 +80,9 @@ EvaluateArgs::PerChannelArgs::PerChannelArgs(grpc_auth_context* auth_context,
absl::string_view EvaluateArgs::GetPath() const {
absl::string_view path;
if (metadata_ != nullptr && metadata_->idx.named.path != nullptr) {
grpc_linked_mdelem* elem = metadata_->idx.named.path;
if (metadata_ != nullptr &&
(*metadata_)->legacy_index()->named.path != nullptr) {
grpc_linked_mdelem* elem = (*metadata_)->legacy_index()->named.path;
const grpc_slice& val = GRPC_MDVALUE(elem->md);
path = StringViewFromSlice(val);
}
@ -90,8 +91,9 @@ absl::string_view EvaluateArgs::GetPath() const {
absl::string_view EvaluateArgs::GetHost() const {
absl::string_view host;
if (metadata_ != nullptr && metadata_->idx.named.host != nullptr) {
grpc_linked_mdelem* elem = metadata_->idx.named.host;
if (metadata_ != nullptr &&
(*metadata_)->legacy_index()->named.host != nullptr) {
grpc_linked_mdelem* elem = (*metadata_)->legacy_index()->named.host;
const grpc_slice& val = GRPC_MDVALUE(elem->md);
host = StringViewFromSlice(val);
}
@ -100,8 +102,9 @@ absl::string_view EvaluateArgs::GetHost() const {
absl::string_view EvaluateArgs::GetMethod() const {
absl::string_view method;
if (metadata_ != nullptr && metadata_->idx.named.method != nullptr) {
grpc_linked_mdelem* elem = metadata_->idx.named.method;
if (metadata_ != nullptr &&
(*metadata_)->legacy_index()->named.method != nullptr) {
grpc_linked_mdelem* elem = (*metadata_)->legacy_index()->named.method;
const grpc_slice& val = GRPC_MDVALUE(elem->md);
method = StringViewFromSlice(val);
}
@ -114,12 +117,11 @@ std::multimap<absl::string_view, absl::string_view> EvaluateArgs::GetHeaders()
if (metadata_ == nullptr) {
return headers;
}
for (grpc_linked_mdelem* elem = metadata_->list.head; elem != nullptr;
elem = elem->next) {
const grpc_slice& key = GRPC_MDKEY(elem->md);
const grpc_slice& val = GRPC_MDVALUE(elem->md);
(*metadata_)->ForEach([&](grpc_mdelem md) {
const grpc_slice& key = GRPC_MDKEY(md);
const grpc_slice& val = GRPC_MDVALUE(md);
headers.emplace(StringViewFromSlice(key), StringViewFromSlice(val));
}
});
return headers;
}

@ -372,13 +372,13 @@ static void client_auth_start_transport_stream_op_batch(
if (batch->send_initial_metadata) {
grpc_metadata_batch* metadata =
batch->payload->send_initial_metadata.send_initial_metadata;
if (metadata->idx.named.path != nullptr) {
calld->method =
grpc_slice_ref_internal(GRPC_MDVALUE(metadata->idx.named.path->md));
if ((*metadata)->legacy_index()->named.path != nullptr) {
calld->method = grpc_slice_ref_internal(
GRPC_MDVALUE((*metadata)->legacy_index()->named.path->md));
}
if (metadata->idx.named.authority != nullptr) {
if ((*metadata)->legacy_index()->named.authority != nullptr) {
calld->host = grpc_slice_ref_internal(
GRPC_MDVALUE(metadata->idx.named.authority->md));
GRPC_MDVALUE((*metadata)->legacy_index()->named.authority->md));
batch->handler_private.extra_arg = elem;
GRPC_CALL_STACK_REF(calld->owning_call, "check_call_host");
GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch,

@ -96,12 +96,10 @@ struct call_data {
static grpc_metadata_array metadata_batch_to_md_array(
const grpc_metadata_batch* batch) {
grpc_linked_mdelem* l;
grpc_metadata_array result;
grpc_metadata_array_init(&result);
for (l = batch->list.head; l != nullptr; l = l->next) {
(*batch)->ForEach([&](grpc_mdelem md) {
grpc_metadata* usr_md = nullptr;
grpc_mdelem md = l->md;
grpc_slice key = GRPC_MDKEY(md);
grpc_slice value = GRPC_MDVALUE(md);
if (result.count == result.capacity) {
@ -112,7 +110,7 @@ static grpc_metadata_array metadata_batch_to_md_array(
usr_md = &result.metadata[result.count++];
usr_md->key = grpc_slice_ref_internal(key);
usr_md->value = grpc_slice_ref_internal(value);
}
});
return result;
}

@ -146,7 +146,7 @@ struct grpc_call {
stream_op_payload(context) {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
metadata_batch[i][j].deadline = GRPC_MILLIS_INF_FUTURE;
metadata_batch[i][j]->ClearDeadline();
}
}
}
@ -1000,52 +1000,56 @@ static grpc_stream_compression_algorithm decode_stream_compression(
static void publish_app_metadata(grpc_call* call, grpc_metadata_batch* b,
int is_trailing) {
if (b->list.count == 0) return;
if ((*b)->non_deadline_count() == 0) return;
if (!call->is_client && is_trailing) return;
if (is_trailing && call->buffered_metadata[1] == nullptr) return;
GPR_TIMER_SCOPE("publish_app_metadata", 0);
grpc_metadata_array* dest;
grpc_metadata* mdusr;
dest = call->buffered_metadata[is_trailing];
if (dest->count + b->list.count > dest->capacity) {
dest->capacity =
GPR_MAX(dest->capacity + b->list.count, dest->capacity * 3 / 2);
if (dest->count + (*b)->non_deadline_count() > dest->capacity) {
dest->capacity = GPR_MAX(dest->capacity + (*b)->non_deadline_count(),
dest->capacity * 3 / 2);
dest->metadata = static_cast<grpc_metadata*>(
gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity));
}
for (grpc_linked_mdelem* l = b->list.head; l != nullptr; l = l->next) {
(*b)->ForEach([&](grpc_mdelem md) {
mdusr = &dest->metadata[dest->count++];
/* we pass back borrowed slices that are valid whilst the call is valid */
mdusr->key = GRPC_MDKEY(l->md);
mdusr->value = GRPC_MDVALUE(l->md);
}
mdusr->key = GRPC_MDKEY(md);
mdusr->value = GRPC_MDVALUE(md);
});
}
static void recv_initial_filter(grpc_call* call, grpc_metadata_batch* b) {
if (b->idx.named.content_encoding != nullptr) {
if ((*b)->legacy_index()->named.content_encoding != nullptr) {
GPR_TIMER_SCOPE("incoming_stream_compression_algorithm", 0);
set_incoming_stream_compression_algorithm(
call, decode_stream_compression(b->idx.named.content_encoding->md));
call, decode_stream_compression(
(*b)->legacy_index()->named.content_encoding->md));
grpc_metadata_batch_remove(b, GRPC_BATCH_CONTENT_ENCODING);
}
if (b->idx.named.grpc_encoding != nullptr) {
if ((*b)->legacy_index()->named.grpc_encoding != nullptr) {
GPR_TIMER_SCOPE("incoming_message_compression_algorithm", 0);
set_incoming_message_compression_algorithm(
call, decode_message_compression(b->idx.named.grpc_encoding->md));
call, decode_message_compression(
(*b)->legacy_index()->named.grpc_encoding->md));
grpc_metadata_batch_remove(b, GRPC_BATCH_GRPC_ENCODING);
}
uint32_t message_encodings_accepted_by_peer = 1u;
uint32_t stream_encodings_accepted_by_peer = 1u;
if (b->idx.named.grpc_accept_encoding != nullptr) {
if ((*b)->legacy_index()->named.grpc_accept_encoding != nullptr) {
GPR_TIMER_SCOPE("encodings_accepted_by_peer", 0);
set_encodings_accepted_by_peer(call, b->idx.named.grpc_accept_encoding->md,
&message_encodings_accepted_by_peer, false);
set_encodings_accepted_by_peer(
call, (*b)->legacy_index()->named.grpc_accept_encoding->md,
&message_encodings_accepted_by_peer, false);
grpc_metadata_batch_remove(b, GRPC_BATCH_GRPC_ACCEPT_ENCODING);
}
if (b->idx.named.accept_encoding != nullptr) {
if ((*b)->legacy_index()->named.accept_encoding != nullptr) {
GPR_TIMER_SCOPE("stream_encodings_accepted_by_peer", 0);
set_encodings_accepted_by_peer(call, b->idx.named.accept_encoding->md,
&stream_encodings_accepted_by_peer, true);
set_encodings_accepted_by_peer(
call, (*b)->legacy_index()->named.accept_encoding->md,
&stream_encodings_accepted_by_peer, true);
grpc_metadata_batch_remove(b, GRPC_BATCH_ACCEPT_ENCODING);
}
call->encodings_accepted_by_peer =
@ -1060,9 +1064,9 @@ static void recv_trailing_filter(void* args, grpc_metadata_batch* b,
grpc_call* call = static_cast<grpc_call*>(args);
if (batch_error != GRPC_ERROR_NONE) {
set_final_status(call, batch_error);
} else if (b->idx.named.grpc_status != nullptr) {
grpc_status_code status_code =
grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md);
} else if ((*b)->legacy_index()->named.grpc_status != nullptr) {
grpc_status_code status_code = grpc_get_status_code_from_metadata(
(*b)->legacy_index()->named.grpc_status->md);
grpc_error_handle error = GRPC_ERROR_NONE;
if (status_code != GRPC_STATUS_OK) {
char* peer = grpc_call_get_peer(call);
@ -1072,10 +1076,11 @@ static void recv_trailing_filter(void* args, grpc_metadata_batch* b,
static_cast<intptr_t>(status_code));
gpr_free(peer);
}
if (b->idx.named.grpc_message != nullptr) {
if ((*b)->legacy_index()->named.grpc_message != nullptr) {
error = grpc_error_set_str(
error, GRPC_ERROR_STR_GRPC_MESSAGE,
grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_message->md)));
grpc_slice_ref_internal(
GRPC_MDVALUE((*b)->legacy_index()->named.grpc_message->md)));
grpc_metadata_batch_remove(b, GRPC_BATCH_GRPC_MESSAGE);
} else if (error != GRPC_ERROR_NONE) {
error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE,
@ -1483,8 +1488,9 @@ static void receiving_initial_metadata_ready(void* bctlp,
GPR_TIMER_SCOPE("validate_filtered_metadata", 0);
validate_filtered_metadata(bctl);
if (md->deadline != GRPC_MILLIS_INF_FUTURE && !call->is_client) {
call->send_deadline = md->deadline;
grpc_millis deadline = (*md)->deadline();
if (deadline != GRPC_MILLIS_INF_FUTURE && !call->is_client) {
call->send_deadline = deadline;
}
} else {
if (reinterpret_cast<grpc_error_handle>(
@ -1670,7 +1676,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
}
/* TODO(ctiller): just make these the same variable? */
if (call->is_client) {
call->metadata_batch[0][0].deadline = call->send_deadline;
call->metadata_batch[0][0]->SetDeadline(call->send_deadline);
}
stream_op_payload->send_initial_metadata.send_initial_metadata =
&call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */];
@ -2025,8 +2031,8 @@ grpc_compression_algorithm grpc_call_compression_for_level(
bool grpc_call_is_trailers_only(const grpc_call* call) {
bool result = call->is_trailers_only;
GPR_DEBUG_ASSERT(
!result || call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]
.list.count == 0);
!result ||
call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]->empty());
return result;
}

@ -1381,20 +1381,25 @@ void Server::CallData::RecvInitialMetadataReady(void* arg,
CallData* calld = static_cast<CallData*>(elem->call_data);
grpc_millis op_deadline;
if (error == GRPC_ERROR_NONE) {
GPR_DEBUG_ASSERT(calld->recv_initial_metadata_->idx.named.path != nullptr);
GPR_DEBUG_ASSERT(calld->recv_initial_metadata_->idx.named.authority !=
nullptr);
calld->path_.emplace(grpc_slice_ref_internal(
GRPC_MDVALUE(calld->recv_initial_metadata_->idx.named.path->md)));
calld->host_.emplace(grpc_slice_ref_internal(
GRPC_MDVALUE(calld->recv_initial_metadata_->idx.named.authority->md)));
GPR_DEBUG_ASSERT(
(*calld->recv_initial_metadata_)->legacy_index()->named.path !=
nullptr);
GPR_DEBUG_ASSERT(
(*calld->recv_initial_metadata_)->legacy_index()->named.authority !=
nullptr);
calld->path_.emplace(grpc_slice_ref_internal(GRPC_MDVALUE(
(*calld->recv_initial_metadata_)->legacy_index()->named.path->md)));
calld->host_.emplace(
grpc_slice_ref_internal(GRPC_MDVALUE((*calld->recv_initial_metadata_)
->legacy_index()
->named.authority->md)));
grpc_metadata_batch_remove(calld->recv_initial_metadata_, GRPC_BATCH_PATH);
grpc_metadata_batch_remove(calld->recv_initial_metadata_,
GRPC_BATCH_AUTHORITY);
} else {
GRPC_ERROR_REF(error);
}
op_deadline = calld->recv_initial_metadata_->deadline;
op_deadline = (*calld->recv_initial_metadata_)->deadline();
if (op_deadline != GRPC_MILLIS_INF_FUTURE) {
calld->deadline_ = op_deadline;
}

@ -33,6 +33,8 @@
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/slice/slice_string_helpers.h"
namespace grpc_core {
static void assert_valid_list(grpc_mdelem_list* list) {
#ifndef NDEBUG
grpc_linked_mdelem* l;
@ -59,48 +61,44 @@ static void assert_valid_list(grpc_mdelem_list* list) {
#endif /* NDEBUG */
}
static void assert_valid_callouts(grpc_metadata_batch* batch) {
void MetadataMap::AssertValidCallouts() {
#ifndef NDEBUG
for (grpc_linked_mdelem* l = batch->list.head; l != nullptr; l = l->next) {
for (grpc_linked_mdelem* l = list_.head; l != nullptr; l = l->next) {
grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md));
grpc_metadata_batch_callouts_index callout_idx =
GRPC_BATCH_INDEX_OF(key_interned);
if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) {
GPR_ASSERT(batch->idx.array[callout_idx] == l);
GPR_ASSERT(idx_.array[callout_idx] == l);
}
grpc_slice_unref_internal(key_interned);
}
#else
// Avoid unused-parameter warning for debug-only parameter
(void)batch;
#endif
}
#ifndef NDEBUG
void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch) {
assert_valid_list(&batch->list);
}
void MetadataMap::AssertOk() { assert_valid_list(&list_); }
#endif /* NDEBUG */
void grpc_metadata_batch_init(grpc_metadata_batch* batch) {
memset(batch, 0, sizeof(*batch));
batch->deadline = GRPC_MILLIS_INF_FUTURE;
MetadataMap::MetadataMap() {
memset(&list_, 0, sizeof(list_));
memset(&idx_, 0, sizeof(idx_));
deadline_ = GRPC_MILLIS_INF_FUTURE;
}
void grpc_metadata_batch_destroy(grpc_metadata_batch* batch) {
grpc_linked_mdelem* l;
for (l = batch->list.head; l; l = l->next) {
GRPC_MDELEM_UNREF(l->md);
}
MetadataMap::MetadataMap(MetadataMap&& other) noexcept {
list_ = other.list_;
idx_ = other.idx_;
deadline_ = other.deadline_;
memset(&other.list_, 0, sizeof(list_));
memset(&other.idx_, 0, sizeof(idx_));
other.deadline_ = GRPC_MILLIS_INF_FUTURE;
}
grpc_error_handle grpc_attach_md_to_error(grpc_error_handle src,
grpc_mdelem md) {
grpc_error_handle out = grpc_error_set_str(
grpc_error_set_str(src, GRPC_ERROR_STR_KEY,
grpc_slice_ref_internal(GRPC_MDKEY(md))),
GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md)));
return out;
MetadataMap::~MetadataMap() {
AssertValidCallouts();
for (auto* l = list_.head; l; l = l->next) {
GRPC_MDELEM_UNREF(l->md);
}
}
static grpc_error_handle GPR_ATTRIBUTE_NOINLINE error_with_md(grpc_mdelem md) {
@ -108,50 +106,56 @@ static grpc_error_handle GPR_ATTRIBUTE_NOINLINE error_with_md(grpc_mdelem md) {
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"), md);
}
static grpc_error_handle link_callout(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx) {
absl::optional<grpc_slice> MetadataMap::Remove(grpc_slice key) {
for (auto* l = list_.head; l; l = l->next) {
if (grpc_slice_eq(GRPC_MDKEY(l->md), key)) {
auto out = grpc_slice_ref_internal(GRPC_MDVALUE(l->md));
Remove(l);
return out;
}
}
return {};
}
grpc_error_handle MetadataMap::LinkCallout(
grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) {
AssertValidCallouts();
GPR_DEBUG_ASSERT(idx >= 0 && idx < GRPC_BATCH_CALLOUTS_COUNT);
if (GPR_LIKELY(batch->idx.array[idx] == nullptr)) {
++batch->list.default_count;
batch->idx.array[idx] = storage;
if (GPR_LIKELY(idx_.array[idx] == nullptr)) {
++list_.default_count;
idx_.array[idx] = storage;
AssertValidCallouts();
return GRPC_ERROR_NONE;
}
AssertValidCallouts();
return error_with_md(storage->md);
}
static grpc_error_handle maybe_link_callout(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage)
GRPC_MUST_USE_RESULT;
static grpc_error_handle maybe_link_callout(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage) {
grpc_error_handle MetadataMap::MaybeLinkCallout(grpc_linked_mdelem* storage) {
grpc_metadata_batch_callouts_index idx =
GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
return GRPC_ERROR_NONE;
}
return link_callout(batch, storage, idx);
return LinkCallout(storage, idx);
}
static void maybe_unlink_callout(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage) {
void MetadataMap::MaybeUnlinkCallout(grpc_linked_mdelem* storage) {
grpc_metadata_batch_callouts_index idx =
GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
return;
}
--batch->list.default_count;
GPR_DEBUG_ASSERT(batch->idx.array[idx] != nullptr);
batch->idx.array[idx] = nullptr;
--list_.default_count;
GPR_DEBUG_ASSERT(idx_.array[idx] != nullptr);
idx_.array[idx] = nullptr;
}
grpc_error_handle grpc_metadata_batch_add_head(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) {
grpc_error_handle MetadataMap::AddHead(grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) {
GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add));
storage->md = elem_to_add;
return grpc_metadata_batch_link_head(batch, storage);
return LinkHead(storage);
}
static void link_head(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
@ -170,43 +174,40 @@ static void link_head(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
assert_valid_list(list);
}
grpc_error_handle grpc_metadata_batch_link_head(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage) {
assert_valid_callouts(batch);
grpc_error_handle err = maybe_link_callout(batch, storage);
grpc_error_handle MetadataMap::LinkHead(grpc_linked_mdelem* storage) {
AssertValidCallouts();
grpc_error_handle err = MaybeLinkCallout(storage);
if (err != GRPC_ERROR_NONE) {
assert_valid_callouts(batch);
AssertValidCallouts();
return err;
}
link_head(&batch->list, storage);
assert_valid_callouts(batch);
link_head(&list_, storage);
AssertValidCallouts();
return GRPC_ERROR_NONE;
}
// TODO(arjunroy): Need to revisit this and see what guarantees exist between
// C-core and the internal-metadata subsystem. E.g. can we ensure a particular
// metadata is never added twice, even in the presence of user supplied data?
grpc_error_handle grpc_metadata_batch_link_head(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx) {
grpc_error_handle MetadataMap::LinkHead(
grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) {
GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx);
assert_valid_callouts(batch);
grpc_error_handle err = link_callout(batch, storage, idx);
AssertValidCallouts();
grpc_error_handle err = LinkCallout(storage, idx);
if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) {
assert_valid_callouts(batch);
AssertValidCallouts();
return err;
}
link_head(&batch->list, storage);
assert_valid_callouts(batch);
link_head(&list_, storage);
AssertValidCallouts();
return GRPC_ERROR_NONE;
}
grpc_error_handle grpc_metadata_batch_add_tail(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) {
grpc_error_handle MetadataMap::AddTail(grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) {
GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add));
storage->md = elem_to_add;
return grpc_metadata_batch_link_tail(batch, storage);
return LinkTail(storage);
}
static void link_tail(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
@ -225,31 +226,29 @@ static void link_tail(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
assert_valid_list(list);
}
grpc_error_handle grpc_metadata_batch_link_tail(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage) {
assert_valid_callouts(batch);
grpc_error_handle err = maybe_link_callout(batch, storage);
grpc_error_handle MetadataMap::LinkTail(grpc_linked_mdelem* storage) {
AssertValidCallouts();
grpc_error_handle err = MaybeLinkCallout(storage);
if (err != GRPC_ERROR_NONE) {
assert_valid_callouts(batch);
AssertValidCallouts();
return err;
}
link_tail(&batch->list, storage);
assert_valid_callouts(batch);
link_tail(&list_, storage);
AssertValidCallouts();
return GRPC_ERROR_NONE;
}
grpc_error_handle grpc_metadata_batch_link_tail(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx) {
grpc_error_handle MetadataMap::LinkTail(
grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) {
GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx);
assert_valid_callouts(batch);
grpc_error_handle err = link_callout(batch, storage, idx);
AssertValidCallouts();
grpc_error_handle err = LinkCallout(storage, idx);
if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) {
assert_valid_callouts(batch);
AssertValidCallouts();
return err;
}
link_tail(&batch->list, storage);
assert_valid_callouts(batch);
link_tail(&list_, storage);
AssertValidCallouts();
return GRPC_ERROR_NONE;
}
@ -270,44 +269,29 @@ static void unlink_storage(grpc_mdelem_list* list,
assert_valid_list(list);
}
void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage) {
assert_valid_callouts(batch);
maybe_unlink_callout(batch, storage);
unlink_storage(&batch->list, storage);
void MetadataMap::Remove(grpc_linked_mdelem* storage) {
AssertValidCallouts();
MaybeUnlinkCallout(storage);
unlink_storage(&list_, storage);
GRPC_MDELEM_UNREF(storage->md);
assert_valid_callouts(batch);
AssertValidCallouts();
}
void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
grpc_metadata_batch_callouts_index idx) {
assert_valid_callouts(batch);
grpc_linked_mdelem* storage = batch->idx.array[idx];
GPR_DEBUG_ASSERT(storage != nullptr);
--batch->list.default_count;
batch->idx.array[idx] = nullptr;
unlink_storage(&batch->list, storage);
GRPC_MDELEM_UNREF(storage->md);
assert_valid_callouts(batch);
void MetadataMap::Remove(grpc_metadata_batch_callouts_index idx) {
AssertValidCallouts();
if (idx_.array[idx] == nullptr) return;
--list_.default_count;
unlink_storage(&list_, idx_.array[idx]);
GRPC_MDELEM_UNREF(idx_.array[idx]->md);
idx_.array[idx] = nullptr;
AssertValidCallouts();
}
void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
const grpc_slice& value) {
grpc_mdelem old_mdelem = storage->md;
grpc_mdelem new_mdelem = grpc_mdelem_from_slices(
grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value);
storage->md = new_mdelem;
GRPC_MDELEM_UNREF(old_mdelem);
}
absl::optional<absl::string_view> grpc_metadata_batch_get_value(
grpc_metadata_batch* batch, absl::string_view target_key,
std::string* concatenated_value) {
absl::optional<absl::string_view> MetadataMap::GetValue(
absl::string_view target_key, std::string* concatenated_value) const {
// Find all values for the specified key.
GPR_DEBUG_ASSERT(batch != nullptr);
absl::InlinedVector<absl::string_view, 1> values;
for (grpc_linked_mdelem* md = batch->list.head; md != nullptr;
md = md->next) {
for (grpc_linked_mdelem* md = list_.head; md != nullptr; md = md->next) {
absl::string_view key = grpc_core::StringViewFromSlice(GRPC_MDKEY(md->md));
absl::string_view value =
grpc_core::StringViewFromSlice(GRPC_MDVALUE(md->md));
@ -324,93 +308,83 @@ absl::optional<absl::string_view> grpc_metadata_batch_get_value(
return *concatenated_value;
}
grpc_error_handle grpc_metadata_batch_substitute(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage,
grpc_mdelem new_mdelem) {
assert_valid_callouts(batch);
grpc_error_handle MetadataMap::Substitute(grpc_linked_mdelem* storage,
grpc_mdelem new_mdelem) {
AssertValidCallouts();
grpc_error_handle error = GRPC_ERROR_NONE;
grpc_mdelem old_mdelem = storage->md;
if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) {
maybe_unlink_callout(batch, storage);
MaybeUnlinkCallout(storage);
storage->md = new_mdelem;
error = maybe_link_callout(batch, storage);
error = MaybeLinkCallout(storage);
if (error != GRPC_ERROR_NONE) {
unlink_storage(&batch->list, storage);
unlink_storage(&list_, storage);
GRPC_MDELEM_UNREF(storage->md);
}
} else {
storage->md = new_mdelem;
}
GRPC_MDELEM_UNREF(old_mdelem);
assert_valid_callouts(batch);
AssertValidCallouts();
return error;
}
void grpc_metadata_batch_clear(grpc_metadata_batch* batch) {
grpc_metadata_batch_destroy(batch);
grpc_metadata_batch_init(batch);
void MetadataMap::Clear() {
this->~MetadataMap();
new (this) MetadataMap();
}
bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch) {
return batch->list.head == nullptr &&
batch->deadline == GRPC_MILLIS_INF_FUTURE;
}
size_t grpc_metadata_batch_size(grpc_metadata_batch* batch) {
size_t MetadataMap::TransportSize() const {
size_t size = 0;
for (grpc_linked_mdelem* elem = batch->list.head; elem != nullptr;
for (grpc_linked_mdelem* elem = list_.head; elem != nullptr;
elem = elem->next) {
size += GRPC_MDELEM_LENGTH(elem->md);
}
return size;
}
static void add_error(grpc_error_handle* composite, grpc_error_handle error,
const char* composite_error_string) {
if (error == GRPC_ERROR_NONE) return;
if (*composite == GRPC_ERROR_NONE) {
*composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string);
bool MetadataMap::ReplaceIfExists(grpc_slice key, grpc_slice value) {
AssertValidCallouts();
for (grpc_linked_mdelem* l = list_.head; l != nullptr; l = l->next) {
if (grpc_slice_eq(GRPC_MDKEY(l->md), key)) {
auto new_mdelem = grpc_mdelem_from_slices(grpc_slice_ref_internal(key),
grpc_slice_ref_internal(value));
GRPC_MDELEM_UNREF(l->md);
l->md = new_mdelem;
AssertValidCallouts();
return true;
}
}
*composite = grpc_error_add_child(*composite, error);
AssertValidCallouts();
return false;
}
grpc_error_handle grpc_metadata_batch_filter(
grpc_metadata_batch* batch, grpc_metadata_batch_filter_func func,
void* user_data, const char* composite_error_string) {
grpc_linked_mdelem* l = batch->list.head;
grpc_error_handle error = GRPC_ERROR_NONE;
while (l) {
grpc_linked_mdelem* next = l->next;
grpc_filtered_mdelem new_mdelem = func(user_data, l->md);
add_error(&error, new_mdelem.error, composite_error_string);
if (GRPC_MDISNULL(new_mdelem.md)) {
grpc_metadata_batch_remove(batch, l);
} else if (new_mdelem.md.payload != l->md.payload) {
grpc_metadata_batch_substitute(batch, l, new_mdelem.md);
}
l = next;
}
return error;
} // namespace grpc_core
void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
const grpc_slice& value) {
grpc_mdelem old_mdelem = storage->md;
grpc_mdelem new_mdelem = grpc_mdelem_from_slices(
grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value);
storage->md = new_mdelem;
GRPC_MDELEM_UNREF(old_mdelem);
}
void grpc_metadata_batch_copy(grpc_metadata_batch* src,
grpc_metadata_batch* dst,
grpc_linked_mdelem* storage) {
grpc_metadata_batch_init(dst);
dst->deadline = src->deadline;
(*dst)->SetDeadline((*src)->deadline());
size_t i = 0;
for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr;
elem = elem->next) {
(*src)->ForEach([&](grpc_mdelem md) {
// If the mdelem is not external, take a ref.
// Otherwise, create a new copy, holding its own refs to the
// underlying slices.
grpc_mdelem md;
if (GRPC_MDELEM_STORAGE(elem->md) != GRPC_MDELEM_STORAGE_EXTERNAL) {
md = GRPC_MDELEM_REF(elem->md);
if (GRPC_MDELEM_STORAGE(md) != GRPC_MDELEM_STORAGE_EXTERNAL) {
md = GRPC_MDELEM_REF(md);
} else {
md = grpc_mdelem_from_slices(
grpc_slice_ref_internal(GRPC_MDKEY(elem->md)),
grpc_slice_ref_internal(GRPC_MDVALUE(elem->md)));
md = grpc_mdelem_from_slices(grpc_slice_ref_internal(GRPC_MDKEY(md)),
grpc_slice_ref_internal(GRPC_MDVALUE(md)));
}
// Error unused in non-debug builds.
grpc_error_handle GRPC_UNUSED error =
@ -420,11 +394,14 @@ void grpc_metadata_batch_copy(grpc_metadata_batch* src,
// the case here, because we would not have been allowed to create
// a source batch that had that kind of conflict.
GPR_DEBUG_ASSERT(error == GRPC_ERROR_NONE);
}
});
}
void grpc_metadata_batch_move(grpc_metadata_batch* src,
grpc_metadata_batch* dst) {
*dst = *src;
grpc_metadata_batch_init(src);
grpc_error_handle grpc_attach_md_to_error(grpc_error_handle src,
grpc_mdelem md) {
grpc_error_handle out = grpc_error_set_str(
grpc_error_set_str(src, GRPC_ERROR_STR_KEY,
grpc_slice_ref_internal(GRPC_MDKEY(md))),
GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md)));
return out;
}

@ -49,42 +49,190 @@ typedef struct grpc_mdelem_list {
grpc_linked_mdelem* tail;
} grpc_mdelem_list;
typedef struct grpc_metadata_batch {
/** Metadata elements in this batch */
grpc_mdelem_list list;
grpc_metadata_batch_callouts idx;
/** Used to calculate grpc-timeout at the point of sending,
or GRPC_MILLIS_INF_FUTURE if this batch does not need to send a
grpc-timeout */
grpc_millis deadline;
struct grpc_filtered_mdelem {
grpc_error_handle error;
grpc_mdelem md;
};
#define GRPC_FILTERED_ERROR(error) \
{ (error), GRPC_MDNULL }
#define GRPC_FILTERED_MDELEM(md) \
{ GRPC_ERROR_NONE, (md) }
#define GRPC_FILTERED_REMOVE() \
{ GRPC_ERROR_NONE, GRPC_MDNULL }
namespace grpc_core {
// MetadataMap encodes the mapping of metadata keys to metadata values.
// Right now the API presented is the minimal one that will allow us to
// substitute this type for grpc_metadata_batch in a relatively easy fashion. At
// that point we'll start iterating this API into something that's ergonomic
// again, whilst minimally holding the performance bar already set (and
// hopefully improving some things).
// In the meantime, we're not going to invest much time in ephemeral API
// documentation, so if you must use one of these API's and it's not obvious
// how, reach out to ctiller.
class MetadataMap {
public:
MetadataMap();
~MetadataMap();
MetadataMap(const MetadataMap&) = delete;
MetadataMap& operator=(const MetadataMap&) = delete;
MetadataMap(MetadataMap&&) noexcept;
MetadataMap& operator=(MetadataMap&&) noexcept;
template <typename Encoder>
void Encode(Encoder* encoder) const {
for (auto* l = list.head; l; l = l->next) {
for (auto* l = list_.head; l; l = l->next) {
encoder->Encode(l->md);
}
if (deadline != GRPC_MILLIS_INF_FUTURE) encoder->EncodeDeadline(deadline);
if (deadline_ != GRPC_MILLIS_INF_FUTURE) encoder->EncodeDeadline(deadline_);
}
} grpc_metadata_batch;
void grpc_metadata_batch_init(grpc_metadata_batch* batch);
void grpc_metadata_batch_destroy(grpc_metadata_batch* batch);
void grpc_metadata_batch_clear(grpc_metadata_batch* batch);
bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch);
template <typename F>
void ForEach(F f) const {
for (auto* l = list_.head; l; l = l->next) {
f(l->md);
}
}
template <typename F>
grpc_error_handle Filter(F f, const char* composite_error_string) {
grpc_linked_mdelem* l = list_.head;
grpc_error_handle error = GRPC_ERROR_NONE;
auto add_error = [&](grpc_error_handle new_error) {
if (new_error == GRPC_ERROR_NONE) return;
if (error == GRPC_ERROR_NONE) {
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string);
}
error = grpc_error_add_child(error, new_error);
};
while (l) {
grpc_linked_mdelem* next = l->next;
grpc_filtered_mdelem new_mdelem = f(l->md);
add_error(new_mdelem.error);
if (GRPC_MDISNULL(new_mdelem.md)) {
Remove(l);
} else if (new_mdelem.md.payload != l->md.payload) {
Substitute(l, new_mdelem.md);
}
l = next;
}
return error;
}
// Set key to value if it exists and return true, otherwise return false.
bool ReplaceIfExists(grpc_slice key, grpc_slice value);
void Clear();
bool empty() const { return count() == 0; }
size_t count() const {
return list_.count + (deadline_ == GRPC_MILLIS_INF_FUTURE ? 0 : 1);
}
size_t non_deadline_count() const { return list_.count; }
size_t default_count() const { return list_.default_count; }
size_t TransportSize() const;
void Remove(grpc_linked_mdelem* storage);
void Remove(grpc_metadata_batch_callouts_index idx);
absl::optional<grpc_slice> Remove(grpc_slice key);
grpc_error_handle Substitute(grpc_linked_mdelem* storage,
grpc_mdelem new_mdelem);
absl::optional<absl::string_view> GetValue(
absl::string_view target_key, std::string* concatenated_value) const;
grpc_error_handle LinkHead(grpc_linked_mdelem* storage) GRPC_MUST_USE_RESULT;
grpc_error_handle LinkHead(grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx)
GRPC_MUST_USE_RESULT;
grpc_error_handle LinkTail(grpc_linked_mdelem* storage) GRPC_MUST_USE_RESULT;
grpc_error_handle LinkTail(grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx)
GRPC_MUST_USE_RESULT;
grpc_error_handle AddHead(grpc_linked_mdelem* storage) GRPC_MUST_USE_RESULT;
grpc_error_handle AddHead(grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT;
grpc_error_handle AddTail(grpc_linked_mdelem* storage) GRPC_MUST_USE_RESULT;
grpc_error_handle AddTail(grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT;
void CopyFrom(MetadataMap* src, grpc_linked_mdelem* storage);
#ifndef NDEBUG
void AssertOk();
#else
void AssertOk() {}
#endif
grpc_millis deadline() const { return deadline_; }
void SetDeadline(grpc_millis deadline) { deadline_ = deadline; }
void ClearDeadline() { SetDeadline(GRPC_MILLIS_INF_FUTURE); }
const grpc_metadata_batch_callouts* legacy_index() const { return &idx_; }
private:
void AssertValidCallouts();
grpc_error_handle LinkCallout(grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx)
GRPC_MUST_USE_RESULT;
grpc_error_handle MaybeLinkCallout(grpc_linked_mdelem* storage)
GRPC_MUST_USE_RESULT;
void MaybeUnlinkCallout(grpc_linked_mdelem* storage);
/** Metadata elements in this batch */
grpc_mdelem_list list_;
grpc_metadata_batch_callouts idx_;
/** Used to calculate grpc-timeout at the point of sending,
or GRPC_MILLIS_INF_FUTURE if this batch does not need to send a
grpc-timeout */
grpc_millis deadline_;
};
} // namespace grpc_core
using grpc_metadata_batch =
grpc_core::ManualConstructor<grpc_core::MetadataMap>;
inline void grpc_metadata_batch_init(grpc_metadata_batch* batch) {
batch->Init();
}
inline void grpc_metadata_batch_destroy(grpc_metadata_batch* batch) {
batch->Destroy();
}
inline void grpc_metadata_batch_clear(grpc_metadata_batch* batch) {
(*batch)->Clear();
}
inline bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch) {
return (*batch)->empty();
}
/* Returns the transport size of the batch. */
size_t grpc_metadata_batch_size(grpc_metadata_batch* batch);
inline size_t grpc_metadata_batch_size(grpc_metadata_batch* batch) {
return (*batch)->TransportSize();
}
/** Remove \a storage from the batch, unreffing the mdelem contained */
void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage);
void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
grpc_metadata_batch_callouts_index idx);
inline void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage) {
(*batch)->Remove(storage);
}
inline void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
grpc_metadata_batch_callouts_index idx) {
(*batch)->Remove(idx);
}
/** Substitute a new mdelem for an old value */
grpc_error_handle grpc_metadata_batch_substitute(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage,
grpc_mdelem new_mdelem);
inline grpc_error_handle grpc_metadata_batch_substitute(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
grpc_mdelem new_mdelem) {
return (*batch)->Substitute(storage, new_mdelem);
}
void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
const grpc_slice& value);
@ -96,33 +244,43 @@ void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
If the key is present more than once in the batch, constructs a
comma-concatenated string of all values in concatenated_value and returns a
string_view of that string. */
absl::optional<absl::string_view> grpc_metadata_batch_get_value(
inline absl::optional<absl::string_view> grpc_metadata_batch_get_value(
grpc_metadata_batch* batch, absl::string_view target_key,
std::string* concatenated_value);
std::string* concatenated_value) {
return (*batch)->GetValue(target_key, concatenated_value);
}
/** Add \a storage to the beginning of \a batch. storage->md is
assumed to be valid.
\a storage is owned by the caller and must survive for the
lifetime of batch. This usually means it should be around
for the lifetime of the call. */
grpc_error_handle grpc_metadata_batch_link_head(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage)
GRPC_MUST_USE_RESULT;
grpc_error_handle grpc_metadata_batch_link_head(
inline GRPC_MUST_USE_RESULT grpc_error_handle grpc_metadata_batch_link_head(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage) {
return (*batch)->LinkHead(storage);
}
inline GRPC_MUST_USE_RESULT grpc_error_handle grpc_metadata_batch_link_head(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx) GRPC_MUST_USE_RESULT;
grpc_metadata_batch_callouts_index idx) {
return (*batch)->LinkHead(storage, idx);
}
/** Add \a storage to the end of \a batch. storage->md is
assumed to be valid.
\a storage is owned by the caller and must survive for the
lifetime of batch. This usually means it should be around
for the lifetime of the call. */
grpc_error_handle grpc_metadata_batch_link_tail(grpc_metadata_batch* batch,
grpc_linked_mdelem* storage)
GRPC_MUST_USE_RESULT;
grpc_error_handle grpc_metadata_batch_link_tail(
inline GRPC_MUST_USE_RESULT grpc_error_handle grpc_metadata_batch_link_tail(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage) {
return (*batch)->LinkTail(storage);
}
inline GRPC_MUST_USE_RESULT grpc_error_handle grpc_metadata_batch_link_tail(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx) GRPC_MUST_USE_RESULT;
grpc_metadata_batch_callouts_index idx) {
return (*batch)->LinkTail(storage, idx);
}
/** Add \a elem_to_add as the first element in \a batch, using
\a storage as backing storage for the linked list element.
@ -130,9 +288,11 @@ grpc_error_handle grpc_metadata_batch_link_tail(
lifetime of batch. This usually means it should be around
for the lifetime of the call.
Takes ownership of \a elem_to_add */
grpc_error_handle grpc_metadata_batch_add_head(
inline grpc_error_handle grpc_metadata_batch_add_head(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT;
grpc_mdelem elem_to_add) {
return (*batch)->AddHead(storage, elem_to_add);
}
// TODO(arjunroy, roth): Remove redundant methods.
// add/link_head/tail are almost identical.
@ -156,9 +316,11 @@ inline grpc_error_handle GRPC_MUST_USE_RESULT grpc_metadata_batch_add_head(
lifetime of batch. This usually means it should be around
for the lifetime of the call.
Takes ownership of \a elem_to_add */
grpc_error_handle grpc_metadata_batch_add_tail(
inline GRPC_MUST_USE_RESULT grpc_error_handle grpc_metadata_batch_add_tail(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT;
grpc_mdelem elem_to_add) {
return (*batch)->AddTail(storage, elem_to_add);
}
inline grpc_error_handle GRPC_MUST_USE_RESULT grpc_metadata_batch_add_tail(
grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
@ -177,30 +339,19 @@ inline grpc_error_handle GRPC_MUST_USE_RESULT grpc_metadata_batch_add_tail(
grpc_error_handle grpc_attach_md_to_error(grpc_error_handle src,
grpc_mdelem md);
struct grpc_filtered_mdelem {
grpc_error_handle error;
grpc_mdelem md;
};
#define GRPC_FILTERED_ERROR(error) \
{ (error), GRPC_MDNULL }
#define GRPC_FILTERED_MDELEM(md) \
{ GRPC_ERROR_NONE, (md) }
#define GRPC_FILTERED_REMOVE() \
{ GRPC_ERROR_NONE, GRPC_MDNULL }
typedef grpc_filtered_mdelem (*grpc_metadata_batch_filter_func)(
void* user_data, grpc_mdelem elem);
grpc_error_handle grpc_metadata_batch_filter(
inline GRPC_MUST_USE_RESULT grpc_error_handle grpc_metadata_batch_filter(
grpc_metadata_batch* batch, grpc_metadata_batch_filter_func func,
void* user_data, const char* composite_error_string) GRPC_MUST_USE_RESULT;
void* user_data, const char* composite_error_string) {
return (*batch)->Filter(
[=](grpc_mdelem elem) { return func(user_data, elem); },
composite_error_string);
}
#ifndef NDEBUG
void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch);
#else
#define grpc_metadata_batch_assert_ok(batch) \
do { \
} while (0)
#endif
inline void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch) {
(*batch)->AssertOk();
}
/// Copies \a src to \a dst. \a storage must point to an array of
/// \a grpc_linked_mdelem structs of at least the same size as \a src.
@ -215,7 +366,9 @@ void grpc_metadata_batch_copy(grpc_metadata_batch* src,
grpc_metadata_batch* dst,
grpc_linked_mdelem* storage);
void grpc_metadata_batch_move(grpc_metadata_batch* src,
grpc_metadata_batch* dst);
inline void grpc_metadata_batch_move(grpc_metadata_batch* src,
grpc_metadata_batch* dst) {
dst->Init(std::move(**src));
}
#endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H */

@ -51,15 +51,16 @@ static void put_metadata(grpc_mdelem md, std::vector<std::string>* out) {
gpr_free(dump);
}
static void put_metadata_list(grpc_metadata_batch md,
static void put_metadata_list(const grpc_metadata_batch& md,
std::vector<std::string>* out) {
grpc_linked_mdelem* m;
for (m = md.list.head; m != nullptr; m = m->next) {
if (m != md.list.head) out->push_back(", ");
put_metadata(m->md, out);
}
if (md.deadline != GRPC_MILLIS_INF_FUTURE) {
out->push_back(absl::StrFormat(" deadline=%" PRId64, md.deadline));
bool first = true;
md->ForEach([&](grpc_mdelem elem) {
if (!first) out->push_back(", ");
first = false;
put_metadata(elem, out);
});
if (md->deadline() != GRPC_MILLIS_INF_FUTURE) {
out->push_back(absl::StrFormat(" deadline=%" PRId64, md->deadline()));
}
}

@ -123,13 +123,15 @@ void OpenCensusCallTracer::OpenCensusCallAttemptTracer::RecordReceivedMessage(
namespace {
void FilterTrailingMetadata(grpc_metadata_batch* b, uint64_t* elapsed_time) {
if (b->idx.named.grpc_server_stats_bin != nullptr) {
if ((*b)->legacy_index()->named.grpc_server_stats_bin != nullptr) {
ServerStatsDeserialize(
reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(
GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md))),
GRPC_SLICE_LENGTH(GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md)),
reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(GRPC_MDVALUE(
(*b)->legacy_index()->named.grpc_server_stats_bin->md))),
GRPC_SLICE_LENGTH(GRPC_MDVALUE(
(*b)->legacy_index()->named.grpc_server_stats_bin->md)),
elapsed_time);
grpc_metadata_batch_remove(b, b->idx.named.grpc_server_stats_bin);
grpc_metadata_batch_remove(
b, (*b)->legacy_index()->named.grpc_server_stats_bin);
}
}

@ -45,17 +45,18 @@ struct ServerMetadataElements {
void FilterInitialMetadata(grpc_metadata_batch* b,
ServerMetadataElements* sml) {
if (b->idx.named.path != nullptr) {
sml->path = grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.path->md));
if ((*b)->legacy_index()->named.path != nullptr) {
sml->path = grpc_slice_ref_internal(
GRPC_MDVALUE((*b)->legacy_index()->named.path->md));
}
if (b->idx.named.grpc_trace_bin != nullptr) {
sml->tracing_slice =
grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_trace_bin->md));
if ((*b)->legacy_index()->named.grpc_trace_bin != nullptr) {
sml->tracing_slice = grpc_slice_ref_internal(
GRPC_MDVALUE((*b)->legacy_index()->named.grpc_trace_bin->md));
grpc_metadata_batch_remove(b, GRPC_BATCH_GRPC_TRACE_BIN);
}
if (b->idx.named.grpc_tags_bin != nullptr) {
sml->census_proto =
grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_tags_bin->md));
if ((*b)->legacy_index()->named.grpc_tags_bin != nullptr) {
sml->census_proto = grpc_slice_ref_internal(
GRPC_MDVALUE((*b)->legacy_index()->named.grpc_tags_bin->md));
grpc_metadata_batch_remove(b, GRPC_BATCH_GRPC_TAGS_BIN);
}
}

@ -263,9 +263,10 @@ static void server_start_transport_stream_op_batch(
if (data->call == g_server_call_stack) {
if (op->send_initial_metadata) {
auto* batch = op->payload->send_initial_metadata.send_initial_metadata;
if (batch->idx.named.status != nullptr) {
if ((*batch)->legacy_index()->named.status != nullptr) {
/* Replace the HTTP status with 404 */
grpc_metadata_batch_substitute(batch, batch->idx.named.status,
grpc_metadata_batch_substitute(batch,
(*batch)->legacy_index()->named.status,
GRPC_MDELEM_STATUS_404);
}
}

@ -145,16 +145,16 @@ MATCHER_P(GrpcErrorMessageContains, msg, "") {
// Verify that the lower-level metadata has the same content as the gRPC
// metadata.
void VerifyMetadataEqual(const Metadata& md, grpc_metadata_batch grpc_md) {
grpc_linked_mdelem* elm = grpc_md.list.head;
for (size_t i = 0; i < md.size(); ++i) {
ASSERT_NE(elm, nullptr);
EXPECT_EQ(grpc_core::StringViewFromSlice(GRPC_MDKEY(elm->md)), md[i].first);
EXPECT_EQ(grpc_core::StringViewFromSlice(GRPC_MDVALUE(elm->md)),
void VerifyMetadataEqual(const Metadata& md,
const grpc_metadata_batch& grpc_md) {
size_t i = 0;
grpc_md->ForEach([&](grpc_mdelem mdelm) {
EXPECT_EQ(grpc_core::StringViewFromSlice(GRPC_MDKEY(mdelm)), md[i].first);
EXPECT_EQ(grpc_core::StringViewFromSlice(GRPC_MDVALUE(mdelm)),
md[i].second);
elm = elm->next;
}
EXPECT_EQ(elm, nullptr);
i++;
});
EXPECT_EQ(md.size(), i);
}
// RAII helper classes for constructing gRPC metadata and receiving callbacks.

@ -169,25 +169,16 @@ static void verify(const verify_params params, const char* expected,
for (i = 0; i < nheaders; i++) {
char* key = va_arg(l, char*);
char* value = va_arg(l, char*);
if (i) {
e[i - 1].next = &e[i];
e[i].prev = &e[i - 1];
}
grpc_slice value_slice = grpc_slice_from_static_string(value);
if (!params.only_intern_key) {
value_slice = grpc_slice_intern(value_slice);
}
e[i].md = grpc_mdelem_from_slices(
grpc_slice_intern(grpc_slice_from_static_string(key)), value_slice);
GPR_ASSERT(GRPC_ERROR_NONE == b->LinkTail(&e[i]));
}
e[0].prev = nullptr;
e[nheaders - 1].next = nullptr;
va_end(l);
b.list.head = &e[0];
b.list.tail = &e[nheaders - 1];
b.list.count = nheaders;
if (cap_to_delete == num_to_delete) {
cap_to_delete = GPR_MAX(2 * cap_to_delete, 1000);
to_delete = static_cast<void**>(
@ -206,7 +197,7 @@ static void verify(const verify_params params, const char* expected,
16384, /* max_frame_size */
&stats /* stats */
};
g_compressor->EncodeHeaders(hopt, b, &output);
g_compressor->EncodeHeaders(hopt, *b, &output);
verify_frames(output, params.eof);
merged = grpc_slice_merge(output.slices, output.count);
grpc_slice_buffer_destroy_internal(&output);
@ -266,9 +257,7 @@ static void verify_continuation_headers(const char* key, const char* value,
e[0].md = elem;
e[0].prev = nullptr;
e[0].next = nullptr;
b.list.head = &e[0];
b.list.tail = &e[0];
b.list.count = 1;
GPR_ASSERT(GRPC_ERROR_NONE == b->LinkTail(&e[0]));
grpc_slice_buffer_init(&output);
grpc_transport_one_way_stats stats;
@ -279,7 +268,7 @@ static void verify_continuation_headers(const char* key, const char* value,
false, /* use_true_binary_metadata */
150, /* max_frame_size */
&stats /* stats */};
g_compressor->EncodeHeaders(hopt, b, &output);
g_compressor->EncodeHeaders(hopt, *b, &output);
verify_frames(output, is_eof);
grpc_slice_buffer_destroy_internal(&output);
grpc_metadata_batch_destroy(&b);
@ -356,9 +345,7 @@ static void verify_table_size_change_match_elem_size(const char* key,
e[0].md = elem;
e[0].prev = nullptr;
e[0].next = nullptr;
b.list.head = &e[0];
b.list.tail = &e[0];
b.list.count = 1;
GPR_ASSERT(GRPC_ERROR_NONE == b->LinkTail(&e[0]));
grpc_slice_buffer_init(&output);
grpc_transport_one_way_stats stats;
@ -369,7 +356,7 @@ static void verify_table_size_change_match_elem_size(const char* key,
use_true_binary, /* use_true_binary_metadata */
16384, /* max_frame_size */
&stats /* stats */};
g_compressor->EncodeHeaders(hopt, b, &output);
g_compressor->EncodeHeaders(hopt, *b, &output);
verify_frames(output, false);
grpc_slice_buffer_destroy_internal(&output);
grpc_metadata_batch_destroy(&b);

@ -71,7 +71,7 @@ static void BM_HpackEncoderEncodeDeadline(benchmark::State& state) {
grpc_metadata_batch b;
grpc_metadata_batch_init(&b);
b.deadline = saved_now + 30 * 1000;
b->SetDeadline(saved_now + 30 * 1000);
grpc_core::HPackCompressor c;
grpc_transport_one_way_stats stats;
@ -87,7 +87,7 @@ static void BM_HpackEncoderEncodeDeadline(benchmark::State& state) {
static_cast<size_t>(1024),
&stats,
},
b, &outbuf);
*b, &outbuf);
grpc_slice_buffer_reset_and_unref_internal(&outbuf);
grpc_core::ExecCtx::Get()->Flush();
}
@ -136,7 +136,7 @@ static void BM_HpackEncoderEncodeHeader(benchmark::State& state) {
static_cast<size_t>(state.range(1) + kEnsureMaxFrameAtLeast),
&stats,
},
b, &outbuf);
*b, &outbuf);
if (!logged_representative_output && state.iterations() > 3) {
logged_representative_output = true;
for (size_t i = 0; i < outbuf.count; i++) {

@ -325,7 +325,7 @@ static void BM_StreamCreateSendInitialMetadataDestroy(benchmark::State& state) {
grpc_metadata_batch b;
grpc_metadata_batch_init(&b);
b.deadline = GRPC_MILLIS_INF_FUTURE;
b->ClearDeadline();
std::vector<grpc_mdelem> elems = Metadata::GetElems();
std::vector<grpc_linked_mdelem> storage(elems.size());
for (size_t i = 0; i < elems.size(); i++) {
@ -429,7 +429,7 @@ static void BM_TransportStreamSend(benchmark::State& state) {
grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> send_stream;
grpc_metadata_batch b;
grpc_metadata_batch_init(&b);
b.deadline = GRPC_MILLIS_INF_FUTURE;
b->ClearDeadline();
std::vector<grpc_mdelem> elems =
RepresentativeClientInitialMetadata::GetElems();
std::vector<grpc_linked_mdelem> storage(elems.size());
@ -573,7 +573,7 @@ static void BM_TransportStreamRecv(benchmark::State& state) {
grpc_metadata_batch_init(&b);
grpc_metadata_batch b_recv;
grpc_metadata_batch_init(&b_recv);
b.deadline = GRPC_MILLIS_INF_FUTURE;
b->ClearDeadline();
std::vector<grpc_mdelem> elems =
RepresentativeClientInitialMetadata::GetElems();
std::vector<grpc_linked_mdelem> storage(elems.size());

Loading…
Cancel
Save