1/* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19#import "TestHelper.h" 20#import <Cronet/Cronet.h> 21#import <grpcpp/impl/codegen/string_ref.h> 22#import <grpcpp/support/config.h> 23 24using grpc::ServerContext; 25using grpc::Status; 26using grpc::testing::EchoRequest; 27using grpc::testing::EchoResponse; 28using grpc::testing::EchoTestService; 29using std::chrono::system_clock; 30 31std::atomic<int> PhonyInterceptor::num_times_run_; 32std::atomic<int> PhonyInterceptor::num_times_run_reverse_; 33 34std::string ToString(const grpc::string_ref& r) { return std::string(r.data(), r.size()); } 35 36void configureCronet(void) { 37 static dispatch_once_t configureCronet; 38 dispatch_once(&configureCronet, ^{ 39 [Cronet setHttp2Enabled:YES]; 40 [Cronet setSslKeyLogFileName:@"Documents/key"]; 41 [Cronet enableTestCertVerifierForTesting]; 42 NSURL* url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory 43 inDomains:NSUserDomainMask] lastObject]; 44 [Cronet start]; 45 [Cronet startNetLogToFile:@"cronet_netlog.json" logBytes:YES]; 46 }); 47} 48 49bool CheckIsLocalhost(const std::string& addr) { 50 const std::string kIpv6("[::1]:"); 51 const std::string kIpv4MappedIpv6("[::ffff:127.0.0.1]:"); 52 const std::string kIpv4("127.0.0.1:"); 53 return addr.substr(0, kIpv4.size()) == kIpv4 || 54 addr.substr(0, kIpv4MappedIpv6.size()) == kIpv4MappedIpv6 || 55 addr.substr(0, kIpv6.size()) == kIpv6; 56} 57 58int GetIntValueFromMetadataHelper(const char* key, 59 const std::multimap<grpc::string_ref, grpc::string_ref>& metadata, 60 int default_value) { 61 if (metadata.find(key) != metadata.end()) { 62 std::istringstream iss(ToString(metadata.find(key)->second)); 63 iss >> default_value; 64 } 65 66 return default_value; 67} 68 69int GetIntValueFromMetadata(const char* key, 70 const std::multimap<grpc::string_ref, grpc::string_ref>& metadata, 71 int default_value) { 72 return GetIntValueFromMetadataHelper(key, metadata, default_value); 73} 74 75// When echo_deadline is requested, deadline seen in the ServerContext is set in 76// the response in seconds. 77void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request, EchoResponse* response) { 78 if (request->has_param() && request->param().echo_deadline()) { 79 gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_REALTIME); 80 if (context->deadline() != system_clock::time_point::max()) { 81 grpc::Timepoint2Timespec(context->deadline(), &deadline); 82 } 83 response->mutable_param()->set_request_deadline(deadline.tv_sec); 84 } 85} 86 87Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request, 88 EchoResponse* response) { 89 // A bit of sleep to make sure that short deadline tests fail 90 if (request->has_param() && request->param().server_sleep_us() > 0) { 91 gpr_sleep_until( 92 gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), 93 gpr_time_from_micros(request->param().server_sleep_us(), GPR_TIMESPAN))); 94 } 95 96 if (request->has_param() && request->param().has_expected_error()) { 97 const auto& error = request->param().expected_error(); 98 return Status(static_cast<grpc::StatusCode>(error.code()), error.error_message(), 99 error.binary_error_details()); 100 } 101 102 if (request->has_param() && request->param().echo_metadata()) { 103 const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata = 104 context->client_metadata(); 105 for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator iter = 106 client_metadata.begin(); 107 iter != client_metadata.end(); ++iter) { 108 context->AddTrailingMetadata(ToString(iter->first), ToString(iter->second)); 109 } 110 // Terminate rpc with error and debug info in trailer. 111 if (request->param().debug_info().stack_entries_size() || 112 !request->param().debug_info().detail().empty()) { 113 std::string serialized_debug_info = request->param().debug_info().SerializeAsString(); 114 context->AddTrailingMetadata(kDebugInfoTrailerKey, serialized_debug_info); 115 return Status::CANCELLED; 116 } 117 } 118 119 response->set_message(request->message()); 120 MaybeEchoDeadline(context, request, response); 121 return Status::OK; 122} 123 124Status TestServiceImpl::RequestStream(ServerContext* context, 125 grpc::ServerReader<EchoRequest>* reader, 126 EchoResponse* response) { 127 EchoRequest request; 128 response->set_message(""); 129 int num_msgs_read = 0; 130 while (reader->Read(&request)) { 131 response->mutable_message()->append(request.message()); 132 ++num_msgs_read; 133 } 134 return Status::OK; 135} 136 137Status TestServiceImpl::ResponseStream(ServerContext* context, const EchoRequest* request, 138 grpc::ServerWriter<EchoResponse>* writer) { 139 EchoResponse response; 140 int server_responses_to_send = 141 GetIntValueFromMetadata(kServerResponseStreamsToSend, context->client_metadata(), 142 kServerDefaultResponseStreamsToSend); 143 for (int i = 0; i < server_responses_to_send; i++) { 144 response.set_message(request->message() + std::to_string(i)); 145 if (i == server_responses_to_send - 1) { 146 writer->WriteLast(response, grpc::WriteOptions()); 147 } else { 148 writer->Write(response); 149 } 150 } 151 return Status::OK; 152} 153 154Status TestServiceImpl::BidiStream(ServerContext* context, 155 grpc::ServerReaderWriter<EchoResponse, EchoRequest>* stream) { 156 EchoRequest request; 157 EchoResponse response; 158 159 // kServerFinishAfterNReads suggests after how many reads, the server should 160 // write the last message and send status (coalesced using WriteLast) 161 int server_write_last = 162 GetIntValueFromMetadata(kServerFinishAfterNReads, context->client_metadata(), 0); 163 164 int read_counts = 0; 165 while (stream->Read(&request)) { 166 read_counts++; 167 response.set_message(request.message()); 168 if (read_counts == server_write_last) { 169 stream->WriteLast(response, grpc::WriteOptions()); 170 } else { 171 stream->Write(response); 172 } 173 } 174 175 return Status::OK; 176} 177 178void PhonyInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* methods) { 179 if (methods->QueryInterceptionHookPoint( 180 grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) { 181 num_times_run_++; 182 } else if (methods->QueryInterceptionHookPoint( 183 grpc::experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) { 184 num_times_run_reverse_++; 185 } 186 methods->Proceed(); 187} 188 189void PhonyInterceptor::Reset() { 190 num_times_run_.store(0); 191 num_times_run_reverse_.store(0); 192} 193 194int PhonyInterceptor::GetNumTimesRun() { 195 NSCAssert(num_times_run_.load() == num_times_run_reverse_.load(), 196 @"Interceptor must run same number of times in both directions"); 197 return num_times_run_.load(); 198} 199