1/* 2 * 3 * Copyright 2015 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 "GRPCChannel.h" 20 21#include <grpc/support/log.h> 22 23#import "../../internal/GRPCCallOptions+Internal.h" 24#import "../GRPCTransport+Private.h" 25#import "ChannelArgsUtil.h" 26#import "GRPCChannelFactory.h" 27#import "GRPCChannelPool.h" 28#import "GRPCCompletionQueue.h" 29#import "GRPCCoreFactory.h" 30#import "GRPCInsecureChannelFactory.h" 31#import "GRPCSecureChannelFactory.h" 32 33#import <GRPCClient/GRPCCall+Cronet.h> 34#import <GRPCClient/GRPCCallOptions.h> 35#import <GRPCClient/version.h> 36 37@implementation GRPCChannelConfiguration 38 39- (instancetype)initWithHost:(NSString *)host callOptions:(GRPCCallOptions *)callOptions { 40 NSAssert(host.length > 0, @"Host must not be empty."); 41 NSAssert(callOptions != nil, @"callOptions must not be empty."); 42 if (host.length == 0 || callOptions == nil) { 43 return nil; 44 } 45 46 if ((self = [super init])) { 47 _host = [host copy]; 48 _callOptions = [callOptions copy]; 49 } 50 return self; 51} 52 53- (id<GRPCChannelFactory>)channelFactory { 54 if (_callOptions.transport != NULL) { 55 id<GRPCTransportFactory> transportFactory = 56 [[GRPCTransportRegistry sharedInstance] getTransportFactoryWithID:_callOptions.transport]; 57 if (![transportFactory 58 respondsToSelector:@selector(createCoreChannelFactoryWithCallOptions:)]) { 59 // impossible because we are using GRPCCore now 60 [NSException raise:NSInternalInconsistencyException 61 format:@"Transport factory type is wrong"]; 62 } 63 id<GRPCCoreTransportFactory> coreTransportFactory = 64 (id<GRPCCoreTransportFactory>)transportFactory; 65 return [coreTransportFactory createCoreChannelFactoryWithCallOptions:_callOptions]; 66 } else { 67 // To maintain backwards compatibility with tranportType 68 GRPCTransportType type = _callOptions.transportType; 69 switch (type) { 70 case GRPCTransportTypeChttp2BoringSSL: 71 // TODO (mxyan): Remove when the API is deprecated 72 { 73 NSError *error; 74 id<GRPCChannelFactory> factory = [GRPCSecureChannelFactory 75 factoryWithPEMRootCertificates:_callOptions.PEMRootCertificates 76 privateKey:_callOptions.PEMPrivateKey 77 certChain:_callOptions.PEMCertificateChain 78 error:&error]; 79 NSAssert(factory != nil, @"Failed to create secure channel factory"); 80 if (factory == nil) { 81 NSLog(@"Error creating secure channel factory: %@", error); 82 } 83 return factory; 84 } 85 case GRPCTransportTypeCronet: { 86 id<GRPCCoreTransportFactory> transportFactory = 87 (id<GRPCCoreTransportFactory>)[[GRPCTransportRegistry sharedInstance] 88 getTransportFactoryWithID:gGRPCCoreCronetID]; 89 return [transportFactory createCoreChannelFactoryWithCallOptions:_callOptions]; 90 } 91 case GRPCTransportTypeInsecure: 92 return [GRPCInsecureChannelFactory sharedInstance]; 93 default: 94 NSLog(@"Unrecognized transport type"); 95 return nil; 96 } 97 } 98} 99 100- (NSDictionary *)channelArgs { 101 NSMutableDictionary *args = [NSMutableDictionary new]; 102 103 NSMutableString *userAgent = [[NSMutableString alloc] init]; 104 NSString *userAgentPrefix = _callOptions.userAgentPrefix; 105 if (userAgentPrefix.length != 0) { 106 [userAgent appendFormat:@"%@ ", userAgentPrefix]; 107 } 108 109 NSString *gRPCUserAgent = [NSString 110 stringWithFormat:@"grpc-objc-%@/%@", [self getTransportTypeString], GRPC_OBJC_VERSION_STRING]; 111 [userAgent appendString:gRPCUserAgent]; 112 113 NSString *userAgentSuffix = _callOptions.userAgentSuffix; 114 if (userAgentSuffix.length != 0) { 115 [userAgent appendFormat:@" %@", userAgentSuffix]; 116 } 117 118 args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = [userAgent copy]; 119 120 NSString *hostNameOverride = _callOptions.hostNameOverride; 121 if (hostNameOverride) { 122 args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = hostNameOverride; 123 } 124 125 if (_callOptions.responseSizeLimit) { 126 args[@GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH] = 127 [NSNumber numberWithUnsignedInteger:_callOptions.responseSizeLimit]; 128 } 129 130 if (_callOptions.compressionAlgorithm != GRPCCompressNone) { 131 args[@GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM] = 132 [NSNumber numberWithInteger:_callOptions.compressionAlgorithm]; 133 } 134 135 if (_callOptions.keepaliveInterval != 0) { 136 args[@GRPC_ARG_KEEPALIVE_TIME_MS] = 137 [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveInterval * 1000)]; 138 args[@GRPC_ARG_KEEPALIVE_TIMEOUT_MS] = 139 [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveTimeout * 1000)]; 140 } 141 142 if (!_callOptions.retryEnabled) { 143 args[@GRPC_ARG_ENABLE_RETRIES] = [NSNumber numberWithInt:_callOptions.retryEnabled ? 1 : 0]; 144 } 145 146 if (_callOptions.connectMinTimeout > 0) { 147 args[@GRPC_ARG_MIN_RECONNECT_BACKOFF_MS] = 148 [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectMinTimeout * 1000)]; 149 } 150 if (_callOptions.connectInitialBackoff > 0) { 151 args[@GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS] = [NSNumber 152 numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectInitialBackoff * 1000)]; 153 } 154 if (_callOptions.connectMaxBackoff > 0) { 155 args[@GRPC_ARG_MAX_RECONNECT_BACKOFF_MS] = 156 [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectMaxBackoff * 1000)]; 157 } 158 159 if (_callOptions.logContext != nil) { 160 args[@GRPC_ARG_MOBILE_LOG_CONTEXT] = _callOptions.logContext; 161 } 162 163 if (_callOptions.channelPoolDomain.length != 0) { 164 args[@GRPC_ARG_CHANNEL_POOL_DOMAIN] = _callOptions.channelPoolDomain; 165 } 166 167 [args addEntriesFromDictionary:_callOptions.additionalChannelArgs]; 168 169 return args; 170} 171 172- (NSString *)getTransportTypeString { 173 switch (_callOptions.transportType) { 174 case GRPCTransportTypeCronet: 175 return @"cronet"; 176 case GRPCTransportTypeInsecure: 177 case GRPCTransportTypeChttp2BoringSSL: 178 return @"cfstream"; 179 default: 180 return @"unknown"; 181 } 182} 183 184- (id)copyWithZone:(NSZone *)zone { 185 GRPCChannelConfiguration *newConfig = 186 [[GRPCChannelConfiguration alloc] initWithHost:_host callOptions:_callOptions]; 187 188 return newConfig; 189} 190 191- (BOOL)isEqual:(id)object { 192 if (![object isKindOfClass:[GRPCChannelConfiguration class]]) { 193 return NO; 194 } 195 GRPCChannelConfiguration *obj = (GRPCChannelConfiguration *)object; 196 if (!(obj.host == _host || (_host != nil && [obj.host isEqualToString:_host]))) return NO; 197 if (!(obj.callOptions == _callOptions || [obj.callOptions hasChannelOptionsEqualTo:_callOptions])) 198 return NO; 199 200 return YES; 201} 202 203- (NSUInteger)hash { 204 NSUInteger result = 31; 205 result ^= _host.hash; 206 result ^= _callOptions.channelOptionsHash; 207 208 return result; 209} 210 211@end 212 213@implementation GRPCChannel { 214 GRPCChannelConfiguration *_configuration; 215 216 grpc_channel *_unmanagedChannel; 217} 218 219- (instancetype)initWithChannelConfiguration:(GRPCChannelConfiguration *)channelConfiguration { 220 NSAssert(channelConfiguration != nil, @"channelConfiguration must not be empty."); 221 if (channelConfiguration == nil) { 222 return nil; 223 } 224 225 if ((self = [super init])) { 226 _configuration = [channelConfiguration copy]; 227 228 // Create gRPC core channel object. 229 NSString *host = channelConfiguration.host; 230 NSAssert(host.length != 0, @"host cannot be nil"); 231 NSDictionary *channelArgs; 232 if (channelConfiguration.callOptions.additionalChannelArgs.count != 0) { 233 NSMutableDictionary *args = [channelConfiguration.channelArgs mutableCopy]; 234 [args addEntriesFromDictionary:channelConfiguration.callOptions.additionalChannelArgs]; 235 channelArgs = args; 236 } else { 237 channelArgs = channelConfiguration.channelArgs; 238 } 239 240 id<GRPCChannelFactory> factory = channelConfiguration.channelFactory; 241 _unmanagedChannel = [factory createChannelWithHost:host channelArgs:channelArgs]; 242 NSAssert(_unmanagedChannel != NULL, @"Failed to create channel"); 243 if (_unmanagedChannel == NULL) { 244 NSLog(@"Unable to create channel."); 245 return nil; 246 } 247 } 248 return self; 249} 250 251- (grpc_call *)unmanagedCallWithPath:(NSString *)path 252 completionQueue:(GRPCCompletionQueue *)queue 253 callOptions:(GRPCCallOptions *)callOptions { 254 NSAssert(path.length > 0, @"path must not be empty."); 255 NSAssert(queue != nil, @"completionQueue must not be empty."); 256 NSAssert(callOptions != nil, @"callOptions must not be empty."); 257 if (path.length == 0) return NULL; 258 if (queue == nil) return NULL; 259 if (callOptions == nil) return NULL; 260 261 grpc_call *call = NULL; 262 // No need to lock here since _unmanagedChannel is only changed in _dealloc 263 NSAssert(_unmanagedChannel != NULL, @"Channel should have valid unmanaged channel."); 264 if (_unmanagedChannel == NULL) return NULL; 265 266 NSString *serverAuthority = 267 callOptions.transportType == GRPCTransportTypeCronet ? nil : callOptions.serverAuthority; 268 NSTimeInterval timeout = callOptions.timeout; 269 NSAssert(timeout >= 0, @"Invalid timeout"); 270 if (timeout < 0) return NULL; 271 grpc_slice host_slice = serverAuthority 272 ? grpc_slice_from_copied_string(serverAuthority.UTF8String) 273 : grpc_empty_slice(); 274 grpc_slice path_slice = grpc_slice_from_copied_string(path.UTF8String); 275 gpr_timespec deadline_ms = 276 timeout == 0 ? gpr_inf_future(GPR_CLOCK_REALTIME) 277 : gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), 278 gpr_time_from_millis((int64_t)(timeout * 1000), GPR_TIMESPAN)); 279 call = grpc_channel_create_call(_unmanagedChannel, NULL, GRPC_PROPAGATE_DEFAULTS, 280 queue.unmanagedQueue, path_slice, 281 serverAuthority ? &host_slice : NULL, deadline_ms, NULL); 282 if (serverAuthority) { 283 grpc_slice_unref(host_slice); 284 } 285 grpc_slice_unref(path_slice); 286 NSAssert(call != nil, @"Unable to create call."); 287 if (call == NULL) { 288 NSLog(@"Unable to create call."); 289 } 290 return call; 291} 292 293- (void)dealloc { 294 @synchronized(self) { 295 if (_unmanagedChannel) { 296 grpc_channel_destroy(_unmanagedChannel); 297 _unmanagedChannel = NULL; 298 } 299 } 300} 301 302@end 303