mirror of https://github.com/grpc/grpc.git
[AuthContext] Introduce an ConnectionContext class to hold arbitrary auth properties types for quick lookup.
PiperOrigin-RevId: 690671976pull/37932/head
parent
ff7d726a91
commit
5c31076cc9
8 changed files with 450 additions and 0 deletions
@ -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 <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include <cstddef> |
||||||
|
|
||||||
|
#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> ConnectionContext::Create() { |
||||||
|
void* p = ConnectionContextStorage(); |
||||||
|
return OrphanablePtr<ConnectionContext>(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<ConnectionContext*>(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
|
@ -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 <grpc/support/port_platform.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "src/core/util/orphanable.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
class ConnectionContext; |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
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<uint16_t>(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 <typename T> |
||||||
|
static uint16_t AllocateId(void (*destroy)(void* ptr)) { |
||||||
|
auto& traits = RegisteredTraits(); |
||||||
|
const uint16_t id = static_cast<uint16_t>(traits.size()); |
||||||
|
traits.push_back(destroy); |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
// Allocate a new context id and register the destruction function.
|
||||||
|
|
||||||
|
static std::vector<void (*)(void*)>& RegisteredTraits() { |
||||||
|
static std::vector<void (*)(void*)> registered_traits; |
||||||
|
return registered_traits; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
class ConnectionContextPropertiesTraits |
||||||
|
: public BaseConnectionContextPropertiesTraits { |
||||||
|
public: |
||||||
|
static uint16_t id() { return id_; } |
||||||
|
template <typename... Args> |
||||||
|
static T* Construct(Args&&... args) { |
||||||
|
return new T(std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
static void Destruct(void* p) { delete reinterpret_cast<T*>(p); } |
||||||
|
|
||||||
|
protected: |
||||||
|
static const uint16_t id_; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
const uint16_t ConnectionContextPropertiesTraits<T>::id_ = |
||||||
|
BaseConnectionContextPropertiesTraits::AllocateId<T>( |
||||||
|
ConnectionContextPropertiesTraits<T>::Destruct); |
||||||
|
|
||||||
|
} // namespace connection_context_detail
|
||||||
|
|
||||||
|
class ConnectionContext final : public Orphanable { |
||||||
|
public: |
||||||
|
static OrphanablePtr<ConnectionContext> 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 <typename Which, typename... Args> |
||||||
|
bool EmplaceIfUnset(Args&&... args) { |
||||||
|
using Traits = |
||||||
|
connection_context_detail::ConnectionContextPropertiesTraits<Which>; |
||||||
|
Which* value = static_cast<Which*>(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 <typename Which, typename... Args> |
||||||
|
void Update(Args&&... args) { |
||||||
|
using Traits = |
||||||
|
connection_context_detail::ConnectionContextPropertiesTraits<Which>; |
||||||
|
Which* prev = static_cast<Which*>(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 <typename Which> |
||||||
|
const Which* Get() { |
||||||
|
return static_cast<Which*>( |
||||||
|
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<void**>(this + 1); |
||||||
|
} |
||||||
|
|
||||||
|
ConnectionContext(); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_SRC_CORE_LIB_SURFACE_CONNECTION_CONTEXT_H
|
@ -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 <gtest/gtest.h> |
||||||
|
|
||||||
|
#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<Foo> {}; |
||||||
|
|
||||||
|
template <> |
||||||
|
struct ConnectionContextProperty<Bar> {}; |
||||||
|
|
||||||
|
TEST(ConnectionAuthContextTest, SimpleStaticPropertyAdditionContext) { |
||||||
|
OrphanablePtr<ConnectionContext> map = ConnectionContext::Create(); |
||||||
|
EXPECT_TRUE(map->EmplaceIfUnset<Foo>(3.0)); |
||||||
|
EXPECT_EQ(map->Get<Foo>()->value(), 3.0); |
||||||
|
EXPECT_FALSE(map->EmplaceIfUnset<Foo>(1.0)); |
||||||
|
EXPECT_EQ(map->Get<Foo>()->value(), 3.0); |
||||||
|
map->Update<Foo>(2.0); |
||||||
|
EXPECT_EQ(map->Get<Foo>()->value(), 2.0); |
||||||
|
|
||||||
|
EXPECT_TRUE(map->EmplaceIfUnset<Bar>(1)); |
||||||
|
EXPECT_EQ(map->Get<Bar>()->value(), 1); |
||||||
|
EXPECT_FALSE(map->EmplaceIfUnset<Bar>(2)); |
||||||
|
EXPECT_EQ(map->Get<Bar>()->value(), 1); |
||||||
|
map->Update<Bar>(1234); |
||||||
|
EXPECT_EQ(map->Get<Bar>()->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(); |
||||||
|
} |
Loading…
Reference in new issue