mirror of https://github.com/grpc/grpc.git
[call-v3] Filter executor (#35533)
A call execution environment for the V3 runtime.
The `CallFilters` class will ultimately be a (private) member of `CallSpine`, and the `StackBuilder` component will be used by a channel when all of the filters it needs are known to allow the call spine to start processing a call.
This is accompanied by a reasonably extensive test suite.
I expect to fine tune semantics, implementation, and tests over the coming weeks/months as we iterate to bring up the rest of the pieces.
Closes #35533
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35533 from ctiller:filters 689c7b527b
PiperOrigin-RevId: 599220150
pull/35549/head
parent
c6755cb4b9
commit
584c0c0c98
29 changed files with 3691 additions and 124 deletions
@ -0,0 +1,343 @@ |
|||||||
|
// 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 <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/transport/call_filters.h" |
||||||
|
|
||||||
|
#include "src/core/lib/gprpp/crash.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
namespace { |
||||||
|
void* Offset(void* base, size_t amt) { return static_cast<char*>(base) + amt; } |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace filters_detail { |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
OperationExecutor<T>::~OperationExecutor() { |
||||||
|
if (promise_data_ != nullptr) { |
||||||
|
ops_->early_destroy(promise_data_); |
||||||
|
gpr_free_aligned(promise_data_); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<ResultOr<T>> OperationExecutor<T>::Start( |
||||||
|
const Layout<FallibleOperator<T>>* layout, T input, void* call_data) { |
||||||
|
ops_ = layout->ops.data(); |
||||||
|
end_ops_ = ops_ + layout->ops.size(); |
||||||
|
if (layout->promise_size == 0) { |
||||||
|
// No call state ==> instantaneously ready
|
||||||
|
auto r = InitStep(std::move(input), call_data); |
||||||
|
GPR_ASSERT(r.ready()); |
||||||
|
return r; |
||||||
|
} |
||||||
|
promise_data_ = |
||||||
|
gpr_malloc_aligned(layout->promise_size, layout->promise_alignment); |
||||||
|
return InitStep(std::move(input), call_data); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<ResultOr<T>> OperationExecutor<T>::InitStep(T input, void* call_data) { |
||||||
|
while (true) { |
||||||
|
if (ops_ == end_ops_) { |
||||||
|
return ResultOr<T>{std::move(input), nullptr}; |
||||||
|
} |
||||||
|
auto p = |
||||||
|
ops_->promise_init(promise_data_, Offset(call_data, ops_->call_offset), |
||||||
|
ops_->channel_data, std::move(input)); |
||||||
|
if (auto* r = p.value_if_ready()) { |
||||||
|
if (r->ok == nullptr) return std::move(*r); |
||||||
|
input = std::move(r->ok); |
||||||
|
++ops_; |
||||||
|
continue; |
||||||
|
} |
||||||
|
return Pending{}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<ResultOr<T>> OperationExecutor<T>::Step(void* call_data) { |
||||||
|
GPR_DEBUG_ASSERT(promise_data_ != nullptr); |
||||||
|
auto p = ContinueStep(call_data); |
||||||
|
if (p.ready()) { |
||||||
|
gpr_free_aligned(promise_data_); |
||||||
|
promise_data_ = nullptr; |
||||||
|
} |
||||||
|
return p; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<ResultOr<T>> OperationExecutor<T>::ContinueStep(void* call_data) { |
||||||
|
auto p = ops_->poll(promise_data_); |
||||||
|
if (auto* r = p.value_if_ready()) { |
||||||
|
if (r->ok == nullptr) return std::move(*r); |
||||||
|
++ops_; |
||||||
|
return InitStep(std::move(r->ok), call_data); |
||||||
|
} |
||||||
|
return Pending{}; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
InfallibleOperationExecutor<T>::~InfallibleOperationExecutor() { |
||||||
|
if (promise_data_ != nullptr) { |
||||||
|
ops_->early_destroy(promise_data_); |
||||||
|
gpr_free_aligned(promise_data_); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<T> InfallibleOperationExecutor<T>::Start( |
||||||
|
const Layout<InfallibleOperator<T>>* layout, T input, void* call_data) { |
||||||
|
ops_ = layout->ops.data(); |
||||||
|
end_ops_ = ops_ + layout->ops.size(); |
||||||
|
if (layout->promise_size == 0) { |
||||||
|
// No call state ==> instantaneously ready
|
||||||
|
auto r = InitStep(std::move(input), call_data); |
||||||
|
GPR_ASSERT(r.ready()); |
||||||
|
return r; |
||||||
|
} |
||||||
|
promise_data_ = |
||||||
|
gpr_malloc_aligned(layout->promise_size, layout->promise_alignment); |
||||||
|
return InitStep(std::move(input), call_data); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<T> InfallibleOperationExecutor<T>::InitStep(T input, void* call_data) { |
||||||
|
while (true) { |
||||||
|
if (ops_ == end_ops_) { |
||||||
|
return input; |
||||||
|
} |
||||||
|
auto p = |
||||||
|
ops_->promise_init(promise_data_, Offset(call_data, ops_->call_offset), |
||||||
|
ops_->channel_data, std::move(input)); |
||||||
|
if (auto* r = p.value_if_ready()) { |
||||||
|
input = std::move(*r); |
||||||
|
++ops_; |
||||||
|
continue; |
||||||
|
} |
||||||
|
return Pending{}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<T> InfallibleOperationExecutor<T>::Step(void* call_data) { |
||||||
|
GPR_DEBUG_ASSERT(promise_data_ != nullptr); |
||||||
|
auto p = ContinueStep(call_data); |
||||||
|
if (p.ready()) { |
||||||
|
gpr_free_aligned(promise_data_); |
||||||
|
promise_data_ = nullptr; |
||||||
|
} |
||||||
|
return p; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
Poll<T> InfallibleOperationExecutor<T>::ContinueStep(void* call_data) { |
||||||
|
auto p = ops_->poll(promise_data_); |
||||||
|
if (auto* r = p.value_if_ready()) { |
||||||
|
++ops_; |
||||||
|
return InitStep(std::move(*r), call_data); |
||||||
|
} |
||||||
|
return Pending{}; |
||||||
|
} |
||||||
|
|
||||||
|
// Explicit instantiations of some types used in filters.h
|
||||||
|
// We'll need to add ServerMetadataHandle to this when it becomes different
|
||||||
|
// to ClientMetadataHandle
|
||||||
|
template class OperationExecutor<ClientMetadataHandle>; |
||||||
|
template class OperationExecutor<MessageHandle>; |
||||||
|
template class InfallibleOperationExecutor<ServerMetadataHandle>; |
||||||
|
} // namespace filters_detail
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CallFilters
|
||||||
|
|
||||||
|
CallFilters::CallFilters() : stack_(nullptr), call_data_(nullptr) {} |
||||||
|
|
||||||
|
CallFilters::CallFilters(RefCountedPtr<Stack> stack) |
||||||
|
: stack_(std::move(stack)), |
||||||
|
call_data_(gpr_malloc_aligned(stack_->data_.call_data_size, |
||||||
|
stack_->data_.call_data_alignment)) { |
||||||
|
client_initial_metadata_state_.Start(); |
||||||
|
client_to_server_message_state_.Start(); |
||||||
|
server_initial_metadata_state_.Start(); |
||||||
|
server_to_client_message_state_.Start(); |
||||||
|
} |
||||||
|
|
||||||
|
CallFilters::~CallFilters() { |
||||||
|
if (call_data_ != nullptr) gpr_free_aligned(call_data_); |
||||||
|
} |
||||||
|
|
||||||
|
void CallFilters::SetStack(RefCountedPtr<Stack> stack) { |
||||||
|
if (call_data_ != nullptr) gpr_free_aligned(call_data_); |
||||||
|
stack_ = std::move(stack); |
||||||
|
call_data_ = gpr_malloc_aligned(stack_->data_.call_data_size, |
||||||
|
stack_->data_.call_data_alignment); |
||||||
|
client_initial_metadata_state_.Start(); |
||||||
|
client_to_server_message_state_.Start(); |
||||||
|
server_initial_metadata_state_.Start(); |
||||||
|
server_to_client_message_state_.Start(); |
||||||
|
} |
||||||
|
|
||||||
|
void CallFilters::Finalize(const grpc_call_final_info* final_info) { |
||||||
|
for (auto& finalizer : stack_->data_.finalizers) { |
||||||
|
finalizer.final(Offset(call_data_, finalizer.call_offset), |
||||||
|
finalizer.channel_data, final_info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CallFilters::CancelDueToFailedPipeOperation() { |
||||||
|
// We expect something cancelled before now
|
||||||
|
if (server_trailing_metadata_ == nullptr) return; |
||||||
|
gpr_log(GPR_DEBUG, "Cancelling due to failed pipe operation"); |
||||||
|
server_trailing_metadata_ = |
||||||
|
ServerMetadataFromStatus(absl::CancelledError("Failed pipe operation")); |
||||||
|
server_trailing_metadata_waiter_.Wake(); |
||||||
|
} |
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CallFilters::StackBuilder
|
||||||
|
|
||||||
|
RefCountedPtr<CallFilters::Stack> CallFilters::StackBuilder::Build() { |
||||||
|
if (data_.call_data_size % data_.call_data_alignment != 0) { |
||||||
|
data_.call_data_size += data_.call_data_alignment - |
||||||
|
data_.call_data_size % data_.call_data_alignment; |
||||||
|
} |
||||||
|
// server -> client needs to be reversed so that we can iterate all stacks
|
||||||
|
// in the same order
|
||||||
|
data_.server_initial_metadata.Reverse(); |
||||||
|
data_.server_to_client_messages.Reverse(); |
||||||
|
data_.server_trailing_metadata.Reverse(); |
||||||
|
return RefCountedPtr<Stack>(new Stack(std::move(data_))); |
||||||
|
} |
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CallFilters::PipeState
|
||||||
|
|
||||||
|
void filters_detail::PipeState::Start() { |
||||||
|
GPR_DEBUG_ASSERT(!started_); |
||||||
|
started_ = true; |
||||||
|
wait_recv_.Wake(); |
||||||
|
} |
||||||
|
|
||||||
|
void filters_detail::PipeState::BeginPush() { |
||||||
|
switch (state_) { |
||||||
|
case ValueState::kIdle: |
||||||
|
state_ = ValueState::kQueued; |
||||||
|
break; |
||||||
|
case ValueState::kWaiting: |
||||||
|
state_ = ValueState::kReady; |
||||||
|
wait_recv_.Wake(); |
||||||
|
break; |
||||||
|
case ValueState::kClosed: |
||||||
|
case ValueState::kError: |
||||||
|
break; |
||||||
|
case ValueState::kQueued: |
||||||
|
case ValueState::kReady: |
||||||
|
case ValueState::kProcessing: |
||||||
|
Crash("Only one push allowed to be outstanding"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void filters_detail::PipeState::DropPush() { |
||||||
|
switch (state_) { |
||||||
|
case ValueState::kQueued: |
||||||
|
case ValueState::kReady: |
||||||
|
case ValueState::kProcessing: |
||||||
|
case ValueState::kWaiting: |
||||||
|
state_ = ValueState::kError; |
||||||
|
wait_recv_.Wake(); |
||||||
|
break; |
||||||
|
case ValueState::kIdle: |
||||||
|
case ValueState::kClosed: |
||||||
|
case ValueState::kError: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void filters_detail::PipeState::DropPull() { |
||||||
|
switch (state_) { |
||||||
|
case ValueState::kQueued: |
||||||
|
case ValueState::kReady: |
||||||
|
case ValueState::kProcessing: |
||||||
|
case ValueState::kWaiting: |
||||||
|
state_ = ValueState::kError; |
||||||
|
wait_send_.Wake(); |
||||||
|
break; |
||||||
|
case ValueState::kIdle: |
||||||
|
case ValueState::kClosed: |
||||||
|
case ValueState::kError: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Poll<StatusFlag> filters_detail::PipeState::PollPush() { |
||||||
|
switch (state_) { |
||||||
|
case ValueState::kIdle: |
||||||
|
// Read completed and new read started => we see waiting here
|
||||||
|
case ValueState::kWaiting: |
||||||
|
case ValueState::kClosed: |
||||||
|
return Success{}; |
||||||
|
case ValueState::kQueued: |
||||||
|
case ValueState::kReady: |
||||||
|
case ValueState::kProcessing: |
||||||
|
return wait_send_.pending(); |
||||||
|
case ValueState::kError: |
||||||
|
return Failure{}; |
||||||
|
} |
||||||
|
GPR_UNREACHABLE_CODE(return Pending{}); |
||||||
|
} |
||||||
|
|
||||||
|
Poll<StatusFlag> filters_detail::PipeState::PollPull() { |
||||||
|
switch (state_) { |
||||||
|
case ValueState::kWaiting: |
||||||
|
return wait_recv_.pending(); |
||||||
|
case ValueState::kIdle: |
||||||
|
state_ = ValueState::kWaiting; |
||||||
|
return wait_recv_.pending(); |
||||||
|
case ValueState::kReady: |
||||||
|
case ValueState::kQueued: |
||||||
|
if (!started_) return wait_recv_.pending(); |
||||||
|
state_ = ValueState::kProcessing; |
||||||
|
return Success{}; |
||||||
|
case ValueState::kProcessing: |
||||||
|
Crash("Only one pull allowed to be outstanding"); |
||||||
|
case ValueState::kClosed: |
||||||
|
case ValueState::kError: |
||||||
|
return Failure{}; |
||||||
|
} |
||||||
|
GPR_UNREACHABLE_CODE(return Pending{}); |
||||||
|
} |
||||||
|
|
||||||
|
void filters_detail::PipeState::AckPull() { |
||||||
|
switch (state_) { |
||||||
|
case ValueState::kProcessing: |
||||||
|
state_ = ValueState::kIdle; |
||||||
|
wait_send_.Wake(); |
||||||
|
break; |
||||||
|
case ValueState::kWaiting: |
||||||
|
case ValueState::kIdle: |
||||||
|
case ValueState::kQueued: |
||||||
|
case ValueState::kReady: |
||||||
|
case ValueState::kClosed: |
||||||
|
Crash("AckPullValue called in invalid state"); |
||||||
|
case ValueState::kError: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@ |
|||||||
|
// 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 <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/transport/message.h" |
||||||
|
|
||||||
|
#include <grpc/impl/grpc_types.h> |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
std::string Message::DebugString() const { |
||||||
|
std::string out = absl::StrCat(payload_.Length(), "b"); |
||||||
|
auto flags = flags_; |
||||||
|
auto explain = [&flags, &out](uint32_t flag, absl::string_view name) { |
||||||
|
if (flags & flag) { |
||||||
|
flags &= ~flag; |
||||||
|
absl::StrAppend(&out, ":", name); |
||||||
|
} |
||||||
|
}; |
||||||
|
explain(GRPC_WRITE_BUFFER_HINT, "write_buffer"); |
||||||
|
explain(GRPC_WRITE_NO_COMPRESS, "no_compress"); |
||||||
|
explain(GRPC_WRITE_THROUGH, "write_through"); |
||||||
|
explain(GRPC_WRITE_INTERNAL_COMPRESS, "compress"); |
||||||
|
explain(GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED, "was_compressed"); |
||||||
|
if (flags != 0) { |
||||||
|
absl::StrAppend(&out, ":huh=0x", absl::Hex(flags)); |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
@ -0,0 +1,61 @@ |
|||||||
|
// 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_TRANSPORT_MESSAGE_H |
||||||
|
#define GRPC_SRC_CORE_LIB_TRANSPORT_MESSAGE_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/resource_quota/arena.h" |
||||||
|
#include "src/core/lib/slice/slice_buffer.h" |
||||||
|
|
||||||
|
/// Internal bit flag for grpc_begin_message's \a flags signaling the use of
|
||||||
|
/// compression for the message. (Does not apply for stream compression.)
|
||||||
|
#define GRPC_WRITE_INTERNAL_COMPRESS (0x80000000u) |
||||||
|
/// Internal bit flag for determining whether the message was compressed and had
|
||||||
|
/// to be decompressed by the message_decompress filter. (Does not apply for
|
||||||
|
/// stream compression.)
|
||||||
|
#define GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED (0x40000000u) |
||||||
|
/// Mask of all valid internal flags.
|
||||||
|
#define GRPC_WRITE_INTERNAL_USED_MASK \ |
||||||
|
(GRPC_WRITE_INTERNAL_COMPRESS | GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED) |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
class Message { |
||||||
|
public: |
||||||
|
Message() = default; |
||||||
|
~Message() = default; |
||||||
|
Message(SliceBuffer payload, uint32_t flags) |
||||||
|
: payload_(std::move(payload)), flags_(flags) {} |
||||||
|
Message(const Message&) = delete; |
||||||
|
Message& operator=(const Message&) = delete; |
||||||
|
|
||||||
|
uint32_t flags() const { return flags_; } |
||||||
|
uint32_t& mutable_flags() { return flags_; } |
||||||
|
SliceBuffer* payload() { return &payload_; } |
||||||
|
const SliceBuffer* payload() const { return &payload_; } |
||||||
|
|
||||||
|
std::string DebugString() const; |
||||||
|
|
||||||
|
private: |
||||||
|
SliceBuffer payload_; |
||||||
|
uint32_t flags_ = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
using MessageHandle = Arena::PoolPtr<Message>; |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_SRC_CORE_LIB_TRANSPORT_MESSAGE_H
|
@ -0,0 +1,37 @@ |
|||||||
|
// 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 <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/transport/metadata.h" |
||||||
|
|
||||||
|
#include "src/core/lib/transport/error_utils.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
ServerMetadataHandle ServerMetadataFromStatus(const absl::Status& status, |
||||||
|
Arena* arena) { |
||||||
|
auto hdl = arena->MakePooled<ServerMetadata>(arena); |
||||||
|
grpc_status_code code; |
||||||
|
std::string message; |
||||||
|
grpc_error_get_status(status, Timestamp::InfFuture(), &code, &message, |
||||||
|
nullptr, nullptr); |
||||||
|
hdl->Set(GrpcStatusMetadata(), code); |
||||||
|
if (!status.ok()) { |
||||||
|
hdl->Set(GrpcMessageMetadata(), Slice::FromCopiedString(message)); |
||||||
|
} |
||||||
|
return hdl; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
@ -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.
|
||||||
|
|
||||||
|
#ifndef GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_H |
||||||
|
#define GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/transport/metadata_batch.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
// Server metadata type
|
||||||
|
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
|
||||||
|
using ServerMetadata = grpc_metadata_batch; |
||||||
|
using ServerMetadataHandle = Arena::PoolPtr<ServerMetadata>; |
||||||
|
|
||||||
|
// Client initial metadata type
|
||||||
|
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
|
||||||
|
using ClientMetadata = grpc_metadata_batch; |
||||||
|
using ClientMetadataHandle = Arena::PoolPtr<ClientMetadata>; |
||||||
|
|
||||||
|
// Ok/not-ok check for trailing metadata, so that it can be used as result types
|
||||||
|
// for TrySeq.
|
||||||
|
inline bool IsStatusOk(const ServerMetadataHandle& m) { |
||||||
|
return m->get(GrpcStatusMetadata()).value_or(GRPC_STATUS_UNKNOWN) == |
||||||
|
GRPC_STATUS_OK; |
||||||
|
} |
||||||
|
|
||||||
|
ServerMetadataHandle ServerMetadataFromStatus( |
||||||
|
const absl::Status& status, Arena* arena = GetContext<Arena>()); |
||||||
|
|
||||||
|
template <> |
||||||
|
struct StatusCastImpl<ServerMetadataHandle, absl::Status> { |
||||||
|
static ServerMetadataHandle Cast(const absl::Status& m) { |
||||||
|
return ServerMetadataFromStatus(m); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template <> |
||||||
|
struct StatusCastImpl<ServerMetadataHandle, const absl::Status&> { |
||||||
|
static ServerMetadataHandle Cast(const absl::Status& m) { |
||||||
|
return ServerMetadataFromStatus(m); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template <> |
||||||
|
struct StatusCastImpl<ServerMetadataHandle, absl::Status&> { |
||||||
|
static ServerMetadataHandle Cast(const absl::Status& m) { |
||||||
|
return ServerMetadataFromStatus(m); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Anything that can be first cast to absl::Status can then be cast to
|
||||||
|
// ServerMetadataHandle.
|
||||||
|
template <typename T> |
||||||
|
struct StatusCastImpl< |
||||||
|
ServerMetadataHandle, T, |
||||||
|
absl::void_t<decltype(&StatusCastImpl<absl::Status, T>::Cast)>> { |
||||||
|
static ServerMetadataHandle Cast(const T& m) { |
||||||
|
return ServerMetadataFromStatus(StatusCast<absl::Status>(m)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_H
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue