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