Convert client_authority_filter to a promise. (#28565)

* Initial ideation

* progress

* progress

* x

* Automated change: Fix sanity tests

* erorrs

* better ordering

* working on it

* xx

* comment

* comment

* Get metadata pointer semantics temporarily right

* fix

* fix

* fix

* fix

* fix

* retry: send at most one cancel_stream op on each call attempt

* fixes

* revert earlier change

* split filter defn from impl

* add tests

* fixes

* Automated change: Fix sanity tests

* review feedback

* review feedback

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
Co-authored-by: Mark D. Roth <roth@google.com>
pull/28655/head
Craig Tiller 3 years ago committed by GitHub
parent 55d7631a99
commit d2042c1c05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      BUILD
  2. 36
      CMakeLists.txt
  3. 16
      build_autogenerated.yaml
  4. 6
      gRPC-C++.podspec
  5. 6
      gRPC-Core.podspec
  6. 3
      grpc.gemspec
  7. 3
      package.xml
  8. 91
      src/core/ext/filters/http/client_authority_filter.cc
  9. 31
      src/core/ext/filters/http/client_authority_filter.h
  10. 8
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  11. 497
      src/core/lib/channel/promise_based_filter.h
  12. 10
      src/core/lib/transport/metadata_batch.h
  13. 22
      src/core/lib/transport/transport_op_string.cc
  14. 5
      test/core/end2end/goaway_server_test.cc
  15. 32
      test/core/filters/BUILD
  16. 119
      test/core/filters/client_authority_filter_test.cc
  17. 3
      tools/doxygen/Doxyfile.c++.internal
  18. 3
      tools/doxygen/Doxyfile.core.internal
  19. 24
      tools/run_tests/generated/tests.json

@ -1934,6 +1934,7 @@ grpc_cc_library(
"src/core/lib/backoff/backoff.h",
"src/core/lib/channel/call_tracer.h",
"src/core/lib/channel/channel_stack.h",
"src/core/lib/channel/promise_based_filter.h",
"src/core/lib/channel/channel_stack_builder.h",
"src/core/lib/channel/channel_trace.h",
"src/core/lib/channel/channelz.h",
@ -2086,6 +2087,7 @@ grpc_cc_library(
visibility = ["@grpc:alt_grpc_base_legacy"],
deps = [
"arena",
"arena_promise",
"avl",
"bitset",
"channel_args",
@ -2108,6 +2110,7 @@ grpc_cc_library(
"json",
"memory_quota",
"orphanable",
"promise",
"ref_counted",
"ref_counted_ptr",
"resolved_address",

36
CMakeLists.txt generated

@ -820,6 +820,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx channelz_test)
add_dependencies(buildtests_cxx chunked_vector_test)
add_dependencies(buildtests_cxx cli_call_test)
add_dependencies(buildtests_cxx client_authority_filter_test)
add_dependencies(buildtests_cxx client_callback_end2end_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx client_channel_stress_test)
@ -9047,6 +9048,41 @@ target_link_libraries(cli_call_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(client_authority_filter_test
test/core/filters/client_authority_filter_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(client_authority_filter_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(client_authority_filter_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc
)
endif()
if(gRPC_BUILD_TESTS)

@ -742,6 +742,7 @@ libs:
- src/core/lib/channel/handshaker.h
- src/core/lib/channel/handshaker_factory.h
- src/core/lib/channel/handshaker_registry.h
- src/core/lib/channel/promise_based_filter.h
- src/core/lib/channel/status_util.h
- src/core/lib/compression/compression_internal.h
- src/core/lib/compression/message_compress.h
@ -848,6 +849,7 @@ libs:
- src/core/lib/json/json_util.h
- src/core/lib/matchers/matchers.h
- src/core/lib/promise/activity.h
- src/core/lib/promise/arena_promise.h
- src/core/lib/promise/context.h
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
@ -858,6 +860,7 @@ libs:
- src/core/lib/promise/loop.h
- src/core/lib/promise/map.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/promise.h
- src/core/lib/promise/race.h
- src/core/lib/promise/seq.h
- src/core/lib/resolver/resolver.h
@ -1851,6 +1854,7 @@ libs:
- src/core/lib/channel/handshaker.h
- src/core/lib/channel/handshaker_factory.h
- src/core/lib/channel/handshaker_registry.h
- src/core/lib/channel/promise_based_filter.h
- src/core/lib/channel/status_util.h
- src/core/lib/compression/compression_internal.h
- src/core/lib/compression/message_compress.h
@ -1956,6 +1960,7 @@ libs:
- src/core/lib/json/json.h
- src/core/lib/json/json_util.h
- src/core/lib/promise/activity.h
- src/core/lib/promise/arena_promise.h
- src/core/lib/promise/context.h
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
@ -1966,6 +1971,7 @@ libs:
- src/core/lib/promise/loop.h
- src/core/lib/promise/map.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/promise.h
- src/core/lib/promise/race.h
- src/core/lib/promise/seq.h
- src/core/lib/resolver/resolver.h
@ -5057,6 +5063,16 @@ targets:
- test/cpp/util/service_describer.cc
deps:
- grpc++_test_util
- name: client_authority_filter_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/filters/client_authority_filter_test.cc
deps:
- grpc
uses_polling: false
- name: client_callback_end2end_test
gtest: true
build: test

6
gRPC-C++.podspec generated

@ -587,6 +587,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/handshaker.h',
'src/core/lib/channel/handshaker_factory.h',
'src/core/lib/channel/handshaker_registry.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression_internal.h',
'src/core/lib/compression/message_compress.h',
@ -721,6 +722,7 @@ Pod::Spec.new do |s|
'src/core/lib/matchers/matchers.h',
'src/core/lib/profiling/timers.h',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
@ -731,6 +733,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
'src/core/lib/promise/seq.h',
'src/core/lib/resolver/resolver.h',
@ -1320,6 +1323,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/handshaker.h',
'src/core/lib/channel/handshaker_factory.h',
'src/core/lib/channel/handshaker_registry.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression_internal.h',
'src/core/lib/compression/message_compress.h',
@ -1454,6 +1458,7 @@ Pod::Spec.new do |s|
'src/core/lib/matchers/matchers.h',
'src/core/lib/profiling/timers.h',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
@ -1464,6 +1469,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
'src/core/lib/promise/seq.h',
'src/core/lib/resolver/resolver.h',

6
gRPC-Core.podspec generated

@ -880,6 +880,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/handshaker_factory.h',
'src/core/lib/channel/handshaker_registry.cc',
'src/core/lib/channel/handshaker_registry.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/status_util.cc',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression.cc',
@ -1174,6 +1175,7 @@ Pod::Spec.new do |s|
'src/core/lib/profiling/timers.h',
'src/core/lib/promise/activity.cc',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
@ -1184,6 +1186,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
'src/core/lib/promise/seq.h',
'src/core/lib/resolver/resolver.cc',
@ -1862,6 +1865,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/handshaker.h',
'src/core/lib/channel/handshaker_factory.h',
'src/core/lib/channel/handshaker_registry.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression_internal.h',
'src/core/lib/compression/message_compress.h',
@ -1996,6 +2000,7 @@ Pod::Spec.new do |s|
'src/core/lib/matchers/matchers.h',
'src/core/lib/profiling/timers.h',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
@ -2006,6 +2011,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
'src/core/lib/promise/seq.h',
'src/core/lib/resolver/resolver.h',

3
grpc.gemspec generated

@ -799,6 +799,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/channel/handshaker_factory.h )
s.files += %w( src/core/lib/channel/handshaker_registry.cc )
s.files += %w( src/core/lib/channel/handshaker_registry.h )
s.files += %w( src/core/lib/channel/promise_based_filter.h )
s.files += %w( src/core/lib/channel/status_util.cc )
s.files += %w( src/core/lib/channel/status_util.h )
s.files += %w( src/core/lib/compression/compression.cc )
@ -1093,6 +1094,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/profiling/timers.h )
s.files += %w( src/core/lib/promise/activity.cc )
s.files += %w( src/core/lib/promise/activity.h )
s.files += %w( src/core/lib/promise/arena_promise.h )
s.files += %w( src/core/lib/promise/context.h )
s.files += %w( src/core/lib/promise/detail/basic_seq.h )
s.files += %w( src/core/lib/promise/detail/promise_factory.h )
@ -1103,6 +1105,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/promise/loop.h )
s.files += %w( src/core/lib/promise/map.h )
s.files += %w( src/core/lib/promise/poll.h )
s.files += %w( src/core/lib/promise/promise.h )
s.files += %w( src/core/lib/promise/race.h )
s.files += %w( src/core/lib/promise/seq.h )
s.files += %w( src/core/lib/resolver/resolver.cc )

3
package.xml generated

@ -779,6 +779,7 @@
<file baseinstalldir="/" name="src/core/lib/channel/handshaker_factory.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/handshaker_registry.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/handshaker_registry.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/promise_based_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/status_util.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/status_util.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/compression/compression.cc" role="src" />
@ -1073,6 +1074,7 @@
<file baseinstalldir="/" name="src/core/lib/profiling/timers.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/activity.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/activity.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/arena_promise.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/context.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/basic_seq.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/promise_factory.h" role="src" />
@ -1083,6 +1085,7 @@
<file baseinstalldir="/" name="src/core/lib/promise/loop.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/map.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/poll.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/promise.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/race.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/seq.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/resolver/resolver.cc" role="src" />

@ -28,7 +28,7 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/ext/filters/http/client_authority_filter.h"
#include "src/core/lib/channel/channel_stack_builder.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gpr/string.h"
@ -37,87 +37,43 @@
#include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/channel_stack_type.h"
namespace {
struct call_data {
grpc_core::CallCombiner* call_combiner;
};
struct channel_data {
grpc_core::Slice default_authority;
};
void client_authority_start_transport_stream_op_batch(
grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
channel_data* chand = static_cast<channel_data*>(elem->channel_data);
// 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->get_pointer(
grpc_core::HttpAuthorityMetadata()) == nullptr) {
batch->payload->send_initial_metadata.send_initial_metadata->Set(
grpc_core::HttpAuthorityMetadata(), chand->default_authority.Ref());
}
// Pass control down the stack.
grpc_call_next_op(elem, batch);
}
/* Constructor for call_data */
grpc_error_handle client_authority_init_call_elem(
grpc_call_element* elem, const grpc_call_element_args* args) {
call_data* calld = static_cast<call_data*>(elem->call_data);
calld->call_combiner = args->call_combiner;
return GRPC_ERROR_NONE;
}
/* Destructor for call_data */
void client_authority_destroy_call_elem(
grpc_call_element* /*elem*/, const grpc_call_final_info* /*final_info*/,
grpc_closure* /*ignored*/) {}
namespace grpc_core {
/* Constructor for channel_data */
grpc_error_handle client_authority_init_channel_elem(
grpc_channel_element* elem, grpc_channel_element_args* args) {
channel_data* chand = new (elem->channel_data) channel_data;
absl::StatusOr<ClientAuthorityFilter> ClientAuthorityFilter::Create(
const grpc_channel_args* args) {
const grpc_arg* default_authority_arg =
grpc_channel_args_find(args->channel_args, GRPC_ARG_DEFAULT_AUTHORITY);
grpc_channel_args_find(args, GRPC_ARG_DEFAULT_AUTHORITY);
if (default_authority_arg == nullptr) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
return absl::InvalidArgumentError(
"GRPC_ARG_DEFAULT_AUTHORITY channel arg. not found. Note that direct "
"channels must explicitly specify a value for this argument.");
}
const char* default_authority_str =
grpc_channel_arg_get_string(default_authority_arg);
if (default_authority_str == nullptr) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
return absl::InvalidArgumentError(
"GRPC_ARG_DEFAULT_AUTHORITY channel arg. must be a string");
}
chand->default_authority =
grpc_core::Slice::FromCopiedString(default_authority_str);
GPR_ASSERT(!args->is_last);
return GRPC_ERROR_NONE;
return ClientAuthorityFilter(Slice::FromCopiedString(default_authority_str));
}
/* Destructor for channel data */
void client_authority_destroy_channel_elem(grpc_channel_element* elem) {
static_cast<channel_data*>(elem->channel_data)->~channel_data();
ArenaPromise<TrailingMetadata> ClientAuthorityFilter::MakeCallPromise(
ClientInitialMetadata initial_metadata,
NextPromiseFactory next_promise_factory) {
// If no authority is set, set the default authority.
if (initial_metadata->get_pointer(HttpAuthorityMetadata()) == nullptr) {
initial_metadata->Set(HttpAuthorityMetadata(), default_authority_.Ref());
}
// We have no asynchronous work, so we can just ask the next promise to run,
// passing down initial_metadata.
return next_promise_factory(std::move(initial_metadata));
}
} // namespace
const grpc_channel_filter grpc_client_authority_filter = {
client_authority_start_transport_stream_op_batch,
grpc_channel_next_op,
sizeof(call_data),
client_authority_init_call_elem,
grpc_call_stack_ignore_set_pollset_or_pollset_set,
client_authority_destroy_call_elem,
sizeof(channel_data),
client_authority_init_channel_elem,
client_authority_destroy_channel_elem,
grpc_channel_next_get_info,
"authority"};
namespace {
const grpc_channel_filter grpc_client_authority_filter =
MakePromiseBasedFilter<ClientAuthorityFilter>();
static bool add_client_authority_filter(grpc_channel_stack_builder* builder) {
bool add_client_authority_filter(grpc_channel_stack_builder* builder) {
const grpc_channel_args* channel_args =
grpc_channel_stack_builder_get_channel_arguments(builder);
const grpc_arg* disable_client_authority_filter_arg = grpc_channel_args_find(
@ -132,12 +88,13 @@ static bool add_client_authority_filter(grpc_channel_stack_builder* builder) {
return grpc_channel_stack_builder_prepend_filter(
builder, &grpc_client_authority_filter, nullptr, nullptr);
}
} // namespace
namespace grpc_core {
void RegisterClientAuthorityFilter(CoreConfiguration::Builder* builder) {
builder->channel_init()->RegisterStage(GRPC_CLIENT_SUBCHANNEL, INT_MAX,
add_client_authority_filter);
builder->channel_init()->RegisterStage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX,
add_client_authority_filter);
}
} // namespace grpc_core

@ -21,14 +21,35 @@
#include <grpc/support/port_platform.h>
#include "absl/status/statusor.h"
#include <grpc/impl/codegen/compression_types.h>
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/slice/slice.h"
namespace grpc_core {
class ClientAuthorityFilter {
public:
static absl::StatusOr<ClientAuthorityFilter> Create(
const grpc_channel_args* args);
static constexpr bool is_client() { return true; }
static constexpr const char* name() { return "authority"; }
// Construct a promise for one call.
ArenaPromise<TrailingMetadata> MakeCallPromise(
ClientInitialMetadata initial_metadata,
NextPromiseFactory next_promise_factory);
/// Filter responsible for setting the authority header, if not already set. It
/// uses the value of the GRPC_ARG_DEFAULT_AUTHORITY channel arg if the initial
/// metadata doesn't already contain an authority value.
private:
explicit ClientAuthorityFilter(Slice default_authority)
: default_authority_(std::move(default_authority)) {}
Slice default_authority_;
};
extern const grpc_channel_filter grpc_client_authority_filter;
} // namespace grpc_core
#endif /* GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_AUTHORITY_FILTER_H */

@ -1353,8 +1353,10 @@ static void perform_stream_op_locked(void* stream_op,
s->context = op->payload->context;
s->traced = op->is_traced;
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
gpr_log(GPR_INFO, "perform_stream_op_locked: %s; on_complete = %p",
grpc_transport_stream_op_batch_string(op).c_str(), op->on_complete);
gpr_log(GPR_INFO,
"perform_stream_op_locked[s=%p; op=%p]: %s; on_complete = %p", s,
op, grpc_transport_stream_op_batch_string(op).c_str(),
op->on_complete);
if (op->send_initial_metadata) {
log_metadata(op_payload->send_initial_metadata.send_initial_metadata,
s->id, t->is_client, true);
@ -1598,7 +1600,7 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
gpr_log(GPR_INFO, "perform_stream_op[s=%p]: %s", s,
gpr_log(GPR_INFO, "perform_stream_op[s=%p; op=%p]: %s", s, op,
grpc_transport_stream_op_batch_string(op).c_str());
}

@ -0,0 +1,497 @@
// Copyright 2022 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_CORE_LIB_CHANNEL_PROMISE_BASED_FILTER_H
#define GRPC_CORE_LIB_CHANNEL_PROMISE_BASED_FILTER_H
// Scaffolding to allow the per-call part of a filter to be authored in a
// promise-style. Most of this will be removed once the promises conversion is
// completed.
#include <grpc/support/port_platform.h>
#include "absl/utility/utility.h"
#include <grpc/status.h>
#include <grpc/support/log.h>
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/transport/error_utils.h"
namespace grpc_core {
namespace promise_filter_detail {
class BaseCallData;
};
// Small unowned "handle" type to ensure one accessor at a time to metadata.
// The focus here is to get promises to use the syntax we'd like - we'll
// probably substitute some other smart pointer later.
template <typename T>
class MetadataHandle {
public:
MetadataHandle() = default;
MetadataHandle(const MetadataHandle&) = delete;
MetadataHandle& operator=(const MetadataHandle&) = delete;
MetadataHandle(MetadataHandle&& other) noexcept : handle_(other.handle_) {
other.handle_ = nullptr;
}
MetadataHandle& operator=(MetadataHandle&& other) noexcept {
handle_ = other.handle_;
other.handle_ = nullptr;
return *this;
}
T* operator->() const { return handle_; }
bool has_value() const { return handle_ != nullptr; }
static MetadataHandle TestOnlyWrap(T* p) { return MetadataHandle(p); }
private:
friend class promise_filter_detail::BaseCallData;
explicit MetadataHandle(T* handle) : handle_(handle) {}
T* Unwrap() {
T* result = handle_;
handle_ = nullptr;
return result;
}
T* handle_ = nullptr;
};
// Trailing metadata type
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
using TrailingMetadata = MetadataHandle<grpc_metadata_batch>;
// Client initial metadata type
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
using ClientInitialMetadata = MetadataHandle<grpc_metadata_batch>;
using NextPromiseFactory =
std::function<ArenaPromise<TrailingMetadata>(ClientInitialMetadata)>;
namespace promise_filter_detail {
// Call data shared between all implementations of promise-based filters.
class BaseCallData {
public:
BaseCallData(grpc_call_element* elem, const grpc_call_element_args* args)
: elem_(elem),
arena_(args->arena),
call_combiner_(args->call_combiner),
deadline_(args->deadline) {}
protected:
class ScopedContext : public promise_detail::Context<Arena> {
public:
explicit ScopedContext(BaseCallData* call_data)
: promise_detail::Context<Arena>(call_data->arena_) {}
};
static MetadataHandle<grpc_metadata_batch> WrapMetadata(
grpc_metadata_batch* p) {
return MetadataHandle<grpc_metadata_batch>(p);
}
static grpc_metadata_batch* UnwrapMetadata(
MetadataHandle<grpc_metadata_batch> p) {
return p.Unwrap();
}
grpc_call_element* elem() const { return elem_; }
CallCombiner* call_combiner() const { return call_combiner_; }
grpc_millis deadline() const { return deadline_; }
private:
grpc_call_element* const elem_;
Arena* const arena_;
CallCombiner* const call_combiner_;
const grpc_millis deadline_;
};
// Specific call data per channel filter.
// Note that we further specialize for clients and servers since their
// implementations are very different.
template <class ChannelFilter, bool kIsClient = ChannelFilter::is_client()>
class CallData;
// Client implementation of call data.
template <class ChannelFilter>
class CallData<ChannelFilter, true> : public BaseCallData {
public:
CallData(grpc_call_element* elem, const grpc_call_element_args* args)
: BaseCallData(elem, args) {
GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready_,
RecvTrailingMetadataReadyCallback, this,
grpc_schedule_on_exec_ctx);
}
~CallData() {
GPR_ASSERT(!is_polling_);
GRPC_ERROR_UNREF(cancelled_error_);
}
// Handle one grpc_transport_stream_op_batch
void StartBatch(grpc_transport_stream_op_batch* batch) {
// Fake out the activity based context.
ScopedContext context(this);
// If this is a cancel stream, cancel anything we have pending and propagate
// the cancellation.
if (batch->cancel_stream) {
GPR_ASSERT(!batch->send_initial_metadata &&
!batch->send_trailing_metadata && !batch->send_message &&
!batch->recv_initial_metadata && !batch->recv_message &&
!batch->recv_trailing_metadata);
Cancel(batch->payload->cancel_stream.cancel_error);
grpc_call_next_op(elem(), batch);
return;
}
// send_initial_metadata: seeing this triggers the start of the promise part
// of this filter.
if (batch->send_initial_metadata) {
// If we're already cancelled, just terminate the batch.
if (send_initial_state_ == SendInitialState::kCancelled) {
grpc_transport_stream_op_batch_finish_with_failure(
batch, GRPC_ERROR_REF(cancelled_error_), call_combiner());
return;
}
// Otherwise, we should not have seen a send_initial_metadata op yet.
GPR_ASSERT(send_initial_state_ == SendInitialState::kInitial);
// Mark ourselves as queued.
send_initial_state_ = SendInitialState::kQueued;
if (batch->recv_trailing_metadata) {
// If there's a recv_trailing_metadata op, we queue that too.
GPR_ASSERT(recv_trailing_state_ == RecvTrailingState::kInitial);
recv_trailing_state_ = RecvTrailingState::kQueued;
}
// This is the queuing!
send_initial_metadata_batch_ = batch;
// And kick start the promise.
StartPromise();
return;
}
// recv_trailing_metadata *without* send_initial_metadata: hook it so we can
// respond to it, and push it down.
if (batch->recv_trailing_metadata) {
GPR_ASSERT(recv_trailing_state_ == RecvTrailingState::kInitial);
recv_trailing_state_ = RecvTrailingState::kForwarded;
HookRecvTrailingMetadata(batch);
}
grpc_call_next_op(elem(), batch);
}
private:
// At what stage is our handling of send initial metadata?
enum class SendInitialState {
// Start state: no op seen
kInitial,
// We've seen the op, and started the promise in response to it, but have
// not yet sent the op to the next filter.
kQueued,
// We've sent the op to the next filter.
kForwarded,
// We were cancelled.
kCancelled
};
// At what stage is our handling of recv trailing metadata?
enum class RecvTrailingState {
// Start state: no op seen
kInitial,
// We saw the op, and since it was bundled with send initial metadata, we
// queued it until the send initial metadata can be sent to the next filter.
kQueued,
// We've forwarded the op to the next filter.
kForwarded,
// The op has completed from below, but we haven't yet forwarded it up (the
// promise gets to interject and mutate it).
kComplete,
// We've called the recv_metadata_ready callback from the original
// recv_trailing_metadata op that was presented to us.
kResponded,
// We've been cancelled and handled that locally.
// (i.e. whilst the recv_trailing_metadata op is queued in this filter).
kCancelled
};
// Handle cancellation.
void Cancel(grpc_error_handle error) {
// Track the latest reason for cancellation.
GRPC_ERROR_UNREF(cancelled_error_);
cancelled_error_ = GRPC_ERROR_REF(error);
// Stop running the promise.
promise_ = ArenaPromise<TrailingMetadata>();
// If we have an op queued, fail that op.
// Record what we've done.
if (send_initial_state_ == SendInitialState::kQueued) {
send_initial_state_ = SendInitialState::kCancelled;
if (recv_trailing_state_ == RecvTrailingState::kQueued) {
recv_trailing_state_ = RecvTrailingState::kCancelled;
}
grpc_transport_stream_op_batch_finish_with_failure(
absl::exchange(send_initial_metadata_batch_, nullptr),
GRPC_ERROR_REF(cancelled_error_), call_combiner());
} else {
send_initial_state_ = SendInitialState::kCancelled;
}
}
// Begin running the promise - which will ultimately take some initial
// metadata and return some trailing metadata.
void StartPromise() {
GPR_ASSERT(send_initial_state_ == SendInitialState::kQueued);
ChannelFilter* filter = static_cast<ChannelFilter*>(elem()->channel_data);
// Construct the promise.
promise_ = filter->MakeCallPromise(
WrapMetadata(send_initial_metadata_batch_->payload
->send_initial_metadata.send_initial_metadata),
[this](ClientInitialMetadata initial_metadata) {
return MakeNextPromise(std::move(initial_metadata));
});
// Poll once.
WakeInsideCombiner();
}
// Interject our callback into the op batch for recv trailing metadata ready.
// Stash a pointer to the trailing metadata that will be filled in, so we can
// manipulate it later.
void HookRecvTrailingMetadata(grpc_transport_stream_op_batch* batch) {
recv_trailing_metadata_ =
batch->payload->recv_trailing_metadata.recv_trailing_metadata;
original_recv_trailing_metadata_ready_ =
batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
&recv_trailing_metadata_ready_;
}
// Construct a promise that will "call" the next filter.
// Effectively:
// - put the modified initial metadata into the batch to be sent down.
// - return a wrapper around PollTrailingMetadata as the promise.
ArenaPromise<TrailingMetadata> MakeNextPromise(
ClientInitialMetadata initial_metadata) {
GPR_ASSERT(send_initial_state_ == SendInitialState::kQueued);
send_initial_metadata_batch_->payload->send_initial_metadata
.send_initial_metadata = UnwrapMetadata(std::move(initial_metadata));
return ArenaPromise<TrailingMetadata>(
[this]() { return PollTrailingMetadata(); });
}
// Wrapper to make it look like we're calling the next filter as a promise.
// First poll: send the send_initial_metadata op down the stack.
// All polls: await receiving the trailing metadata, then return it to the
// application.
Poll<TrailingMetadata> PollTrailingMetadata() {
if (send_initial_state_ == SendInitialState::kQueued) {
// First poll: pass the send_initial_metadata op down the stack.
GPR_ASSERT(send_initial_metadata_batch_ != nullptr);
send_initial_state_ = SendInitialState::kForwarded;
if (recv_trailing_state_ == RecvTrailingState::kQueued) {
// (and the recv_trailing_metadata op if it's part of the queuing)
HookRecvTrailingMetadata(send_initial_metadata_batch_);
recv_trailing_state_ = RecvTrailingState::kForwarded;
}
forward_send_initial_metadata_ = true;
}
switch (recv_trailing_state_) {
case RecvTrailingState::kInitial:
case RecvTrailingState::kQueued:
case RecvTrailingState::kForwarded:
// No trailing metadata yet: we are pending.
// We return that and expect the promise to be repolled later (if it's
// not cancelled).
return Pending{};
case RecvTrailingState::kComplete:
// We've received trailing metadata: pass it to the promise and allow it
// to adjust it.
return WrapMetadata(recv_trailing_metadata_);
case RecvTrailingState::kCancelled: {
// We've been cancelled: synthesize some trailing metadata and pass it
// to the calling promise for adjustment.
recv_trailing_metadata_->Clear();
SetStatusFromError(recv_trailing_metadata_, cancelled_error_);
return WrapMetadata(recv_trailing_metadata_);
}
case RecvTrailingState::kResponded:
// We've already responded to the caller: we can't do anything and we
// should never reach here.
abort();
}
GPR_UNREACHABLE_CODE(return Pending{});
}
static void RecvTrailingMetadataReadyCallback(void* arg,
grpc_error_handle error) {
static_cast<CallData*>(arg)->RecvTrailingMetadataReady(error);
}
void RecvTrailingMetadataReady(grpc_error_handle error) {
// If there was an error, we'll put that into the trailing metadata and
// proceed as if there was not.
if (error != GRPC_ERROR_NONE) {
SetStatusFromError(recv_trailing_metadata_, error);
}
// Record that we've got the callback.
GPR_ASSERT(recv_trailing_state_ == RecvTrailingState::kForwarded);
recv_trailing_state_ = RecvTrailingState::kComplete;
// Repoll the promise.
ScopedContext context(this);
WakeInsideCombiner();
}
// Given an error, fill in TrailingMetadata to represent that error.
void SetStatusFromError(grpc_metadata_batch* metadata,
grpc_error_handle error) {
grpc_status_code status_code = GRPC_STATUS_UNKNOWN;
std::string status_details;
grpc_error_get_status(error, deadline(), &status_code, &status_details,
nullptr, nullptr);
metadata->Set(GrpcStatusMetadata(), status_code);
metadata->Set(GrpcMessageMetadata(),
Slice::FromCopiedString(status_details));
}
// Wakeup and poll the promise if appropriate.
void WakeInsideCombiner() {
GPR_ASSERT(!is_polling_);
grpc_closure* call_closure = nullptr;
is_polling_ = true;
switch (send_initial_state_) {
case SendInitialState::kQueued:
case SendInitialState::kForwarded: {
// Poll the promise once since we're waiting for it.
Poll<TrailingMetadata> poll = promise_();
if (auto* r = absl::get_if<TrailingMetadata>(&poll)) {
GPR_ASSERT(recv_trailing_state_ == RecvTrailingState::kComplete);
GPR_ASSERT(recv_trailing_metadata_ == UnwrapMetadata(std::move(*r)));
recv_trailing_state_ = RecvTrailingState::kResponded;
call_closure =
absl::exchange(original_recv_trailing_metadata_ready_, nullptr);
}
} break;
case SendInitialState::kInitial:
case SendInitialState::kCancelled:
// If we get a response without sending anything, we just propagate that
// up. (note: that situation isn't possible once we finish the promise
// transition).
if (recv_trailing_state_ == RecvTrailingState::kComplete) {
recv_trailing_state_ = RecvTrailingState::kResponded;
call_closure =
absl::exchange(original_recv_trailing_metadata_ready_, nullptr);
}
break;
}
is_polling_ = false;
if (absl::exchange(forward_send_initial_metadata_, false)) {
grpc_call_next_op(elem(),
absl::exchange(send_initial_metadata_batch_, nullptr));
}
if (call_closure != nullptr) {
Closure::Run(DEBUG_LOCATION, call_closure, GRPC_ERROR_NONE);
}
}
// Contained promise
ArenaPromise<TrailingMetadata> promise_;
// Queued batch containing at least a send_initial_metadata op.
grpc_transport_stream_op_batch* send_initial_metadata_batch_ = nullptr;
// Pointer to where trailing metadata will be stored.
grpc_metadata_batch* recv_trailing_metadata_ = nullptr;
// Closure to call when we're done with the trailing metadata.
grpc_closure* original_recv_trailing_metadata_ready_ = nullptr;
// Our closure pointing to RecvTrailingMetadataReadyCallback.
grpc_closure recv_trailing_metadata_ready_;
// Error received during cancellation.
grpc_error_handle cancelled_error_ = GRPC_ERROR_NONE;
// State of the send_initial_metadata op.
SendInitialState send_initial_state_ = SendInitialState::kInitial;
// State of the recv_trailing_metadata op.
RecvTrailingState recv_trailing_state_ = RecvTrailingState::kInitial;
// Whether we're currently polling the promise.
bool is_polling_ = false;
// Whether we should forward send initial metadata after polling?
bool forward_send_initial_metadata_ = false;
};
} // namespace promise_filter_detail
// ChannelFilter contains the following:
// class SomeChannelFilter {
// public:
// static constexpr bool is_client();
// static constexpr const char* name();
// static absl::StatusOr<SomeChannelFilter> Create(
// const grpc_channel_args* args);
// ArenaPromise<TrailingMetadata> MakeCallPromise(
// InitialMetadata* initial_metadata, NextPromiseFactory next_promise);
// };
// TODO(ctiller): allow implementing get_channel_info, start_transport_op in
// some way on ChannelFilter.
template <typename ChannelFilter>
grpc_channel_filter MakePromiseBasedFilter() {
using CallData = promise_filter_detail::CallData<ChannelFilter>;
return grpc_channel_filter{
// start_transport_stream_op_batch
[](grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
static_cast<CallData*>(elem->call_data)->StartBatch(batch);
},
// start_transport_op - for now unsupported
grpc_channel_next_op,
// sizeof_call_data
sizeof(CallData),
// init_call_elem
[](grpc_call_element* elem, const grpc_call_element_args* args) {
new (elem->call_data) CallData(elem, args);
return GRPC_ERROR_NONE;
},
// set_pollset_or_pollset_set
grpc_call_stack_ignore_set_pollset_or_pollset_set,
// destroy_call_elem
[](grpc_call_element* elem, const grpc_call_final_info*, grpc_closure*) {
static_cast<CallData*>(elem->call_data)->~CallData();
},
// sizeof_channel_data
sizeof(ChannelFilter),
// init_channel_elem
[](grpc_channel_element* elem, grpc_channel_element_args* args) {
GPR_ASSERT(!args->is_last);
auto status = ChannelFilter::Create(args->channel_args);
if (!status.ok()) return absl_status_to_grpc_error(status.status());
new (elem->channel_data) ChannelFilter(std::move(*status));
return GRPC_ERROR_NONE;
},
// destroy_channel_elem
[](grpc_channel_element* elem) {
static_cast<ChannelFilter*>(elem->channel_data)->~ChannelFilter();
},
// get_channel_info
grpc_channel_next_get_info,
// name
ChannelFilter::name(),
};
}
} // namespace grpc_core
#endif // GRPC_CORE_LIB_CHANNEL_PROMISE_BASED_FILTER_H

@ -25,6 +25,7 @@
#include <limits>
#include "absl/strings/escaping.h"
#include "absl/strings/match.h"
#include "absl/strings/str_join.h"
#include "absl/types/optional.h"
@ -909,6 +910,15 @@ class MetadataMap {
void Log(absl::FunctionRef<void(absl::string_view, absl::string_view)> log_fn)
const;
std::string DebugString() const {
std::string out;
Log([&out](absl::string_view key, absl::string_view value) {
if (!out.empty()) out.append(", ");
absl::StrAppend(&out, absl::CEscape(key), ": ", absl::CEscape(value));
});
return out;
}
// Get the pointer to the value of some known metadata.
// Returns nullptr if the metadata is not present.
// Causes a compilation error if Which is not an element of Traits.

@ -41,24 +41,14 @@
/* These routines are here to facilitate debugging - they produce string
representations of various transport data structures */
static void put_metadata_list(const grpc_metadata_batch& md,
std::vector<std::string>* out) {
bool first = true;
md.Log([out, &first](absl::string_view key, absl::string_view value) {
if (!first) out->push_back(", ");
first = false;
out->push_back(absl::StrCat(absl::CEscape(key), "=", absl::CEscape(value)));
});
}
std::string grpc_transport_stream_op_batch_string(
grpc_transport_stream_op_batch* op) {
std::vector<std::string> out;
if (op->send_initial_metadata) {
out.push_back(" SEND_INITIAL_METADATA{");
put_metadata_list(*op->payload->send_initial_metadata.send_initial_metadata,
&out);
out.push_back(op->payload->send_initial_metadata.send_initial_metadata
->DebugString());
out.push_back("}");
}
@ -77,8 +67,8 @@ std::string grpc_transport_stream_op_batch_string(
if (op->send_trailing_metadata) {
out.push_back(" SEND_TRAILING_METADATA{");
put_metadata_list(
*op->payload->send_trailing_metadata.send_trailing_metadata, &out);
out.push_back(op->payload->send_trailing_metadata.send_trailing_metadata
->DebugString());
out.push_back("}");
}
@ -97,7 +87,9 @@ std::string grpc_transport_stream_op_batch_string(
if (op->cancel_stream) {
out.push_back(absl::StrCat(
" CANCEL:",
grpc_error_std_string(op->payload->cancel_stream.cancel_error)));
absl::StrFormat(
"%p", static_cast<void*>(op->payload->cancel_stream.cancel_error)),
":", grpc_error_std_string(op->payload->cancel_stream.cancel_error)));
}
return absl::StrJoin(out, "");

@ -165,7 +165,8 @@ int main(int argc, char** argv) {
gpr_mu_init(&g_mu);
grpc_init();
g_default_dns_resolver = grpc_core::GetDNSResolver();
grpc_core::SetDNSResolver(new TestDNSResolver());
auto* resolver = new TestDNSResolver();
grpc_core::SetDNSResolver(resolver);
iomgr_dns_lookup_ares = grpc_dns_lookup_ares;
iomgr_cancel_ares_request = grpc_cancel_ares_request;
grpc_dns_lookup_ares = my_dns_lookup_ares;
@ -395,6 +396,8 @@ int main(int argc, char** argv) {
cq_verifier_destroy(cqv);
grpc_completion_queue_destroy(cq);
grpc_core::SetDNSResolver(g_default_dns_resolver);
delete resolver;
grpc_shutdown();
gpr_mu_destroy(&g_mu);

@ -0,0 +1,32 @@
# Copyright 2022 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
licenses(["notice"])
grpc_package(name = "test/core/filters")
grpc_cc_test(
name = "client_authority_filter_test",
srcs = ["client_authority_filter_test.cc"],
external_deps = ["gtest"],
language = "c++",
uses_polling = False,
deps = [
"//:grpc",
"//:grpc_client_authority_filter",
"//test/core/util:grpc_suppressions",
],
)

@ -0,0 +1,119 @@
// Copyright 2022 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/core/ext/filters/http/client_authority_filter.h"
#include <gtest/gtest.h>
#include "src/core/lib/resource_quota/resource_quota.h"
namespace grpc_core {
namespace {
auto* g_memory_allocator = new MemoryAllocator(
ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator("test"));
class TestChannelArgs {
public:
explicit TestChannelArgs(const char* default_authority)
: arg_(grpc_channel_arg_string_create(
const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY),
const_cast<char*>(default_authority))),
args_{1, &arg_} {}
const grpc_channel_args* args() const { return &args_; }
private:
grpc_arg arg_;
grpc_channel_args args_;
};
TEST(ClientAuthorityFilterTest, DefaultFails) {
EXPECT_FALSE(ClientAuthorityFilter::Create(nullptr).ok());
}
TEST(ClientAuthorityFilterTest, WithArgSucceeds) {
EXPECT_EQ(ClientAuthorityFilter::Create(
TestChannelArgs("foo.test.google.au").args())
.status(),
absl::OkStatus());
}
TEST(ClientAuthorityFilterTest, NonStringArgFails) {
grpc_arg arg = grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY), 123);
grpc_channel_args args = {1, &arg};
EXPECT_FALSE(ClientAuthorityFilter::Create(&args).ok());
}
TEST(ClientAuthorityFilterTest, PromiseCompletesImmediatelyAndSetsAuthority) {
auto filter = *ClientAuthorityFilter::Create(
TestChannelArgs("foo.test.google.au").args());
auto arena = MakeScopedArena(1024, g_memory_allocator);
grpc_metadata_batch initial_metadata_batch(arena.get());
grpc_metadata_batch trailing_metadata_batch(arena.get());
bool seen = false;
// TODO(ctiller): use Activity here, once it's ready.
promise_detail::Context<Arena> context(arena.get());
auto promise = filter.MakeCallPromise(
ClientInitialMetadata::TestOnlyWrap(&initial_metadata_batch),
[&](ClientInitialMetadata initial_metadata) {
EXPECT_EQ(initial_metadata->get_pointer(HttpAuthorityMetadata())
->as_string_view(),
"foo.test.google.au");
seen = true;
return ArenaPromise<TrailingMetadata>([&]() -> Poll<TrailingMetadata> {
return TrailingMetadata::TestOnlyWrap(&trailing_metadata_batch);
});
});
auto result = promise();
EXPECT_TRUE(absl::get_if<TrailingMetadata>(&result) != nullptr);
EXPECT_TRUE(seen);
}
TEST(ClientAuthorityFilterTest,
PromiseCompletesImmediatelyAndDoesNotClobberAlreadySetsAuthority) {
auto filter = *ClientAuthorityFilter::Create(
TestChannelArgs("foo.test.google.au").args());
auto arena = MakeScopedArena(1024, g_memory_allocator);
grpc_metadata_batch initial_metadata_batch(arena.get());
grpc_metadata_batch trailing_metadata_batch(arena.get());
initial_metadata_batch.Set(HttpAuthorityMetadata(),
Slice::FromStaticString("bar.test.google.au"));
bool seen = false;
// TODO(ctiller): use Activity here, once it's ready.
promise_detail::Context<Arena> context(arena.get());
auto promise = filter.MakeCallPromise(
ClientInitialMetadata::TestOnlyWrap(&initial_metadata_batch),
[&](ClientInitialMetadata initial_metadata) {
EXPECT_EQ(initial_metadata->get_pointer(HttpAuthorityMetadata())
->as_string_view(),
"bar.test.google.au");
seen = true;
return ArenaPromise<TrailingMetadata>([&]() -> Poll<TrailingMetadata> {
return TrailingMetadata::TestOnlyWrap(&trailing_metadata_batch);
});
});
auto result = promise();
EXPECT_TRUE(absl::get_if<TrailingMetadata>(&result) != nullptr);
EXPECT_TRUE(seen);
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -1778,6 +1778,7 @@ src/core/lib/channel/handshaker.h \
src/core/lib/channel/handshaker_factory.h \
src/core/lib/channel/handshaker_registry.cc \
src/core/lib/channel/handshaker_registry.h \
src/core/lib/channel/promise_based_filter.h \
src/core/lib/channel/status_util.cc \
src/core/lib/channel/status_util.h \
src/core/lib/compression/compression.cc \
@ -2072,6 +2073,7 @@ src/core/lib/profiling/stap_timers.cc \
src/core/lib/profiling/timers.h \
src/core/lib/promise/activity.cc \
src/core/lib/promise/activity.h \
src/core/lib/promise/arena_promise.h \
src/core/lib/promise/context.h \
src/core/lib/promise/detail/basic_seq.h \
src/core/lib/promise/detail/promise_factory.h \
@ -2082,6 +2084,7 @@ src/core/lib/promise/exec_ctx_wakeup_scheduler.h \
src/core/lib/promise/loop.h \
src/core/lib/promise/map.h \
src/core/lib/promise/poll.h \
src/core/lib/promise/promise.h \
src/core/lib/promise/race.h \
src/core/lib/promise/seq.h \
src/core/lib/resolver/resolver.cc \

@ -1574,6 +1574,7 @@ src/core/lib/channel/handshaker.h \
src/core/lib/channel/handshaker_factory.h \
src/core/lib/channel/handshaker_registry.cc \
src/core/lib/channel/handshaker_registry.h \
src/core/lib/channel/promise_based_filter.h \
src/core/lib/channel/status_util.cc \
src/core/lib/channel/status_util.h \
src/core/lib/compression/compression.cc \
@ -1871,6 +1872,7 @@ src/core/lib/profiling/stap_timers.cc \
src/core/lib/profiling/timers.h \
src/core/lib/promise/activity.cc \
src/core/lib/promise/activity.h \
src/core/lib/promise/arena_promise.h \
src/core/lib/promise/context.h \
src/core/lib/promise/detail/basic_seq.h \
src/core/lib/promise/detail/promise_factory.h \
@ -1881,6 +1883,7 @@ src/core/lib/promise/exec_ctx_wakeup_scheduler.h \
src/core/lib/promise/loop.h \
src/core/lib/promise/map.h \
src/core/lib/promise/poll.h \
src/core/lib/promise/promise.h \
src/core/lib/promise/race.h \
src/core/lib/promise/seq.h \
src/core/lib/resolver/resolver.cc \

@ -3615,6 +3615,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "client_authority_filter_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save