PageRenderTime 79ms CodeModel.GetById 40ms RepoModel.GetById 0ms app.codeStats 2ms

/Pods/PubNub/PubNub/PubNub/PubNub/Network/Transport/PNConnection.m

https://gitlab.com/Swolemate/Swolemate-App
Objective C | 4350 lines | 2632 code | 1316 blank | 402 comment | 512 complexity | 05902f986f242494fa4fae747c1cd8e4 MD5 | raw file
Possible License(s): JSON, BSD-3-Clause, 0BSD

Large files files are truncated, but you can click here to view the full file

  1. //
  2. // PNConnection.m
  3. // pubnub
  4. //
  5. // This is core class for communication over
  6. // the network with PubNub services.
  7. // It allow to establish socket connection and
  8. // organize write packet requests into FIFO queue.
  9. //
  10. // Created by Sergey Mamontov on 12/10/12.
  11. //
  12. //
  13. #import "PNConnection.h"
  14. #import <objc/runtime.h>
  15. #import <SystemConfiguration/SystemConfiguration.h>
  16. #import <Security/SecureTransport.h>
  17. #import "PNConnection+Protected.h"
  18. #import "PNResponseDeserialize.h"
  19. #import "NSObject+PNAdditions.h"
  20. #import "PNContextInformation.h"
  21. #import "PNLogger+Protected.h"
  22. #import "PNResponseProtocol.h"
  23. #import "PubNub+Protected.h"
  24. #import "PNLoggerSymbols.h"
  25. #import "PNWriteBuffer.h"
  26. #import "PNHelper.h"
  27. // ARC check
  28. #if !__has_feature(objc_arc)
  29. #error PubNub connection must be built with ARC.
  30. // You can turn on ARC for only PubNub files by adding '-fobjc-arc' to the build phase for each of its files.
  31. #endif
  32. #pragma mark Structures
  33. typedef NS_OPTIONS(NSUInteger, PNConnectionActionSourceFlag) {
  34. // Flag which allow to set action has been generated by 'wake up' timer or not
  35. PNConnectionWakeUpTimer = 1 << 0,
  36. // Flag which allow to set whether action has been generated by SSL layer from error handling or not
  37. PNConnectionSSL = 1 << 1,
  38. // Flag which allow to set whether action has been generated from socket error handling or not
  39. PNConnectionSocket = 1 << 2
  40. };
  41. typedef NS_OPTIONS(NSUInteger, PNConnectionActionFlag) {
  42. // Flag which allow to set whether client is about to reconnect or not
  43. PNConnectionWillReconnect = 1 << 3,
  44. // Flag which allow to set whether client is reconnecting at this moment or not
  45. PNConnectionReconnect = 1 << 4,
  46. // Flag which allow to set whether client should connect back as soon as disconnection will be completed or not
  47. PNConnectionReconnectOnDisconnect = 1 << 5,
  48. // Flag which allow to set whether client should disconnect or not
  49. PNConnectionDisconnect = 1 << 6
  50. };
  51. typedef NS_OPTIONS(NSUInteger, PNConnectionActionOwnerFlag) {
  52. // Flag which allow to set whether action on connection has been triggered by user or not
  53. PNByUserRequest = 1 << 7,
  54. // Flag which allow to set whether action on connection has been triggered by internal code or not
  55. PNByInternalRequest = 1 << 8,
  56. // Flag which allow to set whether action on connection has been triggered by server or not
  57. PNByServerRequest = 1 << 9
  58. };
  59. typedef NS_OPTIONS(NSUInteger, PNConnectionStateFlag) {
  60. // Flag which allow to set whether read stream configuration started or not
  61. PNReadStreamConfiguring = 1 << 10,
  62. // Flag which allow to set whether write stream configuration started or not
  63. PNWriteStreamConfiguring = 1 << 11,
  64. // Flag which allow to set whether connection configuration started or not
  65. PNConnectionConfiguring = (PNReadStreamConfiguring | PNWriteStreamConfiguring),
  66. // Flag which allow to set whether read stream configured or not
  67. PNReadStreamConfigured = 1 << 12,
  68. // Flag which allow to set whether write stream configured or not
  69. PNWriteStreamConfigured = 1 << 13,
  70. // Flag which allow to set whether connection configured or not
  71. PNConnectionConfigured = (PNReadStreamConfigured | PNWriteStreamConfigured),
  72. // Flag which allow to set whether read stream is connecting right now or not
  73. PNReadStreamConnecting = 1 << 14,
  74. // Flag which allow to set whether write stream is connecting right now or not
  75. PNWriteStreamConnecting = 1 << 15,
  76. // Flag which allow to set whether client is connecting at this moment or not
  77. PNConnectionConnecting = (PNReadStreamConnecting | PNWriteStreamConnecting),
  78. // Flag which allow to set whether read stream is connected right now or not
  79. PNReadStreamConnected = 1 << 16,
  80. // Flag which allow to set whether write stream is connected right now or not
  81. PNWriteStreamConnected = 1 << 17,
  82. // Flag which allow to set whether connection channel is preparing to establish connection
  83. PNConnectionPrepareToConnect = 1 << 18,
  84. // Flag which allow to set whether client is connected or not
  85. PNConnectionConnected = (PNReadStreamConnected | PNWriteStreamConnected),
  86. // Flag which allow to set whether connection is suspended or not or not
  87. PNConnectionResuming = 1 << 19,
  88. // Flag which allow to set whether read stream is disconnecting right now or not
  89. PNReadStreamDisconnecting = 1 << 20,
  90. // Flag which allow to set whether write stream is disconnecting right now or not
  91. PNWriteStreamDisconnecting = 1 << 21,
  92. // Flag which allow to set whether client is disconnecting at this moment or not
  93. PNConnectionDisconnecting = (PNReadStreamDisconnecting | PNWriteStreamDisconnecting),
  94. // Flag which allow to set whether connection is suspending or not or not
  95. PNConnectionSuspending = 1 << 22,
  96. // Flag which allow to set whether read stream is disconnected right now or not
  97. PNReadStreamDisconnected = 1 << 23,
  98. // Flag which allow to set whether write stream is disconnected right now or not
  99. PNWriteStreamDisconnected = 1 << 24,
  100. // Flag which allow to set whether client is disconnected at this moment or not
  101. PNConnectionDisconnected = (PNReadStreamDisconnected | PNWriteStreamDisconnected),
  102. // Flag which stores all states which is responsible for connection 'reconnect' state
  103. PNConnectionReconnection = (PNConnectionWillReconnect | PNConnectionReconnect | PNConnectionReconnectOnDisconnect),
  104. // Flag which allow to set whether connection is suspended or not or not
  105. PNConnectionSuspended = 1 << 25,
  106. // Flag which allow to set whether connection should schedule next requests or not
  107. PNConnectionProcessingRequests = 1 << 26
  108. };
  109. typedef NS_OPTIONS(NSUInteger, PNConnectionDataSendingStateFlag) {
  110. // Flag which allow to set whether action on connection has been triggered by user or not
  111. PNSendingData = 1 << 27
  112. };
  113. typedef NS_OPTIONS(NSUInteger, PNConnectionErrorStateFlag) {
  114. // Flag which allow to set whether error occurred on read stream or not
  115. PNReadStreamError = 1 << 28,
  116. // Flag which allow to set whether error occurred on write stream or not
  117. PNWriteStreamError = 1 << 29,
  118. // Flag which allow to set whether client is experiencing some error or not
  119. PNConnectionError = (PNReadStreamError | PNWriteStreamError)
  120. };
  121. typedef NS_OPTIONS(NSUInteger, PNConnectionCleanStateFlag) {
  122. // Flag which can be used to clean configuration states related to read stream
  123. PNReadStreamCleanConfiguration = (PNReadStreamConfiguring | PNReadStreamConfigured),
  124. // Flag which can be used to clean connection states related to read stream
  125. PNReadStreamCleanConnection = (PNReadStreamConnecting | PNReadStreamConnected),
  126. // Flag which can be used to clean connection states related to read stream
  127. PNReadStreamCleanDisconnection = (PNReadStreamDisconnecting | PNReadStreamDisconnected),
  128. // Flag which can be used to clean all states related to read stream
  129. PNReadStreamCleanAll = (PNReadStreamCleanConfiguration | PNReadStreamCleanConnection |
  130. PNReadStreamCleanDisconnection | PNReadStreamError),
  131. // Flag which can be used to clean configuration states related to write stream
  132. PNWriteStreamCleanConfiguration = (PNWriteStreamConfiguring | PNWriteStreamConfigured),
  133. // Flag which can be used to clean connection states related to write stream
  134. PNWriteStreamCleanConnection = (PNWriteStreamConnecting | PNWriteStreamConnected),
  135. // Flag which can be used to clean connection states related to write stream
  136. PNWriteStreamCleanDisconnection = (PNWriteStreamDisconnecting | PNWriteStreamDisconnected),
  137. // Flag which can be used to clean all states related to write stream
  138. PNWriteStreamCleanAll = (PNWriteStreamCleanConfiguration | PNWriteStreamCleanConnection |
  139. PNWriteStreamCleanDisconnection | PNWriteStreamError),
  140. // Flag which allow to clean up connection 'reconnection' state
  141. PNConnectionCleanReconnection = PNConnectionReconnection,
  142. // Flag which allow to set whether client is experiencing some error or not
  143. PNConnectionErrorCleanAll = (PNReadStreamError | PNWriteStreamError)
  144. };
  145. typedef enum _PNConnectionSSLConfigurationLevel {
  146. // This option will check all information on remote origin SSL certificate to ensure in authority
  147. PNConnectionSSLConfigurationStrict,
  148. // This option will skip most of validations and as fact will allow to work with server which uses invalid SSL
  149. // certificate or certificate from another server
  150. PNConnectionSSLConfigurationBarelySecure,
  151. // This option will tell that connection should be opened w/o SSL (if user won't to discard security options)
  152. PNConnectionSSLConfigurationInsecure,
  153. } PNConnectionSSLConfigurationLevel;
  154. struct PNConnectionIdentifiersStruct PNConnectionIdentifiers = {
  155. .messagingConnection = @"PNMessagingConnectionIdentifier",
  156. .serviceConnection = @"PNServiceConnectionIdentifier"
  157. };
  158. #pragma mark - Static
  159. // Delay which is used by wake up timer to fire
  160. static int64_t const kPNWakeUpTimerInterval = 5;
  161. // Default origin host connection port
  162. static UInt32 const kPNOriginConnectionPort = 80;
  163. // Default origin host SSL connection port
  164. static UInt32 const kPNOriginSSLConnectionPort = 443;
  165. // Default data buffer size (Default: 32kb)
  166. static int const kPNStreamBufferSize = 32768;
  167. // Default connection attempt timeout (Default: 10s)
  168. static int64_t const kPNConnectionTimeout = 10;
  169. // Delay after which connection should retry
  170. static int64_t const kPNConnectionRetryDelay = 2;
  171. static NSTimeInterval const kPNConnectionRetryFastDelay = 0.1f;
  172. // Maximum retry count which can be performed for configuration operation
  173. static NSUInteger const kPNMaximumConfigurationRetryCount = 3;
  174. // Maximum connection retry count which can be performed before library will report connection error
  175. static NSUInteger const kPNMaximumConnectionRetryCount = 3;
  176. #pragma mark - Private interface methods
  177. @interface PNConnection ()
  178. #pragma mark - Properties
  179. // Stores connection name (identifier)
  180. @property (nonatomic, copy) NSString *name;
  181. // Connection configuration information
  182. @property (nonatomic, strong) PNConfiguration *configuration;
  183. // Stores reference on response deserializer which will parse response into objects array and update provided data to
  184. // insert offset on amount of parsed data
  185. @property (nonatomic, strong) PNResponseDeserialize *deserializer;
  186. /**
  187. @brief Stores reference on set of buffers retrieved from server.
  188. @discussion Data stored in this buffer till de-serializer will be able to pull completed packet
  189. from it and notify about amount of data which it processed.
  190. @since 3.7.10
  191. */
  192. @property (nonatomic, strong) NSMutableData *readBuffer;
  193. @property (nonatomic, copy) NSData *notProcessedReadBuffer;
  194. // Stores reference on buffer which should be sent to the PubNub service via socket
  195. @property (nonatomic, strong) PNWriteBuffer *writeBuffer;
  196. @property (nonatomic, assign) NSUInteger configurationRetryCount;
  197. @property (nonatomic, assign) NSUInteger connectionRetryCount;
  198. // Stores connection channel state
  199. @property (nonatomic, assign) unsigned long state;
  200. // Stores reference on timer which should awake connection channel if it doesn't reconnect back because of some
  201. // race of states and conditions
  202. @property (nonatomic, pn_dispatch_property_ownership) dispatch_source_t wakeUpTimer;
  203. @property (nonatomic, assign, getter = isWakeUpTimerSuspended) BOOL wakeUpTimerSuspended;
  204. @property (nonatomic, pn_dispatch_property_ownership) dispatch_source_t connectionTimeoutTimer;
  205. // Socket streams and state
  206. @property (nonatomic, assign) CFReadStreamRef socketReadStream;
  207. @property (nonatomic, assign) CFWriteStreamRef socketWriteStream;
  208. @property (nonatomic, assign, getter = isWriteStreamCanHandleData) BOOL writeStreamCanHandleData;
  209. @property (nonatomic, assign) BOOL fetchingDataForSending;
  210. // Socket streams configuration and security
  211. @property (nonatomic, copy) NSDictionary *proxySettings;
  212. @property (nonatomic, assign) CFMutableDictionaryRef streamSecuritySettings;
  213. @property (nonatomic, assign) PNConnectionSSLConfigurationLevel sslConfigurationLevel;
  214. #pragma mark - Instance methods
  215. /**
  216. * Perform connection initialization with user-provided configuration (they will be obtained from PubNub client)
  217. */
  218. - (id)initWithConfiguration:(PNConfiguration *)configuration andIdentifier:(NSString *)identifier;
  219. #pragma mark - Streams management methods
  220. /**
  221. * Will create read/write pair streams to specific host at
  222. */
  223. - (BOOL)prepareStreams;
  224. - (void)disconnectOnInternalRequest;
  225. /**
  226. * Will destroy both read and write streams
  227. */
  228. - (void)destroyStreams;
  229. /**
  230. * Allow to configure read stream with set of parameters like:
  231. * - proxy
  232. * - security (SSL)
  233. * If stream already configured, it won't accept any new settings.
  234. */
  235. - (void)configureReadStream:(CFReadStreamRef)readStream;
  236. - (void)openReadStream:(CFReadStreamRef)readStream;
  237. - (void)disconnectReadStream:(CFReadStreamRef)readStream;
  238. - (void)destroyReadStream:(CFReadStreamRef)readStream;
  239. /**
  240. * Process response which was fetched from read stream so far
  241. */
  242. - (void)processResponse;
  243. /**
  244. * Read out content which is waiting in read stream
  245. */
  246. - (void)readStreamContent:(CFReadStreamRef)stream;
  247. /**
  248. * Allow to complete write stream configuration (additional settings will be transferred from paired read stream on
  249. * configuration). If stream already configured, it won't accept any new settings.
  250. */
  251. - (void)configureWriteStream:(CFWriteStreamRef)writeStream;
  252. - (void)openWriteStream:(CFWriteStreamRef)writeStream;
  253. - (void)disconnectWriteStream:(CFWriteStreamRef)writeStream;
  254. - (void)destroyWriteStream:(CFWriteStreamRef)writeStream;
  255. /**
  256. * Retrieve and prepare next request which should be sent
  257. */
  258. - (void)prepareNextRequestPacket:(dispatch_block_t)completionBlock;
  259. /**
  260. * Writes buffer portion into socket
  261. */
  262. - (void)writeBufferContent;
  263. #pragma mark - Handler methods
  264. /**
  265. * Called every time when one of streams (read/write) successfully open connection
  266. */
  267. - (void)handleStreamConnection;
  268. /**
  269. * Called every time when one of streams (read/write) disconnected
  270. */
  271. - (void)handleStreamClose;
  272. /**
  273. * Called each time when new portion of data available in socket read stream for reading
  274. */
  275. - (void)handleReadStreamHasData:(CFReadStreamRef)stream;
  276. /**
  277. * Called each time when write stream is ready to accept data from PubNub client
  278. */
  279. - (void)handleWriteStreamCanAcceptData;
  280. /**
  281. * Called when client is about to close write stream and we need to do something with write buffer if it was assigned
  282. */
  283. - (void)handleRequestSendingCancelation;
  284. /**
  285. * Called each time when server close stream because of timeout
  286. */
  287. - (void)handleStreamTimeout;
  288. /**
  289. * Called each time when wake up timer is fired
  290. */
  291. - (void)handleWakeUpTimer;
  292. /**
  293. * Converts stream status enum value into string representation
  294. */
  295. - (NSString *)stringifyStreamStatus:(CFStreamStatus)status;
  296. - (void)handleStreamError:(PNConnectionErrorStateFlag)failedStream fromBlock:(CFErrorRef(^)(void))errorBlock;
  297. - (void)handleStreamError:(CFErrorRef)error;
  298. - (void)handleStreamError:(CFErrorRef)error shouldCloseConnection:(BOOL)shouldCloseConnection;
  299. - (void)handleStreamSetupError;
  300. - (void)handleRequestProcessingError:(CFErrorRef)error withBlock:(dispatch_block_t)notifyCompletionBlock;
  301. #pragma mark - Misc methods
  302. /**
  303. @brief Append new read buffer content.
  304. @param data Data buffer which should be appended to existing read buffer.
  305. @param length Length of the buffer which has been provided.
  306. @since 3.7.10.7
  307. */
  308. - (void)appendToReadBuffer:(const void *)data length:(NSUInteger)length;
  309. /**
  310. * Start/stop connection timeout timer
  311. */
  312. - (void)startTimeoutTimer;
  313. - (void)stopTimeoutTimer;
  314. - (void)stopTimeoutTimer:(BOOL)forRelaunch;
  315. /**
  316. * Construct/reuse and launch/resume/suspend/stop 'wakeup' timer to help restore connection if it will be required
  317. */
  318. - (void)startWakeUpTimer;
  319. - (void)suspendWakeUpTimer;
  320. - (void)resumeWakeUpTimer;
  321. - (void)stopWakeUpTimer;
  322. - (void)resetWakeUpTimer;
  323. /**
  324. * Check whether specified error is from POSIX domain and report that error is caused by connection failure or not
  325. */
  326. - (BOOL)isConnectionIssuesError:(CFErrorRef)error;
  327. - (BOOL)isConnectionUpLinkError:(CFErrorRef)error;
  328. /**
  329. * Check whether specified error is from OSStatus error domain and report that error is caused by SSL issue
  330. */
  331. - (BOOL)isSecurityTransportError:(CFErrorRef)error;
  332. - (BOOL)isInternalSecurityTransportError:(CFErrorRef)error;
  333. - (BOOL)isTemporaryError:(CFErrorRef)error;
  334. - (BOOL)isServerError:(CFErrorRef)error;
  335. - (CFStreamClientContext)streamClientContext;
  336. /**
  337. * Retrieving global network proxy configuration
  338. */
  339. - (void)retrieveSystemProxySettings;
  340. /**
  341. * Stream error processing methods
  342. */
  343. - (PNError *)processStreamError:(CFErrorRef)error;
  344. /**
  345. * Print our current connection state
  346. */
  347. - (NSString *)stateDescription;
  348. #pragma mark -
  349. @end
  350. #pragma mark - Public interface methods
  351. @implementation PNConnection
  352. #pragma mark - Class methods
  353. + (PNConnection *)connectionWithConfiguration:(PNConfiguration *)configuration andIdentifier:(NSString *)identifier {
  354. return [[self alloc] initWithConfiguration:configuration andIdentifier:identifier];
  355. }
  356. #pragma mark - Instance methods
  357. - (id)initWithConfiguration:(PNConfiguration *)configuration andIdentifier:(NSString *)identifier {
  358. // Check whether initialization was successful or not
  359. if ((self = [super init])) {
  360. // Perform connection initialization
  361. self.configuration = configuration;
  362. self.name = identifier;
  363. self.socketReadStream = NULL;
  364. self.socketWriteStream = NULL;
  365. [self pn_setupPrivateSerialQueueWithIdentifier:@"connection" andPriority:DISPATCH_QUEUE_PRIORITY_DEFAULT];
  366. self.deserializer = [PNResponseDeserialize new];
  367. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  368. return @[PNLoggerSymbols.connection.resourceLinkage, (self.name ? self.name : self),
  369. (self.deserializer ? [[NSString alloc] initWithFormat:@"%p", self.deserializer] : [NSNull null])];
  370. }];
  371. // Set initial connection state
  372. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
  373. [self pn_dispatchBlock:^{
  374. // Perform streams initial options and security initializations
  375. [self prepareStreams];
  376. }];
  377. }
  378. return self;
  379. }
  380. #pragma mark - Requests queue execution management
  381. - (void)scheduleNextRequestExecution {
  382. [self pn_dispatchBlock:^{
  383. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionProcessingRequests];
  384. // Ensure that both streams connected at this moment and connection doesn't try to close
  385. // or suspend
  386. if ([self isConnected] && ![self isDisconnecting] && ![self isSuspending]) {
  387. // Check whether sending data at this moment or not
  388. if (![PNBitwiseHelper is:self.state containsBit:PNSendingData] ||
  389. ![self.writeBuffer isSendingBytes]) {
  390. dispatch_block_t continueBlock = ^{
  391. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
  392. if (self.writeBuffer != nil) {
  393. // Try to initiate request sending process
  394. [self writeBufferContent];
  395. }
  396. }
  397. else {
  398. self.writeBuffer = nil;
  399. }
  400. };
  401. if (self.writeBuffer == nil) {
  402. [PNBitwiseHelper removeFrom:&self->_state bit:PNSendingData];
  403. [self prepareNextRequestPacket:continueBlock];
  404. }
  405. else {
  406. [self.writeBuffer reset];
  407. continueBlock();
  408. }
  409. }
  410. else {
  411. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  412. return @[PNLoggerSymbols.connection.stream.write.alreadySendingData,
  413. (self.name ? self.name : self), @(self.state)];
  414. }];
  415. }
  416. }
  417. }];
  418. }
  419. - (void)unscheduleRequestsExecution {
  420. [self pn_dispatchBlock:^{
  421. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionProcessingRequests];
  422. [self handleRequestSendingCancelation];
  423. }];
  424. }
  425. #pragma mark - Streams callback methods
  426. static void readStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
  427. void readStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo) {
  428. if (CFReadStreamGetStatus(stream) != kCFStreamStatusClosed) {
  429. NSCAssert([(__bridge id)clientCallBackInfo isKindOfClass:[PNContextInformation class]],
  430. @"{ERROR}[READ] WRONG CLIENT INSTANCE HAS BEEN SENT AS CLIENT");
  431. PNConnection *connection = ((__bridge PNContextInformation *)clientCallBackInfo).object;
  432. [connection pn_scheduleOnPrivateQueueAssert];
  433. NSString *status = [connection stringifyStreamStatus:CFReadStreamGetStatus(stream)];
  434. CFReadStreamRef retainedStream = (CFReadStreamRef)CFRetain(stream);
  435. [connection pn_dispatchBlock:^{
  436. switch (type) {
  437. // Stream successfully opened
  438. case kCFStreamEventOpenCompleted:
  439. {
  440. [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
  441. return @[PNLoggerSymbols.connection.stream.read.opened,
  442. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  443. (status ? status : [NSNull null]), @(connection.state)];
  444. }];
  445. [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNReadStreamCleanDisconnection];
  446. [PNBitwiseHelper addTo:&(connection->_state) bit:PNReadStreamConnected];
  447. [connection handleStreamConnection];
  448. if (retainedStream) {
  449. CFRelease(retainedStream);
  450. }
  451. }
  452. break;
  453. // Read stream has some data which arrived from remote server
  454. case kCFStreamEventHasBytesAvailable:
  455. {
  456. [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
  457. return @[PNLoggerSymbols.connection.stream.read.hasData,
  458. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  459. (status ? status : [NSNull null]), @(connection.state)];
  460. }];
  461. [connection handleReadStreamHasData:retainedStream];
  462. if (retainedStream) {
  463. CFRelease(retainedStream);
  464. }
  465. }
  466. break;
  467. // Some error occurred on read stream
  468. case kCFStreamEventErrorOccurred:
  469. {
  470. [PNLogger logConnectionErrorMessageFrom:connection withParametersFromBlock:^NSArray *{
  471. return @[PNLoggerSymbols.connection.stream.read.error,
  472. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  473. (status ? status : [NSNull null]), @(connection.state)];
  474. }];
  475. [connection handleStreamError:PNReadStreamError fromBlock:^CFErrorRef {
  476. CFErrorRef cfError = CFReadStreamCopyError(retainedStream);
  477. if (retainedStream) {
  478. CFRelease(retainedStream);
  479. }
  480. return cfError;
  481. }];
  482. }
  483. break;
  484. // Server disconnected socket probably because of timeout
  485. case kCFStreamEventEndEncountered:
  486. {
  487. [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
  488. return @[PNLoggerSymbols.connection.stream.read.cantAcceptDataAnymore,
  489. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  490. (status ? status : [NSNull null]), @(connection.state)];
  491. }];
  492. [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNReadStreamCleanAll];
  493. [PNBitwiseHelper addTo:&(connection->_state) bit:PNReadStreamDisconnected];
  494. [connection handleStreamTimeout];
  495. if (retainedStream) {
  496. CFRelease(retainedStream);
  497. }
  498. }
  499. break;
  500. default:
  501. if (retainedStream) {
  502. CFRelease(retainedStream);
  503. }
  504. break;
  505. }
  506. }];
  507. }
  508. }
  509. static void writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
  510. void writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo) {
  511. if (CFWriteStreamGetStatus(stream) != kCFStreamStatusClosed) {
  512. NSCAssert([(__bridge id)clientCallBackInfo isKindOfClass:[PNContextInformation class]],
  513. @"{ERROR}[READ] WRONG CLIENT INSTANCE HAS BEEN SENT AS CLIENT");
  514. PNConnection *connection = ((__bridge PNContextInformation *)clientCallBackInfo).object;
  515. NSString *status = [connection stringifyStreamStatus:CFWriteStreamGetStatus(stream)];
  516. CFWriteStreamRef retainedStream = (CFWriteStreamRef)CFRetain(stream);
  517. [connection pn_dispatchBlock:^{
  518. switch (type) {
  519. // Stream successfully opened
  520. case kCFStreamEventOpenCompleted:
  521. {
  522. [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
  523. return @[PNLoggerSymbols.connection.stream.write.opened,
  524. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  525. (status ? status : [NSNull null]), @(connection.state)];
  526. }];
  527. [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNWriteStreamCleanDisconnection];
  528. [PNBitwiseHelper addTo:&(connection->_state) bit:PNWriteStreamConnected];
  529. [connection handleStreamConnection];
  530. if (retainedStream) {
  531. CFRelease(retainedStream);
  532. }
  533. }
  534. break;
  535. // Write stream is ready to accept data from data source
  536. case kCFStreamEventCanAcceptBytes:
  537. {
  538. [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
  539. return @[PNLoggerSymbols.connection.stream.write.canSendData,
  540. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  541. (status ? status : [NSNull null]), @(connection.state)];
  542. }];
  543. [connection handleWriteStreamCanAcceptData];
  544. if (retainedStream) {
  545. CFRelease(retainedStream);
  546. }
  547. }
  548. break;
  549. // Some error occurred on write stream
  550. case kCFStreamEventErrorOccurred:
  551. {
  552. [PNLogger logConnectionErrorMessageFrom:connection withParametersFromBlock:^NSArray *{
  553. return @[PNLoggerSymbols.connection.stream.write.error,
  554. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  555. (status ? status : [NSNull null]), @(connection.state)];
  556. }];
  557. [connection handleStreamError:PNWriteStreamError fromBlock:^CFErrorRef {
  558. CFErrorRef cfError = CFWriteStreamCopyError(retainedStream);
  559. if (retainedStream) {
  560. CFRelease(retainedStream);
  561. }
  562. return cfError;
  563. }];
  564. }
  565. break;
  566. // Server disconnected socket probably because of timeout
  567. case kCFStreamEventEndEncountered:
  568. {
  569. [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
  570. return @[PNLoggerSymbols.connection.stream.write.cantSendDataAnymore,
  571. (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
  572. (status ? status : [NSNull null]), @(connection.state)];
  573. }];
  574. [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNWriteStreamCleanAll];
  575. [PNBitwiseHelper addTo:&(connection->_state) bit:PNWriteStreamDisconnected];
  576. [connection handleStreamTimeout];
  577. if (retainedStream) {
  578. CFRelease(retainedStream);
  579. }
  580. }
  581. break;
  582. default:
  583. if (retainedStream) {
  584. CFRelease(retainedStream);
  585. }
  586. break;
  587. }
  588. }];
  589. }
  590. }
  591. static void connectionContextInformationReleaseCallBack(void *info);
  592. void connectionContextInformationReleaseCallBack(void *info) {
  593. if (info) {
  594. CFRelease(info);
  595. }
  596. }
  597. #pragma mark - Connection state
  598. - (BOOL)isConnecting {
  599. // This method should be launched only from within it's private queue
  600. [self pn_scheduleOnPrivateQueueAssert];
  601. // If at least one of the streams is connecting now treat it all as true
  602. return ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnected] &&
  603. [PNBitwiseHelper is:self.state containsBit:PNConnectionConnecting]);
  604. }
  605. - (BOOL)isReconnecting {
  606. // This method should be launched only from within it's private queue
  607. [self pn_scheduleOnPrivateQueueAssert];
  608. // If at least one of the streams is connecting now treat it all as true
  609. return ([PNBitwiseHelper is:self.state containsBit:PNConnectionConnecting] &&
  610. [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnection]);
  611. }
  612. - (BOOL)shouldReconnect {
  613. // This method should be launched only from within it's private queue
  614. [self pn_scheduleOnPrivateQueueAssert];
  615. return [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnection];
  616. }
  617. - (void)checkConnected:(void (^)(BOOL connected))checkCompletionBlock {
  618. [self pn_dispatchBlock:^{
  619. checkCompletionBlock([self isConnected]);
  620. }];
  621. }
  622. - (BOOL)isConnected {
  623. // This method should be launched only from within it's private queue
  624. [self pn_scheduleOnPrivateQueueAssert];
  625. return ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected] &&
  626. ![self isReconnecting]);
  627. }
  628. - (void)checkDisconnected:(void (^)(BOOL disconnected))checkCompletionBlock {
  629. [self pn_dispatchBlock:^{
  630. checkCompletionBlock(([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnected] ||
  631. [PNBitwiseHelper is:self.state containsBit:PNConnectionSuspended]) &&
  632. ![self isConnecting]);
  633. }];
  634. }
  635. - (BOOL)isDisconnecting {
  636. // This method should be launched only from within it's private queue
  637. [self pn_scheduleOnPrivateQueueAssert];
  638. return [PNBitwiseHelper is:self.state containsBit:PNConnectionDisconnecting];
  639. }
  640. #pragma mark - Error identification
  641. - (BOOL)isConnectionIssuesError:(CFErrorRef)error {
  642. BOOL isConnectionIssue = NO;
  643. NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
  644. if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
  645. switch (CFErrorGetCode(error)) {
  646. case ENETDOWN: // Network is down (maybe even no connection on interface level)
  647. // Error cases which may occurs during socket lifecycle, when gateway uplink may go down and additional
  648. // network check should be performed
  649. case ENETUNREACH: // Remote host can't be reached
  650. case EHOSTDOWN: // Remote host is down
  651. case EHOSTUNREACH: // Host can't be reached, because there is no route to it
  652. case ECONNREFUSED: // Remote host doesn't want to accept connection
  653. isConnectionIssue = YES;
  654. break;
  655. default:
  656. break;
  657. }
  658. }
  659. else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
  660. switch (CFErrorGetCode(error)) {
  661. case kCFHostErrorHostNotFound:
  662. isConnectionIssue = YES;
  663. break;
  664. default:
  665. break;
  666. }
  667. }
  668. return isConnectionIssue;
  669. }
  670. - (BOOL)isConnectionUpLinkError:(CFErrorRef)error {
  671. BOOL isConnectionUpLinkError = NO;
  672. NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
  673. if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
  674. switch (CFErrorGetCode(error)) {
  675. case ENETUNREACH: // Remote host can't be reached
  676. case EHOSTDOWN: // Remote host is down
  677. case EHOSTUNREACH: // Host can't be reached, because there is no route to it
  678. case ECONNREFUSED: // Remote host doesn't want to accept connection
  679. isConnectionUpLinkError = YES;
  680. break;
  681. default:
  682. break;
  683. }
  684. }
  685. return isConnectionUpLinkError;
  686. }
  687. - (BOOL)isSecurityTransportError:(CFErrorRef)error {
  688. BOOL isSecurityTransportError = NO;
  689. CFIndex errorCode = CFErrorGetCode(error);
  690. NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
  691. if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainOSStatus]) {
  692. #if __IPHONE_OS_VERSION_MIN_REQUIRED
  693. isSecurityTransportError = (errSSLUnexpectedRecord <= errorCode) && (errorCode <= errSSLProtocol);
  694. #else
  695. isSecurityTransportError = (errSSLLast <= errorCode) && (errorCode <= errSSLProtocol);
  696. #endif
  697. }
  698. else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
  699. isSecurityTransportError = (kCFURLErrorCannotLoadFromNetwork <= errorCode) &&
  700. (errorCode <= kCFURLErrorSecureConnectionFailed);
  701. }
  702. return isSecurityTransportError;
  703. }
  704. - (BOOL)isInternalSecurityTransportError:(CFErrorRef)error {
  705. CFIndex code = CFErrorGetCode(error);
  706. return (code == errSSLInternal) || (code == errSSLClosedAbort);
  707. }
  708. - (BOOL)isTemporaryError:(CFErrorRef)error {
  709. BOOL isTemporaryError = NO;
  710. CFIndex errorCode = CFErrorGetCode(error);
  711. NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
  712. if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
  713. switch (errorCode) {
  714. case ETIMEDOUT: // There is no activity on socket for some time
  715. case ENETRESET: // Remote host crashed w/o sending 'close packet'
  716. case ECONNABORTED: // Connection was aborted locally (by system or device)
  717. // Special cases when connection state was changed by remote server
  718. case ECONNRESET: // Remote host sent 'close packet'
  719. case ENOBUFS: // No space where data for sockets can be stored
  720. case ENOTCONN: // Socket not connected or was disconnected
  721. case ESHUTDOWN: // Socket was closed before new write attempt has been done
  722. case ENOENT: // Rare error when system probably can't prepare sockets for further operation
  723. case EPIPE: // Remote host went down or socket configuration not completed
  724. case EAGAIN: // Requested resource not available
  725. case EISCONN: // Socket already connected
  726. isTemporaryError = YES;
  727. break;
  728. default:
  729. break;
  730. }
  731. }
  732. else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
  733. isTemporaryError = (kCFNetServiceErrorDNSServiceFailure <= errorCode) && (errorCode <= kCFNetServiceErrorUnknown);
  734. if (!isTemporaryError) {
  735. isTemporaryError = errorCode == kCFHostErrorUnknown || errorCode == kCFErrorHTTPConnectionLost;
  736. }
  737. }
  738. return isTemporaryError;
  739. }
  740. - (BOOL)isServerError:(CFErrorRef)error {
  741. BOOL isServerError = NO;
  742. CFIndex errorCode = CFErrorGetCode(error);
  743. NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
  744. if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
  745. switch (errorCode) {
  746. case ECONNRESET: // Remote host sent 'close packet'
  747. isServerError = YES;
  748. break;
  749. default:
  750. break;
  751. }
  752. }
  753. return isServerError;
  754. }
  755. #pragma mark - Connection lifecycle management methods
  756. - (BOOL)prepareStreams {
  757. // This method should be launched only from within it's private queue
  758. [self pn_scheduleOnPrivateQueueAssert];
  759. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  760. return @[PNLoggerSymbols.connection.stream.prepareForUsage, (self.name ? self.name : self), @(self.state)];
  761. }];
  762. BOOL streamsPrepared = YES;
  763. // Check whether stream was prepared and configured before
  764. if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConfigured]) {
  765. NSString *symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlier;
  766. if ([self isConnecting]) {
  767. symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlierAndConnecting;
  768. }
  769. else if ([self isConnected]) {
  770. symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlierAndConnected;
  771. }
  772. if ([self isResuming]) {
  773. symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlierAndResuming;
  774. }
  775. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  776. return @[symbolCode, (self.name ? self.name : self), @(self.state)];
  777. }];
  778. }
  779. else {
  780. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  781. return @[PNLoggerSymbols.connection.stream.configurationStarted, (self.name ? self.name : self), @(self.state)];
  782. }];
  783. // Make sure that streams will be unable to operate (protection in case of state has been interrupted in some
  784. // way)
  785. [self destroyStreams];
  786. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionDisconnecting];
  787. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
  788. // Define connection port which should be used by connection for further usage (depends on current connection
  789. // security policy)
  790. UInt32 targetPort = kPNOriginConnectionPort;
  791. if (self.configuration.shouldUseSecureConnection &&
  792. self.sslConfigurationLevel != PNConnectionSSLConfigurationInsecure) {
  793. targetPort = kPNOriginSSLConnectionPort;
  794. }
  795. // Retrieve connection proxy configuration
  796. [self retrieveSystemProxySettings];
  797. // Create stream pair on socket which is connected to specified remote host
  798. CFReadStreamRef socketReadStream;
  799. CFWriteStreamRef socketWriteStream;
  800. CFStreamCreatePairWithSocketToHost(CFAllocatorGetDefault(), (__bridge CFStringRef)(self.configuration.origin),
  801. targetPort, &socketReadStream, &socketWriteStream);
  802. [self configureReadStream:socketReadStream];
  803. [self configureWriteStream:socketWriteStream];
  804. _socketReadStream = socketReadStream;
  805. _socketWriteStream = socketWriteStream;
  806. // Check whether at least one of the streams was unable to complete configuration
  807. if (![PNBitwiseHelper is:self.state containsBit:PNConnectionConfigured]) {
  808. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  809. return @[PNLoggerSymbols.connection.stream.configurationFailed, (self.name ? self.name : self), @(self.state)];
  810. }];
  811. streamsPrepared = NO;
  812. [self destroyStreams];
  813. [self handleStreamSetupError];
  814. }
  815. else {
  816. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  817. return @[PNLoggerSymbols.connection.stream.configurationCompleted, (self.name ? self.name : self), @(self.state)];
  818. }];
  819. }
  820. }
  821. return streamsPrepared;
  822. }
  823. - (void)connectWithResult:(void (^)(BOOL connecting))resultBlock {
  824. [self pn_dispatchBlock:^{
  825. [PNBitwiseHelper addTo:&self->_state bit:PNByUserRequest];
  826. [self connectByInternalRequest:^(BOOL connecting) {
  827. if (resultBlock) {
  828. resultBlock(connecting);
  829. }
  830. }];
  831. }];
  832. }
  833. - (void)connectByInternalRequest:(void (^)(BOOL connecting))resultBlock {
  834. // This method should be launched only from within it's private queue
  835. [self pn_scheduleOnPrivateQueueAssert];
  836. __block BOOL isStreamOpened = NO;
  837. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  838. return @[PNLoggerSymbols.connection.connectionAttempt, (self.name ? self.name : self),
  839. @([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]), @(self.state)];
  840. }];
  841. // Check whether connection was requested by user or not
  842. if ([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]) {
  843. BOOL shouldDestroyStreams = [self isConnecting] || [self isReconnecting] || [self isDisconnecting] || [self isResuming];
  844. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  845. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect,
  846. BITS_LIST_TERMINATOR];
  847. [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
  848. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
  849. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  850. if (shouldDestroyStreams) {
  851. [self destroyStreams];
  852. }
  853. }
  854. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionPrepareToConnect];
  855. // Ask delegate whether connection can be opened or not (in case if there is no internet connection or
  856. // client was disconnected by user request)
  857. [self.delegate connection:self checkCanConnect:^(BOOL canConnect) {
  858. [self pn_dispatchBlock:^{
  859. if (canConnect) {
  860. // Check whether client has been properly configured or not
  861. if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConfigured]) {
  862. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionPrepareToConnect];
  863. BOOL isAbleToConnect = (![self isConnecting] && ![self isReconnecting] &&
  864. ![self isConnected] && ![self isDisconnecting] &&
  865. ![self isResuming]);
  866. if (isAbleToConnect) {
  867. // Mark that connection currently doesn't connected to the server
  868. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
  869. NSString *symbolCode = @"";
  870. // Check whether connection has been suspended before or not
  871. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSuspended]) {
  872. // If connection is suspended, there is impossible that it may have any errors or ability to reconnect
  873. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, PNConnectionSuspending,
  874. BITS_LIST_TERMINATOR];
  875. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionResuming];
  876. symbolCode = PNLoggerSymbols.connection.connectionResumingInProgress;
  877. }
  878. else if (![PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected]) {
  879. [PNBitwiseHelper removeFrom:&self->_state
  880. bits:PNConnectionSuspending, PNConnectionSuspended, PNConnectionResuming,
  881. BITS_LIST_TERMINATOR];
  882. symbolCode = PNLoggerSymbols.connection.connectionInProgress;
  883. if ([self shouldReconnect]) {
  884. symbolCode = PNLoggerSymbols.connection.reconnectionInProgress;
  885. }
  886. }
  887. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  888. return @[symbolCode, (self.name ? self.name : self), @(self.state)];
  889. }];
  890. isStreamOpened = YES;
  891. [self startTimeoutTimer];
  892. [self suspendWakeUpTimer];
  893. [self openReadStream:self.socketReadStream];
  894. [self openWriteStream:self.socketWriteStream];
  895. if (resultBlock) {
  896. resultBlock(isStreamOpened);
  897. }
  898. }
  899. else {
  900. void(^forciblyConnectionBlock)(void) = ^{
  901. [self stopTimeoutTimer];
  902. [self suspendWakeUpTimer];
  903. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  904. return @[PNLoggerSymbols.connection.outOfSyncWithStateForciblyReconnect, (self.name ? self.name : self),
  905. @(self.state)];
  906. }];
  907. BOOL isConnectingByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
  908. [PNBitwiseHelper removeFrom:&self->_state bit:PNByUserRequest];
  909. if (isConnectingByUserRequest) {
  910. // Mark that disconnection has been called because of internal request
  911. [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionError, BITS_LIST_TERMINATOR];
  912. }
  913. // Forcibly close all connections
  914. [self disconnectByInternalRequest];
  915. if (isConnectingByUserRequest) {
  916. [self connectWithResult:^(BOOL connecting) {
  917. if (resultBlock) {
  918. resultBlock(connecting);
  919. }
  920. }];
  921. }
  922. else {
  923. [self connectByInternalRequest:^(BOOL connecting) {
  924. if (resultBlock) {
  925. resultBlock(connecting);
  926. }
  927. }];
  928. }
  929. };

Large files files are truncated, but you can click here to view the full file