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