• 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 (![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