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 "GRPCHost.h" 20 21#import <GRPCClient/GRPCCall+Cronet.h> 22#import <GRPCClient/GRPCCall.h> 23#import <GRPCClient/GRPCCallOptions.h> 24#import <GRPCClient/GRPCTransport.h> 25 26#include <grpc/grpc.h> 27#include <grpc/grpc_security.h> 28 29#import "../../internal/GRPCCallOptions+Internal.h" 30#import "GRPCChannelFactory.h" 31#import "GRPCCompletionQueue.h" 32#import "GRPCSecureChannelFactory.h" 33#import "NSDictionary+GRPC.h" 34 35NS_ASSUME_NONNULL_BEGIN 36 37static NSMutableDictionary *gHostCache; 38 39@implementation GRPCHost { 40 NSString *_PEMRootCertificates; 41 NSString *_PEMPrivateKey; 42 NSString *_PEMCertificateChain; 43} 44 45+ (nullable instancetype)hostWithAddress:(NSString *)address { 46 return [[self alloc] initWithAddress:address]; 47} 48 49// Default initializer. 50- (nullable instancetype)initWithAddress:(NSString *)address { 51 if (!address) { 52 return nil; 53 } 54 55 // To provide a default port, we try to interpret the address. If it's just a host name without 56 // scheme and without port, we'll use port 443. If it has a scheme, we pass it untouched to the C 57 // gRPC library. 58 // TODO(jcanizales): Add unit tests for the types of addresses we want to let pass untouched. 59 if (![address hasPrefix:@"dns:"] && ![address hasPrefix:@"unix:"] && 60 ![address hasPrefix:@"ipv4:"] && ![address hasPrefix:@"ipv6:"]) { 61 NSURL *hostURL = [NSURL URLWithString:[@"https://" stringByAppendingString:address]]; 62 if (hostURL.host && hostURL.port == nil) { 63 address = [hostURL.host stringByAppendingString:@":443"]; 64 } 65 } 66 67 // Look up the GRPCHost in the cache. 68 static dispatch_once_t cacheInitialization; 69 dispatch_once(&cacheInitialization, ^{ 70 gHostCache = [NSMutableDictionary dictionary]; 71 }); 72 @synchronized(gHostCache) { 73 GRPCHost *cachedHost = gHostCache[address]; 74 if (cachedHost) { 75 return cachedHost; 76 } 77 78 if ((self = [super init])) { 79 _address = [address copy]; 80 _retryEnabled = YES; 81 gHostCache[address] = self; 82 } 83 } 84 return self; 85} 86 87+ (void)resetAllHostSettings { 88 @synchronized(gHostCache) { 89 gHostCache = [NSMutableDictionary dictionary]; 90 } 91} 92 93- (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts 94 withPrivateKey:(nullable NSString *)pemPrivateKey 95 withCertChain:(nullable NSString *)pemCertChain 96 error:(NSError **)errorPtr { 97 _PEMRootCertificates = [pemRootCerts copy]; 98 _PEMPrivateKey = [pemPrivateKey copy]; 99 _PEMCertificateChain = [pemCertChain copy]; 100 return YES; 101} 102 103- (GRPCCallOptions *)callOptions { 104 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 105 options.userAgentPrefix = _userAgentPrefix; 106 options.userAgentSuffix = _userAgentSuffix; 107 options.responseSizeLimit = _responseSizeLimitOverride; 108 options.compressionAlgorithm = (GRPCCompressionAlgorithm)_compressAlgorithm; 109 options.retryEnabled = _retryEnabled; 110 options.keepaliveInterval = (NSTimeInterval)_keepaliveInterval / 1000; 111 options.keepaliveTimeout = (NSTimeInterval)_keepaliveTimeout / 1000; 112 options.connectMinTimeout = (NSTimeInterval)_minConnectTimeout / 1000; 113 options.connectInitialBackoff = (NSTimeInterval)_initialConnectBackoff / 1000; 114 options.connectMaxBackoff = (NSTimeInterval)_maxConnectBackoff / 1000; 115 options.PEMRootCertificates = _PEMRootCertificates; 116 options.PEMPrivateKey = _PEMPrivateKey; 117 options.PEMCertificateChain = _PEMCertificateChain; 118 options.hostNameOverride = _hostNameOverride; 119 if (_transportType == GRPCTransportTypeInsecure) { 120 options.transport = GRPCDefaultTransportImplList.core_insecure; 121 } else if ([GRPCCall isUsingCronet]) { 122 options.transport = gGRPCCoreCronetID; 123 } else { 124 options.transport = GRPCDefaultTransportImplList.core_secure; 125 } 126 options.logContext = _logContext; 127 128 return options; 129} 130 131+ (GRPCCallOptions *)callOptionsForHost:(NSString *)host { 132 // TODO (mxyan): Remove when old API is deprecated 133 GRPCCallOptions *callOptions = nil; 134 @synchronized(gHostCache) { 135 GRPCHost *hostConfig = [GRPCHost hostWithAddress:host]; 136 callOptions = [hostConfig callOptions]; 137 } 138 NSAssert(callOptions != nil, @"Unable to create call options object"); 139 if (callOptions == nil) { 140 NSLog(@"Unable to create call options object"); 141 callOptions = [[GRPCCallOptions alloc] init]; 142 } 143 return callOptions; 144} 145 146@end 147 148NS_ASSUME_NONNULL_END 149