• 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#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