PageRenderTime 45ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  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. };
  930. if (![self isDisconnecting]) {
  931. // Check whether tried to connect while already connected(-ing) or not. This condition take into
  932. // account state of both streams at same time. If one of the stream has different state, this mean
  933. // that connection probably in some wrong (messed) state.
  934. BOOL isConnecting = ([self isConnecting] || [self isReconnecting] ||
  935. [self isConnected] || [self isResuming]);
  936. if (isConnecting) {
  937. NSString *symbolCode = PNLoggerSymbols.connection.alreadyConnecting;
  938. if ([self isConnected]) {
  939. symbolCode = PNLoggerSymbols.connection.alreadyConnected;
  940. }
  941. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  942. return @[symbolCode, (self.name ? self.name : self), @(self.state)];
  943. }];
  944. if (resultBlock) {
  945. resultBlock(isStreamOpened);
  946. }
  947. }
  948. // Looks like tried to connect while was in some intermediate state (both streams in different
  949. // states as for 'connected' or 'connecting'
  950. else {
  951. forciblyConnectionBlock();
  952. }
  953. }
  954. else if ([self isDisconnecting]) {
  955. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  956. return @[PNLoggerSymbols.connection.triedToConnectDuringDisconnection, (self.name ? self.name : self),
  957. @(self.state)];
  958. }];
  959. // Mark that client should try to connect back as soon as disconnection will be completed
  960. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionReconnectOnDisconnect];
  961. if (resultBlock) {
  962. resultBlock(isStreamOpened);
  963. }
  964. }
  965. else {
  966. forciblyConnectionBlock();
  967. }
  968. }
  969. }
  970. // Looks like configuration not completed
  971. else {
  972. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  973. return @[PNLoggerSymbols.connection.notConfigured, (self.name ? self.name : self), @(self.state)];
  974. }];
  975. // Try prepare connection's streams for future usage
  976. if ([self prepareStreams]) {
  977. [self connectByInternalRequest:^(BOOL connecting) {
  978. if (resultBlock) {
  979. resultBlock(connecting);
  980. }
  981. }];
  982. }
  983. else {
  984. if (resultBlock) {
  985. resultBlock(isStreamOpened);
  986. }
  987. }
  988. }
  989. }
  990. // Looks like connection can't be established at this moment. Launch wake up timer
  991. else {
  992. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  993. return @[PNLoggerSymbols.connection.connectionImpossibleAtCurrentMoment, (self.name ? self.name : self),
  994. @(self.state)];
  995. }];
  996. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
  997. [self resumeWakeUpTimer];
  998. if (resultBlock) {
  999. resultBlock(isStreamOpened);
  1000. }
  1001. }
  1002. }];
  1003. }];
  1004. }
  1005. - (BOOL)canRetryConnection {
  1006. return (self.connectionRetryCount < kPNMaximumConnectionRetryCount);
  1007. }
  1008. - (void)retryConnection {
  1009. // This method should be launched only from within it's private queue
  1010. [self pn_scheduleOnPrivateQueueAssert];
  1011. self.connectionRetryCount++;
  1012. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1013. return @[PNLoggerSymbols.connection.connectionRetry, (self.name ? self.name : self), @(self.connectionRetryCount),
  1014. @(kPNMaximumConnectionRetryCount), @(self.state)];
  1015. }];
  1016. // Check whether reconnection was issued because of SSL error or not
  1017. if (self.sslConfigurationLevel == PNConnectionSSLConfigurationInsecure &&
  1018. [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR]) {
  1019. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1020. return @[PNLoggerSymbols.connection.connectionRetryOnSSLError, (self.name ? self.name : self), @(self.state)];
  1021. }];
  1022. }
  1023. // Check whether reconnection was issued because of socket temporary issues or not
  1024. else if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR]) {
  1025. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1026. return @[PNLoggerSymbols.connection.connectionRetryOnTemporaryConnectionIssues, (self.name ? self.name : self), @(self.state)];
  1027. }];
  1028. }
  1029. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWillReconnect];
  1030. [self reconnectWithBlock:NULL];
  1031. }
  1032. - (void)reconnectWithBlock:(dispatch_block_t)processReportBlock {
  1033. [self pn_dispatchBlock:^{
  1034. unsigned long oldStates = self.state;
  1035. __block unsigned long newStates = self.state;
  1036. [self.delegate connection:self checkShouldRestoreConnection:^(BOOL shouldReconnect) {
  1037. [self pn_dispatchBlock:^{
  1038. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionWillReconnect];
  1039. newStates = self.state;
  1040. BOOL stateChangedFromOutside = (oldStates != newStates && ![PNBitwiseHelper is:oldStates containsBit:PNByUserRequest] &&
  1041. [PNBitwiseHelper is:newStates containsBit:PNByUserRequest]);
  1042. if (!stateChangedFromOutside) {
  1043. if (shouldReconnect) {
  1044. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1045. return @[PNLoggerSymbols.connection.connectionRetryAttempt,
  1046. (self.name ? self.name : self), @(self.state)];
  1047. }];
  1048. }
  1049. else {
  1050. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1051. return @[PNLoggerSymbols.connection.connectionRetryImpossibleAtCurrentMoment, (self.name ? self.name : self),
  1052. @(self.state)];
  1053. }];
  1054. }
  1055. // Ask delegat e whether connection should initiate connection to remote host or not
  1056. if (shouldReconnect) {
  1057. BOOL isWaitingForReconnection = [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnect];
  1058. BOOL isReconnectingBecauseOfError = [PNBitwiseHelper is:self.state containsBit:PNConnectionError];
  1059. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionError, BITS_LIST_TERMINATOR];
  1060. // Marking that connection instance is reconnecting now and after last connection will be closed should
  1061. // automatically renew connection
  1062. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionReconnect];
  1063. [self destroyStreams];
  1064. // Check whether 'reconnection' delayed request already has been issued or not
  1065. if (!isWaitingForReconnection) {
  1066. // Attempt to restore connection after small delay defined in 'static' section of this class
  1067. __pn_desired_weak __typeof__(self) weakSelf = self;
  1068. NSTimeInterval delay = (NSTimeInterval) kPNConnectionRetryDelay;
  1069. if (!isReconnectingBecauseOfError) {
  1070. delay = kPNConnectionRetryFastDelay;
  1071. }
  1072. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delay * NSEC_PER_SEC));
  1073. dispatch_after(popTime, [self pn_privateQueue], ^{
  1074. __strong __typeof__(self) strongSelf = weakSelf;
  1075. if ([PNBitwiseHelper is:strongSelf.state containsBit:PNConnectionReconnect]) {
  1076. [self disconnectByInternalRequest];
  1077. }
  1078. if (processReportBlock) {
  1079. processReportBlock();
  1080. }
  1081. });
  1082. }
  1083. else if (processReportBlock) {
  1084. processReportBlock();
  1085. }
  1086. }
  1087. else {
  1088. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
  1089. [self resumeWakeUpTimer];
  1090. if (processReportBlock) {
  1091. processReportBlock();
  1092. }
  1093. }
  1094. }
  1095. else {
  1096. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1097. return @[PNLoggerSymbols.connection.connectionRetryCanceledBecauseStateAltered, (self.name ? self.name : self),
  1098. @(self.state)];
  1099. }];
  1100. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
  1101. [self resumeWakeUpTimer];
  1102. if (processReportBlock) {
  1103. processReportBlock();
  1104. }
  1105. }
  1106. }];
  1107. }];
  1108. }];
  1109. }
  1110. - (void)disconnect {
  1111. [self pn_dispatchBlock:^{
  1112. [PNBitwiseHelper addTo:&self->_state bit:PNByUserRequest];
  1113. self.connectionRetryCount = 0;
  1114. [self disconnectByInternalRequest];
  1115. }];
  1116. }
  1117. - (void)disconnectOnInternalRequest {
  1118. // This method should be launched only from within it's private queue
  1119. [self pn_scheduleOnPrivateQueueAssert];
  1120. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  1121. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
  1122. [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
  1123. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
  1124. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  1125. [self disconnectByInternalRequest];
  1126. }
  1127. - (void)disconnectByInternalRequest {
  1128. [self pn_dispatchBlock:^{
  1129. // Launch 'wake up' timer in case if disconnection was accident or some catch up logic failed because some tragic coincidence
  1130. [self startWakeUpTimer];
  1131. [self stopTimeoutTimer];
  1132. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1133. return @[PNLoggerSymbols.connection.disconnectionAttempt, (self.name ? self.name : self),
  1134. @([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]), @(self.state)];
  1135. }];
  1136. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionConnecting, PNConnectionPrepareToConnect, PNConnectionResuming, BITS_LIST_TERMINATOR];
  1137. // Check whether it was requested to perform disconnection on user request or not
  1138. if ([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]) {
  1139. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  1140. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
  1141. [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
  1142. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
  1143. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  1144. }
  1145. // Clean up cached data
  1146. [self unscheduleRequestsExecution];
  1147. self.proxySettings = nil;
  1148. [self disconnectReadStream:self->_socketReadStream];
  1149. [self disconnectWriteStream:self->_socketWriteStream];
  1150. }];
  1151. }
  1152. - (void)closeConnection {
  1153. [self pn_dispatchBlock:^{
  1154. [self disconnectOnInternalRequest];
  1155. }];
  1156. }
  1157. - (void)destroyStreams {
  1158. // This method should be launched only from within it's private queue
  1159. [self pn_scheduleOnPrivateQueueAssert];
  1160. BOOL isAnyOfStreamsStillValid = _socketReadStream != NULL || _socketWriteStream != NULL;
  1161. if (isAnyOfStreamsStillValid) {
  1162. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1163. return @[PNLoggerSymbols.connection.stream.destroying, (self.name ? self.name : self), @(self.state)];
  1164. }];
  1165. }
  1166. // Clean up cached data
  1167. [self unscheduleRequestsExecution];
  1168. self.proxySettings = nil;
  1169. BOOL isConfiguring = [PNBitwiseHelper is:self.state containsBit:PNConnectionConfiguring];
  1170. BOOL isReadStreamErrorSet = [PNBitwiseHelper is:self.state containsBit:PNReadStreamError];
  1171. BOOL isWriteStreamErrorSet = [PNBitwiseHelper is:self.state containsBit:PNWriteStreamError];
  1172. [self destroyReadStream:_socketReadStream];
  1173. [self destroyWriteStream:_socketWriteStream];
  1174. [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamCleanAll, PNWriteStreamCleanAll, BITS_LIST_TERMINATOR];
  1175. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
  1176. if (isConfiguring) {
  1177. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionConfiguring];
  1178. }
  1179. if (isReadStreamErrorSet || isWriteStreamErrorSet) {
  1180. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionError];
  1181. }
  1182. if (isAnyOfStreamsStillValid) {
  1183. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1184. return @[PNLoggerSymbols.connection.stream.destroyed, (self.name ? self.name : self), @(self.state)];
  1185. }];
  1186. }
  1187. }
  1188. - (void)suspend {
  1189. [self pn_dispatchBlock:^{
  1190. dispatch_block_t suspendProceedBlock = ^{
  1191. [self pn_dispatchBlock:^{
  1192. [self suspendWakeUpTimer];
  1193. [self stopTimeoutTimer];
  1194. }];
  1195. };
  1196. // Check whether connection established to the remote host or not
  1197. if ([self isConnected]) {
  1198. // Check whether connection can be suspended or not
  1199. if (![self isSuspending] && ![self isSuspended]) {
  1200. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  1201. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
  1202. [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
  1203. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
  1204. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  1205. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionSuspending];
  1206. [self disconnectByInternalRequest];
  1207. suspendProceedBlock();
  1208. }
  1209. // Looks like connection already suspended
  1210. else if ([self isSuspended]){
  1211. [self.delegate connectionDidSuspend:self withBlock:suspendProceedBlock];
  1212. }
  1213. }
  1214. else {
  1215. [self disconnectOnInternalRequest];
  1216. [PNBitwiseHelper addTo:&self->_state bits:PNConnectionDisconnected, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  1217. [self.delegate connectionDidSuspend:self withBlock:suspendProceedBlock];
  1218. }
  1219. }];
  1220. }
  1221. - (BOOL)isSuspending {
  1222. // This method should be launched only from within it's private queue
  1223. [self pn_scheduleOnPrivateQueueAssert];
  1224. return [PNBitwiseHelper is:self.state containsBit:PNConnectionSuspending];
  1225. }
  1226. - (void)checkSuspended:(void (^)(BOOL suspended))checkCompletionBlock {
  1227. [self pn_dispatchBlock:^{
  1228. checkCompletionBlock([self isSuspended]);
  1229. }];
  1230. }
  1231. - (BOOL)isSuspended {
  1232. // This method should be launched only from within it's private queue
  1233. [self pn_scheduleOnPrivateQueueAssert];
  1234. return [PNBitwiseHelper is:self.state strictly:YES
  1235. containsBits:PNConnectionDisconnected, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  1236. }
  1237. - (void)resume {
  1238. [self pn_dispatchBlock:^{
  1239. // Check whether connection suspended at this moment or not
  1240. if (![self isConnected] && [self isSuspended] && ![self isResuming]) {
  1241. // In case if because of some reasons connection can't be established we should launch 'wake up' timer to
  1242. // help fix connection state when it will be possible
  1243. [self connectByInternalRequest:^(BOOL connecting) {
  1244. if (!connecting) {
  1245. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
  1246. [self resumeWakeUpTimer];
  1247. }
  1248. }];
  1249. }
  1250. else if ([self isConnected]) {
  1251. [self.delegate connectionDidResume:self withBlock:NULL];
  1252. }
  1253. }];
  1254. }
  1255. - (void)checkResuming:(void (^)(BOOL resuming))checkCompletionBlock {
  1256. [self pn_dispatchBlock:^{
  1257. checkCompletionBlock([self isResuming]);
  1258. }];
  1259. }
  1260. - (BOOL)isResuming {
  1261. // This method should be launched only from within it's private queue
  1262. [self pn_scheduleOnPrivateQueueAssert];
  1263. return [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionConnecting, PNConnectionResuming, BITS_LIST_TERMINATOR];
  1264. }
  1265. #pragma mark - Read stream lifecycle management methods
  1266. - (void)configureReadStream:(CFReadStreamRef)readStream {
  1267. // This method should be launched only from within it's private queue
  1268. [self pn_scheduleOnPrivateQueueAssert];
  1269. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1270. return @[PNLoggerSymbols.connection.stream.read.configurationStarted, (self.name ? self.name : self), @(self.state)];
  1271. }];
  1272. [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamCleanConfiguration];
  1273. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamConfiguring];
  1274. CFOptionFlags options = (kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable |
  1275. kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered);
  1276. CFStreamClientContext client = [self streamClientContext];
  1277. // Configuring connection channel instance as client for read stream with described set of handling events
  1278. BOOL isStreamReady = CFReadStreamSetClient(readStream, options, readStreamCallback, &client);
  1279. if (isStreamReady) {
  1280. isStreamReady = CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
  1281. }
  1282. if (isStreamReady && self.proxySettings) {
  1283. isStreamReady = CFReadStreamSetProperty(readStream, kCFStreamPropertySOCKSProxy, (__bridge CFTypeRef)(self.proxySettings));
  1284. }
  1285. if (self.streamSecuritySettings != NULL && isStreamReady) {
  1286. // Configuring stream to establish SSL connection
  1287. isStreamReady = CFReadStreamSetProperty(readStream,
  1288. (__bridge CFStringRef)NSStreamSocketSecurityLevelKey,
  1289. (__bridge CFStringRef)NSStreamSocketSecurityLevelNegotiatedSSL);
  1290. if (isStreamReady) {
  1291. // Specify connection security options
  1292. isStreamReady = CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, self.streamSecuritySettings);
  1293. }
  1294. }
  1295. if (isStreamReady) {
  1296. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1297. return @[PNLoggerSymbols.connection.stream.read.configurationCompleted, (self.name ? self.name : self),
  1298. @(self.state)];
  1299. }];
  1300. [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamConfiguring];
  1301. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamConfigured];
  1302. // Schedule read stream on current run-loop
  1303. CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  1304. }
  1305. else {
  1306. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
  1307. return @[PNLoggerSymbols.connection.stream.read.configurationFailed, (self.name ? self.name : self), @(self.state)];
  1308. }];
  1309. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
  1310. }
  1311. }
  1312. - (void)openReadStream:(CFReadStreamRef)readStream {
  1313. // This method should be launched only from within it's private queue
  1314. [self pn_scheduleOnPrivateQueueAssert];
  1315. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1316. return @[PNLoggerSymbols.connection.stream.read.initOpening, (self.name ? self.name : self), @(self.state)];
  1317. }];
  1318. [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamCleanConnection];
  1319. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamConnecting];
  1320. if (!CFReadStreamOpen(readStream)) {
  1321. CFErrorRef error = CFReadStreamCopyError(readStream);
  1322. if (error && CFErrorGetCode(error) != 0) {
  1323. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
  1324. return @[PNLoggerSymbols.connection.stream.read.unableToOpen, (self.name ? self.name : self), @(self.state)];
  1325. }];
  1326. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
  1327. [self handleStreamError:error];
  1328. }
  1329. else {
  1330. CFRunLoopRun();
  1331. }
  1332. if (error) {
  1333. CFRelease(error);
  1334. }
  1335. }
  1336. else {
  1337. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1338. return @[PNLoggerSymbols.connection.stream.read.opening, (self.name ? self.name : self), @(self.state)];
  1339. }];
  1340. }
  1341. }
  1342. - (void)disconnectReadStream:(CFReadStreamRef)readStream {
  1343. // This method should be launched only from within it's private queue
  1344. [self pn_scheduleOnPrivateQueueAssert];
  1345. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1346. return @[PNLoggerSymbols.connection.stream.read.disconnecting, (self.name ? self.name : self), @(self.state)];
  1347. }];
  1348. [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamConnecting, PNReadStreamCleanDisconnection, BITS_LIST_TERMINATOR];
  1349. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamDisconnecting];
  1350. if (self.readBuffer != NULL) {
  1351. // Check whether there is some data received from server and try to parse it
  1352. if ([self.readBuffer length]) {
  1353. self.notProcessedReadBuffer = self.readBuffer;
  1354. [self processResponse];
  1355. }
  1356. // Destroying input buffer
  1357. self.readBuffer = NULL;
  1358. }
  1359. BOOL streamHasError = [PNBitwiseHelper is:self.state containsBit:PNReadStreamError];
  1360. [self destroyReadStream:readStream];
  1361. if (streamHasError) {
  1362. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
  1363. }
  1364. [self handleStreamClose];
  1365. }
  1366. - (void)destroyReadStream:(CFReadStreamRef)readStream {
  1367. // This method should be launched only from within it's private queue
  1368. [self pn_scheduleOnPrivateQueueAssert];
  1369. BOOL isStreamExists = readStream != NULL;
  1370. if (isStreamExists) {
  1371. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1372. return @[PNLoggerSymbols.connection.stream.read.destroying, (self.name ? self.name : self), @(self.state)];
  1373. }];
  1374. }
  1375. if (isStreamExists) {
  1376. self.socketReadStream = NULL;
  1377. dispatch_async(dispatch_get_main_queue(), ^{
  1378. CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  1379. CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
  1380. if (CFReadStreamGetStatus(readStream) != kCFStreamStatusClosed) {
  1381. CFReadStreamClose(readStream);
  1382. }
  1383. if (readStream) {
  1384. CFRelease(readStream);
  1385. }
  1386. });
  1387. }
  1388. [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamCleanConfiguration];
  1389. if (isStreamExists) {
  1390. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1391. return @[PNLoggerSymbols.connection.stream.read.destroyed, (self.name ? self.name : self), @(self.state)];
  1392. }];
  1393. }
  1394. }
  1395. #pragma mark - Read stream lifecycle data processing methods
  1396. - (void)readStreamContent:(CFReadStreamRef)stream {
  1397. // This method should be launched only from within it's private queue
  1398. [self pn_scheduleOnPrivateQueueAssert];
  1399. // Check whether there is really some data available right now or not.
  1400. if (CFReadStreamHasBytesAvailable(stream)) {
  1401. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1402. return @[PNLoggerSymbols.connection.stream.read.readingArrivedData,
  1403. (self.name ? self.name : self), @(self.state)];
  1404. }];
  1405. // Read raw data from stream
  1406. __block struct {
  1407. UInt8 buffer[kPNStreamBufferSize];
  1408. } data;
  1409. CFIndex readedBytesCount = CFReadStreamRead(stream, data.buffer, kPNStreamBufferSize);
  1410. // Checking whether client was able to read out some data from stream or not
  1411. if (readedBytesCount > 0) {
  1412. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1413. return @[PNLoggerSymbols.connection.stream.read.readedPortionOfArrivedData,
  1414. (self.name ? self.name : self), @(readedBytesCount), @(self.state)];
  1415. }];
  1416. if ([PNLogger isDumpingHTTPResponse] || [PNLogger isLoggerEnabled]) {
  1417. NSData *tempData = [[NSData alloc] initWithBytes:data.buffer
  1418. length:(NSUInteger)readedBytesCount];
  1419. [PNLogger storeHTTPPacketData:^NSData * {
  1420. return tempData;
  1421. }];
  1422. [PNLogger logConnectionHTTPPacketFrom:self withParametersFromBlock:^NSArray *{
  1423. NSString *responseString = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
  1424. if (!responseString) {
  1425. responseString = [[NSString alloc] initWithData:tempData encoding:NSASCIIStringEncoding];
  1426. }
  1427. if (!responseString) {
  1428. responseString = @"Can't stringify response. Try check response dump on file system (if enabled)";
  1429. }
  1430. return @[PNLoggerSymbols.connection.stream.read.rawHTTPResponse, (self.name ? self.name : self),
  1431. responseString, @(self.state)];
  1432. }];
  1433. }
  1434. [self appendToReadBuffer:data.buffer length:(NSUInteger)readedBytesCount];
  1435. [self processResponse];
  1436. }
  1437. // Looks like there is no data or error occurred while tried to read out stream content
  1438. else if (readedBytesCount < 0) {
  1439. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
  1440. return @[PNLoggerSymbols.connection.stream.read.readingError,
  1441. (self.name ? self.name : self), @(self.state)];
  1442. }];
  1443. CFErrorRef error = CFReadStreamCopyError(stream);
  1444. [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
  1445. [self handleStreamError:error];
  1446. if (error) {
  1447. CFRelease(error);
  1448. }
  1449. }
  1450. }
  1451. }
  1452. - (void)processResponse {
  1453. // This method should be launched only from within it's private queue
  1454. [self pn_scheduleOnPrivateQueueAssert];
  1455. // Retrieve response objects from server response
  1456. __block __pn_desired_weak PNConnection *weakSelf = self;
  1457. [self.deserializer parseBufferContent:(self.readBuffer ?: self.notProcessedReadBuffer)
  1458. withBlock:^(NSArray *responses, NSUInteger fullBufferLength,
  1459. NSUInteger processedBufferLength,
  1460. void(^readBufferPostProcessing)(void)) {
  1461. __strong __typeof__(self) strongSelf = weakSelf;
  1462. [strongSelf pn_dispatchBlock:^{
  1463. // Check whether buffer size has been altered while de-serializer parsed it's content
  1464. // or not.
  1465. NSUInteger readBufferSize = [(self.readBuffer ?: self.notProcessedReadBuffer) length];
  1466. BOOL isReadBufferChanged = (fullBufferLength != readBufferSize);
  1467. // Check whether some data has been processed or not.
  1468. if (processedBufferLength > 0){
  1469. // Update read buffer content by removing from it number of bytes which has been
  1470. // processed.
  1471. if (readBufferSize > 0 && readBufferSize >= processedBufferLength) {
  1472. if (processedBufferLength == readBufferSize) {
  1473. if (strongSelf.readBuffer) {
  1474. strongSelf.readBuffer = [NSMutableData new];
  1475. }
  1476. else {
  1477. strongSelf.notProcessedReadBuffer = nil;
  1478. }
  1479. }
  1480. else {
  1481. NSRange targetRange = NSMakeRange(processedBufferLength, readBufferSize - processedBufferLength);
  1482. if (strongSelf.readBuffer) {
  1483. NSData *processedData = [strongSelf.readBuffer subdataWithRange:targetRange];
  1484. strongSelf.readBuffer = [[NSMutableData alloc] initWithData:processedData];
  1485. }
  1486. else if (strongSelf.notProcessedReadBuffer){
  1487. strongSelf.notProcessedReadBuffer = [strongSelf.notProcessedReadBuffer subdataWithRange:targetRange];
  1488. }
  1489. }
  1490. }
  1491. }
  1492. [PNLogger logConnectionInfoMessageFrom:strongSelf withParametersFromBlock:^NSArray *{
  1493. return @[PNLoggerSymbols.connection.stream.read.processedArrivedData,
  1494. (strongSelf.name ? strongSelf.name : strongSelf), @([responses count]), @(strongSelf.state)];
  1495. }];
  1496. if ([responses count] > 0) {
  1497. [responses enumerateObjectsUsingBlock:^(id response,
  1498. __unused NSUInteger responseIdx,
  1499. __unused BOOL *responseEnumeratorStop) {
  1500. // Check whether server reported that connection will be closed after this portion of data
  1501. if (![PNBitwiseHelper is:strongSelf.state containsBit:PNByServerRequest] &&
  1502. [(id<PNResponseProtocol>)response isLastResponseOnConnection]) {
  1503. [PNBitwiseHelper addTo:&strongSelf->_state bits:PNConnectionDisconnect,
  1504. PNByServerRequest, BITS_LIST_TERMINATOR];
  1505. // Inform delegate that connection will be closed soon by server request
  1506. [strongSelf.delegate connection:strongSelf willDisconnectByServerRequestFromHost:strongSelf.configuration.origin
  1507. withBlock:^{
  1508. // Notify delegate about new event arrival
  1509. [strongSelf.delegate connection:strongSelf didReceiveResponse:response withBlock:NULL];
  1510. }];
  1511. }
  1512. else {
  1513. // Notify delegate about new event arrival
  1514. [strongSelf.delegate connection:strongSelf didReceiveResponse:response withBlock:NULL];
  1515. }
  1516. }];
  1517. }
  1518. readBufferPostProcessing();
  1519. if (isReadBufferChanged) {
  1520. // Try to process retrieved data once more (maybe some full response arrived
  1521. // from remote server)
  1522. [strongSelf processResponse];
  1523. }
  1524. else {
  1525. strongSelf.notProcessedReadBuffer = nil;
  1526. // Check whether client is still connected and there is request from server side to close connection.
  1527. // Connection will be restored after full disconnection
  1528. if ([strongSelf isConnected] && ![strongSelf isReconnecting] && ![strongSelf isDisconnecting] &&
  1529. [PNBitwiseHelper is:strongSelf.state containsBit:PNByServerRequest]) {
  1530. [strongSelf disconnectByInternalRequest];
  1531. }
  1532. }
  1533. }];
  1534. }];
  1535. }
  1536. #pragma mark - Write stream lifecycle management methods
  1537. - (void)configureWriteStream:(CFWriteStreamRef)writeStream {
  1538. // This method should be launched only from within it's private queue
  1539. [self pn_scheduleOnPrivateQueueAssert];
  1540. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1541. return @[PNLoggerSymbols.connection.stream.write.configurationStarted, (self.name ? self.name : self), @(self.state)];
  1542. }];
  1543. [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamCleanConfiguration];
  1544. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamConfiguring];
  1545. CFOptionFlags options = (kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes |
  1546. kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered);
  1547. CFStreamClientContext client = [self streamClientContext];
  1548. // Configuring connection channel instance as client for write stream with described set of handling events
  1549. BOOL isStreamReady = CFWriteStreamSetClient(writeStream, options, writeStreamCallback, &client);
  1550. if (isStreamReady) {
  1551. isStreamReady = CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
  1552. }
  1553. if (isStreamReady && self.proxySettings) {
  1554. isStreamReady = CFWriteStreamSetProperty(writeStream, kCFStreamPropertySOCKSProxy, (__bridge CFTypeRef)(self.proxySettings));
  1555. }
  1556. if (isStreamReady) {
  1557. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1558. return @[PNLoggerSymbols.connection.stream.write.configurationCompleted, (self.name ? self.name : self),
  1559. @(self.state)];
  1560. }];
  1561. [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamConfiguring];
  1562. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamConfigured];
  1563. // Schedule write stream on current run-loop
  1564. CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  1565. }
  1566. else {
  1567. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
  1568. return @[PNLoggerSymbols.connection.stream.write.configurationFailed, (self.name ? self.name : self), @(self.state)];
  1569. }];
  1570. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
  1571. }
  1572. }
  1573. - (void)openWriteStream:(CFWriteStreamRef)writeStream {
  1574. // This method should be launched only from within it's private queue
  1575. [self pn_scheduleOnPrivateQueueAssert];
  1576. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1577. return @[PNLoggerSymbols.connection.stream.write.initOpening, (self.name ? self.name : self), @(self.state)];
  1578. }];
  1579. [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamCleanConnection];
  1580. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamConnecting];
  1581. if (!CFWriteStreamOpen(writeStream)) {
  1582. CFErrorRef error = CFWriteStreamCopyError(writeStream);
  1583. if (error && CFErrorGetCode(error) != 0) {
  1584. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
  1585. return @[PNLoggerSymbols.connection.stream.write.unableToOpen, (self.name ? self.name : self), @(self.state)];
  1586. }];
  1587. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
  1588. [self handleStreamError:error];
  1589. }
  1590. else {
  1591. CFRunLoopRun();
  1592. }
  1593. if (error) {
  1594. CFRelease(error);
  1595. }
  1596. }
  1597. else {
  1598. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1599. return @[PNLoggerSymbols.connection.stream.write.opening, (self.name ? self.name : self), @(self.state)];
  1600. }];
  1601. }
  1602. }
  1603. - (void)disconnectWriteStream:(CFWriteStreamRef)writeStream {
  1604. // This method should be launched only from within it's private queue
  1605. [self pn_scheduleOnPrivateQueueAssert];
  1606. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1607. return @[PNLoggerSymbols.connection.stream.write.disconnecting, (self.name ? self.name : self), @(self.state)];
  1608. }];
  1609. [PNBitwiseHelper removeFrom:&self->_state bits:PNWriteStreamConnecting, PNWriteStreamCleanDisconnection, BITS_LIST_TERMINATOR];
  1610. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamDisconnecting];
  1611. self.writeStreamCanHandleData = NO;
  1612. // Handle canceled request (if there was such)
  1613. [self handleRequestSendingCancelation];
  1614. BOOL streamHasError = [PNBitwiseHelper is:self.state containsBit:PNWriteStreamError];
  1615. [self destroyWriteStream:writeStream];
  1616. if (streamHasError) {
  1617. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
  1618. }
  1619. [self handleStreamClose];
  1620. }
  1621. - (void)destroyWriteStream:(CFWriteStreamRef)writeStream {
  1622. // This method should be launched only from within it's private queue
  1623. [self pn_scheduleOnPrivateQueueAssert];
  1624. BOOL isStreamExists = writeStream != NULL;
  1625. if (isStreamExists) {
  1626. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1627. return @[PNLoggerSymbols.connection.stream.write.destroying, (self.name ? self.name : self), @(self.state)];
  1628. }];
  1629. }
  1630. if (isStreamExists) {
  1631. self.socketWriteStream = NULL;
  1632. dispatch_async(dispatch_get_main_queue(), ^{
  1633. CFWriteStreamUnscheduleFromRunLoop(writeStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  1634. CFWriteStreamSetClient(writeStream, kCFStreamEventNone, NULL, NULL);
  1635. if (CFWriteStreamGetStatus(writeStream) != kCFStreamStatusClosed){
  1636. CFWriteStreamClose(writeStream);
  1637. }
  1638. if (writeStream) {
  1639. CFRelease(writeStream);
  1640. }
  1641. });
  1642. }
  1643. [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamCleanConfiguration];
  1644. if (isStreamExists) {
  1645. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1646. return @[PNLoggerSymbols.connection.stream.write.destroyed, (self.name ? self.name : self), @(self.state)];
  1647. }];
  1648. }
  1649. }
  1650. #pragma mark - Write stream buffer management methods
  1651. - (void)prepareNextRequestPacket:(dispatch_block_t)completionBlock {
  1652. // This method should be launched only from within it's private queue
  1653. [self pn_scheduleOnPrivateQueueAssert];
  1654. // Ensure that connection is able to send next portion of data which will be prepared
  1655. BOOL shouldPrepareData = [self isConnected] && ![self isReconnecting] && ![self isDisconnecting] &&
  1656. ![PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
  1657. PNByServerRequest, BITS_LIST_TERMINATOR] && ![self isResuming];
  1658. if (shouldPrepareData && !self.fetchingDataForSending) {
  1659. self.fetchingDataForSending = YES;
  1660. [self.dataSource checkHasDataForConnection:self withBlock:^(BOOL hasData) {
  1661. // Check whether data source can provide some data right after connection is established or not
  1662. if (hasData) {
  1663. [self.dataSource nextRequestIdentifierForConnection:self withBlock:^(NSString *requestIdentifier) {
  1664. if (requestIdentifier) {
  1665. [self.dataSource connection:self requestDataForIdentifier:requestIdentifier
  1666. withBlock:^(PNWriteBuffer *buffer) {
  1667. [self pn_dispatchBlock:^{
  1668. self.writeBuffer = buffer;
  1669. self.fetchingDataForSending = NO;
  1670. completionBlock();
  1671. }];
  1672. }];
  1673. }
  1674. else {
  1675. [self pn_dispatchBlock:^{
  1676. self.fetchingDataForSending = NO;
  1677. completionBlock();
  1678. }];
  1679. }
  1680. }];
  1681. }
  1682. else {
  1683. [self pn_dispatchBlock:^{
  1684. self.fetchingDataForSending = NO;
  1685. completionBlock();
  1686. }];
  1687. }
  1688. }];
  1689. }
  1690. else {
  1691. completionBlock();
  1692. }
  1693. }
  1694. - (void)writeBufferContent {
  1695. // This method should be launched only from within it's private queue
  1696. [self pn_scheduleOnPrivateQueueAssert];
  1697. BOOL(^writeStreamIsAbleToSend)(void) = ^{
  1698. return (BOOL)(self.socketWriteStream && [self isConnected] && ![self isReconnecting] &&
  1699. ![self isDisconnecting] && ![PNBitwiseHelper is:self.state strictly:YES
  1700. containsBits:PNConnectionDisconnect,
  1701. PNByServerRequest,
  1702. BITS_LIST_TERMINATOR] &&
  1703. self.isWriteStreamCanHandleData && ![self isResuming]);
  1704. };
  1705. // Check whether there is connection which can be used to write data
  1706. if (writeStreamIsAbleToSend()) {
  1707. if (self.writeBuffer != nil) {
  1708. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1709. return @[PNLoggerSymbols.connection.stream.write.writeDataFromBuffer, (self.name ? self.name : self),
  1710. @(self.state)];
  1711. }];
  1712. [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamError];
  1713. if (self.writeBuffer.length > 0) {
  1714. [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamError];
  1715. [PNBitwiseHelper addTo:&self->_state bit:PNSendingData];
  1716. // Check whether connection can pull some data
  1717. // from write buffer or not
  1718. __block BOOL isWriteBufferIsEmpty = ![self.writeBuffer hasData];
  1719. dispatch_block_t emptyBufferHandleBlock = ^{
  1720. if (isWriteBufferIsEmpty) {
  1721. [PNBitwiseHelper removeFrom:&self->_state bit:PNSendingData];
  1722. // Retrieving reference on request's identifier who's body has been sent
  1723. NSString *identifier = self.writeBuffer.requestIdentifier;
  1724. self.writeBuffer = nil;
  1725. [self.dataSource connection:self didSendRequestWithIdentifier:identifier withBlock:^{
  1726. [self pn_dispatchBlock:^{
  1727. // Check whether should try to send next request or not
  1728. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
  1729. if (writeStreamIsAbleToSend()) {
  1730. [self scheduleNextRequestExecution];
  1731. }
  1732. else if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect, PNByServerRequest, BITS_LIST_TERMINATOR]) {
  1733. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1734. return @[PNLoggerSymbols.connection.stream.write.unableProcessNextRequestOnConnectionTermination,
  1735. (self.name ? self.name : self), @(self.state)];
  1736. }];
  1737. }
  1738. }
  1739. }];
  1740. }];
  1741. }
  1742. };
  1743. if (!isWriteBufferIsEmpty) {
  1744. if (self.isWriteStreamCanHandleData) {
  1745. void(^bufferProcessingBlock)(void) = ^{
  1746. if (writeStreamIsAbleToSend() && self.writeBuffer != nil) {
  1747. // Try write data into write stream
  1748. CFIndex bytesWritten = CFWriteStreamWrite(self.socketWriteStream,
  1749. [self.writeBuffer buffer],
  1750. [self.writeBuffer bufferLength]);
  1751. // Check whether error occurred while tried to process request
  1752. if (bytesWritten < 0) {
  1753. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
  1754. return @[PNLoggerSymbols.connection.stream.write.dataWriteError,
  1755. (self.name ? self.name : self), @(self.state)];
  1756. }];
  1757. // Mark that buffer content is not processed at this moment
  1758. self.writeBuffer.sendingBytes = NO;
  1759. self.writeStreamCanHandleData = NO;
  1760. // Retrieve error which occurred while tried to write buffer into socket
  1761. CFErrorRef writeError = CFWriteStreamCopyError(self.socketWriteStream);
  1762. [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
  1763. [self handleRequestProcessingError:writeError withBlock:^{
  1764. if (writeError) {
  1765. CFRelease(writeError);
  1766. }
  1767. [self pn_dispatchBlock:^{
  1768. isWriteBufferIsEmpty = YES;
  1769. emptyBufferHandleBlock();
  1770. }];
  1771. }];
  1772. }
  1773. // Check whether socket was able to transfer whole write buffer at once or not
  1774. else if (bytesWritten == self.writeBuffer.length) {
  1775. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1776. return @[PNLoggerSymbols.connection.stream.write.writenDataFromBufferAtOnce,
  1777. (self.name ? self.name : self), @(bytesWritten), @(self.writeBuffer.length),
  1778. @(self.state)];
  1779. }];
  1780. // Mark that buffer content is not processed at this moment
  1781. self.writeBuffer.sendingBytes = NO;
  1782. // Set readout offset to buffer content length (there is no more data to send)
  1783. self.writeBuffer.offset = self.writeBuffer.length;
  1784. isWriteBufferIsEmpty = YES;
  1785. emptyBufferHandleBlock();
  1786. }
  1787. else {
  1788. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1789. return @[PNLoggerSymbols.connection.stream.write.writenPartOfDataFromBuffer,
  1790. (self.name ? self.name : self), @(self.writeBuffer.offset + bytesWritten),
  1791. @(self.writeBuffer.length), @(self.state)];
  1792. }];
  1793. self.writeStreamCanHandleData = NO;
  1794. // Increase buffer readout offset
  1795. self.writeBuffer.offset = (self.writeBuffer.offset + bytesWritten);
  1796. if (self.writeBuffer.offset == self.writeBuffer.length) {
  1797. self.writeStreamCanHandleData = YES;
  1798. isWriteBufferIsEmpty = YES;
  1799. }
  1800. emptyBufferHandleBlock();
  1801. }
  1802. }
  1803. else {
  1804. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1805. return @[PNLoggerSymbols.connection.stream.write.writeCanceled, (self.name ? self.name : self),
  1806. @(self.state)];
  1807. }];
  1808. emptyBufferHandleBlock();
  1809. }
  1810. };
  1811. // Check whether we just started request processing or not
  1812. if (self.writeBuffer.offset == 0) {
  1813. // Mark that buffer content sending was initiated
  1814. self.writeBuffer.sendingBytes = YES;
  1815. // Notify data source that we started request processing
  1816. [self.dataSource connection:self
  1817. processingRequestWithIdentifier:self.writeBuffer.requestIdentifier
  1818. withBlock:^(BOOL shouldContinue){
  1819. [self pn_dispatchBlock:^{
  1820. if (shouldContinue) {
  1821. bufferProcessingBlock();
  1822. }
  1823. else {
  1824. // Mark that buffer content sending not started yet
  1825. self.writeBuffer.sendingBytes = NO;
  1826. }
  1827. }];
  1828. }];
  1829. }
  1830. else {
  1831. bufferProcessingBlock();
  1832. }
  1833. }
  1834. }
  1835. else {
  1836. emptyBufferHandleBlock();
  1837. }
  1838. }
  1839. // Looks like because of some reasons there is no new data
  1840. else {
  1841. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
  1842. if (writeStreamIsAbleToSend()) {
  1843. [self scheduleNextRequestExecution];
  1844. }
  1845. else if([PNBitwiseHelper is:self.state strictly:YES
  1846. containsBits:PNConnectionDisconnect, PNByServerRequest, BITS_LIST_TERMINATOR]) {
  1847. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1848. return @[PNLoggerSymbols.connection.stream.write.unableProcessNextRequestOnConnectionTermination,
  1849. (self.name ? self.name : self), @(self.state)];
  1850. }];
  1851. }
  1852. }
  1853. }
  1854. }
  1855. else {
  1856. [self scheduleNextRequestExecution];
  1857. }
  1858. }
  1859. else if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected]) {
  1860. if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
  1861. PNByServerRequest, BITS_LIST_TERMINATOR]) {
  1862. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1863. return @[PNLoggerSymbols.connection.stream.write.unableProcessNextRequestOnConnectionTermination,
  1864. (self.name ? self.name : self), @(self.state)];
  1865. }];
  1866. }
  1867. else {
  1868. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1869. return @[PNLoggerSymbols.connection.stream.write.nothingToWrite, (self.name ? self.name : self),
  1870. @(self.state)];
  1871. }];
  1872. }
  1873. }
  1874. }
  1875. #pragma mark - Handler methods
  1876. - (void)handleStreamConnection {
  1877. // This method should be launched only from within it's private queue
  1878. [self pn_scheduleOnPrivateQueueAssert];
  1879. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1880. return @[PNLoggerSymbols.connection.stream.opened, (self.name ? self.name : self), @(self.state)];
  1881. }];
  1882. // Ensure that both read and write streams are connected before notify
  1883. // delegate about successful connection
  1884. if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnecting] &&
  1885. [PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected]) {
  1886. BOOL retriedConnection = self.connectionRetryCount != 0;
  1887. // Resetting some cached data
  1888. self.configurationRetryCount = 0;
  1889. self.connectionRetryCount = 0;
  1890. // Terminate wake up timer
  1891. [self stopWakeUpTimer];
  1892. BOOL isRestoredAfterServerClosed = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
  1893. PNByServerRequest, BITS_LIST_TERMINATOR];
  1894. BOOL isConnectionReset = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionError,
  1895. PNByInternalRequest, BITS_LIST_TERMINATOR];
  1896. BOOL isReconnectedByWakeUpTimer = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
  1897. PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
  1898. BOOL isReconnectedBySSL = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
  1899. PNConnectionSSL, BITS_LIST_TERMINATOR];
  1900. BOOL isReconnectedBySocket = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
  1901. PNConnectionSocket, BITS_LIST_TERMINATOR];
  1902. BOOL connectedAfterError = [PNBitwiseHelper is:self.state containsBit:PNConnectionError];
  1903. BOOL isByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
  1904. BOOL isReconnecting = [self isReconnecting];
  1905. BOOL isResuming = [self isResuming];
  1906. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  1907. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
  1908. [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
  1909. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
  1910. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  1911. if (retriedConnection) {
  1912. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1913. return @[PNLoggerSymbols.connection.connectedOnRetryAttempt, (self.name ? self.name : self),
  1914. @(self.state)];
  1915. }];
  1916. }
  1917. dispatch_block_t streamConnecionCompletionBlock =^{
  1918. [self pn_dispatchBlock:^{
  1919. // Check whether channel should process requests from upper layers or not
  1920. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
  1921. [self scheduleNextRequestExecution];
  1922. }
  1923. }];
  1924. };
  1925. // Check whether connection has been established as result of user calling '-connect' method or not
  1926. if (isByUserRequest) {
  1927. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1928. return @[PNLoggerSymbols.connection.connected, (self.name ? self.name : self),
  1929. @(isByUserRequest), @(self.state)];
  1930. }];
  1931. // Notify delegate that initial connection is established
  1932. [self.delegate connection:self didConnectToHost:self.configuration.origin
  1933. withBlock:streamConnecionCompletionBlock];
  1934. }
  1935. else {
  1936. // Check whether connection is resuming after it was suspended or not
  1937. if (isResuming) {
  1938. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  1939. return @[PNLoggerSymbols.connection.resumed, (self.name ? self.name : self),
  1940. @(self.state)];
  1941. }];
  1942. [self.delegate connectionDidResume:self withBlock:streamConnecionCompletionBlock];
  1943. }
  1944. // Check whether connection has been restored after server closed it (for example when server doesn't
  1945. // support 'keep-alive' connection type
  1946. else if (isRestoredAfterServerClosed) {
  1947. // Inform delegate that connection finally recovered after it has been close by remote server
  1948. [self.delegate connection:self didRestoreAfterServerCloseConnectionToHost:self.configuration.origin
  1949. withBlock:streamConnecionCompletionBlock];
  1950. }
  1951. // Check whether connection has been forcibly reset after found out that it is in wrong state
  1952. else if (isConnectionReset) {
  1953. // Inform delegate that connection has been completely reset and ready to work
  1954. [self.delegate connectionDidReset:self withBlock:streamConnecionCompletionBlock];
  1955. }
  1956. // Check whether connection has been restored after some error occurred on streams
  1957. else if (connectedAfterError) {
  1958. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1959. return @[PNLoggerSymbols.connection.reconnectedAfterError, (self.name ? self.name : self),
  1960. @(self.state)];
  1961. }];
  1962. [self.delegate connection:self didReconnectToHostAfterError:self.configuration.origin
  1963. withBlock:streamConnecionCompletionBlock];
  1964. }
  1965. // Check whether connection has been reconnected by request from 'wake up' timer/SSL/sockets
  1966. else if (isReconnectedByWakeUpTimer || isReconnectedBySSL || isReconnectedBySocket) {
  1967. NSString *symbolCode = PNLoggerSymbols.connection.reconnectedOnWakeUpTimerRequest;
  1968. if (isReconnectedBySSL) {
  1969. symbolCode = PNLoggerSymbols.connection.reconnectedBecauseOfSSLError;
  1970. }
  1971. else if (isReconnectedBySocket) {
  1972. symbolCode = PNLoggerSymbols.connection.reconnectedBecauseOfTemporaryConnectionIssues;
  1973. }
  1974. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1975. return @[symbolCode, (self.name ? self.name : self), @(self.state)];
  1976. }];
  1977. [self.delegate connection:self didReconnectToHostAfterError:self.configuration.origin
  1978. withBlock:streamConnecionCompletionBlock];
  1979. }
  1980. // Check whether connection has been reconnected by request or not
  1981. else if (isReconnecting) {
  1982. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  1983. return @[PNLoggerSymbols.connection.reconnected, (self.name ? self.name : self), @(isByUserRequest),
  1984. @(self.state)];
  1985. }];
  1986. [self.delegate connection:self didReconnectToHost:self.configuration.origin
  1987. withBlock:streamConnecionCompletionBlock];
  1988. }
  1989. else {
  1990. [self.delegate connection:self didConnectToHost:self.configuration.origin
  1991. withBlock:streamConnecionCompletionBlock];
  1992. }
  1993. }
  1994. }
  1995. }
  1996. - (void)handleStreamClose {
  1997. // This method should be launched only from within it's private queue
  1998. [self pn_scheduleOnPrivateQueueAssert];
  1999. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
  2000. return @[PNLoggerSymbols.connection.stream.closed, (self.name ? self.name : self), @(self.state)];
  2001. }];
  2002. // Ensure that both read and write streams reset before notify delegate about connection close event
  2003. if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnecting] &&
  2004. ![PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnected]) {
  2005. BOOL retriedConnection = self.connectionRetryCount != 0;
  2006. // Resetting some cached data
  2007. self.configurationRetryCount = 0;
  2008. [self stopTimeoutTimer];
  2009. BOOL isDisconnectedByServerRequest = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
  2010. PNByServerRequest, BITS_LIST_TERMINATOR];
  2011. BOOL isDisconnectedOnReset = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionError, PNByInternalRequest,
  2012. BITS_LIST_TERMINATOR];
  2013. BOOL isDisconnectedByWakeUpTimer = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
  2014. PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
  2015. BOOL isDisconnectedBySSL = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
  2016. BOOL isDisconnectedBySocket = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSocket,
  2017. BITS_LIST_TERMINATOR];
  2018. BOOL isDisconnectedOnError = [PNBitwiseHelper is:self.state containsBit:PNConnectionError];
  2019. BOOL isByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
  2020. BOOL shouldConnectOnDisconnect = [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnectOnDisconnect];
  2021. BOOL isReconnecting = [self shouldReconnect];
  2022. BOOL isSuspending = [self isSuspending];
  2023. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  2024. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
  2025. [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
  2026. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
  2027. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
  2028. [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamCleanAll, PNWriteStreamCleanAll, BITS_LIST_TERMINATOR];
  2029. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
  2030. if (isDisconnectedOnError) {
  2031. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionError];
  2032. }
  2033. void(^reconnectOnErrorBlock)(void) = ^{
  2034. // Attempt to restore connection after small delay defined in 'static' section of this class
  2035. __pn_desired_weak __typeof__ (self) weakSelf = self;
  2036. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPNConnectionRetryDelay * NSEC_PER_SEC));
  2037. dispatch_after(popTime, [self pn_privateQueue], ^{
  2038. __strong __typeof__(self) strongSelf = weakSelf;
  2039. // Check whether connection is still in bad state before issue connection
  2040. if ([PNBitwiseHelper is:strongSelf.state containsBit:PNConnectionError]) {
  2041. [PNLogger logConnectionInfoMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
  2042. return @[PNLoggerSymbols.connection.closedWithFurtherReconnectionBecauseOfError,
  2043. (strongSelf.name ? strongSelf.name : strongSelf), @(strongSelf.state)];
  2044. }];
  2045. [strongSelf resumeWakeUpTimer];
  2046. [strongSelf stopTimeoutTimer];
  2047. if (isByUserRequest) {
  2048. [PNLogger logConnectionInfoMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
  2049. return @[PNLoggerSymbols.connection.reconnectFromTheUserName,
  2050. (strongSelf.name ? strongSelf.name : strongSelf),
  2051. @(strongSelf.state)];
  2052. }];
  2053. [strongSelf connectWithResult:NULL];
  2054. }
  2055. else {
  2056. [strongSelf connectByInternalRequest:NULL];
  2057. }
  2058. }
  2059. });
  2060. };
  2061. if (retriedConnection) {
  2062. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2063. return @[PNLoggerSymbols.connection.closedOnRetryAttemptWithFurtherReconnection, (self.name ? self.name : self),
  2064. @(self.state)];
  2065. }];
  2066. }
  2067. if (shouldConnectOnDisconnect || isDisconnectedByServerRequest || isDisconnectedOnReset ||
  2068. isDisconnectedByWakeUpTimer || isDisconnectedBySSL || isDisconnectedBySocket || isReconnecting ||
  2069. retriedConnection) {
  2070. __block unsigned long flagsToEnableBack = 0;
  2071. dispatch_block_t connectionRestoreBlock = ^{
  2072. [self pn_dispatchBlock:^{
  2073. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
  2074. [self resumeWakeUpTimer];
  2075. if (isByUserRequest) {
  2076. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2077. return @[PNLoggerSymbols.connection.reconnectFromTheUserName, (self.name ? self.name : self),
  2078. @(self.state)];
  2079. }];
  2080. [self connectWithResult:^(__unused BOOL connecting) {
  2081. // Restore error state flags because of which connection should be restored back
  2082. [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
  2083. }];
  2084. }
  2085. else {
  2086. [self connectByInternalRequest:^(__unused BOOL connecting) {
  2087. // Restore error state flags because of which connection should be restored back
  2088. [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
  2089. }];
  2090. }
  2091. }];
  2092. };
  2093. // Check whether there was attempt to connect while was connection was in disconnection state
  2094. if (shouldConnectOnDisconnect) {
  2095. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2096. return @[PNLoggerSymbols.connection.closedWithFurtherConnectionOnUserRequest, (self.name ? self.name : self),
  2097. @(self.state)];
  2098. }];
  2099. connectionRestoreBlock();
  2100. }
  2101. else if (isDisconnectedByServerRequest) {
  2102. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2103. return @[PNLoggerSymbols.connection.closedOnExpectedCloseWithFurtherReconnection, (self.name ? self.name : self),
  2104. @(self.state)];
  2105. }];
  2106. [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByServerRequest, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
  2107. [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
  2108. // Notify delegate that connection has been terminated by server request
  2109. [self.delegate connection:self didDisconnectByServerRequestFromHost:self.configuration.origin
  2110. withBlock:connectionRestoreBlock];
  2111. }
  2112. else if (isDisconnectedOnReset) {
  2113. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2114. return @[PNLoggerSymbols.connection.closedOnConnectionResetWithFurtherReconnection, (self.name ? self.name : self),
  2115. @(self.state)];
  2116. }];
  2117. [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionError, BITS_LIST_TERMINATOR];
  2118. connectionRestoreBlock();
  2119. }
  2120. else if (isDisconnectedByWakeUpTimer || isDisconnectedBySSL || isDisconnectedBySocket) {
  2121. NSString *symbolCode = PNLoggerSymbols.connection.closedOnWakeUpTimerRequestWithFurtherReconnection;
  2122. [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
  2123. if (isDisconnectedBySSL) {
  2124. [PNBitwiseHelper clear:&flagsToEnableBack];
  2125. [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
  2126. symbolCode = PNLoggerSymbols.connection.closedBecauseOfSSLErrorWithFurtherReconnection;
  2127. }
  2128. else if (isDisconnectedBySocket) {
  2129. [PNBitwiseHelper clear:&flagsToEnableBack];
  2130. [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR];
  2131. symbolCode = PNLoggerSymbols.connection.closedBecauseOfTemporaryConnectionIssuesWithFurtherReconnection;
  2132. }
  2133. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2134. return @[symbolCode, (self.name ? self.name : self), @(self.state)];
  2135. }];
  2136. [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
  2137. // Notify delegate that connection will be restored because of reconnection request
  2138. [self.delegate connection:self willReconnectToHostAfterError:self.configuration.origin
  2139. withBlock:connectionRestoreBlock];
  2140. }
  2141. else if (isReconnecting) {
  2142. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2143. return @[PNLoggerSymbols.connection.closedWithFurtherReconnection, (self.name ? self.name : self),
  2144. @(self.state)];
  2145. }];
  2146. [PNBitwiseHelper addTo:&flagsToEnableBack bit:PNConnectionReconnection];
  2147. // Notify delegate that connection will be restored because of reconnection request
  2148. [self.delegate connection:self willReconnectToHost:self.configuration.origin
  2149. withBlock:connectionRestoreBlock];
  2150. }
  2151. else if (retriedConnection) {
  2152. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2153. return @[PNLoggerSymbols.connection.connectionRetry, (self.name ? self.name : self), @(self.connectionRetryCount),
  2154. @(kPNMaximumConnectionRetryCount), @(self.state)];
  2155. }];
  2156. connectionRestoreBlock();
  2157. }
  2158. }
  2159. // Check whether connection has been closed by user request or not
  2160. else if (isByUserRequest) {
  2161. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2162. return @[PNLoggerSymbols.connection.closedByUserRequest, (self.name ? self.name : self),
  2163. @(self.state)];
  2164. }];
  2165. if (!isDisconnectedOnError) {
  2166. self.connectionRetryCount = 0;
  2167. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
  2168. [self resumeWakeUpTimer];
  2169. [self.delegate connection:self didDisconnectFromHost:self.configuration.origin
  2170. withBlock:NULL];
  2171. }
  2172. else {
  2173. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2174. return @[PNLoggerSymbols.connection.closedBecauseOfError, (self.name ? self.name : self),
  2175. @(self.state)];
  2176. }];
  2177. reconnectOnErrorBlock();
  2178. }
  2179. }
  2180. // Disconnection has been done because of other reasons (configuration error / suspending)
  2181. else {
  2182. if (isSuspending) {
  2183. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2184. return @[PNLoggerSymbols.connection.suspended, (self.name ? self.name : self),
  2185. @(self.state)];
  2186. }];
  2187. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionSuspended];
  2188. [self suspendWakeUpTimer];
  2189. [self stopTimeoutTimer];
  2190. [self.delegate connectionDidSuspend:self withBlock:NULL];
  2191. }
  2192. else {
  2193. NSString *symbolCode = PNLoggerSymbols.connection.disconnected;
  2194. if (isDisconnectedOnError) {
  2195. symbolCode = PNLoggerSymbols.connection.disconnectedBecauseOfError;
  2196. }
  2197. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2198. return @[symbolCode, (self.name ? self.name : self), @(self.state)];
  2199. }];
  2200. // Check whether connection has been terminated because of error or not
  2201. if (isDisconnectedOnError) {
  2202. reconnectOnErrorBlock();
  2203. }
  2204. else {
  2205. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2206. return @[PNLoggerSymbols.connection.notifyDelegateAboutDisconnection, (self.name ? self.name : self),
  2207. @(self.state)];
  2208. }];
  2209. self.connectionRetryCount = 0;
  2210. [self.delegate connection:self didDisconnectFromHost:self.configuration.origin
  2211. withBlock:^{
  2212. [self pn_dispatchBlock:^{
  2213. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
  2214. [self resumeWakeUpTimer];
  2215. [self stopTimeoutTimer];
  2216. }];
  2217. }];
  2218. }
  2219. }
  2220. }
  2221. }
  2222. }
  2223. - (void)handleReadStreamHasData:(CFReadStreamRef)stream {
  2224. [self readStreamContent:stream];
  2225. }
  2226. - (void)handleWriteStreamCanAcceptData {
  2227. // This method should be launched only from within it's private queue
  2228. [self pn_scheduleOnPrivateQueueAssert];
  2229. if (!self.isWriteStreamCanHandleData) {
  2230. [self stopTimeoutTimer];
  2231. }
  2232. self.writeStreamCanHandleData = YES;
  2233. if (![PNBitwiseHelper is:self.state strictly:YES
  2234. containsBits:PNConnectionDisconnect, PNByServerRequest, BITS_LIST_TERMINATOR]) {
  2235. [self writeBufferContent];
  2236. }
  2237. }
  2238. - (void)handleRequestSendingCancelation {
  2239. // This method should be launched only from within it's private queue
  2240. [self pn_scheduleOnPrivateQueueAssert];
  2241. // Check whether data sending layer is processing some request or not
  2242. if ([PNBitwiseHelper is:self.state containsBit:PNSendingData] || self.writeBuffer != nil) {
  2243. NSString *interruptedRequestIdentifier = self.writeBuffer.requestIdentifier;
  2244. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2245. return @[PNLoggerSymbols.connection.unscheduleRequestProcessing, (self.name ? self.name : self),
  2246. (interruptedRequestIdentifier ? interruptedRequestIdentifier : [NSNull null]), @(self.state)];
  2247. }];
  2248. self.writeBuffer = nil;
  2249. [PNBitwiseHelper removeFrom:&self->_state bit:PNSendingData];
  2250. // Notify delegate about that request processing hasn't been completed
  2251. [self.dataSource connection:self didCancelRequestWithIdentifier:interruptedRequestIdentifier
  2252. withBlock:NULL];
  2253. }
  2254. }
  2255. - (void)handleStreamTimeout {
  2256. // This method should be launched only from within it's private queue
  2257. [self pn_scheduleOnPrivateQueueAssert];
  2258. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNByUserRequest, PNByServerRequest, PNByInternalRequest,
  2259. PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
  2260. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionSuspending, PNConnectionSuspended, PNConnectionResuming, BITS_LIST_TERMINATOR];
  2261. [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
  2262. [self reconnectWithBlock:NULL];
  2263. }
  2264. - (void)handleWakeUpTimer {
  2265. // This method should be launched only from within it's private queue
  2266. [self pn_scheduleOnPrivateQueueAssert];
  2267. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2268. return @[PNLoggerSymbols.connection.handleWakeUpTimer, (self.name ? self.name : self),
  2269. @(self.state)];
  2270. }];
  2271. // Check whether connection not connected
  2272. if ((![self isConnected] && ![self isConnecting]) ||
  2273. [PNBitwiseHelper is:self.state containsBit:PNConnectionWakeUpTimer]) {
  2274. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2275. return @[PNLoggerSymbols.connection.stillInBadState, (self.name ? self.name : self),
  2276. @(self.state)];
  2277. }];
  2278. unsigned long oldStates = self.state;
  2279. [self.delegate connection:self checkShouldRestoreConnection:^(BOOL shouldReconnect) {
  2280. [self pn_dispatchBlock:^{
  2281. unsigned long newStates = self.state;
  2282. BOOL stateChangedFromOutside = oldStates != newStates && ![PNBitwiseHelper is:oldStates
  2283. containsBit:PNByUserRequest] &&
  2284. [PNBitwiseHelper is:newStates containsBit:PNByUserRequest];
  2285. if (!stateChangedFromOutside) {
  2286. // Ask delegate on whether connection should be restored or not
  2287. if (shouldReconnect) {
  2288. BOOL actionByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
  2289. // Mark that since state fixing has been called from 'wake up' timer handler method, all further actions
  2290. // performed on internal code request
  2291. [PNBitwiseHelper removeFrom:&self->_state bits:PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
  2292. [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
  2293. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2294. return @[PNLoggerSymbols.connection.stateCanBeRecovered, (self.name ? self.name : self),
  2295. @(self.state)];
  2296. }];
  2297. // Check whether connection should be restored via '-reconnect' method or not
  2298. if ([self shouldReconnect]) {
  2299. [self reconnectWithBlock:NULL];
  2300. }
  2301. else if ([PNBitwiseHelper is:self.state containsBit:PNConnectionPrepareToConnect]) {
  2302. if (actionByUserRequest) {
  2303. [self connectWithResult:NULL];
  2304. }
  2305. else {
  2306. [self connectByInternalRequest:NULL];
  2307. }
  2308. }
  2309. else {
  2310. [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamCleanAll, PNWriteStreamCleanAll,
  2311. PNConnectionReconnection, BITS_LIST_TERMINATOR];
  2312. [self disconnectByInternalRequest];
  2313. }
  2314. }
  2315. else {
  2316. // Looks like connection can't be established, so there can be no 'connecting' state
  2317. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionConnecting, PNConnectionDisconnecting,
  2318. BITS_LIST_TERMINATOR];
  2319. }
  2320. }
  2321. else {
  2322. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2323. return @[PNLoggerSymbols.connection.wakeUpEventCanceledBecauseStateHasBeenAltered, (self.name ? self.name : self),
  2324. @(self.state)];
  2325. }];
  2326. }
  2327. }];
  2328. }];
  2329. }
  2330. }
  2331. - (NSString *)stringifyStreamStatus:(CFStreamStatus)status {
  2332. NSString *stringifiedStatus = @"NOTHING INTERESTING";
  2333. switch (status) {
  2334. case kCFStreamStatusNotOpen:
  2335. stringifiedStatus = @"STREAM NOT OPENED";
  2336. break;
  2337. case kCFStreamStatusOpening:
  2338. stringifiedStatus = @"STREAM IS OPENING";
  2339. break;
  2340. case kCFStreamStatusOpen:
  2341. stringifiedStatus = @"STREAM IS OPENED";
  2342. break;
  2343. case kCFStreamStatusReading:
  2344. stringifiedStatus = @"READING FROM STREAM";
  2345. break;
  2346. case kCFStreamStatusWriting:
  2347. stringifiedStatus = @"WRITING INTO STREAM";
  2348. break;
  2349. case kCFStreamStatusAtEnd:
  2350. stringifiedStatus = @"STREAM CAN'T READ/WRITE DATA";
  2351. break;
  2352. case kCFStreamStatusClosed:
  2353. stringifiedStatus = @"STREAM CLOSED";
  2354. break;
  2355. case kCFStreamStatusError:
  2356. stringifiedStatus = @"STREAM ERROR OCCURRED";
  2357. break;
  2358. }
  2359. return stringifiedStatus;
  2360. }
  2361. - (void)handleStreamError:(PNConnectionErrorStateFlag)failedStream fromBlock:(CFErrorRef(^)(void))errorBlock {
  2362. __block BOOL isErrorProcessed = NO;
  2363. void(^errorProcessingBlock)(CFErrorRef) = ^(CFErrorRef streamError){
  2364. [self pn_dispatchBlock:^{
  2365. CFErrorRef errorReference = streamError;
  2366. if (!isErrorProcessed) {
  2367. if (streamError == NULL) {
  2368. errorReference = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ECONNRESET, NULL);
  2369. }
  2370. // Mark that read stream caught and error
  2371. [PNBitwiseHelper addTo:&self->_state bit:failedStream];
  2372. [self handleStreamError:streamError shouldCloseConnection:YES];
  2373. isErrorProcessed = YES;
  2374. if (errorReference) {
  2375. CFRelease(errorReference);
  2376. }
  2377. }
  2378. else if (errorReference != NULL) {
  2379. if (errorReference) {
  2380. CFRelease(errorReference);
  2381. }
  2382. }
  2383. }];
  2384. };
  2385. // Sending error copy request to another thread to make sure that it won't block
  2386. // run-loop (it looks like in case if stream has been reset, CFReadStreamCopyError blocks code execution
  2387. // and may take significant amount of time to return control back).
  2388. dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  2389. // There is corner case because of which Apple's C API may block run-loop of the thread on which it has been
  2390. // called for very long period. In normal situation error fetching doesn't take more than few milliseconds.
  2391. // Delay for 0.4 second should be more than enough to fetch error and in case if it will take longer, secondary
  2392. // logic will be used to create custom error. In case of error client will have to perform handshacke again and it
  2393. // will take at least 250 ms for one side and 500ms for full round-trip (this is ethernet values). SSL handshake will
  2394. // require twice time from regular connection.
  2395. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  2396. errorProcessingBlock(NULL);
  2397. });
  2398. // Try to retrieve error
  2399. CFErrorRef error = errorBlock();
  2400. errorProcessingBlock(error);
  2401. });
  2402. }
  2403. - (void)handleStreamError:(CFErrorRef)error {
  2404. [self handleStreamError:error shouldCloseConnection:NO];
  2405. }
  2406. - (void)handleStreamError:(CFErrorRef)error shouldCloseConnection:(BOOL)shouldCloseConnection {
  2407. // This method should be launched only from within it's private queue
  2408. [self pn_scheduleOnPrivateQueueAssert];
  2409. if (error && CFErrorGetCode(error) != 0) {
  2410. NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
  2411. PNError *errorObject = [self processStreamError:error];
  2412. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2413. return @[PNLoggerSymbols.connection.error, (self.name ? self.name : self),
  2414. (errorObject ? errorObject : [NSNull null]), @((long long)CFErrorGetCode(error)),
  2415. (errorDomain ? errorDomain : [NSNull null]), @(shouldCloseConnection),
  2416. @(self.state)];
  2417. }];
  2418. // Check whether error is caused by SSL issues or not
  2419. if ([self isSecurityTransportError:error]) {
  2420. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2421. return @[PNLoggerSymbols.connection.errorOnSSLLevel, (self.name ? self.name : self), @(self.state)];
  2422. }];
  2423. if (![self isInternalSecurityTransportError:error]) {
  2424. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2425. return @[PNLoggerSymbols.connection.isSSLLevelReductionAllowed, (self.name ? self.name : self),
  2426. @(self.configuration.shouldReduceSecurityLevelOnError), @(self.state)];
  2427. }];
  2428. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2429. return @[PNLoggerSymbols.connection.isSSLDiscardingAllowed, (self.name ? self.name : self),
  2430. @(self.configuration.canIgnoreSecureConnectionRequirement), @(self.state)];
  2431. }];
  2432. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2433. return @[PNLoggerSymbols.connection.currentSSLConfigurationLevel, (self.name ? self.name : self),
  2434. @(self.sslConfigurationLevel), @(self.state)];
  2435. }];
  2436. // Checking whether user allowed to decrease security options and we can do it
  2437. if (self.configuration.shouldReduceSecurityLevelOnError &&
  2438. self.sslConfigurationLevel == PNConnectionSSLConfigurationStrict) {
  2439. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2440. return @[PNLoggerSymbols.connection.reduceSSLSecurityLevel, (self.name ? self.name : self),
  2441. @(self.state)];
  2442. }];
  2443. shouldCloseConnection = NO;
  2444. self.sslConfigurationLevel = PNConnectionSSLConfigurationBarelySecure;
  2445. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
  2446. [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
  2447. // Try to reconnect with new SSL security settings
  2448. [self reconnectWithBlock:NULL];
  2449. }
  2450. // Check whether connection can fallback and use plain HTTP connection w/o SSL
  2451. else if (self.configuration.canIgnoreSecureConnectionRequirement &&
  2452. self.sslConfigurationLevel == PNConnectionSSLConfigurationBarelySecure) {
  2453. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2454. return @[PNLoggerSymbols.connection.discardSSLLeyer, (self.name ? self.name : self), @(self.state)];
  2455. }];
  2456. shouldCloseConnection = NO;
  2457. self.sslConfigurationLevel = PNConnectionSSLConfigurationInsecure;
  2458. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
  2459. [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
  2460. // Try to reconnect with new SSL security settings
  2461. [self reconnectWithBlock:NULL];
  2462. }
  2463. }
  2464. else {
  2465. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2466. return @[PNLoggerSymbols.connection.internalSSLError, (self.name ? self.name : self), @(self.state)];
  2467. }];
  2468. shouldCloseConnection = NO;
  2469. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
  2470. [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
  2471. [self reconnectWithBlock:NULL];
  2472. }
  2473. }
  2474. else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX] ||
  2475. [errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
  2476. // Check whether connection configuration still valid or destruction process already started
  2477. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionConfigured]) {
  2478. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2479. return @[PNLoggerSymbols.connection.generalError, (self.name ? self.name : self), @(self.state)];
  2480. }];
  2481. // Check whether connection should be reconnected because of critical error
  2482. if ([self isConnectionIssuesError:error]) {
  2483. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2484. return @[PNLoggerSymbols.connection.internetConnectionFailure, (self.name ? self.name : self), @(self.state)];
  2485. }];
  2486. if ([self isConnectionUpLinkError:error]) {
  2487. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2488. return @[PNLoggerSymbols.connection.uplinkConnectionFailure, (self.name ? self.name : self), @(self.state)];
  2489. }];
  2490. }
  2491. if (![self isReconnecting]) {
  2492. if ([self canRetryConnection]) {
  2493. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2494. return @[PNLoggerSymbols.connection.connectionRetryAttemptIsPossible,
  2495. (self.name ? self.name : self), @(self.state)];
  2496. }];
  2497. shouldCloseConnection = NO;
  2498. [self retryConnection];
  2499. }
  2500. else {
  2501. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2502. return @[PNLoggerSymbols.connection.connectionRetryAttemptImpossible, (self.name ? self.name : self),
  2503. @(self.state)];
  2504. }];
  2505. // Mark that we should init streams close because of critical error
  2506. shouldCloseConnection = YES;
  2507. }
  2508. }
  2509. else {
  2510. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2511. return @[PNLoggerSymbols.connection.connectionRetryAttemptInProgress,
  2512. (self.name ? self.name : self), @(self.state)];
  2513. }];
  2514. shouldCloseConnection = NO;
  2515. }
  2516. }
  2517. if ([self isTemporaryError:error]) {
  2518. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
  2519. [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR];
  2520. if ([self isServerError:error]) {
  2521. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2522. return @[PNLoggerSymbols.connection.generalErrorBecauseOfServerActions, (self.name ? self.name : self),
  2523. @(self.state)];
  2524. }];
  2525. }
  2526. else {
  2527. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2528. return @[PNLoggerSymbols.connection.generalErrorOfTemporaryConnectionIssues, (self.name ? self.name : self),
  2529. @(self.state)];
  2530. }];
  2531. }
  2532. if (![self isReconnecting]) {
  2533. // Checking whether connection is able to perform another connection attempt or not
  2534. if ([self canRetryConnection]) {
  2535. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2536. return @[PNLoggerSymbols.connection.connectionRetryAttemptIsPossible,
  2537. (self.name ? self.name : self), @(self.state)];
  2538. }];
  2539. shouldCloseConnection = NO;
  2540. [self retryConnection];
  2541. }
  2542. else {
  2543. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2544. return @[PNLoggerSymbols.connection.connectionRetryAttemptImpossible, (self.name ? self.name : self),
  2545. @(self.state)];
  2546. }];
  2547. // Mark that we should init streams close because of critical error
  2548. shouldCloseConnection = YES;
  2549. }
  2550. }
  2551. else {
  2552. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2553. return @[PNLoggerSymbols.connection.connectionRetryAttemptInProgress,
  2554. (self.name ? self.name : self), @(self.state)];
  2555. }];
  2556. shouldCloseConnection = NO;
  2557. }
  2558. }
  2559. }
  2560. // Looks like connection already invalid and all corresponding actions in a queue.
  2561. else {
  2562. shouldCloseConnection = NO;
  2563. }
  2564. }
  2565. if (shouldCloseConnection) {
  2566. // Check whether we are tried to establish connection and some error occurred there
  2567. if ([self isConnecting]) {
  2568. if (![self isReconnecting]) {
  2569. shouldCloseConnection = (shouldCloseConnection && ![self canRetryConnection] ? YES : shouldCloseConnection);
  2570. if (!shouldCloseConnection) {
  2571. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2572. return @[PNLoggerSymbols.connection.connectionRetryAttemptIsPossible,
  2573. (self.name ? self.name : self), @(self.state)];
  2574. }];
  2575. [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR];
  2576. [self retryConnection];
  2577. }
  2578. else {
  2579. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionSocket];
  2580. }
  2581. }
  2582. else {
  2583. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2584. return @[PNLoggerSymbols.connection.connectionRetryAttemptInProgress,
  2585. (self.name ? self.name : self), @(self.state)];
  2586. }];
  2587. [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionSocket];
  2588. }
  2589. }
  2590. }
  2591. if (shouldCloseConnection) {
  2592. dispatch_block_t errorHandlingCompletionBlock = ^{
  2593. if ([self isConnected] && ![self isDisconnecting]) {
  2594. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2595. return @[PNLoggerSymbols.connection.closingConnectionBecauseOfError, (self.name ? self.name : self),
  2596. @(self.state)];
  2597. }];
  2598. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNReadStreamCleanAll, PNWriteStreamCleanAll,
  2599. PNConnectionDisconnect, PNByServerRequest, PNByInternalRequest,PNByUserRequest,
  2600. PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  2601. self.connectionRetryCount = 0;
  2602. [self.delegate connection:self willDisconnectFromHost:self.configuration.origin
  2603. withError:errorObject andBlock:^{
  2604. [self pn_dispatchBlock:^{
  2605. [self disconnectByInternalRequest];
  2606. }];
  2607. }];
  2608. }
  2609. else if ([self isConnecting]) {
  2610. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2611. return @[PNLoggerSymbols.connection.closingConnectionBecauseOfErrorWhileTriedToConnect,
  2612. (self.name ? self.name : self), @(self.state)];
  2613. }];
  2614. [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNReadStreamCleanAll, PNWriteStreamCleanAll,
  2615. PNConnectionDisconnect, PNByServerRequest, PNByInternalRequest,PNByUserRequest,
  2616. PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
  2617. self.connectionRetryCount = 0;
  2618. [self.delegate connection:self connectionDidFailToHost:self.configuration.origin
  2619. withError:errorObject andBlock:^{
  2620. [self pn_dispatchBlock:^{
  2621. [self disconnectOnInternalRequest];
  2622. }];
  2623. }];
  2624. }
  2625. };
  2626. // Check whether error occurred during data sending or not
  2627. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests] && self.writeBuffer) {
  2628. [self handleRequestProcessingError:error withBlock:^{
  2629. [self pn_dispatchBlock:^{
  2630. errorHandlingCompletionBlock();
  2631. }];
  2632. }];
  2633. }
  2634. else {
  2635. errorHandlingCompletionBlock();
  2636. }
  2637. }
  2638. }
  2639. }
  2640. - (void)handleStreamSetupError {
  2641. // This method should be launched only from within it's private queue
  2642. [self pn_scheduleOnPrivateQueueAssert];
  2643. [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
  2644. return @[PNLoggerSymbols.connection.stream.configurationError, (self.name ? self.name : self),
  2645. @(self.state)];
  2646. }];
  2647. // Check whether error occurred while connection attempted to connect to remote services w/o configuration on
  2648. // user request or not
  2649. if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNByUserRequest, PNConnectionPrepareToConnect, BITS_LIST_TERMINATOR]) {
  2650. // Prepare error message which will be sent to connection channel delegate
  2651. PNError *setupError = [PNError errorWithCode:kPNConnectionErrorOnSetup];
  2652. // Connection instance can't operate anymore, notify delegate about it's state
  2653. [self.delegate connection:self connectionDidFailToHost:self.configuration.origin
  2654. withError:setupError andBlock:NULL];
  2655. }
  2656. // Looks like error occurred during initial configuration or when reconnection has been called within library
  2657. else {
  2658. __pn_desired_weak __typeof__(self) weakSelf = self;
  2659. int64_t delay = 1;
  2660. if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionConfiguring, PNConnectionPrepareToConnect, BITS_LIST_TERMINATOR]) {
  2661. delay = kPNConnectionRetryDelay;
  2662. }
  2663. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delay * NSEC_PER_SEC));
  2664. void(^delayedBlock)(void) = ^{
  2665. __strong __typeof__(self) strongSelf = weakSelf;
  2666. // Check whether connection is still in bad state before issue connection
  2667. if ([PNBitwiseHelper is:strongSelf.state containsBit:PNConnectionConfiguring]) {
  2668. if (strongSelf.configurationRetryCount + 1 < kPNMaximumConfigurationRetryCount) {
  2669. [PNLogger logConnectionErrorMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
  2670. return @[PNLoggerSymbols.connection.stream.configurationRetryAttempt,
  2671. (strongSelf.name ? strongSelf.name : strongSelf),
  2672. @(strongSelf.state)];
  2673. }];
  2674. strongSelf.configurationRetryCount++;
  2675. // Check whether client configuration failed during connection attempt or not
  2676. if ([PNBitwiseHelper is:strongSelf.state strictly:YES containsBits:PNConnectionConfiguring, PNConnectionPrepareToConnect,
  2677. BITS_LIST_TERMINATOR]) {
  2678. [strongSelf connectByInternalRequest:NULL];
  2679. }
  2680. else {
  2681. [strongSelf prepareStreams];
  2682. }
  2683. }
  2684. // Looks like connection instance can't retry anymore because it reached maximum retry count
  2685. else {
  2686. [PNLogger logConnectionErrorMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
  2687. return @[PNLoggerSymbols.connection.stream.configurationRetryAttemptsExceeded,
  2688. (strongSelf.name ? strongSelf.name : strongSelf), @(strongSelf.state)];
  2689. }];
  2690. strongSelf.configurationRetryCount = 0;
  2691. strongSelf.connectionRetryCount = 0;
  2692. // Terminate operation of all streams and buffers (clean up)
  2693. [strongSelf destroyStreams];
  2694. [PNBitwiseHelper clear:&strongSelf->_state];
  2695. [PNBitwiseHelper addTo:&strongSelf->_state bit:PNConnectionDisconnected];
  2696. // Connection instance can't operate anymore, notify delegate about it's state
  2697. [strongSelf.delegate connectionConfigurationDidFail:strongSelf];
  2698. }
  2699. }
  2700. };
  2701. dispatch_after(popTime, [self pn_privateQueue], delayedBlock);
  2702. }
  2703. }
  2704. - (void)handleRequestProcessingError:(CFErrorRef)error withBlock:(dispatch_block_t)notifyCompletionBlock {
  2705. // This method should be launched only from within it's private queue
  2706. [self pn_scheduleOnPrivateQueueAssert];
  2707. if (error && CFErrorGetCode(error) != 0) {
  2708. if (self.writeBuffer && [PNBitwiseHelper is:self.state containsBit:PNSendingData]) {
  2709. [self.dataSource connection:self didFailToProcessRequestWithIdentifier:self.writeBuffer.requestIdentifier
  2710. error:[self processStreamError:error] withBlock:notifyCompletionBlock];
  2711. }
  2712. }
  2713. }
  2714. #pragma mark - Misc methods
  2715. - (void)appendToReadBuffer:(const void *)data length:(NSUInteger)length {
  2716. // This method should be launched only from within it's private queue
  2717. [self pn_scheduleOnPrivateQueueAssert];
  2718. if (self.readBuffer == NULL) {
  2719. self.readBuffer = [[NSMutableData alloc] initWithBytes:data length:length];
  2720. }
  2721. else {
  2722. [self.readBuffer appendBytes:data length:length];
  2723. }
  2724. }
  2725. - (void)startTimeoutTimer {
  2726. // This method should be launched only from within it's private queue
  2727. [self pn_scheduleOnPrivateQueueAssert];
  2728. [self stopTimeoutTimer:YES];
  2729. if (self.connectionTimeoutTimer == NULL || dispatch_source_testcancel(self.connectionTimeoutTimer) > 0) {
  2730. dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
  2731. [self pn_privateQueue]);
  2732. self.connectionTimeoutTimer = timerSource;
  2733. __pn_desired_weak __typeof__(self) weakSelf = self;
  2734. dispatch_source_set_event_handler(timerSource, ^{
  2735. __strong __typeof__(self) strongSelf = weakSelf;
  2736. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2737. return @[PNLoggerSymbols.connection.handleTimeoutTimer,
  2738. (strongSelf.name ? strongSelf.name : strongSelf), @(strongSelf.state)];
  2739. }];
  2740. [strongSelf stopTimeoutTimer];
  2741. [strongSelf handleStreamTimeout];
  2742. });
  2743. dispatch_source_set_cancel_handler(timerSource, ^{
  2744. [PNDispatchHelper release:timerSource];
  2745. });
  2746. dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPNConnectionTimeout * NSEC_PER_SEC));
  2747. dispatch_source_set_timer(timerSource, start, (int64_t)(kPNConnectionTimeout * NSEC_PER_SEC), NSEC_PER_SEC);
  2748. dispatch_resume(timerSource);
  2749. }
  2750. }
  2751. - (void)stopTimeoutTimer {
  2752. [self stopTimeoutTimer:NO];
  2753. }
  2754. - (void)stopTimeoutTimer:(BOOL)__unused forRelaunch {
  2755. // This method should be launched only from within it's private queue
  2756. [self pn_scheduleOnPrivateQueueAssert];
  2757. if (self.connectionTimeoutTimer != NULL && dispatch_source_testcancel(self.connectionTimeoutTimer) == 0) {
  2758. dispatch_source_cancel(self.connectionTimeoutTimer);
  2759. }
  2760. self.connectionTimeoutTimer = NULL;
  2761. }
  2762. - (void)startWakeUpTimer {
  2763. // This method should be launched only from within it's private queue
  2764. [self pn_scheduleOnPrivateQueueAssert];
  2765. if (self.wakeUpTimer == NULL || dispatch_source_testcancel(self.wakeUpTimer) > 0) {
  2766. self.wakeUpTimerSuspended = YES;
  2767. dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
  2768. [self pn_privateQueue]);
  2769. self.wakeUpTimer = timerSource;
  2770. __pn_desired_weak __typeof__(self) weakSelf = self;
  2771. dispatch_source_set_event_handler(timerSource, ^{
  2772. __strong __typeof__(self) strongSelf = weakSelf;
  2773. [strongSelf handleWakeUpTimer];
  2774. });
  2775. dispatch_source_set_cancel_handler(timerSource, ^{
  2776. __strong __typeof__(self) strongSelf = weakSelf;
  2777. [PNDispatchHelper release:timerSource];
  2778. strongSelf.wakeUpTimerSuspended = NO;
  2779. });
  2780. [self resetWakeUpTimer];
  2781. }
  2782. if (self.isWakeUpTimerSuspended) {
  2783. [self resumeWakeUpTimer];
  2784. }
  2785. }
  2786. - (void)suspendWakeUpTimer {
  2787. // This method should be launched only from within it's private queue
  2788. [self pn_scheduleOnPrivateQueueAssert];
  2789. if (self.wakeUpTimer != NULL && dispatch_source_testcancel(self.wakeUpTimer) == 0) {
  2790. if (!self.isWakeUpTimerSuspended) {
  2791. self.wakeUpTimerSuspended = YES;
  2792. dispatch_suspend(self.wakeUpTimer);
  2793. }
  2794. }
  2795. else {
  2796. self.wakeUpTimerSuspended = NO;
  2797. }
  2798. }
  2799. - (void)resumeWakeUpTimer {
  2800. // This method should be launched only from within it's private queue
  2801. [self pn_scheduleOnPrivateQueueAssert];
  2802. if (self.wakeUpTimer == NULL || dispatch_source_testcancel(self.wakeUpTimer) > 0) {
  2803. [self startWakeUpTimer];
  2804. }
  2805. else {
  2806. if (self.isWakeUpTimerSuspended) {
  2807. self.wakeUpTimerSuspended = NO;
  2808. [self resetWakeUpTimer];
  2809. dispatch_resume(self.wakeUpTimer);
  2810. }
  2811. }
  2812. }
  2813. - (void)stopWakeUpTimer {
  2814. // This method should be launched only from within it's private queue
  2815. [self pn_scheduleOnPrivateQueueAssert];
  2816. if (self.wakeUpTimer != NULL && dispatch_source_testcancel(self.wakeUpTimer) == 0) {
  2817. if (self.isWakeUpTimerSuspended) {
  2818. [self resumeWakeUpTimer];
  2819. }
  2820. self.wakeUpTimerSuspended = NO;
  2821. dispatch_source_cancel(self.wakeUpTimer);
  2822. }
  2823. self.wakeUpTimer = NULL;
  2824. }
  2825. - (void)resetWakeUpTimer {
  2826. dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPNWakeUpTimerInterval * NSEC_PER_SEC));
  2827. dispatch_source_set_timer(self.wakeUpTimer, start, (int64_t)(kPNWakeUpTimerInterval * NSEC_PER_SEC), NSEC_PER_SEC);
  2828. }
  2829. #ifndef __clang_analyzer__
  2830. - (CFStreamClientContext)streamClientContext {
  2831. PNContextInformation *information = [PNContextInformation contextWithObject:self];
  2832. return (CFStreamClientContext){0, (__bridge_retained void *)information, NULL,
  2833. connectionContextInformationReleaseCallBack, NULL};
  2834. }
  2835. #endif
  2836. - (CFMutableDictionaryRef)streamSecuritySettings {
  2837. // This method should be launched only from within it's private queue
  2838. [self pn_scheduleOnPrivateQueueAssert];
  2839. if (self.configuration.shouldUseSecureConnection && _streamSecuritySettings == NULL &&
  2840. self.sslConfigurationLevel != PNConnectionSSLConfigurationInsecure) {
  2841. // Configure security settings
  2842. _streamSecuritySettings = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 6, NULL, NULL);
  2843. CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
  2844. CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLPeerName, kCFNull);
  2845. if (self.sslConfigurationLevel == PNConnectionSSLConfigurationStrict) {
  2846. CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLValidatesCertificateChain, kCFBooleanTrue);
  2847. }
  2848. else {
  2849. CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
  2850. }
  2851. }
  2852. else if (_streamSecuritySettings != NULL && (!self.configuration.shouldUseSecureConnection ||
  2853. self.sslConfigurationLevel == PNConnectionSSLConfigurationInsecure)) {
  2854. CFRelease(_streamSecuritySettings);
  2855. _streamSecuritySettings = NULL;
  2856. }
  2857. return _streamSecuritySettings;
  2858. }
  2859. - (void)retrieveSystemProxySettings {
  2860. // This method should be launched only from within it's private queue
  2861. [self pn_scheduleOnPrivateQueueAssert];
  2862. #ifdef PN_SOCKET_PROXY_ENABLED
  2863. if (self.proxySettings == NULL) {
  2864. #if PN_SOCKET_PROXY_ENABLED == 1
  2865. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2866. return @[PNLoggerSymbols.connection.fetchingProxyConfiguration, (self.name ? self.name : self),
  2867. @(self.state)];
  2868. }];
  2869. self.proxySettings = @{(__bridge NSString *)kCFStreamPropertySOCKSProxyHost:PN_SOCKET_PROXY_HOST,
  2870. (__bridge NSString *)kCFStreamPropertySOCKSProxyPort:PN_SOCKET_PROXY_PORT};
  2871. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2872. return @[PNLoggerSymbols.connection.proxyConfigurationInformation, (self.name ? self.name : self),
  2873. (self.proxySettings ? self.proxySettings : [NSNull null]), @(self.state)];
  2874. }];
  2875. #else
  2876. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  2877. return @[PNLoggerSymbols.connection.proxyConfigurationNotRequired, (self.name ? self.name : self),
  2878. @(self.state)];
  2879. }];
  2880. #endif // PN_SOCKET_PROXY_ENABLED
  2881. }
  2882. #endif // PN_SOCKET_PROXY_ENABLED
  2883. }
  2884. - (PNError *)processStreamError:(CFErrorRef)error {
  2885. PNError *errorInstance = nil;
  2886. if (error) {
  2887. NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
  2888. if ([self isConnectionIssuesError:error]) {
  2889. int errorCode = kPNClientConnectionClosedOnInternetFailureError;
  2890. if (self.writeBuffer != nil && [self.writeBuffer hasData] && self.writeBuffer.isSendingBytes) {
  2891. errorCode = kPNRequestExecutionFailedOnInternetFailureError;
  2892. }
  2893. errorInstance = [PNError errorWithCode:errorCode];
  2894. }
  2895. else if ([self isSecurityTransportError:error]) {
  2896. errorInstance = [PNError errorWithCode:kPNClientConnectionClosedOnSSLNegotiationFailureError];
  2897. }
  2898. else if ([self isTemporaryError:error]) {
  2899. NSInteger errorCode = kPNClientConnectionClosedOnSocketsError;
  2900. if ([self isServerError:error]) {
  2901. errorCode = kPNClientConnectionClosedOnServerRequestError;
  2902. }
  2903. errorInstance = [PNError errorWithCode:errorCode];
  2904. }
  2905. else {
  2906. errorInstance = [PNError errorWithDomain:errorDomain code:CFErrorGetCode(error) userInfo:nil];
  2907. }
  2908. }
  2909. return errorInstance;
  2910. }
  2911. - (NSString *)stateDescription {
  2912. NSMutableString *connectionState = [[NSMutableString alloc] initWithFormat:@"\n[CONNECTION::%@ STATE DESCRIPTION",
  2913. self.name ? self.name : self];
  2914. if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConfiguring]) {
  2915. [connectionState appendFormat:@"\n- READ STREAM CONFIGURATION..."];
  2916. }
  2917. if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConfiguring]) {
  2918. [connectionState appendFormat:@"\n- WRITE STREAM CONFIGURATION..."];
  2919. }
  2920. if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConfigured]) {
  2921. [connectionState appendFormat:@"\n- READ STREAM CONFIGURED"];
  2922. }
  2923. if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConfigured]) {
  2924. [connectionState appendFormat:@"\n- WRITE STREAM CONFIGURED"];
  2925. }
  2926. NSString *actionSource = @"";
  2927. NSString *actionSourceReason = @"";
  2928. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionWakeUpTimer]) {
  2929. actionSource = @"WAKE UP TIMER";
  2930. actionSourceReason = @" (BY WAKE UP TIMER REQUEST)";
  2931. }
  2932. else if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSSL]) {
  2933. actionSource = @"SSL LAYER";
  2934. actionSourceReason = @" (BY SSL LAYER REQUEST)";
  2935. }
  2936. else if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSocket]) {
  2937. actionSource = @"SOCKET LAYER";
  2938. actionSourceReason = @" (BY SOCKET LAYER REQUEST)";
  2939. }
  2940. [connectionState appendFormat:@"\n- ACTION SOURCE: %@...", actionSource];
  2941. if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConnecting]) {
  2942. [connectionState appendFormat:@"\n- READ STREAM CONNECTING%@...", actionSourceReason];
  2943. }
  2944. if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConnecting]) {
  2945. [connectionState appendFormat:@"\n- WRITE STREAM CONNECTING%@...", actionSourceReason];
  2946. }
  2947. if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConnected]) {
  2948. [connectionState appendFormat:@"\n- READ STREAM CONNECTED"];
  2949. }
  2950. if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConnected]) {
  2951. [connectionState appendFormat:@"\n- WRITE STREAM CONNECTED"];
  2952. }
  2953. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionPrepareToConnect]) {
  2954. [connectionState appendFormat:@"\n- PREPARING TO CONNECT..."];
  2955. }
  2956. if ([PNBitwiseHelper is:self.state containsBit:PNByInternalRequest]) {
  2957. [connectionState appendFormat:@"\n- CURRENT ACTION PERFORMED BY INTERNAL REQUEST"];
  2958. }
  2959. if ([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]) {
  2960. [connectionState appendFormat:@"\n- CURRENT ACTION PERFORMED BY USER REQUEST"];
  2961. }
  2962. if ([PNBitwiseHelper is:self.state containsBit:PNByServerRequest]) {
  2963. [connectionState appendFormat:@"\n- CONNECTION CLOSE WAS EXPECTED (PROBABLY SERVER DOESN'T SUPPORT "
  2964. "'keep-alive' CONNECTION TYPE)"];
  2965. }
  2966. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionResuming]) {
  2967. [connectionState appendFormat:@"\n- RESUMING..."];
  2968. }
  2969. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionReconnect]) {
  2970. [connectionState appendFormat:@"\n- RECONNECTING..."];
  2971. }
  2972. if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamDisconnecting]) {
  2973. [connectionState appendFormat:@"\n- READ STREAM DISCONNECTING%@...", actionSourceReason];
  2974. }
  2975. if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamDisconnecting]) {
  2976. [connectionState appendFormat:@"\n- WRITE STREAM DISCONNECTING%@...", actionSourceReason];
  2977. }
  2978. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSuspending]) {
  2979. [connectionState appendFormat:@"\n- SUSPENDING..."];
  2980. }
  2981. if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamDisconnected]) {
  2982. [connectionState appendFormat:@"\n- READ STREAM DISCONNECTED"];
  2983. }
  2984. if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamDisconnected]) {
  2985. [connectionState appendFormat:@"\n- WRITE STREAM DISCONNECTED"];
  2986. }
  2987. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionReconnectOnDisconnect]) {
  2988. [connectionState appendFormat:@"\n- WAITING FOR DISCONNECTION TO CONNECT BACK"];
  2989. }
  2990. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSuspended]) {
  2991. [connectionState appendFormat:@"\n- SUSPENDED"];
  2992. }
  2993. if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
  2994. [connectionState appendFormat:@"\n- REQUEST PROCESSING ENABLED"];
  2995. }
  2996. if ([PNBitwiseHelper is:self.state containsBit:PNSendingData]) {
  2997. [connectionState appendFormat:@"\n- SENDING DATA"];
  2998. }
  2999. if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamError]) {
  3000. [connectionState appendFormat:@"\n- READ STREAM ERROR"];
  3001. }
  3002. if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamError]) {
  3003. [connectionState appendFormat:@"\n- WRITE STREAM ERROR"];
  3004. }
  3005. return connectionState;
  3006. }
  3007. #pragma mark - Memory management
  3008. - (void)prepareForTermination {
  3009. [self pn_ignorePrivateQueueRequirement];
  3010. [self stopWakeUpTimer];
  3011. [self stopTimeoutTimer];
  3012. }
  3013. - (void)dealloc {
  3014. [self pn_ignorePrivateQueueRequirement];
  3015. // Closing all streams and free up resources which was allocated for their support
  3016. [self destroyStreams];
  3017. [self stopWakeUpTimer];
  3018. [self stopTimeoutTimer];
  3019. self.delegate = nil;
  3020. self.proxySettings = nil;
  3021. [self pn_destroyPrivateDispatchQueue];
  3022. [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
  3023. return @[PNLoggerSymbols.connection.destroyed, (self->_name ? self->_name : self),
  3024. @(self->_state)];
  3025. }];
  3026. if (_streamSecuritySettings) {
  3027. CFRelease(_streamSecuritySettings);
  3028. }
  3029. }
  3030. #pragma mark -
  3031. @end