From 5c31076cc9e53fa8343d46a606b7d22c476f6170 Mon Sep 17 00:00:00 2001 From: Vignesh Babu Date: Mon, 28 Oct 2024 10:47:25 -0700 Subject: [PATCH] [AuthContext] Introduce an ConnectionContext class to hold arbitrary auth properties types for quick lookup. PiperOrigin-RevId: 690671976 --- CMakeLists.txt | 53 +++++++ build_autogenerated.yaml | 32 ++++ src/core/BUILD | 11 ++ src/core/lib/surface/connection_context.cc | 77 +++++++++ src/core/lib/surface/connection_context.h | 155 +++++++++++++++++++ test/core/surface/BUILD | 20 +++ test/core/surface/connection_context_test.cc | 78 ++++++++++ tools/run_tests/generated/tests.json | 24 +++ 8 files changed, 450 insertions(+) create mode 100644 src/core/lib/surface/connection_context.cc create mode 100644 src/core/lib/surface/connection_context.h create mode 100644 test/core/surface/connection_context_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index ee3bb8099a4..64f800e6c89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1451,6 +1451,7 @@ if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx connected_subchannel_test) endif() + add_dependencies(buildtests_cxx connection_context_test) add_dependencies(buildtests_cxx connection_prefix_bad_client_test) add_dependencies(buildtests_cxx connection_refused_test) add_dependencies(buildtests_cxx connectivity_state_test) @@ -12271,6 +12272,58 @@ endif() endif() if(gRPC_BUILD_TESTS) +add_executable(connection_context_test + src/core/lib/surface/connection_context.cc + test/core/surface/connection_context_test.cc + test/core/test_util/cmdline.cc + test/core/test_util/fuzzer_util.cc + test/core/test_util/grpc_profiler.cc + test/core/test_util/histogram.cc + test/core/test_util/mock_endpoint.cc + test/core/test_util/parse_hexstring.cc + test/core/test_util/resolve_localhost_ip46.cc + test/core/test_util/slice_splitter.cc + test/core/test_util/tracer_util.cc +) +if(WIN32 AND MSVC) + if(BUILD_SHARED_LIBS) + target_compile_definitions(connection_context_test + PRIVATE + "GPR_DLL_IMPORTS" + "GRPC_DLL_IMPORTS" + ) + endif() +endif() +target_compile_features(connection_context_test PUBLIC cxx_std_14) +target_include_directories(connection_context_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(connection_context_test + ${_gRPC_ALLTARGETS_LIBRARIES} + gtest + grpc_test_util +) + + +endif() +if(gRPC_BUILD_TESTS) + add_executable(connection_prefix_bad_client_test test/core/bad_client/bad_client.cc test/core/bad_client/tests/connection_prefix.cc diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index f2a86992787..ebb4614f751 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -8930,6 +8930,38 @@ targets: - linux - posix uses_polling: false +- name: connection_context_test + gtest: true + build: test + language: c++ + headers: + - src/core/lib/surface/connection_context.h + - test/core/test_util/cmdline.h + - test/core/test_util/evaluate_args_test_util.h + - test/core/test_util/fuzzer_util.h + - test/core/test_util/grpc_profiler.h + - test/core/test_util/histogram.h + - test/core/test_util/mock_endpoint.h + - test/core/test_util/parse_hexstring.h + - test/core/test_util/resolve_localhost_ip46.h + - test/core/test_util/slice_splitter.h + - test/core/test_util/tracer_util.h + src: + - src/core/lib/surface/connection_context.cc + - test/core/surface/connection_context_test.cc + - test/core/test_util/cmdline.cc + - test/core/test_util/fuzzer_util.cc + - test/core/test_util/grpc_profiler.cc + - test/core/test_util/histogram.cc + - test/core/test_util/mock_endpoint.cc + - test/core/test_util/parse_hexstring.cc + - test/core/test_util/resolve_localhost_ip46.cc + - test/core/test_util/slice_splitter.cc + - test/core/test_util/tracer_util.cc + deps: + - gtest + - grpc_test_util + uses_polling: false - name: connection_prefix_bad_client_test gtest: true build: test diff --git a/src/core/BUILD b/src/core/BUILD index 515391d5fd7..64d847aa091 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -3668,6 +3668,17 @@ grpc_cc_library( ], ) +grpc_cc_library( + name = "connection_context", + srcs = ["lib/surface/connection_context.cc"], + hdrs = ["lib/surface/connection_context.h"], + deps = [ + "//:gpr", + "//:gpr_platform", + "//:orphanable", + ], +) + grpc_cc_library( name = "subchannel_interface", hdrs = ["load_balancing/subchannel_interface.h"], diff --git a/src/core/lib/surface/connection_context.cc b/src/core/lib/surface/connection_context.cc new file mode 100644 index 00000000000..91d76b6e0fe --- /dev/null +++ b/src/core/lib/surface/connection_context.cc @@ -0,0 +1,77 @@ +// +// +// Copyright 2024 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/lib/surface/connection_context.h" + +#include +#include + +#include + +#include "src/core/util/alloc.h" +#include "src/core/util/orphanable.h" + +namespace grpc_core { + +namespace { + +void* ConnectionContextStorage() { + size_t base_size = sizeof(ConnectionContext) + + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( + connection_context_detail:: + BaseConnectionContextPropertiesTraits::Size()); + static constexpr size_t alignment = + (GPR_CACHELINE_SIZE > GPR_MAX_ALIGNMENT && + GPR_CACHELINE_SIZE % GPR_MAX_ALIGNMENT == 0) + ? GPR_CACHELINE_SIZE + : GPR_MAX_ALIGNMENT; + return gpr_malloc_aligned(base_size, alignment); +} + +} // namespace + +OrphanablePtr ConnectionContext::Create() { + void* p = ConnectionContextStorage(); + return OrphanablePtr(new (p) ConnectionContext()); +} + +ConnectionContext::ConnectionContext() { + for (size_t i = 0; + i < connection_context_detail::BaseConnectionContextPropertiesTraits:: + NumProperties(); + ++i) { + registered_properties()[i] = nullptr; + } +} + +void ConnectionContext::Orphan() { + this->~ConnectionContext(); + gpr_free_aligned(const_cast(this)); +} + +ConnectionContext::~ConnectionContext() { + for (size_t i = 0; + i < connection_context_detail::BaseConnectionContextPropertiesTraits:: + NumProperties(); + ++i) { + connection_context_detail::BaseConnectionContextPropertiesTraits::Destroy( + i, registered_properties()[i]); + } +} + +} // namespace grpc_core diff --git a/src/core/lib/surface/connection_context.h b/src/core/lib/surface/connection_context.h new file mode 100644 index 00000000000..56043991837 --- /dev/null +++ b/src/core/lib/surface/connection_context.h @@ -0,0 +1,155 @@ +// +// +// Copyright 2024 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_SRC_CORE_LIB_SURFACE_CONNECTION_CONTEXT_H +#define GRPC_SRC_CORE_LIB_SURFACE_CONNECTION_CONTEXT_H + +#include +#include + +#include +#include + +#include "src/core/util/orphanable.h" + +namespace grpc_core { + +class ConnectionContext; + +template +struct ConnectionContextProperty; + +namespace connection_context_detail { + +// Tracks all registered/known auth property types (these should only be +// registered via AuthPropertyTraits at static initialization time). +class BaseConnectionContextPropertiesTraits { + public: + // Count of number of registered auth context properties. + static uint16_t NumProperties() { + return static_cast(RegisteredTraits().size()); + } + + // Number of bytes required to store pointers of all registered auth context + // properties. + static size_t Size() { return NumProperties() * sizeof(void*); } + + // Call the registered destruction function for a context. + static void Destroy(uint16_t id, void* ptr) { + if (ptr == nullptr) return; + RegisteredTraits()[id](ptr); + } + + // Allocate a new context id and register the destruction function. + template + static uint16_t AllocateId(void (*destroy)(void* ptr)) { + auto& traits = RegisteredTraits(); + const uint16_t id = static_cast(traits.size()); + traits.push_back(destroy); + return id; + } + + private: + // Allocate a new context id and register the destruction function. + + static std::vector& RegisteredTraits() { + static std::vector registered_traits; + return registered_traits; + } +}; + +template +class ConnectionContextPropertiesTraits + : public BaseConnectionContextPropertiesTraits { + public: + static uint16_t id() { return id_; } + template + static T* Construct(Args&&... args) { + return new T(std::forward(args)...); + } + static void Destruct(void* p) { delete reinterpret_cast(p); } + + protected: + static const uint16_t id_; +}; + +template +const uint16_t ConnectionContextPropertiesTraits::id_ = + BaseConnectionContextPropertiesTraits::AllocateId( + ConnectionContextPropertiesTraits::Destruct); + +} // namespace connection_context_detail + +class ConnectionContext final : public Orphanable { + public: + static OrphanablePtr Create(); + + // Sets the value of a registered property if it is not already set. Return + // false if the property is already set. If the property is not already + // set, an object of type Which is created using the passed args + // and stored in the map. + template + bool EmplaceIfUnset(Args&&... args) { + using Traits = + connection_context_detail::ConnectionContextPropertiesTraits; + Which* value = static_cast(registered_properties()[Traits::id()]); + if (value == nullptr) { + registered_properties()[Traits::id()] = Traits::Construct(args...); + return true; + } + return false; + } + + // Force updates the value of a registered property. It deletes any previously + // set value. + template + void Update(Args&&... args) { + using Traits = + connection_context_detail::ConnectionContextPropertiesTraits; + Which* prev = static_cast(registered_properties()[Traits::id()]); + if (prev != nullptr) { + Traits::Destroy(Traits::id(), prev); + } + registered_properties()[Traits::id()] = Traits::Construct(args...); + } + + // Returns the value of a registered property. If the property is not set, + // returns nullptr. + template + const Which* Get() { + return static_cast( + registered_properties() + [connection_context_detail::ConnectionContextPropertiesTraits< + Which>::id()]); + } + + void Orphan() override; + + ~ConnectionContext() override; + + private: + GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION void** registered_properties() { + return reinterpret_cast(this + 1); + } + + ConnectionContext(); +}; + +} // namespace grpc_core + +#endif // GRPC_SRC_CORE_LIB_SURFACE_CONNECTION_CONTEXT_H diff --git a/test/core/surface/BUILD b/test/core/surface/BUILD index ea2fa27b24a..a828f882ce3 100644 --- a/test/core/surface/BUILD +++ b/test/core/surface/BUILD @@ -208,3 +208,23 @@ grpc_cc_test( "//test/core/test_util:grpc_test_util", ], ) + +grpc_cc_test( + name = "connection_context_test", + srcs = ["connection_context_test.cc"], + external_deps = [ + "absl/log:log", + "gtest", + ], + language = "C++", + uses_event_engine = False, + uses_polling = False, + deps = [ + "//:gpr", + "//:grpc", + "//:orphanable", + "//src/core:connection_context", + "//test/core/test_util:grpc_test_util", + "//test/core/test_util:grpc_test_util_base", + ], +) diff --git a/test/core/surface/connection_context_test.cc b/test/core/surface/connection_context_test.cc new file mode 100644 index 00000000000..fa7c3be6823 --- /dev/null +++ b/test/core/surface/connection_context_test.cc @@ -0,0 +1,78 @@ +// +// +// Copyright 2024 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/lib/surface/connection_context.h" + +#include + +#include "src/core/util/orphanable.h" +#include "test/core/test_util/test_config.h" + +namespace grpc_core { +namespace { + +class Foo { + public: + explicit Foo(double value) : value_(value) {} + double value() const { return value_; } + + private: + double value_; +}; + +class Bar { + public: + explicit Bar(int value) : value_(value) {} + int value() const { return value_; } + + private: + int value_; +}; + +} // namespace + +template <> +struct ConnectionContextProperty {}; + +template <> +struct ConnectionContextProperty {}; + +TEST(ConnectionAuthContextTest, SimpleStaticPropertyAdditionContext) { + OrphanablePtr map = ConnectionContext::Create(); + EXPECT_TRUE(map->EmplaceIfUnset(3.0)); + EXPECT_EQ(map->Get()->value(), 3.0); + EXPECT_FALSE(map->EmplaceIfUnset(1.0)); + EXPECT_EQ(map->Get()->value(), 3.0); + map->Update(2.0); + EXPECT_EQ(map->Get()->value(), 2.0); + + EXPECT_TRUE(map->EmplaceIfUnset(1)); + EXPECT_EQ(map->Get()->value(), 1); + EXPECT_FALSE(map->EmplaceIfUnset(2)); + EXPECT_EQ(map->Get()->value(), 1); + map->Update(1234); + EXPECT_EQ(map->Get()->value(), 1234); +} + +} // namespace grpc_core + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 03bcb4953ba..3ee7c652760 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -2455,6 +2455,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "connection_context_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": false + }, { "args": [], "benchmark": false,