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