|  | @@ -70,19 +70,31 @@ class DummyInterceptor : public experimental::Interceptor {
 | 
	
		
			
				|  |  |      if (methods->QueryInterceptionHookPoint(
 | 
	
		
			
				|  |  |              experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
 | 
	
		
			
				|  |  |        num_times_run_++;
 | 
	
		
			
				|  |  | +    } else if (methods->QueryInterceptionHookPoint(
 | 
	
		
			
				|  |  | +                   experimental::InterceptionHookPoints::
 | 
	
		
			
				|  |  | +                       POST_RECV_INITIAL_METADATA)) {
 | 
	
		
			
				|  |  | +      num_times_run_reverse_++;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      methods->Proceed();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static void Reset() { num_times_run_.store(0); }
 | 
	
		
			
				|  |  | +  static void Reset() {
 | 
	
		
			
				|  |  | +    num_times_run_.store(0);
 | 
	
		
			
				|  |  | +    num_times_run_reverse_.store(0);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static int GetNumTimesRun() { return num_times_run_.load(); }
 | 
	
		
			
				|  |  | +  static int GetNumTimesRun() {
 | 
	
		
			
				|  |  | +    EXPECT_EQ(num_times_run_.load(), num_times_run_reverse_.load());
 | 
	
		
			
				|  |  | +    return num_times_run_.load();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  |    static std::atomic<int> num_times_run_;
 | 
	
		
			
				|  |  | +  static std::atomic<int> num_times_run_reverse_;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  std::atomic<int> DummyInterceptor::num_times_run_;
 | 
	
		
			
				|  |  | +std::atomic<int> DummyInterceptor::num_times_run_reverse_;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class DummyInterceptorFactory
 | 
	
		
			
				|  |  |      : public experimental::ClientInterceptorFactoryInterface {
 | 
	
	
		
			
				|  | @@ -208,7 +220,6 @@ class HijackingInterceptorMakesAnotherCall : public experimental::Interceptor {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    virtual void Intercept(experimental::InterceptorBatchMethods* methods) {
 | 
	
		
			
				|  |  |      gpr_log(GPR_ERROR, "ran this");
 | 
	
		
			
				|  |  | -    bool hijack = false;
 | 
	
		
			
				|  |  |      if (methods->QueryInterceptionHookPoint(
 | 
	
		
			
				|  |  |              experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
 | 
	
		
			
				|  |  |        auto* map = methods->GetSendInitialMetadata();
 | 
	
	
		
			
				|  | @@ -217,7 +228,6 @@ class HijackingInterceptorMakesAnotherCall : public experimental::Interceptor {
 | 
	
		
			
				|  |  |        auto iterator = map->begin();
 | 
	
		
			
				|  |  |        EXPECT_EQ("testkey", iterator->first);
 | 
	
		
			
				|  |  |        EXPECT_EQ("testvalue", iterator->second);
 | 
	
		
			
				|  |  | -      hijack = true;
 | 
	
		
			
				|  |  |        // Make a copy of the map
 | 
	
		
			
				|  |  |        metadata_map_ = *map;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -228,15 +238,20 @@ class HijackingInterceptorMakesAnotherCall : public experimental::Interceptor {
 | 
	
		
			
				|  |  |        auto copied_buffer = *buffer;
 | 
	
		
			
				|  |  |        SerializationTraits<EchoRequest>::Deserialize(&copied_buffer, &req);
 | 
	
		
			
				|  |  |        EXPECT_EQ(req.message(), "Hello");
 | 
	
		
			
				|  |  | +      req_ = req;
 | 
	
		
			
				|  |  |        auto stub = grpc::testing::EchoTestService::NewStub(
 | 
	
		
			
				|  |  |            methods->GetInterceptedChannel());
 | 
	
		
			
				|  |  | -      ClientContext ctx;
 | 
	
		
			
				|  |  | -      EchoResponse resp;
 | 
	
		
			
				|  |  | -      ctx.AddMetadata(metadata_map_.begin()->first,
 | 
	
		
			
				|  |  | -                      metadata_map_.begin()->second);
 | 
	
		
			
				|  |  | -      Status s = stub->Echo(&ctx, req, &resp);
 | 
	
		
			
				|  |  | -      EXPECT_EQ(s.ok(), true);
 | 
	
		
			
				|  |  | -      EXPECT_EQ(resp.message(), "Hello");
 | 
	
		
			
				|  |  | +      ctx_.AddMetadata(metadata_map_.begin()->first,
 | 
	
		
			
				|  |  | +                       metadata_map_.begin()->second);
 | 
	
		
			
				|  |  | +      stub->experimental_async()->Echo(&ctx_, &req_, &resp_,
 | 
	
		
			
				|  |  | +                                       [this, &methods](Status s) {
 | 
	
		
			
				|  |  | +                                         EXPECT_EQ(s.ok(), true);
 | 
	
		
			
				|  |  | +                                         EXPECT_EQ(resp_.message(), "Hello");
 | 
	
		
			
				|  |  | +                                         methods->Hijack();
 | 
	
		
			
				|  |  | +                                       });
 | 
	
		
			
				|  |  | +      // There isn't going to be any other interesting operation in this batch,
 | 
	
		
			
				|  |  | +      // so it is fine to return
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (methods->QueryInterceptionHookPoint(
 | 
	
		
			
				|  |  |              experimental::InterceptionHookPoints::PRE_SEND_CLOSE)) {
 | 
	
	
		
			
				|  | @@ -254,8 +269,7 @@ class HijackingInterceptorMakesAnotherCall : public experimental::Interceptor {
 | 
	
		
			
				|  |  |            static_cast<EchoResponse*>(methods->GetRecvMessage());
 | 
	
		
			
				|  |  |        // Check that we got the hijacked message, and re-insert the expected
 | 
	
		
			
				|  |  |        // message
 | 
	
		
			
				|  |  | -      EXPECT_EQ(resp->message(), "Hello1");
 | 
	
		
			
				|  |  | -      resp->set_message("Hello");
 | 
	
		
			
				|  |  | +      EXPECT_EQ(resp->message(), "Hello");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (methods->QueryInterceptionHookPoint(
 | 
	
		
			
				|  |  |              experimental::InterceptionHookPoints::POST_RECV_STATUS)) {
 | 
	
	
		
			
				|  | @@ -282,28 +296,27 @@ class HijackingInterceptorMakesAnotherCall : public experimental::Interceptor {
 | 
	
		
			
				|  |  |        // Insert a different message than expected
 | 
	
		
			
				|  |  |        EchoResponse* resp =
 | 
	
		
			
				|  |  |            static_cast<EchoResponse*>(methods->GetRecvMessage());
 | 
	
		
			
				|  |  | -      resp->set_message("Hello1");
 | 
	
		
			
				|  |  | +      resp->set_message(resp_.message());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (methods->QueryInterceptionHookPoint(
 | 
	
		
			
				|  |  |              experimental::InterceptionHookPoints::PRE_RECV_STATUS)) {
 | 
	
		
			
				|  |  |        auto* map = methods->GetRecvTrailingMetadata();
 | 
	
		
			
				|  |  |        // insert the metadata that we want
 | 
	
		
			
				|  |  |        EXPECT_EQ(map->size(), 0);
 | 
	
		
			
				|  |  | -      map->insert(std::make_pair("testkey", "testvalue"));
 | 
	
		
			
				|  |  | +      *map = ctx_.GetServerTrailingMetadata();
 | 
	
		
			
				|  |  |        auto* status = methods->GetRecvStatus();
 | 
	
		
			
				|  |  |        *status = Status(StatusCode::OK, "");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    if (hijack) {
 | 
	
		
			
				|  |  | -      gpr_log(GPR_ERROR, "hijacking");
 | 
	
		
			
				|  |  | -      methods->Hijack();
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      methods->Proceed();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    methods->Proceed();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  |    experimental::ClientRpcInfo* info_;
 | 
	
		
			
				|  |  |    std::multimap<grpc::string, grpc::string> metadata_map_;
 | 
	
		
			
				|  |  | +  ClientContext ctx_;
 | 
	
		
			
				|  |  | +  EchoRequest req_;
 | 
	
		
			
				|  |  | +  EchoResponse resp_;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class HijackingInterceptorMakesAnotherCallFactory
 | 
	
	
		
			
				|  | @@ -401,6 +414,32 @@ void MakeCall(std::shared_ptr<Channel> channel) {
 | 
	
		
			
				|  |  |    EXPECT_EQ(resp.message(), "Hello");
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void MakeCallbackCall(std::shared_ptr<Channel> channel) {
 | 
	
		
			
				|  |  | +  auto stub = grpc::testing::EchoTestService::NewStub(channel);
 | 
	
		
			
				|  |  | +  ClientContext ctx;
 | 
	
		
			
				|  |  | +  EchoRequest req;
 | 
	
		
			
				|  |  | +  std::mutex mu;
 | 
	
		
			
				|  |  | +  std::condition_variable cv;
 | 
	
		
			
				|  |  | +  bool done = false;
 | 
	
		
			
				|  |  | +  req.mutable_param()->set_echo_metadata(true);
 | 
	
		
			
				|  |  | +  ctx.AddMetadata("testkey", "testvalue");
 | 
	
		
			
				|  |  | +  req.set_message("Hello");
 | 
	
		
			
				|  |  | +  EchoResponse resp;
 | 
	
		
			
				|  |  | +  stub->experimental_async()->Echo(&ctx, &req, &resp,
 | 
	
		
			
				|  |  | +                                   [&resp, &mu, &done, &cv](Status s) {
 | 
	
		
			
				|  |  | +                                     gpr_log(GPR_ERROR, "got the callback");
 | 
	
		
			
				|  |  | +                                     EXPECT_EQ(s.ok(), true);
 | 
	
		
			
				|  |  | +                                     EXPECT_EQ(resp.message(), "Hello");
 | 
	
		
			
				|  |  | +                                     std::lock_guard<std::mutex> l(mu);
 | 
	
		
			
				|  |  | +                                     done = true;
 | 
	
		
			
				|  |  | +                                     cv.notify_one();
 | 
	
		
			
				|  |  | +                                   });
 | 
	
		
			
				|  |  | +  std::unique_lock<std::mutex> l(mu);
 | 
	
		
			
				|  |  | +  while (!done) {
 | 
	
		
			
				|  |  | +    cv.wait(l);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorLoggingTest) {
 | 
	
		
			
				|  |  |    ChannelArguments args;
 | 
	
		
			
				|  |  |    DummyInterceptor::Reset();
 | 
	
	
		
			
				|  | @@ -444,16 +483,7 @@ TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorHijackingTest) {
 | 
	
		
			
				|  |  |    auto channel = experimental::CreateCustomChannelWithInterceptors(
 | 
	
		
			
				|  |  |        server_address_, InsecureChannelCredentials(), args, std::move(creators));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  auto stub = grpc::testing::EchoTestService::NewStub(channel);
 | 
	
		
			
				|  |  | -  ClientContext ctx;
 | 
	
		
			
				|  |  | -  EchoRequest req;
 | 
	
		
			
				|  |  | -  req.mutable_param()->set_echo_metadata(true);
 | 
	
		
			
				|  |  | -  ctx.AddMetadata("testkey", "testvalue");
 | 
	
		
			
				|  |  | -  req.set_message("Hello");
 | 
	
		
			
				|  |  | -  EchoResponse resp;
 | 
	
		
			
				|  |  | -  Status s = stub->Echo(&ctx, req, &resp);
 | 
	
		
			
				|  |  | -  EXPECT_EQ(s.ok(), true);
 | 
	
		
			
				|  |  | -  EXPECT_EQ(resp.message(), "Hello");
 | 
	
		
			
				|  |  | +  MakeCall(channel);
 | 
	
		
			
				|  |  |    // Make sure only 20 dummy interceptors were run
 | 
	
		
			
				|  |  |    EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -471,16 +501,7 @@ TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorLogThenHijackTest) {
 | 
	
		
			
				|  |  |    auto channel = experimental::CreateCustomChannelWithInterceptors(
 | 
	
		
			
				|  |  |        server_address_, InsecureChannelCredentials(), args, std::move(creators));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  auto stub = grpc::testing::EchoTestService::NewStub(channel);
 | 
	
		
			
				|  |  | -  ClientContext ctx;
 | 
	
		
			
				|  |  | -  EchoRequest req;
 | 
	
		
			
				|  |  | -  req.mutable_param()->set_echo_metadata(true);
 | 
	
		
			
				|  |  | -  ctx.AddMetadata("testkey", "testvalue");
 | 
	
		
			
				|  |  | -  req.set_message("Hello");
 | 
	
		
			
				|  |  | -  EchoResponse resp;
 | 
	
		
			
				|  |  | -  Status s = stub->Echo(&ctx, req, &resp);
 | 
	
		
			
				|  |  | -  EXPECT_EQ(s.ok(), true);
 | 
	
		
			
				|  |  | -  EXPECT_EQ(resp.message(), "Hello");
 | 
	
		
			
				|  |  | +  MakeCall(channel);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  TEST_F(ClientInterceptorsEnd2endTest,
 | 
	
	
		
			
				|  | @@ -491,35 +512,48 @@ TEST_F(ClientInterceptorsEnd2endTest,
 | 
	
		
			
				|  |  |        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
 | 
	
		
			
				|  |  |        new std::vector<
 | 
	
		
			
				|  |  |            std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
 | 
	
		
			
				|  |  | -  // Add 20 dummy interceptors before hijacking interceptor
 | 
	
		
			
				|  |  | -  for (auto i = 0; i < 20; i++) {
 | 
	
		
			
				|  |  | +  // Add 5 dummy interceptors before hijacking interceptor
 | 
	
		
			
				|  |  | +  for (auto i = 0; i < 5; i++) {
 | 
	
		
			
				|  |  |      creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
 | 
	
		
			
				|  |  |          new DummyInterceptorFactory()));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    creators->push_back(
 | 
	
		
			
				|  |  |        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>(
 | 
	
		
			
				|  |  |            new HijackingInterceptorMakesAnotherCallFactory()));
 | 
	
		
			
				|  |  | -  // Add 20 dummy interceptors after hijacking interceptor
 | 
	
		
			
				|  |  | -  for (auto i = 0; i < 20; i++) {
 | 
	
		
			
				|  |  | +  // Add 7 dummy interceptors after hijacking interceptor
 | 
	
		
			
				|  |  | +  for (auto i = 0; i < 7; i++) {
 | 
	
		
			
				|  |  |      creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
 | 
	
		
			
				|  |  |          new DummyInterceptorFactory()));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    auto channel = experimental::CreateCustomChannelWithInterceptors(
 | 
	
		
			
				|  |  |        server_address_, InsecureChannelCredentials(), args, std::move(creators));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  auto stub = grpc::testing::EchoTestService::NewStub(channel);
 | 
	
		
			
				|  |  | -  ClientContext ctx;
 | 
	
		
			
				|  |  | -  EchoRequest req;
 | 
	
		
			
				|  |  | -  req.mutable_param()->set_echo_metadata(true);
 | 
	
		
			
				|  |  | -  ctx.AddMetadata("testkey", "testvalue");
 | 
	
		
			
				|  |  | -  req.set_message("Hello");
 | 
	
		
			
				|  |  | -  EchoResponse resp;
 | 
	
		
			
				|  |  | -  Status s = stub->Echo(&ctx, req, &resp);
 | 
	
		
			
				|  |  | -  EXPECT_EQ(s.ok(), true);
 | 
	
		
			
				|  |  | -  EXPECT_EQ(resp.message(), "Hello");
 | 
	
		
			
				|  |  | +  MakeCall(channel);
 | 
	
		
			
				|  |  |    // Make sure all interceptors were run once, since the hijacking interceptor
 | 
	
		
			
				|  |  |    // makes an RPC on the intercepted channel
 | 
	
		
			
				|  |  | -  EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 40);
 | 
	
		
			
				|  |  | +  EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 12);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +TEST_F(ClientInterceptorsEnd2endTest,
 | 
	
		
			
				|  |  | +       ClientInterceptorLoggingTestWithCallback) {
 | 
	
		
			
				|  |  | +  ChannelArguments args;
 | 
	
		
			
				|  |  | +  DummyInterceptor::Reset();
 | 
	
		
			
				|  |  | +  auto creators = std::unique_ptr<std::vector<
 | 
	
		
			
				|  |  | +      std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
 | 
	
		
			
				|  |  | +      new std::vector<
 | 
	
		
			
				|  |  | +          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
 | 
	
		
			
				|  |  | +  creators->push_back(std::unique_ptr<LoggingInterceptorFactory>(
 | 
	
		
			
				|  |  | +      new LoggingInterceptorFactory()));
 | 
	
		
			
				|  |  | +  // Add 20 dummy interceptors
 | 
	
		
			
				|  |  | +  for (auto i = 0; i < 20; i++) {
 | 
	
		
			
				|  |  | +    creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
 | 
	
		
			
				|  |  | +        new DummyInterceptorFactory()));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  auto channel = experimental::CreateCustomChannelWithInterceptors(
 | 
	
		
			
				|  |  | +      server_address_, InsecureChannelCredentials(), args, std::move(creators));
 | 
	
		
			
				|  |  | +  MakeCallbackCall(channel);
 | 
	
		
			
				|  |  | +  // Make sure all 20 dummy interceptors were run
 | 
	
		
			
				|  |  | +  EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  }  // namespace
 |