/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
Large files files are truncated, but you can click here to view the full file
- //
- // 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);
- }
- }];
- }
- };
- …
Large files files are truncated, but you can click here to view the full file