mirror of https://github.com/grpc/grpc.git
XdsChannelStackModifier class (#27744)
* ChannelStackModifier class * Regenerate projects * clang-tidy * Use CoreConfiguration for inserting xDS HTTP filters * New lines * Move test to test/core/xds * Add comment for placement of filter stack * Fix sanity * Fix memory leak - destroy builder * Don't build xds_channel_stack_modifier_test on non-bazel build systems due to census dependencypull/27827/head
parent
eab375e439
commit
6d2a641dd3
20 changed files with 400 additions and 1 deletions
@ -0,0 +1,113 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2021 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/xds/xds_channel_stack_modifier.h" |
||||
|
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/surface/channel_init.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace { |
||||
|
||||
void* XdsChannelStackModifierArgCopy(void* p) { |
||||
XdsChannelStackModifier* arg = static_cast<XdsChannelStackModifier*>(p); |
||||
return arg->Ref().release(); |
||||
} |
||||
|
||||
void XdsChannelStackModifierArgDestroy(void* p) { |
||||
XdsChannelStackModifier* arg = static_cast<XdsChannelStackModifier*>(p); |
||||
arg->Unref(); |
||||
} |
||||
|
||||
int XdsChannelStackModifierArgCmp(void* p, void* q) { |
||||
return QsortCompare(p, q); |
||||
} |
||||
|
||||
const grpc_arg_pointer_vtable kChannelArgVtable = { |
||||
XdsChannelStackModifierArgCopy, XdsChannelStackModifierArgDestroy, |
||||
XdsChannelStackModifierArgCmp}; |
||||
|
||||
const char* kXdsChannelStackModifierChannelArgName = |
||||
"grpc.internal.xds_channel_stack_modifier"; |
||||
|
||||
} // namespace
|
||||
|
||||
bool XdsChannelStackModifier::ModifyChannelStack( |
||||
grpc_channel_stack_builder* builder) { |
||||
// Insert the filters after the census filter if present.
|
||||
grpc_channel_stack_builder_iterator* it = |
||||
grpc_channel_stack_builder_create_iterator_at_first(builder); |
||||
while (grpc_channel_stack_builder_move_next(it)) { |
||||
if (grpc_channel_stack_builder_iterator_is_end(it)) break; |
||||
const char* filter_name_at_it = |
||||
grpc_channel_stack_builder_iterator_filter_name(it); |
||||
if (strcmp("census_server", filter_name_at_it) == 0 || |
||||
strcmp("opencensus_server", filter_name_at_it) == 0) { |
||||
break; |
||||
} |
||||
} |
||||
if (grpc_channel_stack_builder_iterator_is_end(it)) { |
||||
// No census filter found. Reset iterator to the beginning. This will result
|
||||
// in prepending the list of xDS HTTP filters to the current stack. Note
|
||||
// that this stage is run before the stage that adds the top server filter,
|
||||
// resulting in these filters being finally placed after the `server`
|
||||
// filter.
|
||||
grpc_channel_stack_builder_iterator_destroy(it); |
||||
it = grpc_channel_stack_builder_create_iterator_at_first(builder); |
||||
} |
||||
GPR_ASSERT(grpc_channel_stack_builder_move_next(it)); |
||||
for (const grpc_channel_filter* filter : filters_) { |
||||
GPR_ASSERT(grpc_channel_stack_builder_add_filter_before(it, filter, nullptr, |
||||
nullptr)); |
||||
} |
||||
grpc_channel_stack_builder_iterator_destroy(it); |
||||
return true; |
||||
} |
||||
|
||||
grpc_arg XdsChannelStackModifier::MakeChannelArg() const { |
||||
return grpc_channel_arg_pointer_create( |
||||
const_cast<char*>(kXdsChannelStackModifierChannelArgName), |
||||
const_cast<XdsChannelStackModifier*>(this), &kChannelArgVtable); |
||||
} |
||||
|
||||
RefCountedPtr<XdsChannelStackModifier> |
||||
XdsChannelStackModifier::GetFromChannelArgs(const grpc_channel_args& args) { |
||||
XdsChannelStackModifier* config_selector_provider = |
||||
grpc_channel_args_find_pointer<XdsChannelStackModifier>( |
||||
&args, kXdsChannelStackModifierChannelArgName); |
||||
return config_selector_provider != nullptr ? config_selector_provider->Ref() |
||||
: nullptr; |
||||
} |
||||
|
||||
void RegisterXdsChannelStackModifier(CoreConfiguration::Builder* builder) { |
||||
builder->channel_init()->RegisterStage( |
||||
GRPC_SERVER_CHANNEL, INT_MAX, [](grpc_channel_stack_builder* builder) { |
||||
grpc_core::RefCountedPtr<XdsChannelStackModifier> |
||||
channel_stack_modifier = |
||||
XdsChannelStackModifier::GetFromChannelArgs( |
||||
*grpc_channel_stack_builder_get_channel_arguments(builder)); |
||||
if (channel_stack_modifier != nullptr) { |
||||
return channel_stack_modifier->ModifyChannelStack(builder); |
||||
} |
||||
return true; |
||||
}); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,52 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2021 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_EXT_XDS_XDS_CHANNEL_STACK_MODIFIER_H |
||||
#define GRPC_CORE_EXT_XDS_XDS_CHANNEL_STACK_MODIFIER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <vector> |
||||
|
||||
#include "src/core/lib/channel/channel_stack.h" |
||||
#include "src/core/lib/channel/channel_stack_builder.h" |
||||
#include "src/core/lib/gprpp/ref_counted.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// XdsChannelStackModifier allows for inserting xDS HTTP filters into the
|
||||
// channel stack. It is registered to mutate the `grpc_channel_stack_builder`
|
||||
// object via ChannelInit::Builder::RegisterStage.
|
||||
class XdsChannelStackModifier : public RefCounted<XdsChannelStackModifier> { |
||||
public: |
||||
explicit XdsChannelStackModifier( |
||||
std::vector<const grpc_channel_filter*> filters) |
||||
: filters_(std::move(filters)) {} |
||||
// Returns true on success, false otherwise.
|
||||
bool ModifyChannelStack(grpc_channel_stack_builder* builder); |
||||
grpc_arg MakeChannelArg() const; |
||||
static RefCountedPtr<XdsChannelStackModifier> GetFromChannelArgs( |
||||
const grpc_channel_args& args); |
||||
|
||||
private: |
||||
std::vector<const grpc_channel_filter*> filters_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_EXT_XDS_XDS_CHANNEL_STACK_MODIFIER_H */ |
@ -0,0 +1,171 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2021 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/xds/xds_channel_stack_modifier.h" |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpcpp/opencensus.h> |
||||
|
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/surface/channel_init.h" |
||||
#include "src/core/lib/transport/transport_impl.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace testing { |
||||
namespace { |
||||
|
||||
// Test that XdsChannelStackModifier can be safely copied to channel args
|
||||
// and destroyed
|
||||
TEST(XdsChannelStackModifierTest, CopyChannelArgs) { |
||||
grpc_init(); |
||||
auto server_config_selector_provider = |
||||
MakeRefCounted<XdsChannelStackModifier>( |
||||
std::vector<const grpc_channel_filter*>{}); |
||||
grpc_arg arg = server_config_selector_provider->MakeChannelArg(); |
||||
grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); |
||||
EXPECT_EQ(server_config_selector_provider, |
||||
XdsChannelStackModifier::GetFromChannelArgs(*args)); |
||||
grpc_channel_args_destroy(args); |
||||
grpc_shutdown(); |
||||
} |
||||
|
||||
// Test compare on channel args with the same XdsChannelStackModifier
|
||||
TEST(XdsChannelStackModifierTest, ChannelArgsCompare) { |
||||
grpc_init(); |
||||
auto server_config_selector_provider = |
||||
MakeRefCounted<XdsChannelStackModifier>( |
||||
std::vector<const grpc_channel_filter*>{}); |
||||
grpc_arg arg = server_config_selector_provider->MakeChannelArg(); |
||||
grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); |
||||
grpc_channel_args* new_args = grpc_channel_args_copy(args); |
||||
EXPECT_EQ(XdsChannelStackModifier::GetFromChannelArgs(*new_args), |
||||
XdsChannelStackModifier::GetFromChannelArgs(*args)); |
||||
grpc_channel_args_destroy(args); |
||||
grpc_channel_args_destroy(new_args); |
||||
grpc_shutdown(); |
||||
} |
||||
|
||||
constexpr char kTestFilter1[] = "test_filter_1"; |
||||
constexpr char kTestFilter2[] = "test_filter_2"; |
||||
|
||||
// Test filters insertion
|
||||
TEST(XdsChannelStackModifierTest, XdsHttpFiltersInsertion) { |
||||
CoreConfiguration::Reset(); |
||||
grpc_init(); |
||||
// Add 2 test filters to XdsChannelStackModifier
|
||||
const grpc_channel_filter test_filter_1 = { |
||||
nullptr, nullptr, 0, nullptr, nullptr, nullptr, |
||||
0, nullptr, nullptr, nullptr, kTestFilter1}; |
||||
const grpc_channel_filter test_filter_2 = { |
||||
nullptr, nullptr, 0, nullptr, nullptr, nullptr, |
||||
0, nullptr, nullptr, nullptr, kTestFilter2}; |
||||
auto server_config_selector_provider = |
||||
MakeRefCounted<XdsChannelStackModifier>( |
||||
std::vector<const grpc_channel_filter*>{&test_filter_1, |
||||
&test_filter_2}); |
||||
grpc_arg arg = server_config_selector_provider->MakeChannelArg(); |
||||
// Create a phony grpc_channel_stack_builder object
|
||||
grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); |
||||
grpc_channel_stack_builder* builder = grpc_channel_stack_builder_create(); |
||||
grpc_channel_stack_builder_set_channel_arguments(builder, args); |
||||
grpc_channel_args_destroy(args); |
||||
grpc_transport_vtable fake_transport_vtable; |
||||
memset(&fake_transport_vtable, 0, sizeof(grpc_transport_vtable)); |
||||
fake_transport_vtable.name = "fake"; |
||||
grpc_transport fake_transport = {&fake_transport_vtable}; |
||||
grpc_channel_stack_builder_set_transport(builder, &fake_transport); |
||||
// Construct channel stack and verify that the test filters were successfully
|
||||
// added
|
||||
ASSERT_TRUE(CoreConfiguration::Get().channel_init().CreateStack( |
||||
builder, GRPC_SERVER_CHANNEL)); |
||||
grpc_channel_stack_builder_iterator* it = |
||||
grpc_channel_stack_builder_create_iterator_at_first(builder); |
||||
ASSERT_TRUE(grpc_channel_stack_builder_move_next(it)); |
||||
ASSERT_STREQ(grpc_channel_stack_builder_iterator_filter_name(it), "server"); |
||||
ASSERT_TRUE(grpc_channel_stack_builder_move_next(it)); |
||||
ASSERT_STREQ(grpc_channel_stack_builder_iterator_filter_name(it), |
||||
kTestFilter1); |
||||
ASSERT_TRUE(grpc_channel_stack_builder_move_next(it)); |
||||
ASSERT_STREQ(grpc_channel_stack_builder_iterator_filter_name(it), |
||||
kTestFilter2); |
||||
grpc_channel_stack_builder_iterator_destroy(it); |
||||
grpc_channel_stack_builder_destroy(builder); |
||||
grpc_shutdown(); |
||||
} |
||||
|
||||
// Test filters insertion with OpenCensus plugin registered
|
||||
TEST(XdsChannelStackModifierTest, XdsHttpFiltersInsertionAfterCensus) { |
||||
CoreConfiguration::Reset(); |
||||
grpc::RegisterOpenCensusPlugin(); |
||||
grpc_init(); |
||||
// Add 2 test filters to XdsChannelStackModifier
|
||||
const grpc_channel_filter test_filter_1 = { |
||||
nullptr, nullptr, 0, nullptr, nullptr, nullptr, |
||||
0, nullptr, nullptr, nullptr, kTestFilter1}; |
||||
const grpc_channel_filter test_filter_2 = { |
||||
nullptr, nullptr, 0, nullptr, nullptr, nullptr, |
||||
0, nullptr, nullptr, nullptr, kTestFilter2}; |
||||
auto server_config_selector_provider = |
||||
MakeRefCounted<XdsChannelStackModifier>( |
||||
std::vector<const grpc_channel_filter*>{&test_filter_1, |
||||
&test_filter_2}); |
||||
grpc_arg arg = server_config_selector_provider->MakeChannelArg(); |
||||
// Create a phony grpc_channel_stack_builder object
|
||||
grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); |
||||
grpc_channel_stack_builder* builder = grpc_channel_stack_builder_create(); |
||||
grpc_channel_stack_builder_set_channel_arguments(builder, args); |
||||
grpc_channel_args_destroy(args); |
||||
grpc_transport_vtable fake_transport_vtable; |
||||
memset(&fake_transport_vtable, 0, sizeof(grpc_transport_vtable)); |
||||
fake_transport_vtable.name = "fake"; |
||||
grpc_transport fake_transport = {&fake_transport_vtable}; |
||||
grpc_channel_stack_builder_set_transport(builder, &fake_transport); |
||||
// Construct channel stack and verify that the test filters were successfully
|
||||
// added after the census filter
|
||||
ASSERT_TRUE(CoreConfiguration::Get().channel_init().CreateStack( |
||||
builder, GRPC_SERVER_CHANNEL)); |
||||
grpc_channel_stack_builder_iterator* it = |
||||
grpc_channel_stack_builder_create_iterator_at_first(builder); |
||||
ASSERT_TRUE(grpc_channel_stack_builder_move_next(it)); |
||||
ASSERT_STREQ(grpc_channel_stack_builder_iterator_filter_name(it), "server"); |
||||
ASSERT_TRUE(grpc_channel_stack_builder_move_next(it)); |
||||
ASSERT_STREQ(grpc_channel_stack_builder_iterator_filter_name(it), |
||||
"opencensus_server"); |
||||
ASSERT_TRUE(grpc_channel_stack_builder_move_next(it)); |
||||
ASSERT_STREQ(grpc_channel_stack_builder_iterator_filter_name(it), |
||||
kTestFilter1); |
||||
ASSERT_TRUE(grpc_channel_stack_builder_move_next(it)); |
||||
ASSERT_STREQ(grpc_channel_stack_builder_iterator_filter_name(it), |
||||
kTestFilter2); |
||||
grpc_channel_stack_builder_iterator_destroy(it); |
||||
grpc_channel_stack_builder_destroy(builder); |
||||
grpc_shutdown(); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(argc, argv); |
||||
int ret = RUN_ALL_TESTS(); |
||||
return ret; |
||||
} |
Loading…
Reference in new issue