• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 (!
58        [transportFactory 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  NSString *userAgent = @"grpc-objc/" GRPC_OBJC_VERSION_STRING;
104  NSString *userAgentPrefix = _callOptions.userAgentPrefix;
105  if (userAgentPrefix.length != 0) {
106    args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] =
107        [_callOptions.userAgentPrefix stringByAppendingFormat:@" %@", userAgent];
108  } else {
109    args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = userAgent;
110  }
111
112  NSString *hostNameOverride = _callOptions.hostNameOverride;
113  if (hostNameOverride) {
114    args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = hostNameOverride;
115  }
116
117  if (_callOptions.responseSizeLimit) {
118    args[@GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH] =
119        [NSNumber numberWithUnsignedInteger:_callOptions.responseSizeLimit];
120  }
121
122  if (_callOptions.compressionAlgorithm != GRPC_COMPRESS_NONE) {
123    args[@GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM] =
124        [NSNumber numberWithInteger:_callOptions.compressionAlgorithm];
125  }
126
127  if (_callOptions.keepaliveInterval != 0) {
128    args[@GRPC_ARG_KEEPALIVE_TIME_MS] =
129        [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveInterval * 1000)];
130    args[@GRPC_ARG_KEEPALIVE_TIMEOUT_MS] =
131        [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveTimeout * 1000)];
132  }
133
134  if (!_callOptions.retryEnabled) {
135    args[@GRPC_ARG_ENABLE_RETRIES] = [NSNumber numberWithInt:_callOptions.retryEnabled ? 1 : 0];
136  }
137
138  if (_callOptions.connectMinTimeout > 0) {
139    args[@GRPC_ARG_MIN_RECONNECT_BACKOFF_MS] =
140        [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectMinTimeout * 1000)];
141  }
142  if (_callOptions.connectInitialBackoff > 0) {
143    args[@GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS] = [NSNumber
144        numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectInitialBackoff * 1000)];
145  }
146  if (_callOptions.connectMaxBackoff > 0) {
147    args[@GRPC_ARG_MAX_RECONNECT_BACKOFF_MS] =
148        [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectMaxBackoff * 1000)];
149  }
150
151  if (_callOptions.logContext != nil) {
152    args[@GRPC_ARG_MOBILE_LOG_CONTEXT] = _callOptions.logContext;
153  }
154
155  if (_callOptions.channelPoolDomain.length != 0) {
156    args[@GRPC_ARG_CHANNEL_POOL_DOMAIN] = _callOptions.channelPoolDomain;
157  }
158
159  [args addEntriesFromDictionary:_callOptions.additionalChannelArgs];
160
161  return args;
162}
163
164- (id)copyWithZone:(NSZone *)zone {
165  GRPCChannelConfiguration *newConfig =
166      [[GRPCChannelConfiguration alloc] initWithHost:_host callOptions:_callOptions];
167
168  return newConfig;
169}
170
171- (BOOL)isEqual:(id)object {
172  if (![object isKindOfClass:[GRPCChannelConfiguration class]]) {
173    return NO;
174  }
175  GRPCChannelConfiguration *obj = (GRPCChannelConfiguration *)object;
176  if (!(obj.host == _host || (_host != nil && [obj.host isEqualToString:_host]))) return NO;
177  if (!(obj.callOptions == _callOptions || [obj.callOptions hasChannelOptionsEqualTo:_callOptions]))
178    return NO;
179
180  return YES;
181}
182
183- (NSUInteger)hash {
184  NSUInteger result = 31;
185  result ^= _host.hash;
186  result ^= _callOptions.channelOptionsHash;
187
188  return result;
189}
190
191@end
192
193@implementation GRPCChannel {
194  GRPCChannelConfiguration *_configuration;
195
196  grpc_channel *_unmanagedChannel;
197}
198
199- (instancetype)initWithChannelConfiguration:(GRPCChannelConfiguration *)channelConfiguration {
200  NSAssert(channelConfiguration != nil, @"channelConfiguration must not be empty.");
201  if (channelConfiguration == nil) {
202    return nil;
203  }
204
205  if ((self = [super init])) {
206    _configuration = [channelConfiguration copy];
207
208    // Create gRPC core channel object.
209    NSString *host = channelConfiguration.host;
210    NSAssert(host.length != 0, @"host cannot be nil");
211    NSDictionary *channelArgs;
212    if (channelConfiguration.callOptions.additionalChannelArgs.count != 0) {
213      NSMutableDictionary *args = [channelConfiguration.channelArgs mutableCopy];
214      [args addEntriesFromDictionary:channelConfiguration.callOptions.additionalChannelArgs];
215      channelArgs = args;
216    } else {
217      channelArgs = channelConfiguration.channelArgs;
218    }
219
220    id<GRPCChannelFactory> factory = channelConfiguration.channelFactory;
221    _unmanagedChannel = [factory createChannelWithHost:host channelArgs:channelArgs];
222    NSAssert(_unmanagedChannel != NULL, @"Failed to create channel");
223    if (_unmanagedChannel == NULL) {
224      NSLog(@"Unable to create channel.");
225      return nil;
226    }
227  }
228  return self;
229}
230
231- (grpc_call *)unmanagedCallWithPath:(NSString *)path
232                     completionQueue:(GRPCCompletionQueue *)queue
233                         callOptions:(GRPCCallOptions *)callOptions {
234  NSAssert(path.length > 0, @"path must not be empty.");
235  NSAssert(queue != nil, @"completionQueue must not be empty.");
236  NSAssert(callOptions != nil, @"callOptions must not be empty.");
237  if (path.length == 0) return NULL;
238  if (queue == nil) return NULL;
239  if (callOptions == nil) return NULL;
240
241  grpc_call *call = NULL;
242  // No need to lock here since _unmanagedChannel is only changed in _dealloc
243  NSAssert(_unmanagedChannel != NULL, @"Channel should have valid unmanaged channel.");
244  if (_unmanagedChannel == NULL) return NULL;
245
246  NSString *serverAuthority =
247      callOptions.transportType == GRPCTransportTypeCronet ? nil : callOptions.serverAuthority;
248  NSTimeInterval timeout = callOptions.timeout;
249  NSAssert(timeout >= 0, @"Invalid timeout");
250  if (timeout < 0) return NULL;
251  grpc_slice host_slice = serverAuthority
252                              ? grpc_slice_from_copied_string(serverAuthority.UTF8String)
253                              : grpc_empty_slice();
254  grpc_slice path_slice = grpc_slice_from_copied_string(path.UTF8String);
255  gpr_timespec deadline_ms =
256      timeout == 0 ? gpr_inf_future(GPR_CLOCK_REALTIME)
257                   : gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
258                                  gpr_time_from_millis((int64_t)(timeout * 1000), GPR_TIMESPAN));
259  call = grpc_channel_create_call(_unmanagedChannel, NULL, GRPC_PROPAGATE_DEFAULTS,
260                                  queue.unmanagedQueue, path_slice,
261                                  serverAuthority ? &host_slice : NULL, deadline_ms, NULL);
262  if (serverAuthority) {
263    grpc_slice_unref(host_slice);
264  }
265  grpc_slice_unref(path_slice);
266  NSAssert(call != nil, @"Unable to create call.");
267  if (call == NULL) {
268    NSLog(@"Unable to create call.");
269  }
270  return call;
271}
272
273- (void)dealloc {
274  if (_unmanagedChannel) {
275    grpc_channel_destroy(_unmanagedChannel);
276  }
277}
278
279@end
280