| // |
| // Copyright 2016 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. |
| // |
| |
| // This is similar to the sockaddr resolver, except that it supports a |
| // bunch of query args that are useful for dependency injection in tests. |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include <limits.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/string_util.h> |
| |
| #include "src/core/ext/filters/client_channel/parse_address.h" |
| #include "src/core/ext/filters/client_channel/resolver_registry.h" |
| #include "src/core/ext/filters/client_channel/server_address.h" |
| #include "src/core/lib/channel/channel_args.h" |
| #include "src/core/lib/gpr/string.h" |
| #include "src/core/lib/gpr/useful.h" |
| #include "src/core/lib/iomgr/closure.h" |
| #include "src/core/lib/iomgr/combiner.h" |
| #include "src/core/lib/iomgr/resolve_address.h" |
| #include "src/core/lib/iomgr/unix_sockets_posix.h" |
| #include "src/core/lib/slice/slice_internal.h" |
| #include "src/core/lib/slice/slice_string_helpers.h" |
| |
| #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" |
| |
| namespace grpc_core { |
| |
| // This cannot be in an anonymous namespace, because it is a friend of |
| // FakeResolverResponseGenerator. |
| class FakeResolver : public Resolver { |
| public: |
| explicit FakeResolver(ResolverArgs args); |
| |
| void StartLocked() override; |
| |
| void RequestReresolutionLocked() override; |
| |
| private: |
| friend class FakeResolverResponseGenerator; |
| |
| virtual ~FakeResolver(); |
| |
| void ShutdownLocked() override { active_ = false; } |
| |
| void MaybeSendResultLocked(); |
| |
| static void ReturnReresolutionResult(void* arg, grpc_error* error); |
| |
| // passed-in parameters |
| grpc_channel_args* channel_args_ = nullptr; |
| // If has_next_result_ is true, next_result_ is the next resolution result |
| // to be returned. |
| bool has_next_result_ = false; |
| Result next_result_; |
| // Result to use for the pretended re-resolution in |
| // RequestReresolutionLocked(). |
| bool has_reresolution_result_ = false; |
| Result reresolution_result_; |
| // True between the calls to StartLocked() ShutdownLocked(). |
| bool active_ = false; |
| // if true, return failure |
| bool return_failure_ = false; |
| // pending re-resolution |
| grpc_closure reresolution_closure_; |
| bool reresolution_closure_pending_ = false; |
| }; |
| |
| FakeResolver::FakeResolver(ResolverArgs args) |
| : Resolver(args.combiner, std::move(args.result_handler)) { |
| GRPC_CLOSURE_INIT(&reresolution_closure_, ReturnReresolutionResult, this, |
| grpc_combiner_scheduler(combiner())); |
| channel_args_ = grpc_channel_args_copy(args.args); |
| FakeResolverResponseGenerator* response_generator = |
| FakeResolverResponseGenerator::GetFromArgs(args.args); |
| if (response_generator != nullptr) { |
| response_generator->resolver_ = this; |
| if (response_generator->has_result_) { |
| response_generator->SetResponse(std::move(response_generator->result_)); |
| response_generator->has_result_ = false; |
| } |
| } |
| } |
| |
| FakeResolver::~FakeResolver() { grpc_channel_args_destroy(channel_args_); } |
| |
| void FakeResolver::StartLocked() { |
| active_ = true; |
| MaybeSendResultLocked(); |
| } |
| |
| void FakeResolver::RequestReresolutionLocked() { |
| if (has_reresolution_result_ || return_failure_) { |
| next_result_ = reresolution_result_; |
| has_next_result_ = true; |
| // Return the result in a different closure, so that we don't call |
| // back into the LB policy while it's still processing the previous |
| // update. |
| if (!reresolution_closure_pending_) { |
| reresolution_closure_pending_ = true; |
| Ref().release(); // ref held by closure |
| GRPC_CLOSURE_SCHED(&reresolution_closure_, GRPC_ERROR_NONE); |
| } |
| } |
| } |
| |
| void FakeResolver::MaybeSendResultLocked() { |
| if (!active_) return; |
| if (return_failure_) { |
| // TODO(roth): Change resolver result generator to be able to inject |
| // the error to be returned. |
| result_handler()->ReturnError(grpc_error_set_int( |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver transient failure"), |
| GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); |
| return_failure_ = false; |
| } else if (has_next_result_) { |
| Result result; |
| result.addresses = std::move(next_result_.addresses); |
| result.service_config = std::move(next_result_.service_config); |
| // TODO(roth): Use std::move() once grpc_error is converted to C++. |
| result.service_config_error = next_result_.service_config_error; |
| next_result_.service_config_error = GRPC_ERROR_NONE; |
| // When both next_results_ and channel_args_ contain an arg with the same |
| // name, only the one in next_results_ will be kept since next_results_ is |
| // before channel_args_. |
| result.args = grpc_channel_args_union(next_result_.args, channel_args_); |
| result_handler()->ReturnResult(std::move(result)); |
| has_next_result_ = false; |
| } |
| } |
| |
| void FakeResolver::ReturnReresolutionResult(void* arg, grpc_error* error) { |
| FakeResolver* self = static_cast<FakeResolver*>(arg); |
| self->reresolution_closure_pending_ = false; |
| self->MaybeSendResultLocked(); |
| self->Unref(); |
| } |
| |
| // |
| // FakeResolverResponseGenerator |
| // |
| |
| struct SetResponseClosureArg { |
| grpc_closure set_response_closure; |
| FakeResolverResponseGenerator* generator; |
| Resolver::Result result; |
| bool has_result = false; |
| bool immediate = true; |
| }; |
| |
| void FakeResolverResponseGenerator::SetResponseLocked(void* arg, |
| grpc_error* error) { |
| SetResponseClosureArg* closure_arg = static_cast<SetResponseClosureArg*>(arg); |
| FakeResolver* resolver = closure_arg->generator->resolver_; |
| resolver->next_result_ = std::move(closure_arg->result); |
| resolver->has_next_result_ = true; |
| resolver->MaybeSendResultLocked(); |
| closure_arg->generator->Unref(); |
| Delete(closure_arg); |
| } |
| |
| void FakeResolverResponseGenerator::SetResponse(Resolver::Result result) { |
| if (resolver_ != nullptr) { |
| Ref().release(); // ref to be held by closure |
| SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>(); |
| closure_arg->generator = this; |
| closure_arg->result = std::move(result); |
| GRPC_CLOSURE_SCHED( |
| GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetResponseLocked, |
| closure_arg, |
| grpc_combiner_scheduler(resolver_->combiner())), |
| GRPC_ERROR_NONE); |
| } else { |
| GPR_ASSERT(!has_result_); |
| has_result_ = true; |
| result_ = std::move(result); |
| } |
| } |
| |
| void FakeResolverResponseGenerator::SetReresolutionResponseLocked( |
| void* arg, grpc_error* error) { |
| SetResponseClosureArg* closure_arg = static_cast<SetResponseClosureArg*>(arg); |
| FakeResolver* resolver = closure_arg->generator->resolver_; |
| resolver->reresolution_result_ = std::move(closure_arg->result); |
| resolver->has_reresolution_result_ = closure_arg->has_result; |
| Delete(closure_arg); |
| } |
| |
| void FakeResolverResponseGenerator::SetReresolutionResponse( |
| Resolver::Result result) { |
| GPR_ASSERT(resolver_ != nullptr); |
| SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>(); |
| closure_arg->generator = this; |
| closure_arg->result = std::move(result); |
| closure_arg->has_result = true; |
| GRPC_CLOSURE_SCHED( |
| GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, |
| SetReresolutionResponseLocked, closure_arg, |
| grpc_combiner_scheduler(resolver_->combiner())), |
| GRPC_ERROR_NONE); |
| } |
| |
| void FakeResolverResponseGenerator::UnsetReresolutionResponse() { |
| GPR_ASSERT(resolver_ != nullptr); |
| SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>(); |
| closure_arg->generator = this; |
| GRPC_CLOSURE_SCHED( |
| GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, |
| SetReresolutionResponseLocked, closure_arg, |
| grpc_combiner_scheduler(resolver_->combiner())), |
| GRPC_ERROR_NONE); |
| } |
| |
| void FakeResolverResponseGenerator::SetFailureLocked(void* arg, |
| grpc_error* error) { |
| SetResponseClosureArg* closure_arg = static_cast<SetResponseClosureArg*>(arg); |
| FakeResolver* resolver = closure_arg->generator->resolver_; |
| resolver->return_failure_ = true; |
| if (closure_arg->immediate) resolver->MaybeSendResultLocked(); |
| Delete(closure_arg); |
| } |
| |
| void FakeResolverResponseGenerator::SetFailure() { |
| GPR_ASSERT(resolver_ != nullptr); |
| SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>(); |
| closure_arg->generator = this; |
| GRPC_CLOSURE_SCHED( |
| GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked, |
| closure_arg, |
| grpc_combiner_scheduler(resolver_->combiner())), |
| GRPC_ERROR_NONE); |
| } |
| |
| void FakeResolverResponseGenerator::SetFailureOnReresolution() { |
| GPR_ASSERT(resolver_ != nullptr); |
| SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>(); |
| closure_arg->generator = this; |
| closure_arg->immediate = false; |
| GRPC_CLOSURE_SCHED( |
| GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked, |
| closure_arg, |
| grpc_combiner_scheduler(resolver_->combiner())), |
| GRPC_ERROR_NONE); |
| } |
| |
| namespace { |
| |
| static void* response_generator_arg_copy(void* p) { |
| FakeResolverResponseGenerator* generator = |
| static_cast<FakeResolverResponseGenerator*>(p); |
| // TODO(roth): We currently deal with this ref manually. Once the |
| // new channel args code is converted to C++, find a way to track this ref |
| // in a cleaner way. |
| RefCountedPtr<FakeResolverResponseGenerator> copy = generator->Ref(); |
| copy.release(); |
| return p; |
| } |
| |
| static void response_generator_arg_destroy(void* p) { |
| FakeResolverResponseGenerator* generator = |
| static_cast<FakeResolverResponseGenerator*>(p); |
| generator->Unref(); |
| } |
| |
| static int response_generator_cmp(void* a, void* b) { return GPR_ICMP(a, b); } |
| |
| static const grpc_arg_pointer_vtable response_generator_arg_vtable = { |
| response_generator_arg_copy, response_generator_arg_destroy, |
| response_generator_cmp}; |
| |
| } // namespace |
| |
| grpc_arg FakeResolverResponseGenerator::MakeChannelArg( |
| FakeResolverResponseGenerator* generator) { |
| grpc_arg arg; |
| arg.type = GRPC_ARG_POINTER; |
| arg.key = (char*)GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR; |
| arg.value.pointer.p = generator; |
| arg.value.pointer.vtable = &response_generator_arg_vtable; |
| return arg; |
| } |
| |
| FakeResolverResponseGenerator* FakeResolverResponseGenerator::GetFromArgs( |
| const grpc_channel_args* args) { |
| const grpc_arg* arg = |
| grpc_channel_args_find(args, GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR); |
| if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return nullptr; |
| return static_cast<FakeResolverResponseGenerator*>(arg->value.pointer.p); |
| } |
| |
| // |
| // Factory |
| // |
| |
| namespace { |
| |
| class FakeResolverFactory : public ResolverFactory { |
| public: |
| OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override { |
| return OrphanablePtr<Resolver>(New<FakeResolver>(std::move(args))); |
| } |
| |
| const char* scheme() const override { return "fake"; } |
| }; |
| |
| } // namespace |
| |
| } // namespace grpc_core |
| |
| void grpc_resolver_fake_init() { |
| grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( |
| grpc_core::UniquePtr<grpc_core::ResolverFactory>( |
| grpc_core::New<grpc_core::FakeResolverFactory>())); |
| } |
| |
| void grpc_resolver_fake_shutdown() {} |