| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 | /* * * Copyright 2018 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 <map>#include <grpcpp/support/client_interceptor.h>#ifdef BAZEL_BUILD#include "examples/protos/keyvaluestore.grpc.pb.h"#else#include "keyvaluestore.grpc.pb.h"#endif// This is a naive implementation of a cache. A new cache is for each call. For// each new key request, the key is first searched in the map and if found, the// interceptor fills in the return value without making a request to the server.// Only if the key is not found in the cache do we make a request.class CachingInterceptor : public grpc::experimental::Interceptor { public:  CachingInterceptor(grpc::experimental::ClientRpcInfo* info) {}  void Intercept(      ::grpc::experimental::InterceptorBatchMethods* methods) override {    bool hijack = false;    if (methods->QueryInterceptionHookPoint(            grpc::experimental::InterceptionHookPoints::                PRE_SEND_INITIAL_METADATA)) {      // Hijack all calls      hijack = true;      // Create a stream on which this interceptor can make requests      stub_ = keyvaluestore::KeyValueStore::NewStub(          methods->GetInterceptedChannel());      stream_ = stub_->GetValues(&context_);    }    if (methods->QueryInterceptionHookPoint(            grpc::experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) {      // We know that clients perform a Read and a Write in a loop, so we don't      // need to maintain a list of the responses.      std::string requested_key;      const keyvaluestore::Request* req_msg =          static_cast<const keyvaluestore::Request*>(methods->GetSendMessage());      if (req_msg != nullptr) {        requested_key = req_msg->key();      } else {        // The non-serialized form would not be available in certain scenarios,        // so add a fallback        keyvaluestore::Request req_msg;        auto* buffer = methods->GetSerializedSendMessage();        auto copied_buffer = *buffer;        GPR_ASSERT(            grpc::SerializationTraits<keyvaluestore::Request>::Deserialize(                &copied_buffer, &req_msg)                .ok());        requested_key = req_msg.key();      }      // Check if the key is present in the map      auto search = cached_map_.find(requested_key);      if (search != cached_map_.end()) {        std::cout << "Key " << requested_key << "found in map";        response_ = search->second;      } else {        std::cout << "Key " << requested_key << "not found in cache";        // Key was not found in the cache, so make a request        keyvaluestore::Request req;        req.set_key(requested_key);        stream_->Write(req);        keyvaluestore::Response resp;        stream_->Read(&resp);        response_ = resp.value();        // Insert the pair in the cache for future requests        cached_map_.insert({requested_key, response_});      }    }    if (methods->QueryInterceptionHookPoint(            grpc::experimental::InterceptionHookPoints::PRE_SEND_CLOSE)) {      stream_->WritesDone();    }    if (methods->QueryInterceptionHookPoint(            grpc::experimental::InterceptionHookPoints::PRE_RECV_MESSAGE)) {      keyvaluestore::Response* resp =          static_cast<keyvaluestore::Response*>(methods->GetRecvMessage());      resp->set_value(response_);    }    if (methods->QueryInterceptionHookPoint(            grpc::experimental::InterceptionHookPoints::PRE_RECV_STATUS)) {      auto* status = methods->GetRecvStatus();      *status = grpc::Status::OK;    }    // One of Hijack or Proceed always needs to be called to make progress.    if (hijack) {      // Hijack is called only once when PRE_SEND_INITIAL_METADATA is present in      // the hook points      methods->Hijack();    } else {      // Proceed is an indicator that the interceptor is done intercepting the      // batch.      methods->Proceed();    }  } private:  grpc::ClientContext context_;  std::unique_ptr<keyvaluestore::KeyValueStore::Stub> stub_;  std::unique_ptr<      grpc::ClientReaderWriter<keyvaluestore::Request, keyvaluestore::Response>>      stream_;  std::map<std::string, std::string> cached_map_;  std::string response_;};class CachingInterceptorFactory    : public grpc::experimental::ClientInterceptorFactoryInterface { public:  grpc::experimental::Interceptor* CreateClientInterceptor(      grpc::experimental::ClientRpcInfo* info) override {    return new CachingInterceptor(info);  }};
 |