/Pods/PubNub/PubNub/PubNub/PubNub/Network/Transport/PNConnection.m
Objective C | 4350 lines | 2632 code | 1316 blank | 402 comment | 512 complexity | 05902f986f242494fa4fae747c1cd8e4 MD5 | raw file
Possible License(s): JSON, BSD-3-Clause, 0BSD
- //
- // PNConnection.m
- // pubnub
- //
- // This is core class for communication over
- // the network with PubNub services.
- // It allow to establish socket connection and
- // organize write packet requests into FIFO queue.
- //
- // Created by Sergey Mamontov on 12/10/12.
- //
- //
- #import "PNConnection.h"
- #import <objc/runtime.h>
- #import <SystemConfiguration/SystemConfiguration.h>
- #import <Security/SecureTransport.h>
- #import "PNConnection+Protected.h"
- #import "PNResponseDeserialize.h"
- #import "NSObject+PNAdditions.h"
- #import "PNContextInformation.h"
- #import "PNLogger+Protected.h"
- #import "PNResponseProtocol.h"
- #import "PubNub+Protected.h"
- #import "PNLoggerSymbols.h"
- #import "PNWriteBuffer.h"
- #import "PNHelper.h"
- // ARC check
- #if !__has_feature(objc_arc)
- #error PubNub connection must be built with ARC.
- // You can turn on ARC for only PubNub files by adding '-fobjc-arc' to the build phase for each of its files.
- #endif
- #pragma mark Structures
- typedef NS_OPTIONS(NSUInteger, PNConnectionActionSourceFlag) {
- // Flag which allow to set action has been generated by 'wake up' timer or not
- PNConnectionWakeUpTimer = 1 << 0,
- // Flag which allow to set whether action has been generated by SSL layer from error handling or not
- PNConnectionSSL = 1 << 1,
- // Flag which allow to set whether action has been generated from socket error handling or not
- PNConnectionSocket = 1 << 2
- };
- typedef NS_OPTIONS(NSUInteger, PNConnectionActionFlag) {
-
- // Flag which allow to set whether client is about to reconnect or not
- PNConnectionWillReconnect = 1 << 3,
-
- // Flag which allow to set whether client is reconnecting at this moment or not
- PNConnectionReconnect = 1 << 4,
- // Flag which allow to set whether client should connect back as soon as disconnection will be completed or not
- PNConnectionReconnectOnDisconnect = 1 << 5,
- // Flag which allow to set whether client should disconnect or not
- PNConnectionDisconnect = 1 << 6
- };
- typedef NS_OPTIONS(NSUInteger, PNConnectionActionOwnerFlag) {
- // Flag which allow to set whether action on connection has been triggered by user or not
- PNByUserRequest = 1 << 7,
- // Flag which allow to set whether action on connection has been triggered by internal code or not
- PNByInternalRequest = 1 << 8,
- // Flag which allow to set whether action on connection has been triggered by server or not
- PNByServerRequest = 1 << 9
- };
- typedef NS_OPTIONS(NSUInteger, PNConnectionStateFlag) {
- // Flag which allow to set whether read stream configuration started or not
- PNReadStreamConfiguring = 1 << 10,
- // Flag which allow to set whether write stream configuration started or not
- PNWriteStreamConfiguring = 1 << 11,
- // Flag which allow to set whether connection configuration started or not
- PNConnectionConfiguring = (PNReadStreamConfiguring | PNWriteStreamConfiguring),
- // Flag which allow to set whether read stream configured or not
- PNReadStreamConfigured = 1 << 12,
- // Flag which allow to set whether write stream configured or not
- PNWriteStreamConfigured = 1 << 13,
- // Flag which allow to set whether connection configured or not
- PNConnectionConfigured = (PNReadStreamConfigured | PNWriteStreamConfigured),
- // Flag which allow to set whether read stream is connecting right now or not
- PNReadStreamConnecting = 1 << 14,
- // Flag which allow to set whether write stream is connecting right now or not
- PNWriteStreamConnecting = 1 << 15,
- // Flag which allow to set whether client is connecting at this moment or not
- PNConnectionConnecting = (PNReadStreamConnecting | PNWriteStreamConnecting),
- // Flag which allow to set whether read stream is connected right now or not
- PNReadStreamConnected = 1 << 16,
- // Flag which allow to set whether write stream is connected right now or not
- PNWriteStreamConnected = 1 << 17,
- // Flag which allow to set whether connection channel is preparing to establish connection
- PNConnectionPrepareToConnect = 1 << 18,
- // Flag which allow to set whether client is connected or not
- PNConnectionConnected = (PNReadStreamConnected | PNWriteStreamConnected),
- // Flag which allow to set whether connection is suspended or not or not
- PNConnectionResuming = 1 << 19,
- // Flag which allow to set whether read stream is disconnecting right now or not
- PNReadStreamDisconnecting = 1 << 20,
- // Flag which allow to set whether write stream is disconnecting right now or not
- PNWriteStreamDisconnecting = 1 << 21,
- // Flag which allow to set whether client is disconnecting at this moment or not
- PNConnectionDisconnecting = (PNReadStreamDisconnecting | PNWriteStreamDisconnecting),
- // Flag which allow to set whether connection is suspending or not or not
- PNConnectionSuspending = 1 << 22,
- // Flag which allow to set whether read stream is disconnected right now or not
- PNReadStreamDisconnected = 1 << 23,
- // Flag which allow to set whether write stream is disconnected right now or not
- PNWriteStreamDisconnected = 1 << 24,
- // Flag which allow to set whether client is disconnected at this moment or not
- PNConnectionDisconnected = (PNReadStreamDisconnected | PNWriteStreamDisconnected),
- // Flag which stores all states which is responsible for connection 'reconnect' state
- PNConnectionReconnection = (PNConnectionWillReconnect | PNConnectionReconnect | PNConnectionReconnectOnDisconnect),
- // Flag which allow to set whether connection is suspended or not or not
- PNConnectionSuspended = 1 << 25,
- // Flag which allow to set whether connection should schedule next requests or not
- PNConnectionProcessingRequests = 1 << 26
- };
- typedef NS_OPTIONS(NSUInteger, PNConnectionDataSendingStateFlag) {
- // Flag which allow to set whether action on connection has been triggered by user or not
- PNSendingData = 1 << 27
- };
- typedef NS_OPTIONS(NSUInteger, PNConnectionErrorStateFlag) {
- // Flag which allow to set whether error occurred on read stream or not
- PNReadStreamError = 1 << 28,
- // Flag which allow to set whether error occurred on write stream or not
- PNWriteStreamError = 1 << 29,
- // Flag which allow to set whether client is experiencing some error or not
- PNConnectionError = (PNReadStreamError | PNWriteStreamError)
- };
- typedef NS_OPTIONS(NSUInteger, PNConnectionCleanStateFlag) {
- // Flag which can be used to clean configuration states related to read stream
- PNReadStreamCleanConfiguration = (PNReadStreamConfiguring | PNReadStreamConfigured),
- // Flag which can be used to clean connection states related to read stream
- PNReadStreamCleanConnection = (PNReadStreamConnecting | PNReadStreamConnected),
- // Flag which can be used to clean connection states related to read stream
- PNReadStreamCleanDisconnection = (PNReadStreamDisconnecting | PNReadStreamDisconnected),
- // Flag which can be used to clean all states related to read stream
- PNReadStreamCleanAll = (PNReadStreamCleanConfiguration | PNReadStreamCleanConnection |
- PNReadStreamCleanDisconnection | PNReadStreamError),
- // Flag which can be used to clean configuration states related to write stream
- PNWriteStreamCleanConfiguration = (PNWriteStreamConfiguring | PNWriteStreamConfigured),
- // Flag which can be used to clean connection states related to write stream
- PNWriteStreamCleanConnection = (PNWriteStreamConnecting | PNWriteStreamConnected),
- // Flag which can be used to clean connection states related to write stream
- PNWriteStreamCleanDisconnection = (PNWriteStreamDisconnecting | PNWriteStreamDisconnected),
- // Flag which can be used to clean all states related to write stream
- PNWriteStreamCleanAll = (PNWriteStreamCleanConfiguration | PNWriteStreamCleanConnection |
- PNWriteStreamCleanDisconnection | PNWriteStreamError),
- // Flag which allow to clean up connection 'reconnection' state
- PNConnectionCleanReconnection = PNConnectionReconnection,
- // Flag which allow to set whether client is experiencing some error or not
- PNConnectionErrorCleanAll = (PNReadStreamError | PNWriteStreamError)
- };
- typedef enum _PNConnectionSSLConfigurationLevel {
- // This option will check all information on remote origin SSL certificate to ensure in authority
- PNConnectionSSLConfigurationStrict,
- // This option will skip most of validations and as fact will allow to work with server which uses invalid SSL
- // certificate or certificate from another server
- PNConnectionSSLConfigurationBarelySecure,
- // This option will tell that connection should be opened w/o SSL (if user won't to discard security options)
- PNConnectionSSLConfigurationInsecure,
- } PNConnectionSSLConfigurationLevel;
- struct PNConnectionIdentifiersStruct PNConnectionIdentifiers = {
-
- .messagingConnection = @"PNMessagingConnectionIdentifier",
- .serviceConnection = @"PNServiceConnectionIdentifier"
- };
- #pragma mark - Static
- // Delay which is used by wake up timer to fire
- static int64_t const kPNWakeUpTimerInterval = 5;
- // Default origin host connection port
- static UInt32 const kPNOriginConnectionPort = 80;
- // Default origin host SSL connection port
- static UInt32 const kPNOriginSSLConnectionPort = 443;
- // Default data buffer size (Default: 32kb)
- static int const kPNStreamBufferSize = 32768;
- // Default connection attempt timeout (Default: 10s)
- static int64_t const kPNConnectionTimeout = 10;
- // Delay after which connection should retry
- static int64_t const kPNConnectionRetryDelay = 2;
- static NSTimeInterval const kPNConnectionRetryFastDelay = 0.1f;
- // Maximum retry count which can be performed for configuration operation
- static NSUInteger const kPNMaximumConfigurationRetryCount = 3;
- // Maximum connection retry count which can be performed before library will report connection error
- static NSUInteger const kPNMaximumConnectionRetryCount = 3;
- #pragma mark - Private interface methods
- @interface PNConnection ()
- #pragma mark - Properties
- // Stores connection name (identifier)
- @property (nonatomic, copy) NSString *name;
- // Connection configuration information
- @property (nonatomic, strong) PNConfiguration *configuration;
- // Stores reference on response deserializer which will parse response into objects array and update provided data to
- // insert offset on amount of parsed data
- @property (nonatomic, strong) PNResponseDeserialize *deserializer;
- /**
- @brief Stores reference on set of buffers retrieved from server.
- @discussion Data stored in this buffer till de-serializer will be able to pull completed packet
- from it and notify about amount of data which it processed.
-
- @since 3.7.10
- */
- @property (nonatomic, strong) NSMutableData *readBuffer;
- @property (nonatomic, copy) NSData *notProcessedReadBuffer;
- // Stores reference on buffer which should be sent to the PubNub service via socket
- @property (nonatomic, strong) PNWriteBuffer *writeBuffer;
- @property (nonatomic, assign) NSUInteger configurationRetryCount;
- @property (nonatomic, assign) NSUInteger connectionRetryCount;
- // Stores connection channel state
- @property (nonatomic, assign) unsigned long state;
- // Stores reference on timer which should awake connection channel if it doesn't reconnect back because of some
- // race of states and conditions
- @property (nonatomic, pn_dispatch_property_ownership) dispatch_source_t wakeUpTimer;
- @property (nonatomic, assign, getter = isWakeUpTimerSuspended) BOOL wakeUpTimerSuspended;
- @property (nonatomic, pn_dispatch_property_ownership) dispatch_source_t connectionTimeoutTimer;
- // Socket streams and state
- @property (nonatomic, assign) CFReadStreamRef socketReadStream;
- @property (nonatomic, assign) CFWriteStreamRef socketWriteStream;
- @property (nonatomic, assign, getter = isWriteStreamCanHandleData) BOOL writeStreamCanHandleData;
- @property (nonatomic, assign) BOOL fetchingDataForSending;
- // Socket streams configuration and security
- @property (nonatomic, copy) NSDictionary *proxySettings;
- @property (nonatomic, assign) CFMutableDictionaryRef streamSecuritySettings;
- @property (nonatomic, assign) PNConnectionSSLConfigurationLevel sslConfigurationLevel;
- #pragma mark - Instance methods
- /**
- * Perform connection initialization with user-provided configuration (they will be obtained from PubNub client)
- */
- - (id)initWithConfiguration:(PNConfiguration *)configuration andIdentifier:(NSString *)identifier;
- #pragma mark - Streams management methods
- /**
- * Will create read/write pair streams to specific host at
- */
- - (BOOL)prepareStreams;
- - (void)disconnectOnInternalRequest;
- /**
- * Will destroy both read and write streams
- */
- - (void)destroyStreams;
- /**
- * Allow to configure read stream with set of parameters like:
- * - proxy
- * - security (SSL)
- * If stream already configured, it won't accept any new settings.
- */
- - (void)configureReadStream:(CFReadStreamRef)readStream;
- - (void)openReadStream:(CFReadStreamRef)readStream;
- - (void)disconnectReadStream:(CFReadStreamRef)readStream;
- - (void)destroyReadStream:(CFReadStreamRef)readStream;
- /**
- * Process response which was fetched from read stream so far
- */
- - (void)processResponse;
- /**
- * Read out content which is waiting in read stream
- */
- - (void)readStreamContent:(CFReadStreamRef)stream;
- /**
- * Allow to complete write stream configuration (additional settings will be transferred from paired read stream on
- * configuration). If stream already configured, it won't accept any new settings.
- */
- - (void)configureWriteStream:(CFWriteStreamRef)writeStream;
- - (void)openWriteStream:(CFWriteStreamRef)writeStream;
- - (void)disconnectWriteStream:(CFWriteStreamRef)writeStream;
- - (void)destroyWriteStream:(CFWriteStreamRef)writeStream;
- /**
- * Retrieve and prepare next request which should be sent
- */
- - (void)prepareNextRequestPacket:(dispatch_block_t)completionBlock;
- /**
- * Writes buffer portion into socket
- */
- - (void)writeBufferContent;
- #pragma mark - Handler methods
- /**
- * Called every time when one of streams (read/write) successfully open connection
- */
- - (void)handleStreamConnection;
- /**
- * Called every time when one of streams (read/write) disconnected
- */
- - (void)handleStreamClose;
- /**
- * Called each time when new portion of data available in socket read stream for reading
- */
- - (void)handleReadStreamHasData:(CFReadStreamRef)stream;
- /**
- * Called each time when write stream is ready to accept data from PubNub client
- */
- - (void)handleWriteStreamCanAcceptData;
- /**
- * Called when client is about to close write stream and we need to do something with write buffer if it was assigned
- */
- - (void)handleRequestSendingCancelation;
- /**
- * Called each time when server close stream because of timeout
- */
- - (void)handleStreamTimeout;
- /**
- * Called each time when wake up timer is fired
- */
- - (void)handleWakeUpTimer;
- /**
- * Converts stream status enum value into string representation
- */
- - (NSString *)stringifyStreamStatus:(CFStreamStatus)status;
- - (void)handleStreamError:(PNConnectionErrorStateFlag)failedStream fromBlock:(CFErrorRef(^)(void))errorBlock;
- - (void)handleStreamError:(CFErrorRef)error;
- - (void)handleStreamError:(CFErrorRef)error shouldCloseConnection:(BOOL)shouldCloseConnection;
- - (void)handleStreamSetupError;
- - (void)handleRequestProcessingError:(CFErrorRef)error withBlock:(dispatch_block_t)notifyCompletionBlock;
- #pragma mark - Misc methods
- /**
- @brief Append new read buffer content.
-
- @param data Data buffer which should be appended to existing read buffer.
- @param length Length of the buffer which has been provided.
-
- @since 3.7.10.7
- */
- - (void)appendToReadBuffer:(const void *)data length:(NSUInteger)length;
- /**
- * Start/stop connection timeout timer
- */
- - (void)startTimeoutTimer;
- - (void)stopTimeoutTimer;
- - (void)stopTimeoutTimer:(BOOL)forRelaunch;
- /**
- * Construct/reuse and launch/resume/suspend/stop 'wakeup' timer to help restore connection if it will be required
- */
- - (void)startWakeUpTimer;
- - (void)suspendWakeUpTimer;
- - (void)resumeWakeUpTimer;
- - (void)stopWakeUpTimer;
- - (void)resetWakeUpTimer;
- /**
- * Check whether specified error is from POSIX domain and report that error is caused by connection failure or not
- */
- - (BOOL)isConnectionIssuesError:(CFErrorRef)error;
- - (BOOL)isConnectionUpLinkError:(CFErrorRef)error;
- /**
- * Check whether specified error is from OSStatus error domain and report that error is caused by SSL issue
- */
- - (BOOL)isSecurityTransportError:(CFErrorRef)error;
- - (BOOL)isInternalSecurityTransportError:(CFErrorRef)error;
- - (BOOL)isTemporaryError:(CFErrorRef)error;
- - (BOOL)isServerError:(CFErrorRef)error;
- - (CFStreamClientContext)streamClientContext;
- /**
- * Retrieving global network proxy configuration
- */
- - (void)retrieveSystemProxySettings;
- /**
- * Stream error processing methods
- */
- - (PNError *)processStreamError:(CFErrorRef)error;
- /**
- * Print our current connection state
- */
- - (NSString *)stateDescription;
- #pragma mark -
- @end
- #pragma mark - Public interface methods
- @implementation PNConnection
- #pragma mark - Class methods
- + (PNConnection *)connectionWithConfiguration:(PNConfiguration *)configuration andIdentifier:(NSString *)identifier {
- return [[self alloc] initWithConfiguration:configuration andIdentifier:identifier];
- }
- #pragma mark - Instance methods
- - (id)initWithConfiguration:(PNConfiguration *)configuration andIdentifier:(NSString *)identifier {
- // Check whether initialization was successful or not
- if ((self = [super init])) {
- // Perform connection initialization
- self.configuration = configuration;
- self.name = identifier;
- self.socketReadStream = NULL;
- self.socketWriteStream = NULL;
- [self pn_setupPrivateSerialQueueWithIdentifier:@"connection" andPriority:DISPATCH_QUEUE_PRIORITY_DEFAULT];
- self.deserializer = [PNResponseDeserialize new];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
-
- return @[PNLoggerSymbols.connection.resourceLinkage, (self.name ? self.name : self),
- (self.deserializer ? [[NSString alloc] initWithFormat:@"%p", self.deserializer] : [NSNull null])];
- }];
- // Set initial connection state
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
- [self pn_dispatchBlock:^{
- // Perform streams initial options and security initializations
- [self prepareStreams];
- }];
- }
- return self;
- }
- #pragma mark - Requests queue execution management
- - (void)scheduleNextRequestExecution {
- [self pn_dispatchBlock:^{
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionProcessingRequests];
- // Ensure that both streams connected at this moment and connection doesn't try to close
- // or suspend
- if ([self isConnected] && ![self isDisconnecting] && ![self isSuspending]) {
- // Check whether sending data at this moment or not
- if (![PNBitwiseHelper is:self.state containsBit:PNSendingData] ||
- ![self.writeBuffer isSendingBytes]) {
- dispatch_block_t continueBlock = ^{
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
- if (self.writeBuffer != nil) {
- // Try to initiate request sending process
- [self writeBufferContent];
- }
- }
- else {
- self.writeBuffer = nil;
- }
- };
- if (self.writeBuffer == nil) {
- [PNBitwiseHelper removeFrom:&self->_state bit:PNSendingData];
- [self prepareNextRequestPacket:continueBlock];
- }
- else {
- [self.writeBuffer reset];
- continueBlock();
- }
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.stream.write.alreadySendingData,
- (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- }];
- }
- - (void)unscheduleRequestsExecution {
- [self pn_dispatchBlock:^{
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionProcessingRequests];
- [self handleRequestSendingCancelation];
- }];
- }
- #pragma mark - Streams callback methods
- static void readStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
- void readStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo) {
- if (CFReadStreamGetStatus(stream) != kCFStreamStatusClosed) {
-
- NSCAssert([(__bridge id)clientCallBackInfo isKindOfClass:[PNContextInformation class]],
- @"{ERROR}[READ] WRONG CLIENT INSTANCE HAS BEEN SENT AS CLIENT");
- PNConnection *connection = ((__bridge PNContextInformation *)clientCallBackInfo).object;
- [connection pn_scheduleOnPrivateQueueAssert];
- NSString *status = [connection stringifyStreamStatus:CFReadStreamGetStatus(stream)];
- CFReadStreamRef retainedStream = (CFReadStreamRef)CFRetain(stream);
- [connection pn_dispatchBlock:^{
- switch (type) {
- // Stream successfully opened
- case kCFStreamEventOpenCompleted:
- {
- [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.opened,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
- [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNReadStreamCleanDisconnection];
- [PNBitwiseHelper addTo:&(connection->_state) bit:PNReadStreamConnected];
- [connection handleStreamConnection];
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- }
- break;
- // Read stream has some data which arrived from remote server
- case kCFStreamEventHasBytesAvailable:
- {
- [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.hasData,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
-
- [connection handleReadStreamHasData:retainedStream];
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- }
- break;
- // Some error occurred on read stream
- case kCFStreamEventErrorOccurred:
- {
- [PNLogger logConnectionErrorMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.error,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
- [connection handleStreamError:PNReadStreamError fromBlock:^CFErrorRef {
- CFErrorRef cfError = CFReadStreamCopyError(retainedStream);
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- return cfError;
- }];
- }
- break;
- // Server disconnected socket probably because of timeout
- case kCFStreamEventEndEncountered:
- {
- [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.cantAcceptDataAnymore,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
- [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNReadStreamCleanAll];
- [PNBitwiseHelper addTo:&(connection->_state) bit:PNReadStreamDisconnected];
- [connection handleStreamTimeout];
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- }
- break;
- default:
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- break;
- }
- }];
- }
- }
- static void writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
- void writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo) {
-
- if (CFWriteStreamGetStatus(stream) != kCFStreamStatusClosed) {
- NSCAssert([(__bridge id)clientCallBackInfo isKindOfClass:[PNContextInformation class]],
- @"{ERROR}[READ] WRONG CLIENT INSTANCE HAS BEEN SENT AS CLIENT");
- PNConnection *connection = ((__bridge PNContextInformation *)clientCallBackInfo).object;
- NSString *status = [connection stringifyStreamStatus:CFWriteStreamGetStatus(stream)];
-
- CFWriteStreamRef retainedStream = (CFWriteStreamRef)CFRetain(stream);
- [connection pn_dispatchBlock:^{
- switch (type) {
- // Stream successfully opened
- case kCFStreamEventOpenCompleted:
- {
- [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.opened,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
- [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNWriteStreamCleanDisconnection];
- [PNBitwiseHelper addTo:&(connection->_state) bit:PNWriteStreamConnected];
- [connection handleStreamConnection];
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- }
- break;
- // Write stream is ready to accept data from data source
- case kCFStreamEventCanAcceptBytes:
- {
- [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.canSendData,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
- [connection handleWriteStreamCanAcceptData];
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- }
- break;
- // Some error occurred on write stream
- case kCFStreamEventErrorOccurred:
- {
- [PNLogger logConnectionErrorMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.error,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
- [connection handleStreamError:PNWriteStreamError fromBlock:^CFErrorRef {
- CFErrorRef cfError = CFWriteStreamCopyError(retainedStream);
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- return cfError;
- }];
- }
- break;
- // Server disconnected socket probably because of timeout
- case kCFStreamEventEndEncountered:
- {
- [PNLogger logConnectionInfoMessageFrom:connection withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.cantSendDataAnymore,
- (connection ? (connection.name ? connection.name : connection) : [NSNull null]),
- (status ? status : [NSNull null]), @(connection.state)];
- }];
- [PNBitwiseHelper removeFrom:&(connection->_state) bit:PNWriteStreamCleanAll];
- [PNBitwiseHelper addTo:&(connection->_state) bit:PNWriteStreamDisconnected];
- [connection handleStreamTimeout];
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- }
- break;
- default:
-
- if (retainedStream) {
-
- CFRelease(retainedStream);
- }
- break;
- }
- }];
- }
- }
- static void connectionContextInformationReleaseCallBack(void *info);
- void connectionContextInformationReleaseCallBack(void *info) {
-
- if (info) {
-
- CFRelease(info);
- }
- }
- #pragma mark - Connection state
- - (BOOL)isConnecting {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- // If at least one of the streams is connecting now treat it all as true
- return ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnected] &&
- [PNBitwiseHelper is:self.state containsBit:PNConnectionConnecting]);
- }
- - (BOOL)isReconnecting {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- // If at least one of the streams is connecting now treat it all as true
- return ([PNBitwiseHelper is:self.state containsBit:PNConnectionConnecting] &&
- [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnection]);
- }
- - (BOOL)shouldReconnect {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- return [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnection];
- }
- - (void)checkConnected:(void (^)(BOOL connected))checkCompletionBlock {
- [self pn_dispatchBlock:^{
- checkCompletionBlock([self isConnected]);
- }];
- }
- - (BOOL)isConnected {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- return ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected] &&
- ![self isReconnecting]);
- }
- - (void)checkDisconnected:(void (^)(BOOL disconnected))checkCompletionBlock {
- [self pn_dispatchBlock:^{
- checkCompletionBlock(([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnected] ||
- [PNBitwiseHelper is:self.state containsBit:PNConnectionSuspended]) &&
- ![self isConnecting]);
- }];
- }
- - (BOOL)isDisconnecting {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- return [PNBitwiseHelper is:self.state containsBit:PNConnectionDisconnecting];
- }
- #pragma mark - Error identification
- - (BOOL)isConnectionIssuesError:(CFErrorRef)error {
- BOOL isConnectionIssue = NO;
- NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
- if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
- switch (CFErrorGetCode(error)) {
- case ENETDOWN: // Network is down (maybe even no connection on interface level)
- // Error cases which may occurs during socket lifecycle, when gateway uplink may go down and additional
- // network check should be performed
- case ENETUNREACH: // Remote host can't be reached
- case EHOSTDOWN: // Remote host is down
- case EHOSTUNREACH: // Host can't be reached, because there is no route to it
- case ECONNREFUSED: // Remote host doesn't want to accept connection
- isConnectionIssue = YES;
- break;
- default:
- break;
- }
- }
- else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
- switch (CFErrorGetCode(error)) {
- case kCFHostErrorHostNotFound:
- isConnectionIssue = YES;
- break;
- default:
- break;
- }
- }
- return isConnectionIssue;
- }
- - (BOOL)isConnectionUpLinkError:(CFErrorRef)error {
- BOOL isConnectionUpLinkError = NO;
- NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
- if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
- switch (CFErrorGetCode(error)) {
- case ENETUNREACH: // Remote host can't be reached
- case EHOSTDOWN: // Remote host is down
- case EHOSTUNREACH: // Host can't be reached, because there is no route to it
- case ECONNREFUSED: // Remote host doesn't want to accept connection
- isConnectionUpLinkError = YES;
- break;
- default:
- break;
- }
- }
- return isConnectionUpLinkError;
- }
- - (BOOL)isSecurityTransportError:(CFErrorRef)error {
-
- BOOL isSecurityTransportError = NO;
- CFIndex errorCode = CFErrorGetCode(error);
- NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
- if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainOSStatus]) {
- #if __IPHONE_OS_VERSION_MIN_REQUIRED
- isSecurityTransportError = (errSSLUnexpectedRecord <= errorCode) && (errorCode <= errSSLProtocol);
- #else
- isSecurityTransportError = (errSSLLast <= errorCode) && (errorCode <= errSSLProtocol);
- #endif
- }
- else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
-
- isSecurityTransportError = (kCFURLErrorCannotLoadFromNetwork <= errorCode) &&
- (errorCode <= kCFURLErrorSecureConnectionFailed);
- }
-
-
- return isSecurityTransportError;
- }
- - (BOOL)isInternalSecurityTransportError:(CFErrorRef)error {
- CFIndex code = CFErrorGetCode(error);
- return (code == errSSLInternal) || (code == errSSLClosedAbort);
- }
- - (BOOL)isTemporaryError:(CFErrorRef)error {
-
- BOOL isTemporaryError = NO;
-
- CFIndex errorCode = CFErrorGetCode(error);
- NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
-
- if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
-
- switch (errorCode) {
- case ETIMEDOUT: // There is no activity on socket for some time
- case ENETRESET: // Remote host crashed w/o sending 'close packet'
- case ECONNABORTED: // Connection was aborted locally (by system or device)
- // Special cases when connection state was changed by remote server
- case ECONNRESET: // Remote host sent 'close packet'
- case ENOBUFS: // No space where data for sockets can be stored
- case ENOTCONN: // Socket not connected or was disconnected
- case ESHUTDOWN: // Socket was closed before new write attempt has been done
- case ENOENT: // Rare error when system probably can't prepare sockets for further operation
- case EPIPE: // Remote host went down or socket configuration not completed
- case EAGAIN: // Requested resource not available
- case EISCONN: // Socket already connected
- isTemporaryError = YES;
- break;
- default:
- break;
- }
- }
- else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
- isTemporaryError = (kCFNetServiceErrorDNSServiceFailure <= errorCode) && (errorCode <= kCFNetServiceErrorUnknown);
- if (!isTemporaryError) {
- isTemporaryError = errorCode == kCFHostErrorUnknown || errorCode == kCFErrorHTTPConnectionLost;
- }
- }
-
-
- return isTemporaryError;
- }
- - (BOOL)isServerError:(CFErrorRef)error {
-
- BOOL isServerError = NO;
-
- CFIndex errorCode = CFErrorGetCode(error);
- NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
-
- if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX]) {
-
- switch (errorCode) {
-
- case ECONNRESET: // Remote host sent 'close packet'
-
- isServerError = YES;
- break;
- default:
- break;
- }
- }
-
-
- return isServerError;
- }
- #pragma mark - Connection lifecycle management methods
- - (BOOL)prepareStreams {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.prepareForUsage, (self.name ? self.name : self), @(self.state)];
- }];
- BOOL streamsPrepared = YES;
- // Check whether stream was prepared and configured before
- if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConfigured]) {
- NSString *symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlier;
- if ([self isConnecting]) {
- symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlierAndConnecting;
- }
- else if ([self isConnected]) {
- symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlierAndConnected;
- }
- if ([self isResuming]) {
- symbolCode = PNLoggerSymbols.connection.stream.configurationCompletedEarlierAndResuming;
- }
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[symbolCode, (self.name ? self.name : self), @(self.state)];
- }];
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.configurationStarted, (self.name ? self.name : self), @(self.state)];
- }];
- // Make sure that streams will be unable to operate (protection in case of state has been interrupted in some
- // way)
- [self destroyStreams];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionDisconnecting];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
- // Define connection port which should be used by connection for further usage (depends on current connection
- // security policy)
- UInt32 targetPort = kPNOriginConnectionPort;
- if (self.configuration.shouldUseSecureConnection &&
- self.sslConfigurationLevel != PNConnectionSSLConfigurationInsecure) {
- targetPort = kPNOriginSSLConnectionPort;
- }
- // Retrieve connection proxy configuration
- [self retrieveSystemProxySettings];
-
- // Create stream pair on socket which is connected to specified remote host
- CFReadStreamRef socketReadStream;
- CFWriteStreamRef socketWriteStream;
- CFStreamCreatePairWithSocketToHost(CFAllocatorGetDefault(), (__bridge CFStringRef)(self.configuration.origin),
- targetPort, &socketReadStream, &socketWriteStream);
- [self configureReadStream:socketReadStream];
- [self configureWriteStream:socketWriteStream];
- _socketReadStream = socketReadStream;
- _socketWriteStream = socketWriteStream;
- // Check whether at least one of the streams was unable to complete configuration
- if (![PNBitwiseHelper is:self.state containsBit:PNConnectionConfigured]) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.stream.configurationFailed, (self.name ? self.name : self), @(self.state)];
- }];
- streamsPrepared = NO;
- [self destroyStreams];
- [self handleStreamSetupError];
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.configurationCompleted, (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- return streamsPrepared;
- }
- - (void)connectWithResult:(void (^)(BOOL connecting))resultBlock {
- [self pn_dispatchBlock:^{
- [PNBitwiseHelper addTo:&self->_state bit:PNByUserRequest];
- [self connectByInternalRequest:^(BOOL connecting) {
- if (resultBlock) {
- resultBlock(connecting);
- }
- }];
- }];
- }
- - (void)connectByInternalRequest:(void (^)(BOOL connecting))resultBlock {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- __block BOOL isStreamOpened = NO;
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.connectionAttempt, (self.name ? self.name : self),
- @([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]), @(self.state)];
- }];
- // Check whether connection was requested by user or not
- if ([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]) {
- BOOL shouldDestroyStreams = [self isConnecting] || [self isReconnecting] || [self isDisconnecting] || [self isResuming];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect,
- BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- if (shouldDestroyStreams) {
- [self destroyStreams];
- }
- }
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionPrepareToConnect];
- // Ask delegate whether connection can be opened or not (in case if there is no internet connection or
- // client was disconnected by user request)
- [self.delegate connection:self checkCanConnect:^(BOOL canConnect) {
- [self pn_dispatchBlock:^{
- if (canConnect) {
- // Check whether client has been properly configured or not
- if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConfigured]) {
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionPrepareToConnect];
- BOOL isAbleToConnect = (![self isConnecting] && ![self isReconnecting] &&
- ![self isConnected] && ![self isDisconnecting] &&
- ![self isResuming]);
- if (isAbleToConnect) {
- // Mark that connection currently doesn't connected to the server
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
- NSString *symbolCode = @"";
- // Check whether connection has been suspended before or not
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSuspended]) {
- // If connection is suspended, there is impossible that it may have any errors or ability to reconnect
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, PNConnectionSuspending,
- BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionResuming];
- symbolCode = PNLoggerSymbols.connection.connectionResumingInProgress;
- }
- else if (![PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected]) {
- [PNBitwiseHelper removeFrom:&self->_state
- bits:PNConnectionSuspending, PNConnectionSuspended, PNConnectionResuming,
- BITS_LIST_TERMINATOR];
- symbolCode = PNLoggerSymbols.connection.connectionInProgress;
- if ([self shouldReconnect]) {
- symbolCode = PNLoggerSymbols.connection.reconnectionInProgress;
- }
- }
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[symbolCode, (self.name ? self.name : self), @(self.state)];
- }];
- isStreamOpened = YES;
- [self startTimeoutTimer];
- [self suspendWakeUpTimer];
- [self openReadStream:self.socketReadStream];
- [self openWriteStream:self.socketWriteStream];
- if (resultBlock) {
- resultBlock(isStreamOpened);
- }
- }
- else {
- void(^forciblyConnectionBlock)(void) = ^{
- [self stopTimeoutTimer];
- [self suspendWakeUpTimer];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.outOfSyncWithStateForciblyReconnect, (self.name ? self.name : self),
- @(self.state)];
- }];
- BOOL isConnectingByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNByUserRequest];
- if (isConnectingByUserRequest) {
- // Mark that disconnection has been called because of internal request
- [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionError, BITS_LIST_TERMINATOR];
- }
- // Forcibly close all connections
- [self disconnectByInternalRequest];
- if (isConnectingByUserRequest) {
- [self connectWithResult:^(BOOL connecting) {
- if (resultBlock) {
- resultBlock(connecting);
- }
- }];
- }
- else {
- [self connectByInternalRequest:^(BOOL connecting) {
- if (resultBlock) {
- resultBlock(connecting);
- }
- }];
- }
- };
- if (![self isDisconnecting]) {
- // Check whether tried to connect while already connected(-ing) or not. This condition take into
- // account state of both streams at same time. If one of the stream has different state, this mean
- // that connection probably in some wrong (messed) state.
- BOOL isConnecting = ([self isConnecting] || [self isReconnecting] ||
- [self isConnected] || [self isResuming]);
- if (isConnecting) {
- NSString *symbolCode = PNLoggerSymbols.connection.alreadyConnecting;
- if ([self isConnected]) {
- symbolCode = PNLoggerSymbols.connection.alreadyConnected;
- }
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[symbolCode, (self.name ? self.name : self), @(self.state)];
- }];
- if (resultBlock) {
- resultBlock(isStreamOpened);
- }
- }
- // Looks like tried to connect while was in some intermediate state (both streams in different
- // states as for 'connected' or 'connecting'
- else {
- forciblyConnectionBlock();
- }
- }
- else if ([self isDisconnecting]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.triedToConnectDuringDisconnection, (self.name ? self.name : self),
- @(self.state)];
- }];
- // Mark that client should try to connect back as soon as disconnection will be completed
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionReconnectOnDisconnect];
- if (resultBlock) {
- resultBlock(isStreamOpened);
- }
- }
- else {
- forciblyConnectionBlock();
- }
- }
- }
- // Looks like configuration not completed
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.notConfigured, (self.name ? self.name : self), @(self.state)];
- }];
- // Try prepare connection's streams for future usage
- if ([self prepareStreams]) {
- [self connectByInternalRequest:^(BOOL connecting) {
- if (resultBlock) {
- resultBlock(connecting);
- }
- }];
- }
- else {
- if (resultBlock) {
- resultBlock(isStreamOpened);
- }
- }
- }
- }
- // Looks like connection can't be established at this moment. Launch wake up timer
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.connectionImpossibleAtCurrentMoment, (self.name ? self.name : self),
- @(self.state)];
- }];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
- [self resumeWakeUpTimer];
- if (resultBlock) {
- resultBlock(isStreamOpened);
- }
- }
- }];
- }];
- }
- - (BOOL)canRetryConnection {
- return (self.connectionRetryCount < kPNMaximumConnectionRetryCount);
- }
- - (void)retryConnection {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
-
- self.connectionRetryCount++;
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetry, (self.name ? self.name : self), @(self.connectionRetryCount),
- @(kPNMaximumConnectionRetryCount), @(self.state)];
- }];
-
- // Check whether reconnection was issued because of SSL error or not
- if (self.sslConfigurationLevel == PNConnectionSSLConfigurationInsecure &&
- [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR]) {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryOnSSLError, (self.name ? self.name : self), @(self.state)];
- }];
- }
- // Check whether reconnection was issued because of socket temporary issues or not
- else if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR]) {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryOnTemporaryConnectionIssues, (self.name ? self.name : self), @(self.state)];
- }];
- }
-
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWillReconnect];
- [self reconnectWithBlock:NULL];
- }
- - (void)reconnectWithBlock:(dispatch_block_t)processReportBlock {
-
- [self pn_dispatchBlock:^{
-
- unsigned long oldStates = self.state;
- __block unsigned long newStates = self.state;
-
- [self.delegate connection:self checkShouldRestoreConnection:^(BOOL shouldReconnect) {
-
- [self pn_dispatchBlock:^{
-
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionWillReconnect];
- newStates = self.state;
-
- BOOL stateChangedFromOutside = (oldStates != newStates && ![PNBitwiseHelper is:oldStates containsBit:PNByUserRequest] &&
- [PNBitwiseHelper is:newStates containsBit:PNByUserRequest]);
-
- if (!stateChangedFromOutside) {
-
- if (shouldReconnect) {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttempt,
- (self.name ? self.name : self), @(self.state)];
- }];
- }
- else {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryImpossibleAtCurrentMoment, (self.name ? self.name : self),
- @(self.state)];
- }];
- }
-
- // Ask delegat e whether connection should initiate connection to remote host or not
- if (shouldReconnect) {
-
- BOOL isWaitingForReconnection = [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnect];
- BOOL isReconnectingBecauseOfError = [PNBitwiseHelper is:self.state containsBit:PNConnectionError];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionError, BITS_LIST_TERMINATOR];
-
- // Marking that connection instance is reconnecting now and after last connection will be closed should
- // automatically renew connection
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionReconnect];
- [self destroyStreams];
-
- // Check whether 'reconnection' delayed request already has been issued or not
- if (!isWaitingForReconnection) {
-
- // Attempt to restore connection after small delay defined in 'static' section of this class
- __pn_desired_weak __typeof__(self) weakSelf = self;
- NSTimeInterval delay = (NSTimeInterval) kPNConnectionRetryDelay;
- if (!isReconnectingBecauseOfError) {
-
- delay = kPNConnectionRetryFastDelay;
- }
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delay * NSEC_PER_SEC));
- dispatch_after(popTime, [self pn_privateQueue], ^{
-
- __strong __typeof__(self) strongSelf = weakSelf;
-
- if ([PNBitwiseHelper is:strongSelf.state containsBit:PNConnectionReconnect]) {
-
- [self disconnectByInternalRequest];
- }
- if (processReportBlock) {
- processReportBlock();
- }
- });
- }
- else if (processReportBlock) {
- processReportBlock();
- }
- }
- else {
-
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
-
- [self resumeWakeUpTimer];
- if (processReportBlock) {
- processReportBlock();
- }
- }
- }
- else {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryCanceledBecauseStateAltered, (self.name ? self.name : self),
- @(self.state)];
- }];
-
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
-
- [self resumeWakeUpTimer];
- if (processReportBlock) {
- processReportBlock();
- }
- }
- }];
- }];
- }];
- }
- - (void)disconnect {
- [self pn_dispatchBlock:^{
- [PNBitwiseHelper addTo:&self->_state bit:PNByUserRequest];
- self.connectionRetryCount = 0;
- [self disconnectByInternalRequest];
- }];
- }
- - (void)disconnectOnInternalRequest {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- [self disconnectByInternalRequest];
- }
- - (void)disconnectByInternalRequest {
- [self pn_dispatchBlock:^{
- // Launch 'wake up' timer in case if disconnection was accident or some catch up logic failed because some tragic coincidence
- [self startWakeUpTimer];
- [self stopTimeoutTimer];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.disconnectionAttempt, (self.name ? self.name : self),
- @([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]), @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionConnecting, PNConnectionPrepareToConnect, PNConnectionResuming, BITS_LIST_TERMINATOR];
- // Check whether it was requested to perform disconnection on user request or not
- if ([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]) {
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- }
- // Clean up cached data
- [self unscheduleRequestsExecution];
- self.proxySettings = nil;
- [self disconnectReadStream:self->_socketReadStream];
- [self disconnectWriteStream:self->_socketWriteStream];
- }];
- }
- - (void)closeConnection {
- [self pn_dispatchBlock:^{
- [self disconnectOnInternalRequest];
- }];
- }
- - (void)destroyStreams {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- BOOL isAnyOfStreamsStillValid = _socketReadStream != NULL || _socketWriteStream != NULL;
- if (isAnyOfStreamsStillValid) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.destroying, (self.name ? self.name : self), @(self.state)];
- }];
- }
- // Clean up cached data
- [self unscheduleRequestsExecution];
- self.proxySettings = nil;
- BOOL isConfiguring = [PNBitwiseHelper is:self.state containsBit:PNConnectionConfiguring];
- BOOL isReadStreamErrorSet = [PNBitwiseHelper is:self.state containsBit:PNReadStreamError];
- BOOL isWriteStreamErrorSet = [PNBitwiseHelper is:self.state containsBit:PNWriteStreamError];
- [self destroyReadStream:_socketReadStream];
- [self destroyWriteStream:_socketWriteStream];
-
- [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamCleanAll, PNWriteStreamCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
- if (isConfiguring) {
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionConfiguring];
- }
- if (isReadStreamErrorSet || isWriteStreamErrorSet) {
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionError];
- }
- if (isAnyOfStreamsStillValid) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.destroyed, (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- - (void)suspend {
- [self pn_dispatchBlock:^{
- dispatch_block_t suspendProceedBlock = ^{
- [self pn_dispatchBlock:^{
- [self suspendWakeUpTimer];
- [self stopTimeoutTimer];
- }];
- };
- // Check whether connection established to the remote host or not
- if ([self isConnected]) {
- // Check whether connection can be suspended or not
- if (![self isSuspending] && ![self isSuspended]) {
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionSuspending];
- [self disconnectByInternalRequest];
- suspendProceedBlock();
- }
- // Looks like connection already suspended
- else if ([self isSuspended]){
- [self.delegate connectionDidSuspend:self withBlock:suspendProceedBlock];
- }
- }
- else {
- [self disconnectOnInternalRequest];
- [PNBitwiseHelper addTo:&self->_state bits:PNConnectionDisconnected, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- [self.delegate connectionDidSuspend:self withBlock:suspendProceedBlock];
- }
- }];
- }
- - (BOOL)isSuspending {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- return [PNBitwiseHelper is:self.state containsBit:PNConnectionSuspending];
- }
- - (void)checkSuspended:(void (^)(BOOL suspended))checkCompletionBlock {
- [self pn_dispatchBlock:^{
- checkCompletionBlock([self isSuspended]);
- }];
- }
- - (BOOL)isSuspended {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- return [PNBitwiseHelper is:self.state strictly:YES
- containsBits:PNConnectionDisconnected, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- }
- - (void)resume {
- [self pn_dispatchBlock:^{
- // Check whether connection suspended at this moment or not
- if (![self isConnected] && [self isSuspended] && ![self isResuming]) {
- // In case if because of some reasons connection can't be established we should launch 'wake up' timer to
- // help fix connection state when it will be possible
- [self connectByInternalRequest:^(BOOL connecting) {
- if (!connecting) {
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
- [self resumeWakeUpTimer];
- }
- }];
- }
- else if ([self isConnected]) {
- [self.delegate connectionDidResume:self withBlock:NULL];
- }
- }];
- }
- - (void)checkResuming:(void (^)(BOOL resuming))checkCompletionBlock {
- [self pn_dispatchBlock:^{
- checkCompletionBlock([self isResuming]);
- }];
- }
- - (BOOL)isResuming {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- return [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionConnecting, PNConnectionResuming, BITS_LIST_TERMINATOR];
- }
- #pragma mark - Read stream lifecycle management methods
- - (void)configureReadStream:(CFReadStreamRef)readStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.configurationStarted, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamCleanConfiguration];
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamConfiguring];
- CFOptionFlags options = (kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable |
- kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered);
- CFStreamClientContext client = [self streamClientContext];
- // Configuring connection channel instance as client for read stream with described set of handling events
- BOOL isStreamReady = CFReadStreamSetClient(readStream, options, readStreamCallback, &client);
- if (isStreamReady) {
- isStreamReady = CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
- }
- if (isStreamReady && self.proxySettings) {
-
- isStreamReady = CFReadStreamSetProperty(readStream, kCFStreamPropertySOCKSProxy, (__bridge CFTypeRef)(self.proxySettings));
- }
- if (self.streamSecuritySettings != NULL && isStreamReady) {
- // Configuring stream to establish SSL connection
- isStreamReady = CFReadStreamSetProperty(readStream,
- (__bridge CFStringRef)NSStreamSocketSecurityLevelKey,
- (__bridge CFStringRef)NSStreamSocketSecurityLevelNegotiatedSSL);
- if (isStreamReady) {
- // Specify connection security options
- isStreamReady = CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, self.streamSecuritySettings);
- }
- }
- if (isStreamReady) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.configurationCompleted, (self.name ? self.name : self),
- @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamConfiguring];
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamConfigured];
- // Schedule read stream on current run-loop
- CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- }
- else {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.configurationFailed, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
- }
- }
- - (void)openReadStream:(CFReadStreamRef)readStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.initOpening, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamCleanConnection];
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamConnecting];
- if (!CFReadStreamOpen(readStream)) {
- CFErrorRef error = CFReadStreamCopyError(readStream);
- if (error && CFErrorGetCode(error) != 0) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.unableToOpen, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
- [self handleStreamError:error];
- }
- else {
- CFRunLoopRun();
- }
-
- if (error) {
-
- CFRelease(error);
- }
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.opening, (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- - (void)disconnectReadStream:(CFReadStreamRef)readStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.disconnecting, (self.name ? self.name : self), @(self.state)];
- }];
-
- [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamConnecting, PNReadStreamCleanDisconnection, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamDisconnecting];
- if (self.readBuffer != NULL) {
-
- // Check whether there is some data received from server and try to parse it
- if ([self.readBuffer length]) {
- self.notProcessedReadBuffer = self.readBuffer;
- [self processResponse];
- }
-
- // Destroying input buffer
- self.readBuffer = NULL;
- }
- BOOL streamHasError = [PNBitwiseHelper is:self.state containsBit:PNReadStreamError];
- [self destroyReadStream:readStream];
- if (streamHasError) {
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
- }
- [self handleStreamClose];
- }
- - (void)destroyReadStream:(CFReadStreamRef)readStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- BOOL isStreamExists = readStream != NULL;
- if (isStreamExists) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.destroying, (self.name ? self.name : self), @(self.state)];
- }];
- }
- if (isStreamExists) {
-
- self.socketReadStream = NULL;
- dispatch_async(dispatch_get_main_queue(), ^{
-
- CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
- if (CFReadStreamGetStatus(readStream) != kCFStreamStatusClosed) {
- CFReadStreamClose(readStream);
- }
-
- if (readStream) {
-
- CFRelease(readStream);
- }
- });
- }
- [PNBitwiseHelper removeFrom:&self->_state bit:PNReadStreamCleanConfiguration];
- if (isStreamExists) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.destroyed, (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- #pragma mark - Read stream lifecycle data processing methods
- - (void)readStreamContent:(CFReadStreamRef)stream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- // Check whether there is really some data available right now or not.
- if (CFReadStreamHasBytesAvailable(stream)) {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
-
- return @[PNLoggerSymbols.connection.stream.read.readingArrivedData,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- // Read raw data from stream
- __block struct {
- UInt8 buffer[kPNStreamBufferSize];
- } data;
- CFIndex readedBytesCount = CFReadStreamRead(stream, data.buffer, kPNStreamBufferSize);
-
- // Checking whether client was able to read out some data from stream or not
- if (readedBytesCount > 0) {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
-
- return @[PNLoggerSymbols.connection.stream.read.readedPortionOfArrivedData,
- (self.name ? self.name : self), @(readedBytesCount), @(self.state)];
- }];
-
- if ([PNLogger isDumpingHTTPResponse] || [PNLogger isLoggerEnabled]) {
- NSData *tempData = [[NSData alloc] initWithBytes:data.buffer
- length:(NSUInteger)readedBytesCount];
- [PNLogger storeHTTPPacketData:^NSData * {
-
- return tempData;
- }];
-
- [PNLogger logConnectionHTTPPacketFrom:self withParametersFromBlock:^NSArray *{
-
- NSString *responseString = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
- if (!responseString) {
-
- responseString = [[NSString alloc] initWithData:tempData encoding:NSASCIIStringEncoding];
- }
- if (!responseString) {
-
- responseString = @"Can't stringify response. Try check response dump on file system (if enabled)";
- }
-
- return @[PNLoggerSymbols.connection.stream.read.rawHTTPResponse, (self.name ? self.name : self),
- responseString, @(self.state)];
- }];
- }
- [self appendToReadBuffer:data.buffer length:(NSUInteger)readedBytesCount];
-
- [self processResponse];
- }
- // Looks like there is no data or error occurred while tried to read out stream content
- else if (readedBytesCount < 0) {
-
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
-
- return @[PNLoggerSymbols.connection.stream.read.readingError,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- CFErrorRef error = CFReadStreamCopyError(stream);
- [PNBitwiseHelper addTo:&self->_state bit:PNReadStreamError];
- [self handleStreamError:error];
-
- if (error) {
-
- CFRelease(error);
- }
- }
- }
- }
- - (void)processResponse {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- // Retrieve response objects from server response
- __block __pn_desired_weak PNConnection *weakSelf = self;
- [self.deserializer parseBufferContent:(self.readBuffer ?: self.notProcessedReadBuffer)
- withBlock:^(NSArray *responses, NSUInteger fullBufferLength,
- NSUInteger processedBufferLength,
- void(^readBufferPostProcessing)(void)) {
- __strong __typeof__(self) strongSelf = weakSelf;
- [strongSelf pn_dispatchBlock:^{
- // Check whether buffer size has been altered while de-serializer parsed it's content
- // or not.
- NSUInteger readBufferSize = [(self.readBuffer ?: self.notProcessedReadBuffer) length];
- BOOL isReadBufferChanged = (fullBufferLength != readBufferSize);
- // Check whether some data has been processed or not.
- if (processedBufferLength > 0){
- // Update read buffer content by removing from it number of bytes which has been
- // processed.
- if (readBufferSize > 0 && readBufferSize >= processedBufferLength) {
- if (processedBufferLength == readBufferSize) {
- if (strongSelf.readBuffer) {
- strongSelf.readBuffer = [NSMutableData new];
- }
- else {
- strongSelf.notProcessedReadBuffer = nil;
- }
- }
- else {
- NSRange targetRange = NSMakeRange(processedBufferLength, readBufferSize - processedBufferLength);
- if (strongSelf.readBuffer) {
- NSData *processedData = [strongSelf.readBuffer subdataWithRange:targetRange];
- strongSelf.readBuffer = [[NSMutableData alloc] initWithData:processedData];
- }
- else if (strongSelf.notProcessedReadBuffer){
- strongSelf.notProcessedReadBuffer = [strongSelf.notProcessedReadBuffer subdataWithRange:targetRange];
- }
- }
- }
- }
- [PNLogger logConnectionInfoMessageFrom:strongSelf withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.read.processedArrivedData,
- (strongSelf.name ? strongSelf.name : strongSelf), @([responses count]), @(strongSelf.state)];
- }];
- if ([responses count] > 0) {
- [responses enumerateObjectsUsingBlock:^(id response,
- __unused NSUInteger responseIdx,
- __unused BOOL *responseEnumeratorStop) {
- // Check whether server reported that connection will be closed after this portion of data
- if (![PNBitwiseHelper is:strongSelf.state containsBit:PNByServerRequest] &&
- [(id<PNResponseProtocol>)response isLastResponseOnConnection]) {
- [PNBitwiseHelper addTo:&strongSelf->_state bits:PNConnectionDisconnect,
- PNByServerRequest, BITS_LIST_TERMINATOR];
- // Inform delegate that connection will be closed soon by server request
- [strongSelf.delegate connection:strongSelf willDisconnectByServerRequestFromHost:strongSelf.configuration.origin
- withBlock:^{
- // Notify delegate about new event arrival
- [strongSelf.delegate connection:strongSelf didReceiveResponse:response withBlock:NULL];
- }];
- }
- else {
- // Notify delegate about new event arrival
- [strongSelf.delegate connection:strongSelf didReceiveResponse:response withBlock:NULL];
- }
- }];
- }
- readBufferPostProcessing();
- if (isReadBufferChanged) {
- // Try to process retrieved data once more (maybe some full response arrived
- // from remote server)
- [strongSelf processResponse];
- }
- else {
-
- strongSelf.notProcessedReadBuffer = nil;
- // Check whether client is still connected and there is request from server side to close connection.
- // Connection will be restored after full disconnection
- if ([strongSelf isConnected] && ![strongSelf isReconnecting] && ![strongSelf isDisconnecting] &&
- [PNBitwiseHelper is:strongSelf.state containsBit:PNByServerRequest]) {
- [strongSelf disconnectByInternalRequest];
- }
- }
- }];
- }];
- }
- #pragma mark - Write stream lifecycle management methods
- - (void)configureWriteStream:(CFWriteStreamRef)writeStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.configurationStarted, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamCleanConfiguration];
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamConfiguring];
- CFOptionFlags options = (kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes |
- kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered);
- CFStreamClientContext client = [self streamClientContext];
- // Configuring connection channel instance as client for write stream with described set of handling events
- BOOL isStreamReady = CFWriteStreamSetClient(writeStream, options, writeStreamCallback, &client);
- if (isStreamReady) {
-
- isStreamReady = CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
- }
- if (isStreamReady && self.proxySettings) {
-
- isStreamReady = CFWriteStreamSetProperty(writeStream, kCFStreamPropertySOCKSProxy, (__bridge CFTypeRef)(self.proxySettings));
- }
- if (isStreamReady) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.configurationCompleted, (self.name ? self.name : self),
- @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamConfiguring];
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamConfigured];
- // Schedule write stream on current run-loop
- CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- }
- else {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.configurationFailed, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
- }
- }
- - (void)openWriteStream:(CFWriteStreamRef)writeStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.initOpening, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamCleanConnection];
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamConnecting];
- if (!CFWriteStreamOpen(writeStream)) {
- CFErrorRef error = CFWriteStreamCopyError(writeStream);
- if (error && CFErrorGetCode(error) != 0) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.unableToOpen, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
- [self handleStreamError:error];
- }
- else {
- CFRunLoopRun();
- }
-
- if (error) {
-
- CFRelease(error);
- }
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.opening, (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- - (void)disconnectWriteStream:(CFWriteStreamRef)writeStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.disconnecting, (self.name ? self.name : self), @(self.state)];
- }];
-
- [PNBitwiseHelper removeFrom:&self->_state bits:PNWriteStreamConnecting, PNWriteStreamCleanDisconnection, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamDisconnecting];
- self.writeStreamCanHandleData = NO;
- // Handle canceled request (if there was such)
- [self handleRequestSendingCancelation];
- BOOL streamHasError = [PNBitwiseHelper is:self.state containsBit:PNWriteStreamError];
- [self destroyWriteStream:writeStream];
- if (streamHasError) {
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
- }
- [self handleStreamClose];
- }
- - (void)destroyWriteStream:(CFWriteStreamRef)writeStream {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- BOOL isStreamExists = writeStream != NULL;
- if (isStreamExists) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.destroying, (self.name ? self.name : self), @(self.state)];
- }];
- }
- if (isStreamExists) {
-
- self.socketWriteStream = NULL;
- dispatch_async(dispatch_get_main_queue(), ^{
-
- CFWriteStreamUnscheduleFromRunLoop(writeStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- CFWriteStreamSetClient(writeStream, kCFStreamEventNone, NULL, NULL);
- if (CFWriteStreamGetStatus(writeStream) != kCFStreamStatusClosed){
- CFWriteStreamClose(writeStream);
- }
-
- if (writeStream) {
-
- CFRelease(writeStream);
- }
- });
- }
- [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamCleanConfiguration];
- if (isStreamExists) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.destroyed, (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- #pragma mark - Write stream buffer management methods
- - (void)prepareNextRequestPacket:(dispatch_block_t)completionBlock {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- // Ensure that connection is able to send next portion of data which will be prepared
- BOOL shouldPrepareData = [self isConnected] && ![self isReconnecting] && ![self isDisconnecting] &&
- ![PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
- PNByServerRequest, BITS_LIST_TERMINATOR] && ![self isResuming];
- if (shouldPrepareData && !self.fetchingDataForSending) {
- self.fetchingDataForSending = YES;
- [self.dataSource checkHasDataForConnection:self withBlock:^(BOOL hasData) {
- // Check whether data source can provide some data right after connection is established or not
- if (hasData) {
- [self.dataSource nextRequestIdentifierForConnection:self withBlock:^(NSString *requestIdentifier) {
- if (requestIdentifier) {
-
- [self.dataSource connection:self requestDataForIdentifier:requestIdentifier
- withBlock:^(PNWriteBuffer *buffer) {
-
- [self pn_dispatchBlock:^{
- self.writeBuffer = buffer;
- self.fetchingDataForSending = NO;
- completionBlock();
- }];
- }];
- }
- else {
- [self pn_dispatchBlock:^{
- self.fetchingDataForSending = NO;
- completionBlock();
- }];
- }
- }];
- }
- else {
-
- [self pn_dispatchBlock:^{
- self.fetchingDataForSending = NO;
- completionBlock();
- }];
- }
- }];
- }
- else {
- completionBlock();
- }
- }
- - (void)writeBufferContent {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- BOOL(^writeStreamIsAbleToSend)(void) = ^{
- return (BOOL)(self.socketWriteStream && [self isConnected] && ![self isReconnecting] &&
- ![self isDisconnecting] && ![PNBitwiseHelper is:self.state strictly:YES
- containsBits:PNConnectionDisconnect,
- PNByServerRequest,
- BITS_LIST_TERMINATOR] &&
- self.isWriteStreamCanHandleData && ![self isResuming]);
- };
- // Check whether there is connection which can be used to write data
- if (writeStreamIsAbleToSend()) {
- if (self.writeBuffer != nil) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.writeDataFromBuffer, (self.name ? self.name : self),
- @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamError];
- if (self.writeBuffer.length > 0) {
- [PNBitwiseHelper removeFrom:&self->_state bit:PNWriteStreamError];
- [PNBitwiseHelper addTo:&self->_state bit:PNSendingData];
- // Check whether connection can pull some data
- // from write buffer or not
- __block BOOL isWriteBufferIsEmpty = ![self.writeBuffer hasData];
- dispatch_block_t emptyBufferHandleBlock = ^{
- if (isWriteBufferIsEmpty) {
- [PNBitwiseHelper removeFrom:&self->_state bit:PNSendingData];
- // Retrieving reference on request's identifier who's body has been sent
- NSString *identifier = self.writeBuffer.requestIdentifier;
- self.writeBuffer = nil;
- [self.dataSource connection:self didSendRequestWithIdentifier:identifier withBlock:^{
- [self pn_dispatchBlock:^{
- // Check whether should try to send next request or not
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
- if (writeStreamIsAbleToSend()) {
- [self scheduleNextRequestExecution];
- }
- else if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect, PNByServerRequest, BITS_LIST_TERMINATOR]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.unableProcessNextRequestOnConnectionTermination,
- (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- }];
- }];
- }
- };
- if (!isWriteBufferIsEmpty) {
- if (self.isWriteStreamCanHandleData) {
- void(^bufferProcessingBlock)(void) = ^{
- if (writeStreamIsAbleToSend() && self.writeBuffer != nil) {
- // Try write data into write stream
- CFIndex bytesWritten = CFWriteStreamWrite(self.socketWriteStream,
- [self.writeBuffer buffer],
- [self.writeBuffer bufferLength]);
- // Check whether error occurred while tried to process request
- if (bytesWritten < 0) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.dataWriteError,
- (self.name ? self.name : self), @(self.state)];
- }];
- // Mark that buffer content is not processed at this moment
- self.writeBuffer.sendingBytes = NO;
- self.writeStreamCanHandleData = NO;
- // Retrieve error which occurred while tried to write buffer into socket
- CFErrorRef writeError = CFWriteStreamCopyError(self.socketWriteStream);
- [PNBitwiseHelper addTo:&self->_state bit:PNWriteStreamError];
- [self handleRequestProcessingError:writeError withBlock:^{
-
- if (writeError) {
-
- CFRelease(writeError);
- }
- [self pn_dispatchBlock:^{
-
- isWriteBufferIsEmpty = YES;
- emptyBufferHandleBlock();
- }];
- }];
- }
- // Check whether socket was able to transfer whole write buffer at once or not
- else if (bytesWritten == self.writeBuffer.length) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.writenDataFromBufferAtOnce,
- (self.name ? self.name : self), @(bytesWritten), @(self.writeBuffer.length),
- @(self.state)];
- }];
- // Mark that buffer content is not processed at this moment
- self.writeBuffer.sendingBytes = NO;
- // Set readout offset to buffer content length (there is no more data to send)
- self.writeBuffer.offset = self.writeBuffer.length;
- isWriteBufferIsEmpty = YES;
- emptyBufferHandleBlock();
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.writenPartOfDataFromBuffer,
- (self.name ? self.name : self), @(self.writeBuffer.offset + bytesWritten),
- @(self.writeBuffer.length), @(self.state)];
- }];
- self.writeStreamCanHandleData = NO;
- // Increase buffer readout offset
- self.writeBuffer.offset = (self.writeBuffer.offset + bytesWritten);
- if (self.writeBuffer.offset == self.writeBuffer.length) {
- self.writeStreamCanHandleData = YES;
- isWriteBufferIsEmpty = YES;
- }
- emptyBufferHandleBlock();
- }
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.writeCanceled, (self.name ? self.name : self),
- @(self.state)];
- }];
- emptyBufferHandleBlock();
- }
- };
- // Check whether we just started request processing or not
- if (self.writeBuffer.offset == 0) {
- // Mark that buffer content sending was initiated
- self.writeBuffer.sendingBytes = YES;
- // Notify data source that we started request processing
- [self.dataSource connection:self
- processingRequestWithIdentifier:self.writeBuffer.requestIdentifier
- withBlock:^(BOOL shouldContinue){
- [self pn_dispatchBlock:^{
- if (shouldContinue) {
-
- bufferProcessingBlock();
- }
- else {
-
- // Mark that buffer content sending not started yet
- self.writeBuffer.sendingBytes = NO;
- }
- }];
- }];
- }
- else {
- bufferProcessingBlock();
- }
- }
- }
- else {
- emptyBufferHandleBlock();
- }
- }
- // Looks like because of some reasons there is no new data
- else {
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
- if (writeStreamIsAbleToSend()) {
- [self scheduleNextRequestExecution];
- }
- else if([PNBitwiseHelper is:self.state strictly:YES
- containsBits:PNConnectionDisconnect, PNByServerRequest, BITS_LIST_TERMINATOR]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.unableProcessNextRequestOnConnectionTermination,
- (self.name ? self.name : self), @(self.state)];
- }];
- }
- }
- }
- }
- else {
- [self scheduleNextRequestExecution];
- }
- }
- else if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected]) {
- if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
- PNByServerRequest, BITS_LIST_TERMINATOR]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.unableProcessNextRequestOnConnectionTermination,
- (self.name ? self.name : self), @(self.state)];
- }];
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.write.nothingToWrite, (self.name ? self.name : self),
- @(self.state)];
- }];
- }
- }
- }
- #pragma mark - Handler methods
- - (void)handleStreamConnection {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.opened, (self.name ? self.name : self), @(self.state)];
- }];
- // Ensure that both read and write streams are connected before notify
- // delegate about successful connection
- if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnecting] &&
- [PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionConnected]) {
- BOOL retriedConnection = self.connectionRetryCount != 0;
- // Resetting some cached data
- self.configurationRetryCount = 0;
- self.connectionRetryCount = 0;
- // Terminate wake up timer
- [self stopWakeUpTimer];
-
- BOOL isRestoredAfterServerClosed = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
- PNByServerRequest, BITS_LIST_TERMINATOR];
- BOOL isConnectionReset = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionError,
- PNByInternalRequest, BITS_LIST_TERMINATOR];
- BOOL isReconnectedByWakeUpTimer = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
- PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
- BOOL isReconnectedBySSL = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
- PNConnectionSSL, BITS_LIST_TERMINATOR];
- BOOL isReconnectedBySocket = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
- PNConnectionSocket, BITS_LIST_TERMINATOR];
-
- BOOL connectedAfterError = [PNBitwiseHelper is:self.state containsBit:PNConnectionError];
- BOOL isByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
- BOOL isReconnecting = [self isReconnecting];
- BOOL isResuming = [self isResuming];
-
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- if (retriedConnection) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.connectedOnRetryAttempt, (self.name ? self.name : self),
- @(self.state)];
- }];
- }
-
- dispatch_block_t streamConnecionCompletionBlock =^{
- [self pn_dispatchBlock:^{
- // Check whether channel should process requests from upper layers or not
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
- [self scheduleNextRequestExecution];
- }
- }];
- };
- // Check whether connection has been established as result of user calling '-connect' method or not
- if (isByUserRequest) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.connected, (self.name ? self.name : self),
- @(isByUserRequest), @(self.state)];
- }];
- // Notify delegate that initial connection is established
- [self.delegate connection:self didConnectToHost:self.configuration.origin
- withBlock:streamConnecionCompletionBlock];
- }
- else {
- // Check whether connection is resuming after it was suspended or not
- if (isResuming) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.resumed, (self.name ? self.name : self),
- @(self.state)];
- }];
- [self.delegate connectionDidResume:self withBlock:streamConnecionCompletionBlock];
- }
- // Check whether connection has been restored after server closed it (for example when server doesn't
- // support 'keep-alive' connection type
- else if (isRestoredAfterServerClosed) {
- // Inform delegate that connection finally recovered after it has been close by remote server
- [self.delegate connection:self didRestoreAfterServerCloseConnectionToHost:self.configuration.origin
- withBlock:streamConnecionCompletionBlock];
- }
- // Check whether connection has been forcibly reset after found out that it is in wrong state
- else if (isConnectionReset) {
- // Inform delegate that connection has been completely reset and ready to work
- [self.delegate connectionDidReset:self withBlock:streamConnecionCompletionBlock];
- }
- // Check whether connection has been restored after some error occurred on streams
- else if (connectedAfterError) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.reconnectedAfterError, (self.name ? self.name : self),
- @(self.state)];
- }];
- [self.delegate connection:self didReconnectToHostAfterError:self.configuration.origin
- withBlock:streamConnecionCompletionBlock];
- }
- // Check whether connection has been reconnected by request from 'wake up' timer/SSL/sockets
- else if (isReconnectedByWakeUpTimer || isReconnectedBySSL || isReconnectedBySocket) {
- NSString *symbolCode = PNLoggerSymbols.connection.reconnectedOnWakeUpTimerRequest;
- if (isReconnectedBySSL) {
- symbolCode = PNLoggerSymbols.connection.reconnectedBecauseOfSSLError;
- }
- else if (isReconnectedBySocket) {
- symbolCode = PNLoggerSymbols.connection.reconnectedBecauseOfTemporaryConnectionIssues;
- }
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[symbolCode, (self.name ? self.name : self), @(self.state)];
- }];
- [self.delegate connection:self didReconnectToHostAfterError:self.configuration.origin
- withBlock:streamConnecionCompletionBlock];
- }
- // Check whether connection has been reconnected by request or not
- else if (isReconnecting) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.reconnected, (self.name ? self.name : self), @(isByUserRequest),
- @(self.state)];
- }];
- [self.delegate connection:self didReconnectToHost:self.configuration.origin
- withBlock:streamConnecionCompletionBlock];
- }
- else {
- [self.delegate connection:self didConnectToHost:self.configuration.origin
- withBlock:streamConnecionCompletionBlock];
- }
- }
- }
- }
- - (void)handleStreamClose {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray *{
- return @[PNLoggerSymbols.connection.stream.closed, (self.name ? self.name : self), @(self.state)];
- }];
- // Ensure that both read and write streams reset before notify delegate about connection close event
- if ([PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnecting] &&
- ![PNBitwiseHelper is:self.state strictly:YES containsBit:PNConnectionDisconnected]) {
- BOOL retriedConnection = self.connectionRetryCount != 0;
- // Resetting some cached data
- self.configurationRetryCount = 0;
- [self stopTimeoutTimer];
- BOOL isDisconnectedByServerRequest = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionDisconnect,
- PNByServerRequest, BITS_LIST_TERMINATOR];
- BOOL isDisconnectedOnReset = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionError, PNByInternalRequest,
- BITS_LIST_TERMINATOR];
- BOOL isDisconnectedByWakeUpTimer = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest,
- PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
- BOOL isDisconnectedBySSL = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
- BOOL isDisconnectedBySocket = [PNBitwiseHelper is:self.state strictly:YES containsBits:PNByInternalRequest, PNConnectionSocket,
- BITS_LIST_TERMINATOR];
- BOOL isDisconnectedOnError = [PNBitwiseHelper is:self.state containsBit:PNConnectionError];
- BOOL isByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
- BOOL shouldConnectOnDisconnect = [PNBitwiseHelper is:self.state containsBit:PNConnectionReconnectOnDisconnect];
- BOOL isReconnecting = [self shouldReconnect];
- BOOL isSuspending = [self isSuspending];
-
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionReconnect, PNConnectionReconnectOnDisconnect, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNByInternalRequest, PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionResuming, PNConnectionSuspending, PNConnectionSuspended, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamCleanAll, PNWriteStreamCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
- if (isDisconnectedOnError) {
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionError];
- }
- void(^reconnectOnErrorBlock)(void) = ^{
- // Attempt to restore connection after small delay defined in 'static' section of this class
- __pn_desired_weak __typeof__ (self) weakSelf = self;
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPNConnectionRetryDelay * NSEC_PER_SEC));
- dispatch_after(popTime, [self pn_privateQueue], ^{
-
- __strong __typeof__(self) strongSelf = weakSelf;
- // Check whether connection is still in bad state before issue connection
- if ([PNBitwiseHelper is:strongSelf.state containsBit:PNConnectionError]) {
- [PNLogger logConnectionInfoMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedWithFurtherReconnectionBecauseOfError,
- (strongSelf.name ? strongSelf.name : strongSelf), @(strongSelf.state)];
- }];
- [strongSelf resumeWakeUpTimer];
- [strongSelf stopTimeoutTimer];
- if (isByUserRequest) {
- [PNLogger logConnectionInfoMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.reconnectFromTheUserName,
- (strongSelf.name ? strongSelf.name : strongSelf),
- @(strongSelf.state)];
- }];
- [strongSelf connectWithResult:NULL];
- }
- else {
- [strongSelf connectByInternalRequest:NULL];
- }
- }
- });
- };
- if (retriedConnection) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedOnRetryAttemptWithFurtherReconnection, (self.name ? self.name : self),
- @(self.state)];
- }];
- }
- if (shouldConnectOnDisconnect || isDisconnectedByServerRequest || isDisconnectedOnReset ||
- isDisconnectedByWakeUpTimer || isDisconnectedBySSL || isDisconnectedBySocket || isReconnecting ||
- retriedConnection) {
- __block unsigned long flagsToEnableBack = 0;
- dispatch_block_t connectionRestoreBlock = ^{
- [self pn_dispatchBlock:^{
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
- [self resumeWakeUpTimer];
- if (isByUserRequest) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.reconnectFromTheUserName, (self.name ? self.name : self),
- @(self.state)];
- }];
- [self connectWithResult:^(__unused BOOL connecting) {
-
- // Restore error state flags because of which connection should be restored back
- [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
- }];
- }
- else {
- [self connectByInternalRequest:^(__unused BOOL connecting) {
-
- // Restore error state flags because of which connection should be restored back
- [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
- }];
- }
- }];
- };
- // Check whether there was attempt to connect while was connection was in disconnection state
- if (shouldConnectOnDisconnect) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedWithFurtherConnectionOnUserRequest, (self.name ? self.name : self),
- @(self.state)];
- }];
- connectionRestoreBlock();
- }
- else if (isDisconnectedByServerRequest) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedOnExpectedCloseWithFurtherReconnection, (self.name ? self.name : self),
- @(self.state)];
- }];
-
- [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByServerRequest, PNConnectionDisconnect, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
- // Notify delegate that connection has been terminated by server request
- [self.delegate connection:self didDisconnectByServerRequestFromHost:self.configuration.origin
- withBlock:connectionRestoreBlock];
- }
- else if (isDisconnectedOnReset) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedOnConnectionResetWithFurtherReconnection, (self.name ? self.name : self),
- @(self.state)];
- }];
-
- [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionError, BITS_LIST_TERMINATOR];
- connectionRestoreBlock();
- }
- else if (isDisconnectedByWakeUpTimer || isDisconnectedBySSL || isDisconnectedBySocket) {
- NSString *symbolCode = PNLoggerSymbols.connection.closedOnWakeUpTimerRequestWithFurtherReconnection;
- [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
- if (isDisconnectedBySSL) {
- [PNBitwiseHelper clear:&flagsToEnableBack];
- [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
- symbolCode = PNLoggerSymbols.connection.closedBecauseOfSSLErrorWithFurtherReconnection;
- }
- else if (isDisconnectedBySocket) {
-
- [PNBitwiseHelper clear:&flagsToEnableBack];
- [PNBitwiseHelper addTo:&flagsToEnableBack bits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR];
- symbolCode = PNLoggerSymbols.connection.closedBecauseOfTemporaryConnectionIssuesWithFurtherReconnection;
- }
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[symbolCode, (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper addTo:&self->_state bit:flagsToEnableBack];
- // Notify delegate that connection will be restored because of reconnection request
- [self.delegate connection:self willReconnectToHostAfterError:self.configuration.origin
- withBlock:connectionRestoreBlock];
- }
- else if (isReconnecting) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedWithFurtherReconnection, (self.name ? self.name : self),
- @(self.state)];
- }];
- [PNBitwiseHelper addTo:&flagsToEnableBack bit:PNConnectionReconnection];
- // Notify delegate that connection will be restored because of reconnection request
- [self.delegate connection:self willReconnectToHost:self.configuration.origin
- withBlock:connectionRestoreBlock];
- }
- else if (retriedConnection) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.connectionRetry, (self.name ? self.name : self), @(self.connectionRetryCount),
- @(kPNMaximumConnectionRetryCount), @(self.state)];
- }];
- connectionRestoreBlock();
- }
- }
- // Check whether connection has been closed by user request or not
- else if (isByUserRequest) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedByUserRequest, (self.name ? self.name : self),
- @(self.state)];
- }];
- if (!isDisconnectedOnError) {
- self.connectionRetryCount = 0;
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
- [self resumeWakeUpTimer];
- [self.delegate connection:self didDisconnectFromHost:self.configuration.origin
- withBlock:NULL];
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closedBecauseOfError, (self.name ? self.name : self),
- @(self.state)];
- }];
- reconnectOnErrorBlock();
- }
- }
- // Disconnection has been done because of other reasons (configuration error / suspending)
- else {
- if (isSuspending) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.suspended, (self.name ? self.name : self),
- @(self.state)];
- }];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionSuspended];
- [self suspendWakeUpTimer];
- [self stopTimeoutTimer];
- [self.delegate connectionDidSuspend:self withBlock:NULL];
- }
- else {
- NSString *symbolCode = PNLoggerSymbols.connection.disconnected;
- if (isDisconnectedOnError) {
- symbolCode = PNLoggerSymbols.connection.disconnectedBecauseOfError;
- }
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[symbolCode, (self.name ? self.name : self), @(self.state)];
- }];
- // Check whether connection has been terminated because of error or not
- if (isDisconnectedOnError) {
- reconnectOnErrorBlock();
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.notifyDelegateAboutDisconnection, (self.name ? self.name : self),
- @(self.state)];
- }];
- self.connectionRetryCount = 0;
- [self.delegate connection:self didDisconnectFromHost:self.configuration.origin
- withBlock:^{
- [self pn_dispatchBlock:^{
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionWakeUpTimer];
- [self resumeWakeUpTimer];
- [self stopTimeoutTimer];
- }];
- }];
- }
- }
- }
- }
- }
- - (void)handleReadStreamHasData:(CFReadStreamRef)stream {
- [self readStreamContent:stream];
- }
- - (void)handleWriteStreamCanAcceptData {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- if (!self.isWriteStreamCanHandleData) {
-
- [self stopTimeoutTimer];
- }
- self.writeStreamCanHandleData = YES;
- if (![PNBitwiseHelper is:self.state strictly:YES
- containsBits:PNConnectionDisconnect, PNByServerRequest, BITS_LIST_TERMINATOR]) {
- [self writeBufferContent];
- }
- }
- - (void)handleRequestSendingCancelation {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- // Check whether data sending layer is processing some request or not
- if ([PNBitwiseHelper is:self.state containsBit:PNSendingData] || self.writeBuffer != nil) {
- NSString *interruptedRequestIdentifier = self.writeBuffer.requestIdentifier;
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.unscheduleRequestProcessing, (self.name ? self.name : self),
- (interruptedRequestIdentifier ? interruptedRequestIdentifier : [NSNull null]), @(self.state)];
- }];
- self.writeBuffer = nil;
- [PNBitwiseHelper removeFrom:&self->_state bit:PNSendingData];
- // Notify delegate about that request processing hasn't been completed
- [self.dataSource connection:self didCancelRequestWithIdentifier:interruptedRequestIdentifier
- withBlock:NULL];
- }
- }
- - (void)handleStreamTimeout {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNByUserRequest, PNByServerRequest, PNByInternalRequest,
- PNConnectionErrorCleanAll, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionSuspending, PNConnectionSuspended, PNConnectionResuming, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bit:PNConnectionDisconnected];
- [self reconnectWithBlock:NULL];
- }
- - (void)handleWakeUpTimer {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.handleWakeUpTimer, (self.name ? self.name : self),
- @(self.state)];
- }];
- // Check whether connection not connected
- if ((![self isConnected] && ![self isConnecting]) ||
- [PNBitwiseHelper is:self.state containsBit:PNConnectionWakeUpTimer]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.stillInBadState, (self.name ? self.name : self),
- @(self.state)];
- }];
- unsigned long oldStates = self.state;
- [self.delegate connection:self checkShouldRestoreConnection:^(BOOL shouldReconnect) {
- [self pn_dispatchBlock:^{
- unsigned long newStates = self.state;
- BOOL stateChangedFromOutside = oldStates != newStates && ![PNBitwiseHelper is:oldStates
- containsBit:PNByUserRequest] &&
- [PNBitwiseHelper is:newStates containsBit:PNByUserRequest];
- if (!stateChangedFromOutside) {
- // Ask delegate on whether connection should be restored or not
- if (shouldReconnect) {
- BOOL actionByUserRequest = [PNBitwiseHelper is:self.state containsBit:PNByUserRequest];
- // Mark that since state fixing has been called from 'wake up' timer handler method, all further actions
- // performed on internal code request
- [PNBitwiseHelper removeFrom:&self->_state bits:PNByUserRequest, PNByServerRequest, BITS_LIST_TERMINATOR];
- [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionWakeUpTimer, BITS_LIST_TERMINATOR];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.stateCanBeRecovered, (self.name ? self.name : self),
- @(self.state)];
- }];
- // Check whether connection should be restored via '-reconnect' method or not
- if ([self shouldReconnect]) {
- [self reconnectWithBlock:NULL];
- }
- else if ([PNBitwiseHelper is:self.state containsBit:PNConnectionPrepareToConnect]) {
- if (actionByUserRequest) {
- [self connectWithResult:NULL];
- }
- else {
- [self connectByInternalRequest:NULL];
- }
- }
- else {
- [PNBitwiseHelper removeFrom:&self->_state bits:PNReadStreamCleanAll, PNWriteStreamCleanAll,
- PNConnectionReconnection, BITS_LIST_TERMINATOR];
- [self disconnectByInternalRequest];
- }
- }
- else {
- // Looks like connection can't be established, so there can be no 'connecting' state
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionConnecting, PNConnectionDisconnecting,
- BITS_LIST_TERMINATOR];
- }
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.wakeUpEventCanceledBecauseStateHasBeenAltered, (self.name ? self.name : self),
- @(self.state)];
- }];
- }
- }];
- }];
- }
- }
- - (NSString *)stringifyStreamStatus:(CFStreamStatus)status {
- NSString *stringifiedStatus = @"NOTHING INTERESTING";
- switch (status) {
- case kCFStreamStatusNotOpen:
- stringifiedStatus = @"STREAM NOT OPENED";
- break;
- case kCFStreamStatusOpening:
- stringifiedStatus = @"STREAM IS OPENING";
- break;
- case kCFStreamStatusOpen:
- stringifiedStatus = @"STREAM IS OPENED";
- break;
- case kCFStreamStatusReading:
- stringifiedStatus = @"READING FROM STREAM";
- break;
- case kCFStreamStatusWriting:
- stringifiedStatus = @"WRITING INTO STREAM";
- break;
- case kCFStreamStatusAtEnd:
- stringifiedStatus = @"STREAM CAN'T READ/WRITE DATA";
- break;
- case kCFStreamStatusClosed:
- stringifiedStatus = @"STREAM CLOSED";
- break;
- case kCFStreamStatusError:
- stringifiedStatus = @"STREAM ERROR OCCURRED";
- break;
- }
- return stringifiedStatus;
- }
- - (void)handleStreamError:(PNConnectionErrorStateFlag)failedStream fromBlock:(CFErrorRef(^)(void))errorBlock {
-
- __block BOOL isErrorProcessed = NO;
- void(^errorProcessingBlock)(CFErrorRef) = ^(CFErrorRef streamError){
- [self pn_dispatchBlock:^{
- CFErrorRef errorReference = streamError;
- if (!isErrorProcessed) {
- if (streamError == NULL) {
- errorReference = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ECONNRESET, NULL);
- }
- // Mark that read stream caught and error
- [PNBitwiseHelper addTo:&self->_state bit:failedStream];
- [self handleStreamError:streamError shouldCloseConnection:YES];
- isErrorProcessed = YES;
- if (errorReference) {
-
- CFRelease(errorReference);
- }
- }
- else if (errorReference != NULL) {
-
- if (errorReference) {
-
- CFRelease(errorReference);
- }
- }
- }];
- };
-
- // Sending error copy request to another thread to make sure that it won't block
- // run-loop (it looks like in case if stream has been reset, CFReadStreamCopyError blocks code execution
- // and may take significant amount of time to return control back).
- dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-
- // There is corner case because of which Apple's C API may block run-loop of the thread on which it has been
- // called for very long period. In normal situation error fetching doesn't take more than few milliseconds.
- // Delay for 0.4 second should be more than enough to fetch error and in case if it will take longer, secondary
- // logic will be used to create custom error. In case of error client will have to perform handshacke again and it
- // will take at least 250 ms for one side and 500ms for full round-trip (this is ethernet values). SSL handshake will
- // require twice time from regular connection.
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
-
- errorProcessingBlock(NULL);
- });
-
- // Try to retrieve error
- CFErrorRef error = errorBlock();
- errorProcessingBlock(error);
- });
- }
- - (void)handleStreamError:(CFErrorRef)error {
- [self handleStreamError:error shouldCloseConnection:NO];
- }
- - (void)handleStreamError:(CFErrorRef)error shouldCloseConnection:(BOOL)shouldCloseConnection {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- if (error && CFErrorGetCode(error) != 0) {
- NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
- PNError *errorObject = [self processStreamError:error];
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.error, (self.name ? self.name : self),
- (errorObject ? errorObject : [NSNull null]), @((long long)CFErrorGetCode(error)),
- (errorDomain ? errorDomain : [NSNull null]), @(shouldCloseConnection),
- @(self.state)];
- }];
- // Check whether error is caused by SSL issues or not
- if ([self isSecurityTransportError:error]) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.errorOnSSLLevel, (self.name ? self.name : self), @(self.state)];
- }];
- if (![self isInternalSecurityTransportError:error]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.isSSLLevelReductionAllowed, (self.name ? self.name : self),
- @(self.configuration.shouldReduceSecurityLevelOnError), @(self.state)];
- }];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.isSSLDiscardingAllowed, (self.name ? self.name : self),
- @(self.configuration.canIgnoreSecureConnectionRequirement), @(self.state)];
- }];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.currentSSLConfigurationLevel, (self.name ? self.name : self),
- @(self.sslConfigurationLevel), @(self.state)];
- }];
-
- // Checking whether user allowed to decrease security options and we can do it
- if (self.configuration.shouldReduceSecurityLevelOnError &&
- self.sslConfigurationLevel == PNConnectionSSLConfigurationStrict) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.reduceSSLSecurityLevel, (self.name ? self.name : self),
- @(self.state)];
- }];
- shouldCloseConnection = NO;
-
- self.sslConfigurationLevel = PNConnectionSSLConfigurationBarelySecure;
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
- [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
- // Try to reconnect with new SSL security settings
- [self reconnectWithBlock:NULL];
- }
- // Check whether connection can fallback and use plain HTTP connection w/o SSL
- else if (self.configuration.canIgnoreSecureConnectionRequirement &&
- self.sslConfigurationLevel == PNConnectionSSLConfigurationBarelySecure) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.discardSSLLeyer, (self.name ? self.name : self), @(self.state)];
- }];
- shouldCloseConnection = NO;
-
- self.sslConfigurationLevel = PNConnectionSSLConfigurationInsecure;
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
- [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
-
- // Try to reconnect with new SSL security settings
- [self reconnectWithBlock:NULL];
- }
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.internalSSLError, (self.name ? self.name : self), @(self.state)];
- }];
- shouldCloseConnection = NO;
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
- [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSSL, BITS_LIST_TERMINATOR];
- [self reconnectWithBlock:NULL];
- }
- }
- else if ([errorDomain isEqualToString:(NSString *)kCFErrorDomainPOSIX] ||
- [errorDomain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
- // Check whether connection configuration still valid or destruction process already started
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionConfigured]) {
-
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.generalError, (self.name ? self.name : self), @(self.state)];
- }];
- // Check whether connection should be reconnected because of critical error
- if ([self isConnectionIssuesError:error]) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.internetConnectionFailure, (self.name ? self.name : self), @(self.state)];
- }];
- if ([self isConnectionUpLinkError:error]) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.uplinkConnectionFailure, (self.name ? self.name : self), @(self.state)];
- }];
- }
- if (![self isReconnecting]) {
-
- if ([self canRetryConnection]) {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttemptIsPossible,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- shouldCloseConnection = NO;
- [self retryConnection];
- }
- else {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttemptImpossible, (self.name ? self.name : self),
- @(self.state)];
- }];
-
- // Mark that we should init streams close because of critical error
- shouldCloseConnection = YES;
- }
- }
- else {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttemptInProgress,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- shouldCloseConnection = NO;
- }
- }
-
- if ([self isTemporaryError:error]) {
-
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionErrorCleanAll];
- [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR];
- if ([self isServerError:error]) {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.generalErrorBecauseOfServerActions, (self.name ? self.name : self),
- @(self.state)];
- }];
- }
- else {
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.generalErrorOfTemporaryConnectionIssues, (self.name ? self.name : self),
- @(self.state)];
- }];
- }
-
- if (![self isReconnecting]) {
-
- // Checking whether connection is able to perform another connection attempt or not
- if ([self canRetryConnection]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttemptIsPossible,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- shouldCloseConnection = NO;
- [self retryConnection];
- }
- else {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.connectionRetryAttemptImpossible, (self.name ? self.name : self),
- @(self.state)];
- }];
-
- // Mark that we should init streams close because of critical error
- shouldCloseConnection = YES;
- }
- }
- else {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttemptInProgress,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- shouldCloseConnection = NO;
- }
- }
- }
- // Looks like connection already invalid and all corresponding actions in a queue.
- else {
-
- shouldCloseConnection = NO;
- }
- }
- if (shouldCloseConnection) {
-
- // Check whether we are tried to establish connection and some error occurred there
- if ([self isConnecting]) {
-
- if (![self isReconnecting]) {
-
- shouldCloseConnection = (shouldCloseConnection && ![self canRetryConnection] ? YES : shouldCloseConnection);
- if (!shouldCloseConnection) {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttemptIsPossible,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- [PNBitwiseHelper addTo:&self->_state bits:PNByInternalRequest, PNConnectionSocket, BITS_LIST_TERMINATOR];
- [self retryConnection];
- }
- else {
-
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionSocket];
- }
- }
- else {
-
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
-
- return @[PNLoggerSymbols.connection.connectionRetryAttemptInProgress,
- (self.name ? self.name : self), @(self.state)];
- }];
-
- [PNBitwiseHelper removeFrom:&self->_state bit:PNConnectionSocket];
- }
- }
- }
- if (shouldCloseConnection) {
- dispatch_block_t errorHandlingCompletionBlock = ^{
- if ([self isConnected] && ![self isDisconnecting]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closingConnectionBecauseOfError, (self.name ? self.name : self),
- @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNReadStreamCleanAll, PNWriteStreamCleanAll,
- PNConnectionDisconnect, PNByServerRequest, PNByInternalRequest,PNByUserRequest,
- PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
- self.connectionRetryCount = 0;
- [self.delegate connection:self willDisconnectFromHost:self.configuration.origin
- withError:errorObject andBlock:^{
- [self pn_dispatchBlock:^{
- [self disconnectByInternalRequest];
- }];
- }];
- }
- else if ([self isConnecting]) {
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.closingConnectionBecauseOfErrorWhileTriedToConnect,
- (self.name ? self.name : self), @(self.state)];
- }];
- [PNBitwiseHelper removeFrom:&self->_state bits:PNConnectionCleanReconnection, PNReadStreamCleanAll, PNWriteStreamCleanAll,
- PNConnectionDisconnect, PNByServerRequest, PNByInternalRequest,PNByUserRequest,
- PNConnectionWakeUpTimer, PNConnectionSSL, PNConnectionSocket, BITS_LIST_TERMINATOR];
-
- self.connectionRetryCount = 0;
- [self.delegate connection:self connectionDidFailToHost:self.configuration.origin
- withError:errorObject andBlock:^{
- [self pn_dispatchBlock:^{
- [self disconnectOnInternalRequest];
- }];
- }];
- }
- };
- // Check whether error occurred during data sending or not
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests] && self.writeBuffer) {
- [self handleRequestProcessingError:error withBlock:^{
- [self pn_dispatchBlock:^{
- errorHandlingCompletionBlock();
- }];
- }];
- }
- else {
- errorHandlingCompletionBlock();
- }
- }
- }
- }
- - (void)handleStreamSetupError {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [PNLogger logConnectionErrorMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.stream.configurationError, (self.name ? self.name : self),
- @(self.state)];
- }];
- // Check whether error occurred while connection attempted to connect to remote services w/o configuration on
- // user request or not
- if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNByUserRequest, PNConnectionPrepareToConnect, BITS_LIST_TERMINATOR]) {
- // Prepare error message which will be sent to connection channel delegate
- PNError *setupError = [PNError errorWithCode:kPNConnectionErrorOnSetup];
- // Connection instance can't operate anymore, notify delegate about it's state
- [self.delegate connection:self connectionDidFailToHost:self.configuration.origin
- withError:setupError andBlock:NULL];
- }
- // Looks like error occurred during initial configuration or when reconnection has been called within library
- else {
- __pn_desired_weak __typeof__(self) weakSelf = self;
- int64_t delay = 1;
- if ([PNBitwiseHelper is:self.state strictly:YES containsBits:PNConnectionConfiguring, PNConnectionPrepareToConnect, BITS_LIST_TERMINATOR]) {
- delay = kPNConnectionRetryDelay;
- }
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delay * NSEC_PER_SEC));
- void(^delayedBlock)(void) = ^{
-
- __strong __typeof__(self) strongSelf = weakSelf;
- // Check whether connection is still in bad state before issue connection
- if ([PNBitwiseHelper is:strongSelf.state containsBit:PNConnectionConfiguring]) {
- if (strongSelf.configurationRetryCount + 1 < kPNMaximumConfigurationRetryCount) {
- [PNLogger logConnectionErrorMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.stream.configurationRetryAttempt,
- (strongSelf.name ? strongSelf.name : strongSelf),
- @(strongSelf.state)];
- }];
- strongSelf.configurationRetryCount++;
- // Check whether client configuration failed during connection attempt or not
- if ([PNBitwiseHelper is:strongSelf.state strictly:YES containsBits:PNConnectionConfiguring, PNConnectionPrepareToConnect,
- BITS_LIST_TERMINATOR]) {
- [strongSelf connectByInternalRequest:NULL];
- }
- else {
- [strongSelf prepareStreams];
- }
- }
- // Looks like connection instance can't retry anymore because it reached maximum retry count
- else {
- [PNLogger logConnectionErrorMessageFrom:strongSelf withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.stream.configurationRetryAttemptsExceeded,
- (strongSelf.name ? strongSelf.name : strongSelf), @(strongSelf.state)];
- }];
- strongSelf.configurationRetryCount = 0;
- strongSelf.connectionRetryCount = 0;
- // Terminate operation of all streams and buffers (clean up)
- [strongSelf destroyStreams];
-
- [PNBitwiseHelper clear:&strongSelf->_state];
- [PNBitwiseHelper addTo:&strongSelf->_state bit:PNConnectionDisconnected];
- // Connection instance can't operate anymore, notify delegate about it's state
- [strongSelf.delegate connectionConfigurationDidFail:strongSelf];
- }
- }
- };
- dispatch_after(popTime, [self pn_privateQueue], delayedBlock);
- }
- }
- - (void)handleRequestProcessingError:(CFErrorRef)error withBlock:(dispatch_block_t)notifyCompletionBlock {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- if (error && CFErrorGetCode(error) != 0) {
- if (self.writeBuffer && [PNBitwiseHelper is:self.state containsBit:PNSendingData]) {
- [self.dataSource connection:self didFailToProcessRequestWithIdentifier:self.writeBuffer.requestIdentifier
- error:[self processStreamError:error] withBlock:notifyCompletionBlock];
- }
- }
- }
- #pragma mark - Misc methods
- - (void)appendToReadBuffer:(const void *)data length:(NSUInteger)length {
-
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
-
- if (self.readBuffer == NULL) {
-
- self.readBuffer = [[NSMutableData alloc] initWithBytes:data length:length];
- }
- else {
-
- [self.readBuffer appendBytes:data length:length];
- }
- }
- - (void)startTimeoutTimer {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- [self stopTimeoutTimer:YES];
- if (self.connectionTimeoutTimer == NULL || dispatch_source_testcancel(self.connectionTimeoutTimer) > 0) {
- dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
- [self pn_privateQueue]);
- self.connectionTimeoutTimer = timerSource;
- __pn_desired_weak __typeof__(self) weakSelf = self;
- dispatch_source_set_event_handler(timerSource, ^{
-
- __strong __typeof__(self) strongSelf = weakSelf;
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.handleTimeoutTimer,
- (strongSelf.name ? strongSelf.name : strongSelf), @(strongSelf.state)];
- }];
- [strongSelf stopTimeoutTimer];
- [strongSelf handleStreamTimeout];
- });
- dispatch_source_set_cancel_handler(timerSource, ^{
- [PNDispatchHelper release:timerSource];
- });
- dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPNConnectionTimeout * NSEC_PER_SEC));
- dispatch_source_set_timer(timerSource, start, (int64_t)(kPNConnectionTimeout * NSEC_PER_SEC), NSEC_PER_SEC);
- dispatch_resume(timerSource);
- }
- }
- - (void)stopTimeoutTimer {
-
- [self stopTimeoutTimer:NO];
- }
- - (void)stopTimeoutTimer:(BOOL)__unused forRelaunch {
-
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
-
- if (self.connectionTimeoutTimer != NULL && dispatch_source_testcancel(self.connectionTimeoutTimer) == 0) {
-
- dispatch_source_cancel(self.connectionTimeoutTimer);
- }
- self.connectionTimeoutTimer = NULL;
- }
- - (void)startWakeUpTimer {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- if (self.wakeUpTimer == NULL || dispatch_source_testcancel(self.wakeUpTimer) > 0) {
- self.wakeUpTimerSuspended = YES;
- dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
- [self pn_privateQueue]);
- self.wakeUpTimer = timerSource;
-
- __pn_desired_weak __typeof__(self) weakSelf = self;
- dispatch_source_set_event_handler(timerSource, ^{
-
- __strong __typeof__(self) strongSelf = weakSelf;
- [strongSelf handleWakeUpTimer];
- });
- dispatch_source_set_cancel_handler(timerSource, ^{
-
- __strong __typeof__(self) strongSelf = weakSelf;
- [PNDispatchHelper release:timerSource];
- strongSelf.wakeUpTimerSuspended = NO;
- });
- [self resetWakeUpTimer];
- }
- if (self.isWakeUpTimerSuspended) {
- [self resumeWakeUpTimer];
- }
- }
- - (void)suspendWakeUpTimer {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- if (self.wakeUpTimer != NULL && dispatch_source_testcancel(self.wakeUpTimer) == 0) {
- if (!self.isWakeUpTimerSuspended) {
- self.wakeUpTimerSuspended = YES;
- dispatch_suspend(self.wakeUpTimer);
- }
- }
- else {
- self.wakeUpTimerSuspended = NO;
- }
- }
- - (void)resumeWakeUpTimer {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- if (self.wakeUpTimer == NULL || dispatch_source_testcancel(self.wakeUpTimer) > 0) {
- [self startWakeUpTimer];
- }
- else {
- if (self.isWakeUpTimerSuspended) {
- self.wakeUpTimerSuspended = NO;
- [self resetWakeUpTimer];
- dispatch_resume(self.wakeUpTimer);
- }
- }
- }
- - (void)stopWakeUpTimer {
-
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
-
- if (self.wakeUpTimer != NULL && dispatch_source_testcancel(self.wakeUpTimer) == 0) {
-
- if (self.isWakeUpTimerSuspended) {
-
- [self resumeWakeUpTimer];
- }
- self.wakeUpTimerSuspended = NO;
- dispatch_source_cancel(self.wakeUpTimer);
- }
- self.wakeUpTimer = NULL;
- }
- - (void)resetWakeUpTimer {
- dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPNWakeUpTimerInterval * NSEC_PER_SEC));
- dispatch_source_set_timer(self.wakeUpTimer, start, (int64_t)(kPNWakeUpTimerInterval * NSEC_PER_SEC), NSEC_PER_SEC);
- }
- #ifndef __clang_analyzer__
- - (CFStreamClientContext)streamClientContext {
- PNContextInformation *information = [PNContextInformation contextWithObject:self];
-
-
- return (CFStreamClientContext){0, (__bridge_retained void *)information, NULL,
- connectionContextInformationReleaseCallBack, NULL};
- }
- #endif
- - (CFMutableDictionaryRef)streamSecuritySettings {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- if (self.configuration.shouldUseSecureConnection && _streamSecuritySettings == NULL &&
- self.sslConfigurationLevel != PNConnectionSSLConfigurationInsecure) {
- // Configure security settings
- _streamSecuritySettings = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 6, NULL, NULL);
- CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
- CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLPeerName, kCFNull);
- if (self.sslConfigurationLevel == PNConnectionSSLConfigurationStrict) {
- CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLValidatesCertificateChain, kCFBooleanTrue);
- }
- else {
- CFDictionarySetValue(_streamSecuritySettings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
- }
- }
- else if (_streamSecuritySettings != NULL && (!self.configuration.shouldUseSecureConnection ||
- self.sslConfigurationLevel == PNConnectionSSLConfigurationInsecure)) {
-
- CFRelease(_streamSecuritySettings);
- _streamSecuritySettings = NULL;
- }
- return _streamSecuritySettings;
- }
- - (void)retrieveSystemProxySettings {
- // This method should be launched only from within it's private queue
- [self pn_scheduleOnPrivateQueueAssert];
- #ifdef PN_SOCKET_PROXY_ENABLED
- if (self.proxySettings == NULL) {
- #if PN_SOCKET_PROXY_ENABLED == 1
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.fetchingProxyConfiguration, (self.name ? self.name : self),
- @(self.state)];
- }];
- self.proxySettings = @{(__bridge NSString *)kCFStreamPropertySOCKSProxyHost:PN_SOCKET_PROXY_HOST,
- (__bridge NSString *)kCFStreamPropertySOCKSProxyPort:PN_SOCKET_PROXY_PORT};
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.proxyConfigurationInformation, (self.name ? self.name : self),
- (self.proxySettings ? self.proxySettings : [NSNull null]), @(self.state)];
- }];
- #else
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.proxyConfigurationNotRequired, (self.name ? self.name : self),
- @(self.state)];
- }];
- #endif // PN_SOCKET_PROXY_ENABLED
- }
- #endif // PN_SOCKET_PROXY_ENABLED
- }
- - (PNError *)processStreamError:(CFErrorRef)error {
- PNError *errorInstance = nil;
- if (error) {
- NSString *errorDomain = (__bridge NSString *)CFErrorGetDomain(error);
- if ([self isConnectionIssuesError:error]) {
- int errorCode = kPNClientConnectionClosedOnInternetFailureError;
- if (self.writeBuffer != nil && [self.writeBuffer hasData] && self.writeBuffer.isSendingBytes) {
- errorCode = kPNRequestExecutionFailedOnInternetFailureError;
- }
- errorInstance = [PNError errorWithCode:errorCode];
- }
- else if ([self isSecurityTransportError:error]) {
- errorInstance = [PNError errorWithCode:kPNClientConnectionClosedOnSSLNegotiationFailureError];
- }
- else if ([self isTemporaryError:error]) {
-
- NSInteger errorCode = kPNClientConnectionClosedOnSocketsError;
- if ([self isServerError:error]) {
-
- errorCode = kPNClientConnectionClosedOnServerRequestError;
- }
-
- errorInstance = [PNError errorWithCode:errorCode];
- }
- else {
- errorInstance = [PNError errorWithDomain:errorDomain code:CFErrorGetCode(error) userInfo:nil];
- }
- }
- return errorInstance;
- }
- - (NSString *)stateDescription {
- NSMutableString *connectionState = [[NSMutableString alloc] initWithFormat:@"\n[CONNECTION::%@ STATE DESCRIPTION",
- self.name ? self.name : self];
- if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConfiguring]) {
- [connectionState appendFormat:@"\n- READ STREAM CONFIGURATION..."];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConfiguring]) {
- [connectionState appendFormat:@"\n- WRITE STREAM CONFIGURATION..."];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConfigured]) {
- [connectionState appendFormat:@"\n- READ STREAM CONFIGURED"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConfigured]) {
- [connectionState appendFormat:@"\n- WRITE STREAM CONFIGURED"];
- }
- NSString *actionSource = @"";
- NSString *actionSourceReason = @"";
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionWakeUpTimer]) {
- actionSource = @"WAKE UP TIMER";
- actionSourceReason = @" (BY WAKE UP TIMER REQUEST)";
- }
- else if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSSL]) {
- actionSource = @"SSL LAYER";
- actionSourceReason = @" (BY SSL LAYER REQUEST)";
- }
- else if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSocket]) {
- actionSource = @"SOCKET LAYER";
- actionSourceReason = @" (BY SOCKET LAYER REQUEST)";
- }
- [connectionState appendFormat:@"\n- ACTION SOURCE: %@...", actionSource];
- if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConnecting]) {
- [connectionState appendFormat:@"\n- READ STREAM CONNECTING%@...", actionSourceReason];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConnecting]) {
- [connectionState appendFormat:@"\n- WRITE STREAM CONNECTING%@...", actionSourceReason];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamConnected]) {
- [connectionState appendFormat:@"\n- READ STREAM CONNECTED"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamConnected]) {
- [connectionState appendFormat:@"\n- WRITE STREAM CONNECTED"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionPrepareToConnect]) {
- [connectionState appendFormat:@"\n- PREPARING TO CONNECT..."];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNByInternalRequest]) {
- [connectionState appendFormat:@"\n- CURRENT ACTION PERFORMED BY INTERNAL REQUEST"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNByUserRequest]) {
- [connectionState appendFormat:@"\n- CURRENT ACTION PERFORMED BY USER REQUEST"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNByServerRequest]) {
- [connectionState appendFormat:@"\n- CONNECTION CLOSE WAS EXPECTED (PROBABLY SERVER DOESN'T SUPPORT "
- "'keep-alive' CONNECTION TYPE)"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionResuming]) {
- [connectionState appendFormat:@"\n- RESUMING..."];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionReconnect]) {
- [connectionState appendFormat:@"\n- RECONNECTING..."];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamDisconnecting]) {
- [connectionState appendFormat:@"\n- READ STREAM DISCONNECTING%@...", actionSourceReason];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamDisconnecting]) {
- [connectionState appendFormat:@"\n- WRITE STREAM DISCONNECTING%@...", actionSourceReason];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSuspending]) {
- [connectionState appendFormat:@"\n- SUSPENDING..."];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamDisconnected]) {
- [connectionState appendFormat:@"\n- READ STREAM DISCONNECTED"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamDisconnected]) {
- [connectionState appendFormat:@"\n- WRITE STREAM DISCONNECTED"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionReconnectOnDisconnect]) {
- [connectionState appendFormat:@"\n- WAITING FOR DISCONNECTION TO CONNECT BACK"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionSuspended]) {
- [connectionState appendFormat:@"\n- SUSPENDED"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNConnectionProcessingRequests]) {
- [connectionState appendFormat:@"\n- REQUEST PROCESSING ENABLED"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNSendingData]) {
- [connectionState appendFormat:@"\n- SENDING DATA"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNReadStreamError]) {
- [connectionState appendFormat:@"\n- READ STREAM ERROR"];
- }
- if ([PNBitwiseHelper is:self.state containsBit:PNWriteStreamError]) {
- [connectionState appendFormat:@"\n- WRITE STREAM ERROR"];
- }
- return connectionState;
- }
- #pragma mark - Memory management
- - (void)prepareForTermination {
-
- [self pn_ignorePrivateQueueRequirement];
- [self stopWakeUpTimer];
- [self stopTimeoutTimer];
- }
- - (void)dealloc {
- [self pn_ignorePrivateQueueRequirement];
- // Closing all streams and free up resources which was allocated for their support
- [self destroyStreams];
- [self stopWakeUpTimer];
- [self stopTimeoutTimer];
- self.delegate = nil;
- self.proxySettings = nil;
-
- [self pn_destroyPrivateDispatchQueue];
- [PNLogger logConnectionInfoMessageFrom:self withParametersFromBlock:^NSArray * {
- return @[PNLoggerSymbols.connection.destroyed, (self->_name ? self->_name : self),
- @(self->_state)];
- }];
-
- if (_streamSecuritySettings) {
-
- CFRelease(_streamSecuritySettings);
- }
- }
- #pragma mark -
- @end