OSDN Git Service

Bug fix for issue introduced in r105.
[syncr/master.git] / CocoaAsyncSocket / CocoaAsyncSocket-5.5.1 / AsyncSocket.m
diff --git a/CocoaAsyncSocket/CocoaAsyncSocket-5.5.1/AsyncSocket.m b/CocoaAsyncSocket/CocoaAsyncSocket-5.5.1/AsyncSocket.m
deleted file mode 100644 (file)
index 4bb05b7..0000000
+++ /dev/null
@@ -1,3184 +0,0 @@
-//
-//  AsyncSocket.m
-//  
-//  This class is in the public domain.
-//  Originally created by Dustin Voss on Wed Jan 29 2003.
-//  Updated and maintained by Deusty Designs and the Mac development community.
-//
-//  http://code.google.com/p/cocoaasyncsocket/
-//
-
-#import "AsyncSocket.h"
-#import <sys/socket.h>
-#import <netinet/in.h>
-#import <arpa/inet.h>
-#import <netdb.h>
-
-#if TARGET_OS_IPHONE
-// Note: You may need to add the CFNetwork Framework to your project
-#import <CFNetwork/CFNetwork.h>
-#endif
-
-#pragma mark Declarations
-
-#define DEFAULT_PREBUFFERING YES        // Whether pre-buffering is enabled by default
-
-#define READQUEUE_CAPACITY     5           // Initial capacity
-#define WRITEQUEUE_CAPACITY 5           // Initial capacity
-#define READALL_CHUNKSIZE      256         // Incremental increase in buffer size
-#define WRITE_CHUNKSIZE    (1024 * 4)   // Limit on size of each write pass
-
-NSString *const AsyncSocketException = @"AsyncSocketException";
-NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain";
-
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
-// Mutex lock used by all instances of AsyncSocket, to protect getaddrinfo.
-// Prior to Mac OS X 10.5 this method was not thread-safe.
-static NSString *getaddrinfoLock = @"lock";
-#endif
-
-enum AsyncSocketFlags
-{
-       kEnablePreBuffering      = 1 <<  0,  // If set, pre-buffering is enabled
-       kDidPassConnectMethod    = 1 <<  1,  // If set, disconnection results in delegate call
-       kDidCompleteOpenForRead  = 1 <<  2,  // If set, open callback has been called for read stream
-       kDidCompleteOpenForWrite = 1 <<  3,  // If set, open callback has been called for write stream
-       kStartingReadTLS         = 1 <<  4,  // If set, we're waiting for TLS negotiation to complete
-       kStartingWriteTLS        = 1 <<  5,  // If set, we're waiting for TLS negotiation to complete
-       kForbidReadsWrites       = 1 <<  6,  // If set, no new reads or writes are allowed
-       kDisconnectAfterReads    = 1 <<  7,  // If set, disconnect after no more reads are queued
-       kDisconnectAfterWrites   = 1 <<  8,  // If set, disconnect after no more writes are queued
-       kClosingWithError        = 1 <<  9,  // If set, the socket is being closed due to an error
-       kDequeueReadScheduled    = 1 << 10,  // If set, a maybeDequeueRead operation is already scheduled
-       kDequeueWriteScheduled   = 1 << 11,  // If set, a maybeDequeueWrite operation is already scheduled
-       kSocketCanAcceptBytes    = 1 << 12,  // If set, we know socket can accept bytes. If unset, it's unknown.
-       kSocketHasBytesAvailable = 1 << 13,  // If set, we know socket has bytes available. If unset, it's unknown.
-};
-
-@interface AsyncSocket (Private)
-
-// Connecting
-- (void)startConnectTimeout:(NSTimeInterval)timeout;
-- (void)endConnectTimeout;
-
-// Socket Implementation
-- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr;
-- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
-- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
-- (BOOL)configureSocketAndReturnError:(NSError **)errPtr;
-- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
-- (void)doAcceptWithSocket:(CFSocketNativeHandle)newSocket;
-- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err;
-
-// Stream Implementation
-- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr;
-- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
-- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
-- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr;
-- (BOOL)openStreamsAndReturnError:(NSError **)errPtr;
-- (void)doStreamOpen;
-- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr;
-
-// Disconnect Implementation
-- (void)closeWithError:(NSError *)err;
-- (void)recoverUnreadData;
-- (void)emptyQueues;
-- (void)close;
-
-// Errors
-- (NSError *)getErrnoError;
-- (NSError *)getAbortError;
-- (NSError *)getStreamError;
-- (NSError *)getSocketError;
-- (NSError *)getConnectTimeoutError;
-- (NSError *)getReadMaxedOutError;
-- (NSError *)getReadTimeoutError;
-- (NSError *)getWriteTimeoutError;
-- (NSError *)errorFromCFStreamError:(CFStreamError)err;
-
-// Diagnostics
-- (BOOL)isSocketConnected;
-- (BOOL)areStreamsConnected;
-- (NSString *)connectedHost:(CFSocketRef)socket;
-- (UInt16)connectedPort:(CFSocketRef)socket;
-- (NSString *)localHost:(CFSocketRef)socket;
-- (UInt16)localPort:(CFSocketRef)socket;
-- (NSString *)addressHost:(CFDataRef)cfaddr;
-- (UInt16)addressPort:(CFDataRef)cfaddr;
-
-// Reading
-- (void)doBytesAvailable;
-- (void)completeCurrentRead;
-- (void)endCurrentRead;
-- (void)scheduleDequeueRead;
-- (void)maybeDequeueRead;
-- (void)doReadTimeout:(NSTimer *)timer;
-
-// Writing
-- (void)doSendBytes;
-- (void)completeCurrentWrite;
-- (void)endCurrentWrite;
-- (void)scheduleDequeueWrite;
-- (void)maybeDequeueWrite;
-- (void)maybeScheduleDisconnect;
-- (void)doWriteTimeout:(NSTimer *)timer;
-
-// Run Loop
-- (void)runLoopAddSource:(CFRunLoopSourceRef)source;
-- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source;
-- (void)runLoopAddTimer:(NSTimer *)timer;
-- (void)runLoopRemoveTimer:(NSTimer *)timer;
-- (void)runLoopUnscheduleReadStream;
-- (void)runLoopUnscheduleWriteStream;
-
-// Security
-- (void)maybeStartTLS;
-- (void)onTLSHandshakeSuccessful;
-
-// Callbacks
-- (void)doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData;
-- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream;
-- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream;
-
-@end
-
-static void MyCFSocketCallback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
-static void MyCFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *pInfo);
-static void MyCFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *pInfo);
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark -
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * The AsyncReadPacket encompasses the instructions for any given read.
- * The content of a read packet allows the code to determine if we're:
- *  - reading to a certain length
- *  - reading to a certain separator
- *  - or simply reading the first chunk of available data
-**/
-@interface AsyncReadPacket : NSObject
-{
-  @public
-       NSMutableData *buffer;
-       CFIndex bytesDone;
-       NSTimeInterval timeout;
-       CFIndex maxLength;
-       long tag;
-       NSData *term;
-       BOOL readAllAvailableData;
-}
-- (id)initWithData:(NSMutableData *)d
-                  timeout:(NSTimeInterval)t
-                          tag:(long)i
-  readAllAvailable:(BOOL)a
-               terminator:(NSData *)e
-                maxLength:(CFIndex)m;
-
-- (unsigned)readLengthForTerm;
-
-- (unsigned)prebufferReadLengthForTerm;
-- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes;
-@end
-
-@implementation AsyncReadPacket
-
-- (id)initWithData:(NSMutableData *)d
-                  timeout:(NSTimeInterval)t
-                          tag:(long)i
-  readAllAvailable:(BOOL)a
-               terminator:(NSData *)e
-         maxLength:(CFIndex)m
-{
-       if((self = [super init]))
-       {
-               buffer = [d retain];
-               timeout = t;
-               tag = i;
-               readAllAvailableData = a;
-               term = [e copy];
-               bytesDone = 0;
-               maxLength = m;
-       }
-       return self;
-}
-
-/**
- * For read packets with a set terminator, returns the safe length of data that can be read
- * without going over a terminator, or the maxLength.
- * 
- * It is assumed the terminator has not already been read.
-**/
-- (unsigned)readLengthForTerm
-{
-       NSAssert(term != nil, @"Searching for term in data when there is no term.");
-       
-       // What we're going to do is look for a partial sequence of the terminator at the end of the buffer.
-       // If a partial sequence occurs, then we must assume the next bytes to arrive will be the rest of the term,
-       // and we can only read that amount.
-       // Otherwise, we're safe to read the entire length of the term.
-       
-       unsigned result = [term length];
-       
-       // Shortcut when term is a single byte
-       if(result == 1) return result;
-       
-       // i = index within buffer at which to check data
-       // j = length of term to check against
-       
-       // Note: Beware of implicit casting rules
-       // This could give you -1: MAX(0, (0 - [term length] + 1));
-       
-       CFIndex i = MAX(0, (CFIndex)(bytesDone - [term length] + 1));
-       CFIndex j = MIN([term length] - 1, bytesDone);
-       
-       while(i < bytesDone)
-       {
-               const void *subBuffer = [buffer bytes] + i;
-               
-               if(memcmp(subBuffer, [term bytes], j) == 0)
-               {
-                       result = [term length] - j;
-                       break;
-               }
-               
-               i++;
-               j--;
-       }
-       
-       if(maxLength > 0)
-               return MIN(result, (maxLength - bytesDone));
-       else
-               return result;
-}
-
-/**
- * Assuming pre-buffering is enabled, returns the amount of data that can be read
- * without going over the maxLength.
-**/
-- (unsigned)prebufferReadLengthForTerm
-{
-       if(maxLength > 0)
-               return MIN(READALL_CHUNKSIZE, (maxLength - bytesDone));
-       else
-               return READALL_CHUNKSIZE;
-}
-
-/**
- * For read packets with a set terminator, scans the packet buffer for the term.
- * It is assumed the terminator had not been fully read prior to the new bytes.
- * 
- * If the term is found, the number of excess bytes after the term are returned.
- * If the term is not found, this method will return -1.
- * 
- * Note: A return value of zero means the term was found at the very end.
-**/
-- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes
-{
-       NSAssert(term != nil, @"Searching for term in data when there is no term.");
-       
-       // We try to start the search such that the first new byte read matches up with the last byte of the term.
-       // We continue searching forward after this until the term no longer fits into the buffer.
-       
-       // Note: Beware of implicit casting rules
-       // This could give you -1: MAX(0, 1 - 1 - [term length] + 1);
-       
-       CFIndex i = MAX(0, (CFIndex)(bytesDone - numBytes - [term length] + 1));
-       
-       while(i + [term length] <= bytesDone)
-       {
-               const void *subBuffer = [buffer bytes] + i;
-               
-               if(memcmp(subBuffer, [term bytes], [term length]) == 0)
-               {
-                       return bytesDone - (i + [term length]);
-               }
-               
-               i++;
-       }
-       
-       return -1;
-}
-
-- (void)dealloc
-{
-       [buffer release];
-       [term release];
-       [super dealloc];
-}
-
-@end
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark -
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * The AsyncWritePacket encompasses the instructions for any given write.
-**/
-@interface AsyncWritePacket : NSObject
-{
-  @public
-       NSData *buffer;
-       CFIndex bytesDone;
-       long tag;
-       NSTimeInterval timeout;
-}
-- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i;
-@end
-
-@implementation AsyncWritePacket
-
-- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i
-{
-       if((self = [super init]))
-       {
-               buffer = [d retain];
-               timeout = t;
-               tag = i;
-               bytesDone = 0;
-       }
-       return self;
-}
-
-- (void)dealloc
-{
-       [buffer release];
-       [super dealloc];
-}
-
-@end
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark -
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * The AsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues.
- * This class my be altered to support more than just TLS in the future.
-**/
-@interface AsyncSpecialPacket : NSObject
-{
-  @public
-       NSDictionary *tlsSettings;
-}
-- (id)initWithTLSSettings:(NSDictionary *)settings;
-@end
-
-@implementation AsyncSpecialPacket
-
-- (id)initWithTLSSettings:(NSDictionary *)settings
-{
-       if((self = [super init]))
-       {
-               tlsSettings = [settings copy];
-       }
-       return self;
-}
-
-- (void)dealloc
-{
-       [tlsSettings release];
-       [super dealloc];
-}
-
-@end
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark -
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-@implementation AsyncSocket
-
-- (id)init
-{
-       return [self initWithDelegate:nil userData:0];
-}
-
-- (id)initWithDelegate:(id)delegate
-{
-       return [self initWithDelegate:delegate userData:0];
-}
-
-// Designated initializer.
-- (id)initWithDelegate:(id)delegate userData:(long)userData
-{
-       if((self = [super init]))
-       {
-               theFlags = DEFAULT_PREBUFFERING ? kEnablePreBuffering : 0;
-               theDelegate = delegate;
-               theUserData = userData;
-               
-               theSocket4 = NULL;
-               theSource4 = NULL;
-               theSocket6 = NULL;
-               theSource6 = NULL;
-               theRunLoop = NULL;
-               theReadStream = NULL;
-               theWriteStream = NULL;
-               
-               theConnectTimer = nil;
-               
-               theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY];
-               theCurrentRead = nil;
-               theReadTimer = nil;
-               
-               partialReadBuffer = [[NSMutableData alloc] initWithCapacity:READALL_CHUNKSIZE];
-               
-               theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY];
-               theCurrentWrite = nil;
-               theWriteTimer = nil;
-               
-               // Socket context
-               NSAssert(sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext != CFStreamClientContext");
-               theContext.version = 0;
-               theContext.info = self;
-               theContext.retain = nil;
-               theContext.release = nil;
-               theContext.copyDescription = nil;
-               
-               // Default run loop modes
-               theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain];
-       }
-       return self;
-}
-
-// The socket may been initialized in a connected state and auto-released, so this should close it down cleanly.
-- (void)dealloc
-{
-       [self close];
-       [theReadQueue release];
-       [theWriteQueue release];
-       [theRunLoopModes release];
-       [partialReadBuffer release];
-       [NSObject cancelPreviousPerformRequestsWithTarget:self];
-       [super dealloc];
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Accessors
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (long)userData
-{
-       return theUserData;
-}
-
-- (void)setUserData:(long)userData
-{
-       theUserData = userData;
-}
-
-- (id)delegate
-{
-       return theDelegate;
-}
-
-- (void)setDelegate:(id)delegate
-{
-       theDelegate = delegate;
-}
-
-- (BOOL)canSafelySetDelegate
-{
-       return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil && theCurrentWrite == nil);
-}
-
-- (CFSocketRef)getCFSocket
-{
-       if(theSocket4)
-               return theSocket4;
-       else
-               return theSocket6;
-}
-
-- (CFReadStreamRef)getCFReadStream
-{
-       return theReadStream;
-}
-
-- (CFWriteStreamRef)getCFWriteStream
-{
-       return theWriteStream;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Progress
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
-{
-       // Check to make sure we're actually reading something right now,
-       // and that the read packet isn't an AsyncSpecialPacket (upgrade to TLS).
-       if (!theCurrentRead || ![theCurrentRead isKindOfClass:[AsyncReadPacket class]]) return NAN;
-       
-       // It's only possible to know the progress of our read if we're reading to a certain length
-       // If we're reading to data, we of course have no idea when the data will arrive
-       // If we're reading to timeout, then we have no idea when the next chunk of data will arrive.
-       BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO && theCurrentRead->term == nil);
-       
-       CFIndex d = theCurrentRead->bytesDone;
-       CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0;
-       if (tag != NULL)   *tag = theCurrentRead->tag;
-       if (done != NULL)  *done = d;
-       if (total != NULL) *total = t;
-       float ratio = (float)d/(float)t;
-       return isnan(ratio) ? 1.0F : ratio; // 0 of 0 bytes is 100% done.
-}
-
-- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
-{
-       // Check to make sure we're actually writing something right now,
-       // and that the write packet isn't an AsyncSpecialPacket (upgrade to TLS).
-       if (!theCurrentWrite || ![theCurrentWrite isKindOfClass:[AsyncWritePacket class]]) return NAN;
-       
-       CFIndex d = theCurrentWrite->bytesDone;
-       CFIndex t = [theCurrentWrite->buffer length];
-       if (tag != NULL)   *tag = theCurrentWrite->tag;
-       if (done != NULL)  *done = d;
-       if (total != NULL) *total = t;
-       return (float)d/(float)t;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Run Loop
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (void)runLoopAddSource:(CFRunLoopSourceRef)source
-{
-       unsigned i, count = [theRunLoopModes count];
-       for(i = 0; i < count; i++)
-       {
-               CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
-               CFRunLoopAddSource(theRunLoop, source, runLoopMode);
-       }
-}
-
-- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source
-{
-       unsigned i, count = [theRunLoopModes count];
-       for(i = 0; i < count; i++)
-       {
-               CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
-               CFRunLoopRemoveSource(theRunLoop, source, runLoopMode);
-       }
-}
-
-- (void)runLoopAddTimer:(NSTimer *)timer
-{
-       unsigned i, count = [theRunLoopModes count];
-       for(i = 0; i < count; i++)
-       {
-               CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
-               CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
-       }
-}
-
-- (void)runLoopRemoveTimer:(NSTimer *)timer
-{
-       unsigned i, count = [theRunLoopModes count];
-       for(i = 0; i < count; i++)              
-       {
-               CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
-               CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
-       }
-}
-
-- (void)runLoopUnscheduleReadStream
-{
-       unsigned i, count = [theRunLoopModes count];
-       for(i = 0; i < count; i++)
-       {
-               CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
-               CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode);
-       }
-       CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL);
-}
-
-- (void)runLoopUnscheduleWriteStream
-{
-       unsigned i, count = [theRunLoopModes count];
-       for(i = 0; i < count; i++)
-       {
-               CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
-               CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode);
-       }
-       CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Configuration
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * See the header file for a full explanation of pre-buffering.
-**/
-- (void)enablePreBuffering
-{
-       theFlags |= kEnablePreBuffering;
-}
-
-/**
- * See the header file for a full explanation of this method.
-**/
-- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop
-{
-       NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
-                        @"moveToRunLoop must be called from within the current RunLoop!");
-       
-       if(runLoop == nil)
-       {
-               return NO;
-       }
-       if(theRunLoop == [runLoop getCFRunLoop])
-       {
-               return YES;
-       }
-       
-       [NSObject cancelPreviousPerformRequestsWithTarget:self];
-       theFlags &= ~kDequeueReadScheduled;
-       theFlags &= ~kDequeueWriteScheduled;
-       
-       if(theReadStream && theWriteStream)
-    {
-        [self runLoopUnscheduleReadStream];
-        [self runLoopUnscheduleWriteStream];
-    }
-    
-       if(theSource4) [self runLoopRemoveSource:theSource4];
-       if(theSource6) [self runLoopRemoveSource:theSource6];
-    
-       // We do not retain the timers - they get retained by the runloop when we add them as a source.
-       // Since we're about to remove them as a source, we retain now, and release again below.
-       [theReadTimer retain];
-       [theWriteTimer retain];
-       
-       if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
-       if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
-       
-       theRunLoop = [runLoop getCFRunLoop];
-       
-       if(theReadTimer) [self runLoopAddTimer:theReadTimer];
-       if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
-       
-       // Release timers since we retained them above
-       [theReadTimer release];
-       [theWriteTimer release];
-       
-       if(theSource4) [self runLoopAddSource:theSource4];
-       if(theSource6) [self runLoopAddSource:theSource6];
-    
-    if(theReadStream && theWriteStream)
-       {
-               if(![self attachStreamsToRunLoop:runLoop error:nil])
-               {
-                       return NO;
-               }
-       }
-       
-       [runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes];
-       [runLoop performSelector:@selector(maybeDequeueWrite) target:self argument:nil order:0 modes:theRunLoopModes];
-       [runLoop performSelector:@selector(maybeScheduleDisconnect) target:self argument:nil order:0 modes:theRunLoopModes];
-       
-       return YES;
-}
-
-/**
- * See the header file for a full explanation of this method.
-**/
-- (BOOL)setRunLoopModes:(NSArray *)runLoopModes
-{
-       NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
-                        @"setRunLoopModes must be called from within the current RunLoop!");
-       
-       if([runLoopModes count] == 0)
-       {
-               return NO;
-       }
-       if([theRunLoopModes isEqualToArray:runLoopModes])
-       {
-               return YES;
-       }
-       
-       [NSObject cancelPreviousPerformRequestsWithTarget:self];
-       theFlags &= ~kDequeueReadScheduled;
-       theFlags &= ~kDequeueWriteScheduled;
-       
-       if(theReadStream && theWriteStream)
-    {
-        [self runLoopUnscheduleReadStream];
-        [self runLoopUnscheduleWriteStream];
-    }
-    
-       if(theSource4) [self runLoopRemoveSource:theSource4];
-       if(theSource6) [self runLoopRemoveSource:theSource6];
-    
-       // We do not retain the timers - they get retained by the runloop when we add them as a source.
-       // Since we're about to remove them as a source, we retain now, and release again below.
-       [theReadTimer retain];
-       [theWriteTimer retain];
-       
-       if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
-       if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
-       
-       [theRunLoopModes release];
-       theRunLoopModes = [runLoopModes copy];
-       
-       if(theReadTimer) [self runLoopAddTimer:theReadTimer];
-       if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
-       
-       // Release timers since we retained them above
-       [theReadTimer release];
-       [theWriteTimer release];
-       
-       if(theSource4) [self runLoopAddSource:theSource4];
-       if(theSource6) [self runLoopAddSource:theSource6];
-    
-       if(theReadStream && theWriteStream)
-       {
-               // Note: theRunLoop variable is a CFRunLoop, and NSRunLoop is NOT toll-free bridged with CFRunLoop.
-               // So we cannot pass theRunLoop to the method below, which is expecting a NSRunLoop parameter.
-               // Instead we pass nil, which will result in the method properly using the current run loop.
-               
-               if(![self attachStreamsToRunLoop:nil error:nil])
-               {
-                       return NO;
-               }
-       }
-       
-       [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
-       [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
-       [self performSelector:@selector(maybeScheduleDisconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
-       
-       return YES;
-}
-
-- (NSArray *)runLoopModes
-{
-       return [[theRunLoopModes retain] autorelease];
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Accepting
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr
-{
-       return [self acceptOnInterface:nil port:port error:errPtr];
-}
-       
-/**
- * To accept on a certain interface, pass the address to accept on.
- * To accept on any interface, pass nil or an empty string.
- * To accept only connections from localhost pass "localhost" or "loopback".
-**/
-- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr
-{
-       if (theDelegate == NULL)
-    {
-               [NSException raise:AsyncSocketException
-                           format:@"Attempting to accept without a delegate. Set a delegate first."];
-    }
-       
-       if (theSocket4 != NULL || theSocket6 != NULL)
-    {
-               [NSException raise:AsyncSocketException
-                           format:@"Attempting to accept while connected or accepting connections. Disconnect first."];
-    }
-
-       // Set up the listen sockaddr structs if needed.
-       
-       NSData *address4 = nil, *address6 = nil;
-       if(interface == nil || ([interface length] == 0))
-       {
-               // Accept on ANY address
-               struct sockaddr_in nativeAddr4;
-               nativeAddr4.sin_len         = sizeof(struct sockaddr_in);
-               nativeAddr4.sin_family      = AF_INET;
-               nativeAddr4.sin_port        = htons(port);
-               nativeAddr4.sin_addr.s_addr = htonl(INADDR_ANY);
-               memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
-               
-               struct sockaddr_in6 nativeAddr6;
-               nativeAddr6.sin6_len       = sizeof(struct sockaddr_in6);
-               nativeAddr6.sin6_family    = AF_INET6;
-               nativeAddr6.sin6_port      = htons(port);
-               nativeAddr6.sin6_flowinfo  = 0;
-               nativeAddr6.sin6_addr      = in6addr_any;
-               nativeAddr6.sin6_scope_id  = 0;
-               
-               // Wrap the native address structures for CFSocketSetAddress.
-               address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
-               address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
-       }
-       else if([interface isEqualToString:@"localhost"] || [interface isEqualToString:@"loopback"])
-       {
-               // Accept only on LOOPBACK address
-               struct sockaddr_in nativeAddr4;
-               nativeAddr4.sin_len         = sizeof(struct sockaddr_in);
-               nativeAddr4.sin_family      = AF_INET;
-               nativeAddr4.sin_port        = htons(port);
-               nativeAddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-               memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
-       
-               struct sockaddr_in6 nativeAddr6;
-               nativeAddr6.sin6_len       = sizeof(struct sockaddr_in6);
-               nativeAddr6.sin6_family    = AF_INET6;
-               nativeAddr6.sin6_port      = htons(port);
-               nativeAddr6.sin6_flowinfo  = 0;
-               nativeAddr6.sin6_addr      = in6addr_loopback;
-               nativeAddr6.sin6_scope_id  = 0;
-               
-               // Wrap the native address structures for CFSocketSetAddress.
-               address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
-               address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
-       }
-       else
-       {
-               NSString *portStr = [NSString stringWithFormat:@"%hu", port];
-
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
-               @synchronized (getaddrinfoLock)
-#endif
-               {
-                       struct addrinfo hints, *res, *res0;
-                       
-                       memset(&hints, 0, sizeof(hints));
-                       hints.ai_family   = PF_UNSPEC;
-                       hints.ai_socktype = SOCK_STREAM;
-                       hints.ai_protocol = IPPROTO_TCP;
-                       hints.ai_flags    = AI_PASSIVE;
-                       
-                       int error = getaddrinfo([interface UTF8String], [portStr UTF8String], &hints, &res0);
-                       
-                       if(error)
-                       {
-                               if(errPtr)
-                               {
-                                       NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
-                                       NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-                                       
-                                       *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
-                               }
-                       }
-                       
-                       for(res = res0; res; res = res->ai_next)
-                       {
-                               if(!address4 && (res->ai_family == AF_INET))
-                               {
-                                       // Found IPv4 address
-                                       // Wrap the native address structures for CFSocketSetAddress.
-                                       address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
-                               }
-                               else if(!address6 && (res->ai_family == AF_INET6))
-                               {
-                                       // Found IPv6 address
-                                       // Wrap the native address structures for CFSocketSetAddress.
-                                       address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
-                               }
-                       }
-                       freeaddrinfo(res0);
-               }
-               
-               if(!address4 && !address6) return NO;
-       }
-
-       // Create the sockets.
-
-       if (address4)
-       {
-               theSocket4 = [self newAcceptSocketForAddress:address4 error:errPtr];
-               if (theSocket4 == NULL) goto Failed;
-       }
-       
-       if (address6)
-       {
-               theSocket6 = [self newAcceptSocketForAddress:address6 error:errPtr];
-               
-               // Note: The iPhone doesn't currently support IPv6
-               
-#if !TARGET_OS_IPHONE
-               if (theSocket6 == NULL) goto Failed;
-#endif
-       }
-       
-       // Attach the sockets to the run loop so that callback methods work
-       
-       [self attachSocketsToRunLoop:nil error:nil];
-       
-       // Set the SO_REUSEADDR flags.
-
-       int reuseOn = 1;
-       if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
-       if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
-
-       // Set the local bindings which causes the sockets to start listening.
-
-       CFSocketError err;
-       if (theSocket4)
-       {
-               err = CFSocketSetAddress (theSocket4, (CFDataRef)address4);
-               if (err != kCFSocketSuccess) goto Failed;
-               
-               //NSLog(@"theSocket4: %hu", [self localPort:theSocket4]);
-       }
-       
-       if(port == 0 && theSocket4 && theSocket6)
-       {
-               // The user has passed in port 0, which means he wants to allow the kernel to choose the port for them
-               // However, the kernel will choose a different port for both theSocket4 and theSocket6
-               // So we grab the port the kernel choose for theSocket4, and set it as the port for theSocket6
-               UInt16 chosenPort = [self localPort:theSocket4];
-               
-               struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes];
-               pSockAddr6->sin6_port = htons(chosenPort);
-    }
-       
-       if (theSocket6)
-       {
-               err = CFSocketSetAddress (theSocket6, (CFDataRef)address6);
-               if (err != kCFSocketSuccess) goto Failed;
-               
-               //NSLog(@"theSocket6: %hu", [self localPort:theSocket6]);
-       }
-
-       theFlags |= kDidPassConnectMethod;
-       return YES;
-       
-Failed:
-       if(errPtr) *errPtr = [self getSocketError];
-       if(theSocket4 != NULL)
-       {
-               CFSocketInvalidate(theSocket4);
-               CFRelease(theSocket4);
-               theSocket4 = NULL;
-       }
-       if(theSocket6 != NULL)
-       {
-               CFSocketInvalidate(theSocket6);
-               CFRelease(theSocket6);
-               theSocket6 = NULL;
-       }
-       return NO;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Connecting
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr
-{
-       return [self connectToHost:hostname onPort:port withTimeout:-1 error:errPtr];
-}
-
-/**
- * This method creates an initial CFReadStream and CFWriteStream to the given host on the given port.
- * The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds.
- *
- * Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection,
- * specifically in the onSocketWillConnect: method.
-**/
-- (BOOL)connectToHost:(NSString *)hostname
-                          onPort:(UInt16)port
-                 withTimeout:(NSTimeInterval)timeout
-                               error:(NSError **)errPtr
-{
-       if(theDelegate == NULL)
-       {
-               [NSException raise:AsyncSocketException
-                           format:@"Attempting to connect without a delegate. Set a delegate first."];
-       }
-
-       if(theSocket4 != NULL || theSocket6 != NULL)
-       {
-               [NSException raise:AsyncSocketException
-                           format:@"Attempting to connect while connected or accepting connections. Disconnect first."];
-       }
-       
-       if(![self createStreamsToHost:hostname onPort:port error:errPtr]) goto Failed;
-       if(![self attachStreamsToRunLoop:nil error:errPtr])               goto Failed;
-       if(![self configureStreamsAndReturnError:errPtr])                 goto Failed;
-       if(![self openStreamsAndReturnError:errPtr])                      goto Failed;
-       
-       [self startConnectTimeout:timeout];
-       theFlags |= kDidPassConnectMethod;
-       
-       return YES;
-       
-Failed:
-       [self close];
-       return NO;
-}
-
-- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
-{
-       return [self connectToAddress:remoteAddr withTimeout:-1 error:errPtr];
-}
-
-/**
- * This method creates an initial CFSocket to the given address.
- * The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be
- * created from the low-level sockets after the connection succeeds.
- *
- * Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection,
- * specifically in the onSocketWillConnect: method.
- * 
- * Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from
- * NSNetservice addresses method.
- * If you have an existing struct sockaddr you can convert it to an NSData object like so:
- * struct sockaddr sa  -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
- * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
-**/
-- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
-{
-       if (theDelegate == NULL)
-       {
-               [NSException raise:AsyncSocketException
-                           format:@"Attempting to connect without a delegate. Set a delegate first."];
-       }
-       
-       if (theSocket4 != NULL || theSocket6 != NULL)
-       {
-               [NSException raise:AsyncSocketException
-                           format:@"Attempting to connect while connected or accepting connections. Disconnect first."];
-       }
-       
-       if(![self createSocketForAddress:remoteAddr error:errPtr])   goto Failed;
-       if(![self attachSocketsToRunLoop:nil error:errPtr])          goto Failed;
-       if(![self configureSocketAndReturnError:errPtr])             goto Failed;
-       if(![self connectSocketToAddress:remoteAddr error:errPtr])   goto Failed;
-       
-       [self startConnectTimeout:timeout];
-       theFlags |= kDidPassConnectMethod;
-       
-       return YES;
-       
-Failed:
-       [self close];
-       return NO;
-}
-
-- (void)startConnectTimeout:(NSTimeInterval)timeout
-{
-       if(timeout >= 0.0)
-       {
-               theConnectTimer = [NSTimer timerWithTimeInterval:timeout
-                                                                                             target:self 
-                                                                                           selector:@selector(doConnectTimeout:)
-                                                                                           userInfo:nil
-                                                                                            repeats:NO];
-               [self runLoopAddTimer:theConnectTimer];
-       }
-}
-
-- (void)endConnectTimeout
-{
-       [theConnectTimer invalidate];
-       theConnectTimer = nil;
-}
-
-- (void)doConnectTimeout:(NSTimer *)timer
-{
-       [self endConnectTimeout];
-       [self closeWithError:[self getConnectTimeoutError]];
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Socket Implementation
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Creates the accept sockets.
- * Returns true if either IPv4 or IPv6 is created.
- * If either is missing, an error is returned (even though the method may return true).
-**/
-- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr
-{
-       struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes];
-       int addressFamily = pSockAddr->sa_family;
-       
-       CFSocketRef theSocket = CFSocketCreate(kCFAllocatorDefault,
-                                              addressFamily,
-                                              SOCK_STREAM,
-                                              0,
-                                              kCFSocketAcceptCallBack,                // Callback flags
-                                              (CFSocketCallBack)&MyCFSocketCallback,  // Callback method
-                                              &theContext);
-
-       if(theSocket == NULL)
-       {
-               if(errPtr) *errPtr = [self getSocketError];
-       }
-       
-       return theSocket;
-}
-
-- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr
-{
-       struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes];
-       
-       if(pSockAddr->sa_family == AF_INET)
-       {
-               theSocket4 = CFSocketCreate(NULL,                                   // Default allocator
-                                           PF_INET,                                // Protocol Family
-                                           SOCK_STREAM,                            // Socket Type
-                                           IPPROTO_TCP,                            // Protocol
-                                           kCFSocketConnectCallBack,               // Callback flags
-                                           (CFSocketCallBack)&MyCFSocketCallback,  // Callback method
-                                           &theContext);                           // Socket Context
-               
-               if(theSocket4 == NULL)
-               {
-                       if (errPtr) *errPtr = [self getSocketError];
-                       return NO;
-               }
-       }
-       else if(pSockAddr->sa_family == AF_INET6)
-       {
-               theSocket6 = CFSocketCreate(NULL,                                   // Default allocator
-                                                                   PF_INET6,                               // Protocol Family
-                                                                   SOCK_STREAM,                            // Socket Type
-                                                                   IPPROTO_TCP,                            // Protocol
-                                                                   kCFSocketConnectCallBack,               // Callback flags
-                                                                   (CFSocketCallBack)&MyCFSocketCallback,  // Callback method
-                                                                   &theContext);                           // Socket Context
-               
-               if(theSocket6 == NULL)
-               {
-                       if (errPtr) *errPtr = [self getSocketError];
-                       return NO;
-               }
-       }
-       else
-       {
-               if (errPtr) *errPtr = [self getSocketError];
-               return NO;
-       }
-       
-       return YES;
-}
-
-/**
- * Adds the CFSocket's to the run-loop so that callbacks will work properly.
-**/
-- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
-{      
-       // Get the CFRunLoop to which the socket should be attached.
-       theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
-       
-       if(theSocket4)
-       {
-               theSource4 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket4, 0);
-        [self runLoopAddSource:theSource4];
-       }
-       
-       if(theSocket6)
-       {
-               theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0);
-        [self runLoopAddSource:theSource6];
-       }
-       
-       return YES;
-}
-
-/**
- * Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect.
- * Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened.
-**/
-- (BOOL)configureSocketAndReturnError:(NSError **)errPtr
-{
-       // Call the delegate method for further configuration.
-       if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
-       {
-               if([theDelegate onSocketWillConnect:self] == NO)
-               {
-                       if (errPtr) *errPtr = [self getAbortError];
-                       return NO;
-               }
-       }
-       return YES;
-}
-
-- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
-{
-       // Start connecting to the given address in the background
-       // The MyCFSocketCallback method will be called when the connection succeeds or fails
-       if(theSocket4)
-       {
-               CFSocketError err = CFSocketConnectToAddress(theSocket4, (CFDataRef)remoteAddr, -1);
-               if(err != kCFSocketSuccess)
-               {
-                       if (errPtr) *errPtr = [self getSocketError];
-                       return NO;
-               }
-       }
-       else if(theSocket6)
-       {
-               CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1);
-               if(err != kCFSocketSuccess)
-               {
-                       if (errPtr) *errPtr = [self getSocketError];
-                       return NO;
-               }
-       }
-       
-       return YES;
-}
-
-/**
- * Attempt to make the new socket.
- * If an error occurs, ignore this event.
-**/
-- (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative
-{
-       // New socket inherits same delegate and run loop modes.
-       // Note: We use [self class] to support subclassing AsyncSocket.
-       AsyncSocket *newSocket = [[[[self class] alloc] initWithDelegate:theDelegate] autorelease];
-       [newSocket setRunLoopModes:theRunLoopModes];
-       
-       if(newSocket)
-       {
-               if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)])
-                       [theDelegate onSocket:self didAcceptNewSocket:newSocket];
-               
-               NSRunLoop *runLoop = nil;
-               if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)])
-                       runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket];
-               
-               BOOL pass = YES;
-               
-               if(pass && ![newSocket createStreamsFromNative:newNative error:nil]) pass = NO;
-               if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil])    pass = NO;
-               if(pass && ![newSocket configureStreamsAndReturnError:nil])          pass = NO;
-               if(pass && ![newSocket openStreamsAndReturnError:nil])               pass = NO;
-               
-               if(pass)
-                       newSocket->theFlags |= kDidPassConnectMethod;
-               else {
-                       // No NSError, but errors will still get logged from the above functions.
-                       [newSocket close];
-               }
-               
-       }
-}
-
-/**
- * Description forthcoming...
-**/
-- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError
-{
-       NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
-       
-       if(socketError == kCFSocketTimeout || socketError == kCFSocketError)
-       {
-               [self closeWithError:[self getSocketError]];
-               return;
-       }
-       
-       // Get the underlying native (BSD) socket
-       CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock);
-       
-       // Setup the socket so that invalidating the socket will not close the native socket
-       CFSocketSetSocketFlags(sock, 0);
-       
-       // Invalidate and release the CFSocket - All we need from here on out is the nativeSocket
-       // Note: If we don't invalidate the socket (leaving the native socket open)
-       // then theReadStream and theWriteStream won't function properly.
-       // Specifically, their callbacks won't work, with the exception of kCFStreamEventOpenCompleted.
-       // I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created,
-       // as opposed to the CFReadStream/CFWriteStream.
-       
-       CFSocketInvalidate(sock);
-       CFRelease(sock);
-       theSocket4 = NULL;
-       theSocket6 = NULL;
-       
-       NSError *err;
-       BOOL pass = YES;
-       
-       if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO;
-       if(pass && ![self attachStreamsToRunLoop:nil error:&err])           pass = NO;
-       if(pass && ![self openStreamsAndReturnError:&err])                  pass = NO;
-       
-       if(!pass)
-       {
-               [self closeWithError:err];
-       }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Stream Implementation
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Creates the CFReadStream and CFWriteStream from the given native socket.
- * The CFSocket may be extracted from either stream after the streams have been opened.
- * 
- * Note: The given native socket must already be connected!
-**/
-- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr
-{
-       // Create the socket & streams.
-       CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream);
-       if (theReadStream == NULL || theWriteStream == NULL)
-       {
-               NSError *err = [self getStreamError];
-               
-               NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err);
-               
-               if (errPtr) *errPtr = err;
-               return NO;
-       }
-       
-       // Ensure the CF & BSD socket is closed when the streams are closed.
-       CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
-       CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
-       
-       return YES;
-}
-
-/**
- * Creates the CFReadStream and CFWriteStream from the given hostname and port number.
- * The CFSocket may be extracted from either stream after the streams have been opened.
-**/
-- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr
-{
-       // Create the socket & streams.
-       CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream);
-       if (theReadStream == NULL || theWriteStream == NULL)
-       {
-               if (errPtr) *errPtr = [self getStreamError];
-               return NO;
-       }
-       
-       // Ensure the CF & BSD socket is closed when the streams are closed.
-       CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
-       CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
-       
-       return YES;
-}
-
-- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
-{
-       // Get the CFRunLoop to which the socket should be attached.
-       theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
-
-       // Setup read stream callbacks
-       
-       CFOptionFlags readStreamEvents = kCFStreamEventHasBytesAvailable | 
-                                        kCFStreamEventErrorOccurred     |
-                                        kCFStreamEventEndEncountered    |
-                                        kCFStreamEventOpenCompleted;
-       
-       if (!CFReadStreamSetClient(theReadStream,
-                                                          readStreamEvents,
-                                                          (CFReadStreamClientCallBack)&MyCFReadStreamCallback,
-                                                          (CFStreamClientContext *)(&theContext)))
-       {
-               NSError *err = [self getStreamError];
-               
-               NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self);
-               NSLog (@"Error: %@", err);
-               
-               if (errPtr) *errPtr = err;
-               return NO;
-       }
-
-       // Setup write stream callbacks
-       
-       CFOptionFlags writeStreamEvents = kCFStreamEventCanAcceptBytes |
-                                         kCFStreamEventErrorOccurred  |
-                                         kCFStreamEventEndEncountered |
-                                         kCFStreamEventOpenCompleted;
-       
-       if (!CFWriteStreamSetClient (theWriteStream,
-                                                                writeStreamEvents,
-                                                                (CFWriteStreamClientCallBack)&MyCFWriteStreamCallback,
-                                                                (CFStreamClientContext *)(&theContext)))
-       {
-               NSError *err = [self getStreamError];
-               
-               NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self);
-               NSLog (@"Error: %@", err);
-               
-               if (errPtr) *errPtr = err;
-               return NO;
-       }
-       
-       // Add read and write streams to run loop
-       
-       unsigned i, count = [theRunLoopModes count];
-       for(i = 0; i < count; i++)
-       {
-               CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
-               CFReadStreamScheduleWithRunLoop(theReadStream, theRunLoop, runLoopMode);
-               CFWriteStreamScheduleWithRunLoop(theWriteStream, theRunLoop, runLoopMode);
-       }
-       
-       return YES;
-}
-
-/**
- * Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect.
- * Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened.
-**/
-- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr
-{
-       // Call the delegate method for further configuration.
-       if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
-       {
-               if([theDelegate onSocketWillConnect:self] == NO)
-               {
-                       if (errPtr) *errPtr = [self getAbortError];
-                       return NO;
-               }
-       }
-       return YES;
-}
-
-- (BOOL)openStreamsAndReturnError:(NSError **)errPtr
-{
-       BOOL pass = YES;
-       
-       if(pass && !CFReadStreamOpen (theReadStream))
-       {
-               NSLog (@"AsyncSocket %p couldn't open read stream,", self);
-               pass = NO;
-       }
-       
-       if(pass && !CFWriteStreamOpen (theWriteStream))
-       {
-               NSLog (@"AsyncSocket %p couldn't open write stream,", self);
-               pass = NO;
-       }
-       
-       if(!pass)
-       {
-               if (errPtr) *errPtr = [self getStreamError];
-       }
-       
-       return pass;
-}
-
-/**
- * Called when read or write streams open.
- * When the socket is connected and both streams are open, consider the AsyncSocket instance to be ready.
-**/
-- (void)doStreamOpen
-{
-       NSError *err = nil;
-       if ((theFlags & kDidCompleteOpenForRead) && (theFlags & kDidCompleteOpenForWrite))
-       {
-               // Get the socket.
-               if (![self setSocketFromStreamsAndReturnError: &err])
-               {
-                       NSLog (@"AsyncSocket %p couldn't get socket from streams, %@. Disconnecting.", self, err);
-                       [self closeWithError:err];
-                       return;
-               }
-               
-        // Stop the connection attempt timeout timer
-               [self endConnectTimeout];
-        
-               if ([theDelegate respondsToSelector:@selector(onSocket:didConnectToHost:port:)])
-               {
-                       [theDelegate onSocket:self didConnectToHost:[self connectedHost] port:[self connectedPort]];
-               }
-               
-               // Immediately deal with any already-queued requests.
-               [self maybeDequeueRead];
-               [self maybeDequeueWrite];
-       }
-}
-
-- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr
-{
-       // Get the CFSocketNativeHandle from theReadStream
-       CFSocketNativeHandle native;
-       CFDataRef nativeProp = CFReadStreamCopyProperty(theReadStream, kCFStreamPropertySocketNativeHandle);
-       if(nativeProp == NULL)
-       {
-               if (errPtr) *errPtr = [self getStreamError];
-               return NO;
-       }
-       
-       CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native);
-       CFRelease(nativeProp);
-       
-       CFSocketRef theSocket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL);
-       if(theSocket == NULL)
-       {
-               if (errPtr) *errPtr = [self getSocketError];
-               return NO;
-       }
-       
-       // Determine whether the connection was IPv4 or IPv6
-       CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket);
-       if(peeraddr == NULL)
-       {
-               NSLog(@"AsyncSocket couldn't determine IP version of socket");
-               
-               CFRelease(theSocket);
-               
-               if (errPtr) *errPtr = [self getSocketError];
-               return NO;
-       }
-       struct sockaddr *sa = (struct sockaddr *)CFDataGetBytePtr(peeraddr);
-       
-       if(sa->sa_family == AF_INET)
-       {
-               theSocket4 = theSocket;
-       }
-       else
-       {
-               theSocket6 = theSocket;
-       }
-       
-       CFRelease(peeraddr);
-
-       return YES;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Disconnect Implementation
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Sends error message and disconnects
-- (void)closeWithError:(NSError *)err
-{
-       theFlags |= kClosingWithError;
-       
-       if (theFlags & kDidPassConnectMethod)
-       {
-               // Try to salvage what data we can.
-               [self recoverUnreadData];
-               
-               // Let the delegate know, so it can try to recover if it likes.
-               if ([theDelegate respondsToSelector:@selector(onSocket:willDisconnectWithError:)])
-               {
-                       [theDelegate onSocket:self willDisconnectWithError:err];
-               }
-       }
-       [self close];
-}
-
-// Prepare partially read data for recovery.
-- (void)recoverUnreadData
-{
-       if(theCurrentRead != nil)
-       {
-               // We never finished the current read.
-               // Check to see if it's a normal read packet (not AsyncSpecialPacket) and if it had read anything yet.
-               
-               if(([theCurrentRead isKindOfClass:[AsyncReadPacket class]]) && (theCurrentRead->bytesDone > 0))
-               {
-                       // We need to move its data into the front of the partial read buffer.
-                       
-                       [partialReadBuffer replaceBytesInRange:NSMakeRange(0, 0)
-                                                                                withBytes:[theCurrentRead->buffer bytes]
-                                                                                       length:theCurrentRead->bytesDone];
-               }
-       }
-       
-       [self emptyQueues];
-}
-
-- (void)emptyQueues
-{
-       if (theCurrentRead != nil)      [self endCurrentRead];
-       if (theCurrentWrite != nil)     [self endCurrentWrite];
-       
-       [theReadQueue removeAllObjects];
-       [theWriteQueue removeAllObjects];
-       
-       [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueRead) object:nil];
-       [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueWrite) object:nil];
-       
-       theFlags &= ~kDequeueReadScheduled;
-       theFlags &= ~kDequeueWriteScheduled;
-}
-
-/**
- * Disconnects. This is called for both error and clean disconnections.
-**/
-- (void)close
-{
-       // Empty queues
-       [self emptyQueues];
-       
-       // Clear partialReadBuffer (pre-buffer and also unreadData buffer in case of error)
-       [partialReadBuffer replaceBytesInRange:NSMakeRange(0, [partialReadBuffer length]) withBytes:NULL length:0];
-       
-       [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(disconnect) object:nil];
-       
-       // Stop the connection attempt timeout timer
-       if (theConnectTimer != nil)
-       {
-               [self endConnectTimeout];
-       }
-       
-       // Close streams.
-       if (theReadStream != NULL)
-       {
-        [self runLoopUnscheduleReadStream];
-               CFReadStreamClose(theReadStream);
-               CFRelease(theReadStream);
-               theReadStream = NULL;
-       }
-       if (theWriteStream != NULL)
-       {
-        [self runLoopUnscheduleWriteStream];
-               CFWriteStreamClose(theWriteStream);
-               CFRelease(theWriteStream);
-               theWriteStream = NULL;
-       }
-       
-       // Close sockets.
-       if (theSocket4 != NULL)
-       {
-               CFSocketInvalidate (theSocket4);
-               CFRelease (theSocket4);
-               theSocket4 = NULL;
-       }
-       if (theSocket6 != NULL)
-       {
-               CFSocketInvalidate (theSocket6);
-               CFRelease (theSocket6);
-               theSocket6 = NULL;
-       }
-    if (theSource4 != NULL) 
-    {
-        [self runLoopRemoveSource:theSource4];
-               CFRelease (theSource4);
-               theSource4 = NULL;
-       }
-       if (theSource6 != NULL)
-       {
-        [self runLoopRemoveSource:theSource6];
-               CFRelease (theSource6);
-               theSource6 = NULL;
-       }
-       theRunLoop = NULL;
-       
-       // If the client has passed the connect/accept method, then the connection has at least begun.
-       // Notify delegate that it is now ending.
-       BOOL shouldCallDelegate = (theFlags & kDidPassConnectMethod);
-       
-       // Clear all flags (except the pre-buffering flag, which should remain as is)
-       theFlags &= kEnablePreBuffering;
-       
-       if (shouldCallDelegate)
-       {
-               if ([theDelegate respondsToSelector: @selector(onSocketDidDisconnect:)])
-               {
-                       [theDelegate onSocketDidDisconnect:self];
-               }
-       }
-       
-       // Do not access any instance variables after calling onSocketDidDisconnect.
-       // This gives the delegate freedom to release us without returning here and crashing.
-}
-
-/**
- * Disconnects immediately. Any pending reads or writes are dropped.
-**/
-- (void)disconnect
-{
-       [self close];
-}
-
-/**
- * Diconnects after all pending reads have completed.
-**/
-- (void)disconnectAfterReading
-{
-       theFlags |= (kForbidReadsWrites | kDisconnectAfterReads);
-       
-       [self maybeScheduleDisconnect];
-}
-
-/**
- * Disconnects after all pending writes have completed.
-**/
-- (void)disconnectAfterWriting
-{
-       theFlags |= (kForbidReadsWrites | kDisconnectAfterWrites);
-       
-       [self maybeScheduleDisconnect];
-}
-
-/**
- * Disconnects after all pending reads and writes have completed.
-**/
-- (void)disconnectAfterReadingAndWriting
-{
-       theFlags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites);
-       
-       [self maybeScheduleDisconnect];
-}
-
-/**
- * Schedules a call to disconnect if possible.
- * That is, if all writes have completed, and we're set to disconnect after writing,
- * or if all reads have completed, and we're set to disconnect after reading.
-**/
-- (void)maybeScheduleDisconnect
-{
-       BOOL shouldDisconnect = NO;
-       
-       if(theFlags & kDisconnectAfterReads)
-       {
-               if(([theReadQueue count] == 0) && (theCurrentRead == nil))
-               {
-                       if(theFlags & kDisconnectAfterWrites)
-                       {
-                               if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
-                               {
-                                       shouldDisconnect = YES;
-                               }
-                       }
-                       else
-                       {
-                               shouldDisconnect = YES;
-                       }
-               }
-       }
-       else if(theFlags & kDisconnectAfterWrites)
-       {
-               if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
-               {
-                       shouldDisconnect = YES;
-               }
-       }
-       
-       if(shouldDisconnect)
-       {
-               [self performSelector:@selector(disconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
-       }
-}
-
-/**
- * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
- * any data that's left on the socket.
-**/
-- (NSData *)unreadData
-{
-       // Ensure this method will only return data in the event of an error
-       if(!(theFlags & kClosingWithError)) return nil;
-       
-       if(theReadStream == NULL) return nil;
-       
-       CFIndex totalBytesRead = [partialReadBuffer length];
-       BOOL error = NO;
-       while(!error && CFReadStreamHasBytesAvailable(theReadStream))
-       {
-               [partialReadBuffer increaseLengthBy:READALL_CHUNKSIZE];
-               
-               // Number of bytes to read is space left in packet buffer.
-               CFIndex bytesToRead = [partialReadBuffer length] - totalBytesRead;
-               
-               // Read data into packet buffer
-               UInt8 *packetbuf = (UInt8 *)( [partialReadBuffer mutableBytes] + totalBytesRead );
-               CFIndex bytesRead = CFReadStreamRead(theReadStream, packetbuf, bytesToRead);
-               
-               // Check results
-               if(bytesRead < 0)
-               {
-                       error = YES;
-               }
-               else
-               {
-                       totalBytesRead += bytesRead;
-               }
-       }
-       
-       [partialReadBuffer setLength:totalBytesRead];
-       
-       return partialReadBuffer;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Errors
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Returns a standard error object for the current errno value.
- * Errno is used for low-level BSD socket errors.
-**/
-- (NSError *)getErrnoError
-{
-       NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)];
-       NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey];
-       
-       return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
-}
-
-/**
- * Returns a standard error message for a CFSocket error.
- * Unfortunately, CFSocket offers no feedback on its errors.
-**/
-- (NSError *)getSocketError
-{
-       NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCFSocketError",
-                                                                                                                @"AsyncSocket", [NSBundle mainBundle],
-                                                                                                                @"General CFSocket error", nil);
-       
-       NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-       
-       return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCFSocketError userInfo:info];
-}
-
-- (NSError *)getStreamError
-{
-       CFStreamError err;
-       if (theReadStream != NULL)
-       {
-               err = CFReadStreamGetError (theReadStream);
-               if (err.error != 0) return [self errorFromCFStreamError: err];
-       }
-       
-       if (theWriteStream != NULL)
-       {
-               err = CFWriteStreamGetError (theWriteStream);
-               if (err.error != 0) return [self errorFromCFStreamError: err];
-       }
-       
-       return nil;
-}
-
-/**
- * Returns a standard AsyncSocket abort error.
-**/
-- (NSError *)getAbortError
-{
-       NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCanceledError",
-                                                                                                                @"AsyncSocket", [NSBundle mainBundle],
-                                                                                                                @"Connection canceled", nil);
-       
-       NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-       
-       return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCanceledError userInfo:info];
-}
-
-/**
- * Returns a standard AsyncSocket connect timeout error.
-**/
-- (NSError *)getConnectTimeoutError
-{
-       NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketConnectTimeoutError",
-                                                                                                                @"AsyncSocket", [NSBundle mainBundle],
-                                                                                                                @"Attempt to connect to host timed out", nil);
-       
-       NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-       
-       return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketConnectTimeoutError userInfo:info];
-}
-
-/**
- * Returns a standard AsyncSocket maxed out error.
-**/
-- (NSError *)getReadMaxedOutError
-{
-       NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadMaxedOutError",
-                                                                                                                @"AsyncSocket", [NSBundle mainBundle],
-                                                                                                                @"Read operation reached set maximum length", nil);
-       
-       NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-       
-       return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadMaxedOutError userInfo:info];
-}
-
-/**
- * Returns a standard AsyncSocket read timeout error.
-**/
-- (NSError *)getReadTimeoutError
-{
-       NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadTimeoutError",
-                                                                                                                @"AsyncSocket", [NSBundle mainBundle],
-                                                                                                                @"Read operation timed out", nil);
-       
-       NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-       
-       return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadTimeoutError userInfo:info];
-}
-
-/**
- * Returns a standard AsyncSocket write timeout error.
-**/
-- (NSError *)getWriteTimeoutError
-{
-       NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketWriteTimeoutError",
-                                                                                                                @"AsyncSocket", [NSBundle mainBundle],
-                                                                                                                @"Write operation timed out", nil);
-       
-       NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-       
-       return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketWriteTimeoutError userInfo:info];
-}
-
-- (NSError *)errorFromCFStreamError:(CFStreamError)err
-{
-       if (err.domain == 0 && err.error == 0) return nil;
-       
-       // Can't use switch; these constants aren't int literals.
-       NSString *domain = @"CFStreamError (unlisted domain)";
-       NSString *message = nil;
-       
-       if(err.domain == kCFStreamErrorDomainPOSIX) {
-               domain = NSPOSIXErrorDomain;
-       }
-       else if(err.domain == kCFStreamErrorDomainMacOSStatus) {
-               domain = NSOSStatusErrorDomain;
-       }
-       else if(err.domain == kCFStreamErrorDomainMach) {
-               domain = NSMachErrorDomain;
-       }
-       else if(err.domain == kCFStreamErrorDomainNetDB)
-       {
-               domain = @"kCFStreamErrorDomainNetDB";
-               message = [NSString stringWithCString:gai_strerror(err.error) encoding:NSASCIIStringEncoding];
-       }
-       else if(err.domain == kCFStreamErrorDomainNetServices) {
-               domain = @"kCFStreamErrorDomainNetServices";
-       }
-       else if(err.domain == kCFStreamErrorDomainSOCKS) {
-               domain = @"kCFStreamErrorDomainSOCKS";
-       }
-       else if(err.domain == kCFStreamErrorDomainSystemConfiguration) {
-               domain = @"kCFStreamErrorDomainSystemConfiguration";
-       }
-       else if(err.domain == kCFStreamErrorDomainSSL) {
-               domain = @"kCFStreamErrorDomainSSL";
-       }
-       
-       NSDictionary *info = nil;
-       if(message != nil)
-       {
-               info = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
-       }
-       return [NSError errorWithDomain:domain code:err.error userInfo:info];
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Diagnostics
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (BOOL)isConnected
-{
-       return [self isSocketConnected] && [self areStreamsConnected];
-}
-
-- (NSString *)connectedHost
-{
-       if(theSocket4)
-               return [self connectedHost:theSocket4];
-       else
-               return [self connectedHost:theSocket6];
-}
-
-- (UInt16)connectedPort
-{
-       if(theSocket4)
-               return [self connectedPort:theSocket4];
-       else
-               return [self connectedPort:theSocket6];
-}
-
-- (NSString *)localHost
-{
-       if(theSocket4)
-               return [self localHost:theSocket4];
-       else
-               return [self localHost:theSocket6];
-}
-
-- (UInt16)localPort
-{
-       if(theSocket4)
-               return [self localPort:theSocket4];
-       else
-               return [self localPort:theSocket6];
-}
-
-- (NSString *)connectedHost:(CFSocketRef)theSocket
-{
-       if (theSocket == NULL) return nil;
-       
-       CFDataRef peeraddr;
-       NSString *peerstr = nil;
-
-       if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
-       {
-               peerstr = [self addressHost:peeraddr];
-               CFRelease (peeraddr);
-       }
-
-       return peerstr;
-}
-
-- (UInt16)connectedPort:(CFSocketRef)theSocket
-{
-       if (theSocket == NULL) return 0;
-       
-       CFDataRef peeraddr;
-       UInt16 peerport = 0;
-
-       if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
-       {
-               peerport = [self addressPort:peeraddr];
-               CFRelease (peeraddr);
-       }
-
-       return peerport;
-}
-
-- (NSString *)localHost:(CFSocketRef)theSocket
-{
-       if (theSocket == NULL) return nil;
-       
-       CFDataRef selfaddr;
-       NSString *selfstr = nil;
-
-       if((selfaddr = CFSocketCopyAddress(theSocket)))
-       {
-               selfstr = [self addressHost:selfaddr];
-               CFRelease (selfaddr);
-       }
-
-       return selfstr;
-}
-
-- (UInt16)localPort:(CFSocketRef)theSocket
-{
-       if (theSocket == NULL) return 0;
-       
-       CFDataRef selfaddr;
-       UInt16 selfport = 0;
-
-       if ((selfaddr = CFSocketCopyAddress(theSocket)))
-       {
-               selfport = [self addressPort:selfaddr];
-               CFRelease (selfaddr);
-       }
-
-       return selfport;
-}
-
-- (NSString *)addressHost:(CFDataRef)cfaddr
-{
-       if (cfaddr == NULL) return nil;
-       
-       char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
-       struct sockaddr *pSockAddr = (struct sockaddr *) CFDataGetBytePtr (cfaddr);
-       struct sockaddr_in  *pSockAddrV4 = (struct sockaddr_in  *)pSockAddr;
-       struct sockaddr_in6 *pSockAddrV6 = (struct sockaddr_in6 *)pSockAddr;
-
-       const void *pAddr = (pSockAddr->sa_family == AF_INET) ?
-                                                       (void *)(&(pSockAddrV4->sin_addr)) :
-                                                       (void *)(&(pSockAddrV6->sin6_addr));
-
-       const char *pStr = inet_ntop (pSockAddr->sa_family, pAddr, addrBuf, sizeof(addrBuf));
-       if (pStr == NULL) [NSException raise: NSInternalInconsistencyException
-                                                                 format: @"Cannot convert address to string."];
-
-       return [NSString stringWithCString:pStr encoding:NSASCIIStringEncoding];
-}
-
-- (UInt16)addressPort:(CFDataRef)cfaddr
-{
-       if (cfaddr == NULL) return 0;
-    
-       struct sockaddr_in *pAddr = (struct sockaddr_in *) CFDataGetBytePtr (cfaddr);
-       return ntohs (pAddr->sin_port);
-}
-
-- (NSData *)connectedAddress
-{
-    CFSocketRef theSocket;
-    
-    if (theSocket4)
-        theSocket = theSocket4;
-    else
-        theSocket = theSocket6;
-    
-    if (theSocket == NULL) return nil;
-       
-       CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket);
-    
-    if (peeraddr == NULL) return nil;
-    
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
-    NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(peeraddr) length:CFDataGetLength(peeraddr)];
-    CFRelease(peeraddr);
-    return result;
-#else
-    return [(NSData *)NSMakeCollectable(peeraddr) autorelease];
-#endif
-}
-
-- (NSData *)localAddress
-{
-    CFSocketRef theSocket;
-    
-    if (theSocket4)
-        theSocket = theSocket4;
-    else
-        theSocket = theSocket6;
-    
-    if (theSocket == NULL) return nil;
-    
-    CFDataRef selfaddr = CFSocketCopyAddress(theSocket);
-    
-    if (selfaddr == NULL) return nil;
-    
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
-    NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(selfaddr) length:CFDataGetLength(selfaddr)];
-    CFRelease(selfaddr);
-    return result;
-#else
-    return [(NSData *)NSMakeCollectable(selfaddr) autorelease];
-#endif
-}
-
-- (BOOL)isIPv4
-{
-       return (theSocket4 != NULL);
-}
-
-- (BOOL)isIPv6
-{
-       return (theSocket6 != NULL);
-}
-
-- (BOOL)isSocketConnected
-{
-       if(theSocket4 != NULL)
-               return CFSocketIsValid(theSocket4);
-       else if(theSocket6 != NULL)
-               return CFSocketIsValid(theSocket6);
-       else
-               return NO;
-}
-
-- (BOOL)areStreamsConnected
-{
-       CFStreamStatus s;
-    
-       if (theReadStream != NULL)
-       {
-               s = CFReadStreamGetStatus (theReadStream);
-               if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusReading || s == kCFStreamStatusError) )
-                       return NO;
-       }
-       else return NO;
-    
-       if (theWriteStream != NULL)
-       {
-               s = CFWriteStreamGetStatus (theWriteStream);
-               if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusWriting || s == kCFStreamStatusError) )
-                       return NO;
-       }
-       else return NO;
-    
-       return YES;
-}
-
-- (NSString *)description
-{
-       static const char *statstr[] = {"not open","opening","open","reading","writing","at end","closed","has error"};
-       CFStreamStatus rs = (theReadStream != NULL) ? CFReadStreamGetStatus(theReadStream) : 0;
-       CFStreamStatus ws = (theWriteStream != NULL) ? CFWriteStreamGetStatus(theWriteStream) : 0;
-       
-       NSString *peerstr, *selfstr;
-       CFDataRef peeraddr4 = NULL, peeraddr6 = NULL, selfaddr4 = NULL, selfaddr6 = NULL;
-
-       if (theSocket4 || theSocket6)
-       {
-               if (theSocket4) peeraddr4 = CFSocketCopyPeerAddress(theSocket4);
-               if (theSocket6) peeraddr6 = CFSocketCopyPeerAddress(theSocket6);
-       
-               if(theSocket4 && theSocket6)
-               {
-                       peerstr = [NSString stringWithFormat: @"%@/%@ %u", 
-                                          [self addressHost:peeraddr4], [self addressHost:peeraddr6], [self addressPort:peeraddr4]];
-               }
-               else if(theSocket4)
-               {
-                       peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr4], [self addressPort:peeraddr4]];
-               }
-               else
-               {
-                       peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr6], [self addressPort:peeraddr6]];
-               }
-               
-               if(peeraddr4) CFRelease(peeraddr4);
-               if(peeraddr6) CFRelease(peeraddr6);
-               peeraddr4 = NULL;
-               peeraddr6 = NULL;
-       }
-       else peerstr = @"nowhere";
-
-       if (theSocket4 || theSocket6)
-       {
-               if (theSocket4) selfaddr4 = CFSocketCopyAddress (theSocket4);
-               if (theSocket6) selfaddr6 = CFSocketCopyAddress (theSocket6);
-       
-               if (theSocket4 && theSocket6)
-               {
-                       selfstr = [NSString stringWithFormat: @"%@/%@ %u",
-                                          [self addressHost:selfaddr4], [self addressHost:selfaddr6], [self addressPort:selfaddr4]];
-               }
-               else if (theSocket4)
-               {
-                       selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr4], [self addressPort:selfaddr4]];
-               }
-               else
-               {
-                       selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr6], [self addressPort:selfaddr6]];
-               }
-
-               if(selfaddr4) CFRelease(selfaddr4);
-               if(selfaddr6) CFRelease(selfaddr6);
-               selfaddr4 = NULL;
-               selfaddr6 = NULL;
-       }
-       else selfstr = @"nowhere";
-       
-       NSMutableString *ms = [[NSMutableString alloc] initWithCapacity:150];
-       
-       [ms appendString:[NSString stringWithFormat:@"<AsyncSocket %p", self]];
-       [ms appendString:[NSString stringWithFormat:@" local %@ remote %@ ", selfstr, peerstr]];
-       
-       unsigned readQueueCount  = (unsigned)[theReadQueue count];
-       unsigned writeQueueCount = (unsigned)[theWriteQueue count];
-       
-       [ms appendString:[NSString stringWithFormat:@"has queued %u reads %u writes, ", readQueueCount, writeQueueCount]];
-
-       if (theCurrentRead == nil)
-               [ms appendString: @"no current read, "];
-       else
-       {
-               int percentDone;
-               if ([theCurrentRead->buffer length] != 0)
-                       percentDone = (float)theCurrentRead->bytesDone /
-                                                 (float)[theCurrentRead->buffer length] * 100.0F;
-               else
-                       percentDone = 100.0F;
-
-               [ms appendString: [NSString stringWithFormat:@"currently read %u bytes (%d%% done), ",
-                       (unsigned int)[theCurrentRead->buffer length],
-                       theCurrentRead->bytesDone ? percentDone : 0]];
-       }
-
-       if (theCurrentWrite == nil)
-               [ms appendString: @"no current write, "];
-       else
-       {
-               int percentDone;
-               if ([theCurrentWrite->buffer length] != 0)
-                       percentDone = (float)theCurrentWrite->bytesDone /
-                                                 (float)[theCurrentWrite->buffer length] * 100.0F;
-               else
-                       percentDone = 100.0F;
-
-               [ms appendString: [NSString stringWithFormat:@"currently written %u (%d%%), ",
-                       (unsigned int)[theCurrentWrite->buffer length],
-                       theCurrentWrite->bytesDone ? percentDone : 0]];
-       }
-       
-       [ms appendString:[NSString stringWithFormat:@"read stream %p %s, ", theReadStream, statstr[rs]]];
-       [ms appendString:[NSString stringWithFormat:@"write stream %p %s", theWriteStream, statstr[ws]]];
-       
-       if(theFlags & kDisconnectAfterReads)
-       {
-               if(theFlags & kDisconnectAfterWrites)
-                       [ms appendString: @", will disconnect after reads & writes"];
-               else
-                       [ms appendString: @", will disconnect after reads"];
-       }
-       else if(theFlags & kDisconnectAfterWrites)
-       {
-               [ms appendString: @", will disconnect after writes"];
-       }
-       
-       if (![self isConnected]) [ms appendString: @", not connected"];
-
-       [ms appendString:@">"];
-
-       return [ms autorelease];
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Reading
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag
-{
-       if(length == 0) return;
-       if(theFlags & kForbidReadsWrites) return;
-       
-       NSMutableData *buffer = [[NSMutableData alloc] initWithLength:length];
-       AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
-                                                                                                                       timeout:timeout
-                                                                                                                               tag:tag
-                                                                                                  readAllAvailable:NO
-                                                                                                                terminator:nil
-                                                                                                                 maxLength:length];
-
-       [theReadQueue addObject:packet];
-       [self scheduleDequeueRead];
-
-       [packet release];
-       [buffer release];
-}
-
-- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
-{
-       [self readDataToData:data withTimeout:timeout maxLength:-1 tag:tag];
-}
-
-- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag
-{
-       if(data == nil || [data length] == 0) return;
-       if(length >= 0 && length < [data length]) return;
-       if(theFlags & kForbidReadsWrites) return;
-       
-       NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
-       AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
-                                                                                                                       timeout:timeout
-                                                                                                                               tag:tag 
-                                                                                                  readAllAvailable:NO 
-                                                                                                                terminator:data
-                                                                                                                 maxLength:length];
-       
-       [theReadQueue addObject:packet];
-       [self scheduleDequeueRead];
-       
-       [packet release];
-       [buffer release];
-}
-
-- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
-{
-       if (theFlags & kForbidReadsWrites) return;
-       
-       NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
-       AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
-                                                                                                                       timeout:timeout
-                                                                                                                               tag:tag
-                                                                                                  readAllAvailable:YES
-                                                                                                                terminator:nil
-                                                                                                                 maxLength:-1];
-       
-       [theReadQueue addObject:packet];
-       [self scheduleDequeueRead];
-       
-       [packet release];
-       [buffer release];
-}
-
-/**
- * Puts a maybeDequeueRead on the run loop. 
- * An assumption here is that selectors will be performed consecutively within their priority.
-**/
-- (void)scheduleDequeueRead
-{
-       if((theFlags & kDequeueReadScheduled) == 0)
-       {
-               theFlags |= kDequeueReadScheduled;
-               [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
-       }
-}
-
-/**
- * This method starts a new read, if needed.
- * It is called when a user requests a read,
- * or when a stream opens that may have requested reads sitting in the queue, etc.
-**/
-- (void)maybeDequeueRead
-{
-       // Unset the flag indicating a call to this method is scheduled
-       theFlags &= ~kDequeueReadScheduled;
-       
-       // If we're not currently processing a read AND we have an available read stream
-       if((theCurrentRead == nil) && (theReadStream != NULL))
-       {
-               if([theReadQueue count] > 0)
-               {
-                       // Dequeue the next object in the write queue
-                       theCurrentRead = [[theReadQueue objectAtIndex:0] retain];
-                       [theReadQueue removeObjectAtIndex:0];
-                       
-                       if([theCurrentRead isKindOfClass:[AsyncSpecialPacket class]])
-                       {
-                               // Attempt to start TLS
-                               theFlags |= kStartingReadTLS;
-                               
-                               // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set
-                               [self maybeStartTLS];
-                       }
-                       else
-                       {
-                               // Start time-out timer
-                               if(theCurrentRead->timeout >= 0.0)
-                               {
-                                       theReadTimer = [NSTimer timerWithTimeInterval:theCurrentRead->timeout
-                                                                                                                  target:self 
-                                                                                                                selector:@selector(doReadTimeout:)
-                                                                                                                userInfo:nil
-                                                                                                                 repeats:NO];
-                                       [self runLoopAddTimer:theReadTimer];
-                               }
-                               
-                               // Immediately read, if possible
-                               [self doBytesAvailable];
-                       }
-               }
-               else if(theFlags & kDisconnectAfterReads)
-               {
-                       if(theFlags & kDisconnectAfterWrites)
-                       {
-                               if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
-                               {
-                                       [self disconnect];
-                               }
-                       }
-                       else
-                       {
-                               [self disconnect];
-                       }
-               }
-       }
-}
-
-/**
- * Call this method in doBytesAvailable instead of CFReadStreamHasBytesAvailable().
- * This method supports pre-buffering properly as well as the kSocketHasBytesAvailable flag.
-**/
-- (BOOL)hasBytesAvailable
-{
-       if ((theFlags & kSocketHasBytesAvailable) || ([partialReadBuffer length] > 0))
-       {
-               return YES;
-       }
-       else
-       {
-               return CFReadStreamHasBytesAvailable(theReadStream);
-       }
-}
-
-/**
- * Call this method in doBytesAvailable instead of CFReadStreamRead().
- * This method support pre-buffering properly.
-**/
-- (CFIndex)readIntoBuffer:(UInt8 *)buffer maxLength:(CFIndex)length
-{
-       if([partialReadBuffer length] > 0)
-       {
-               // Determine the maximum amount of data to read
-               CFIndex bytesToRead = MIN(length, [partialReadBuffer length]);
-               
-               // Copy the bytes from the buffer
-               memcpy(buffer, [partialReadBuffer bytes], bytesToRead);
-               
-               // Remove the copied bytes from the buffer
-               [partialReadBuffer replaceBytesInRange:NSMakeRange(0, bytesToRead) withBytes:NULL length:0];
-               
-               return bytesToRead;
-       }
-       else
-       {
-               // Unset the "has-bytes-available" flag
-               theFlags &= ~kSocketHasBytesAvailable;
-               
-               return CFReadStreamRead(theReadStream, buffer, length);
-       }
-}
-
-/**
- * This method is called when a new read is taken from the read queue or when new data becomes available on the stream.
-**/
-- (void)doBytesAvailable
-{
-       // If data is available on the stream, but there is no read request, then we don't need to process the data yet.
-       // Also, if there is a read request, but no read stream setup yet, we can't process any data yet.
-       if((theCurrentRead != nil) && (theReadStream != NULL))
-       {
-               // Note: This method is not called if theCurrentRead is an AsyncSpecialPacket (startTLS packet)
-
-               CFIndex totalBytesRead = 0;
-               
-               BOOL done = NO;
-               BOOL socketError = NO;
-               BOOL maxoutError = NO;
-               
-               while(!done && !socketError && !maxoutError && [self hasBytesAvailable])
-               {
-                       BOOL didPreBuffer = NO;
-                       
-                       // There are 3 types of read packets:
-                       // 
-                       // 1) Read a specific length of data.
-                       // 2) Read all available data.
-                       // 3) Read up to a particular terminator.
-                       
-                       if(theCurrentRead->readAllAvailableData == YES)
-                       {
-                               // We're reading all available data.
-                               // 
-                               // Make sure there is at least READALL_CHUNKSIZE bytes available.
-                               // We don't want to increase the buffer any more than this or we'll waste space.
-                               // With prebuffering it's possible to read in a small chunk on the first read.
-                               
-                               unsigned buffInc = READALL_CHUNKSIZE - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
-                               [theCurrentRead->buffer increaseLengthBy:buffInc];
-                       }
-                       else if(theCurrentRead->term != nil)
-                       {
-                               // We're reading up to a terminator.
-                               // 
-                               // We may only want to read a few bytes.
-                               // Just enough to ensure we don't go past our term or over our max limit.
-                               // Unless pre-buffering is enabled, in which case we may want to read in a larger chunk.
-                               
-                               // If we already have data pre-buffered, we obviously don't want to pre-buffer it again.
-                               // So in this case we'll just read as usual.
-                               
-                               if(([partialReadBuffer length] > 0) || !(theFlags & kEnablePreBuffering))
-                               {
-                                       unsigned maxToRead = [theCurrentRead readLengthForTerm];
-                                       
-                                       unsigned bufInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
-                                       [theCurrentRead->buffer increaseLengthBy:bufInc];
-                               }
-                               else
-                               {
-                                       didPreBuffer = YES;
-                                       unsigned maxToRead = [theCurrentRead prebufferReadLengthForTerm];
-                                       
-                                       unsigned buffInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
-                                       [theCurrentRead->buffer increaseLengthBy:buffInc];
-
-                               }
-                       }
-                       
-                       // Number of bytes to read is space left in packet buffer.
-                       CFIndex bytesToRead = [theCurrentRead->buffer length] - theCurrentRead->bytesDone;
-                       
-                       // Read data into packet buffer
-                       UInt8 *subBuffer = (UInt8 *)([theCurrentRead->buffer mutableBytes] + theCurrentRead->bytesDone);
-                       CFIndex bytesRead = [self readIntoBuffer:subBuffer maxLength:bytesToRead];
-                       
-                       // Check results
-                       if(bytesRead < 0)
-                       {
-                               socketError = YES;
-                       }
-                       else
-                       {
-                               // Update total amount read for the current read
-                               theCurrentRead->bytesDone += bytesRead;
-                               
-                               // Update total amount read in this method invocation
-                               totalBytesRead += bytesRead;
-                       }
-
-                       // Is packet done?
-                       if(theCurrentRead->readAllAvailableData != YES)
-                       {
-                               if(theCurrentRead->term != nil)
-                               {
-                                       if(didPreBuffer)
-                                       {
-                                               // Search for the terminating sequence within the big chunk we just read.
-                                               CFIndex overflow = [theCurrentRead searchForTermAfterPreBuffering:bytesRead];
-                                               
-                                               if(overflow > 0)
-                                               {
-                                                       // Copy excess data into partialReadBuffer
-                                                       NSMutableData *buffer = theCurrentRead->buffer;
-                                                       const void *overflowBuffer = [buffer bytes] + theCurrentRead->bytesDone - overflow;
-                                                       
-                                                       [partialReadBuffer appendBytes:overflowBuffer length:overflow];
-                                                       
-                                                       // Update the bytesDone variable.
-                                                       // Note: The completeCurrentRead method will trim the buffer for us.
-                                                       theCurrentRead->bytesDone -= overflow;
-                                               }
-                                               
-                                               done = (overflow >= 0);
-                                       }
-                                       else
-                                       {
-                                               // Search for the terminating sequence at the end of the buffer
-                                               int termlen = [theCurrentRead->term length];
-                                               if(theCurrentRead->bytesDone >= termlen)
-                                               {
-                                                       const void *buf = [theCurrentRead->buffer bytes] + (theCurrentRead->bytesDone - termlen);
-                                                       const void *seq = [theCurrentRead->term bytes];
-                                                       done = (memcmp (buf, seq, termlen) == 0);
-                                               }
-                                       }
-                                       
-                                       if(!done && theCurrentRead->maxLength >= 0 && theCurrentRead->bytesDone >= theCurrentRead->maxLength)
-                                       {
-                                               // There's a set maxLength, and we've reached that maxLength without completing the read
-                                               maxoutError = YES;
-                                       }
-                               }
-                               else
-                               {
-                                       // Done when (sized) buffer is full.
-                                       done = ([theCurrentRead->buffer length] == theCurrentRead->bytesDone);
-                               }
-                       }
-                       // else readAllAvailable doesn't end until all readable is read.
-               }
-               
-               if(theCurrentRead->readAllAvailableData && theCurrentRead->bytesDone > 0)
-               {
-                       // Ran out of bytes, so the "read-all-available-data" type packet is done
-                       done = YES;
-               }
-
-               if(done)
-               {
-                       [self completeCurrentRead];
-                       if (!socketError) [self scheduleDequeueRead];
-               }
-               else if(totalBytesRead > 0)
-               {
-                       // We're not done with the readToLength or readToData yet, but we have read in some bytes
-                       if ([theDelegate respondsToSelector:@selector(onSocket:didReadPartialDataOfLength:tag:)])
-                       {
-                               [theDelegate onSocket:self didReadPartialDataOfLength:totalBytesRead tag:theCurrentRead->tag];
-                       }
-               }
-
-               if(socketError)
-               {
-                       CFStreamError err = CFReadStreamGetError(theReadStream);
-                       [self closeWithError:[self errorFromCFStreamError:err]];
-                       return;
-               }
-               if(maxoutError)
-               {
-                       [self closeWithError:[self getReadMaxedOutError]];
-                       return;
-               }
-       }
-}
-
-// Ends current read and calls delegate.
-- (void)completeCurrentRead
-{
-       NSAssert(theCurrentRead, @"Trying to complete current read when there is no current read.");
-       
-       [theCurrentRead->buffer setLength:theCurrentRead->bytesDone];
-       if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)])
-       {
-               [theDelegate onSocket:self didReadData:theCurrentRead->buffer withTag:theCurrentRead->tag];
-       }
-       
-       if (theCurrentRead != nil) [self endCurrentRead]; // Caller may have disconnected.
-}
-
-// Ends current read.
-- (void)endCurrentRead
-{
-       NSAssert(theCurrentRead, @"Trying to end current read when there is no current read.");
-       
-       [theReadTimer invalidate];
-       theReadTimer = nil;
-       
-       [theCurrentRead release];
-       theCurrentRead = nil;
-}
-
-- (void)doReadTimeout:(NSTimer *)timer
-{
-       NSTimeInterval timeoutExtension = 0.0;
-       
-       if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutReadWithTag:elapsed:bytesDone:)])
-       {
-               timeoutExtension = [theDelegate onSocket:self shouldTimeoutReadWithTag:theCurrentRead->tag
-                                                                              elapsed:theCurrentRead->timeout
-                                                                            bytesDone:theCurrentRead->bytesDone];
-       }
-       
-       if(timeoutExtension > 0.0)
-       {
-               theCurrentRead->timeout += timeoutExtension;
-               
-               theReadTimer = [NSTimer timerWithTimeInterval:timeoutExtension
-                                                                                          target:self 
-                                                                                        selector:@selector(doReadTimeout:)
-                                                                                        userInfo:nil
-                                                                                         repeats:NO];
-               [self runLoopAddTimer:theReadTimer];
-       }
-       else
-       {
-               // Do not call endCurrentRead here.
-               // We must allow the delegate access to any partial read in the unreadData method.
-               
-               [self closeWithError:[self getReadTimeoutError]];
-       }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Writing
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
-{
-       if (data == nil || [data length] == 0) return;
-       if (theFlags & kForbidReadsWrites) return;
-       
-       AsyncWritePacket *packet = [[AsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
-       
-       [theWriteQueue addObject:packet];
-       [self scheduleDequeueWrite];
-       
-       [packet release];
-}
-
-- (void)scheduleDequeueWrite
-{
-       if((theFlags & kDequeueWriteScheduled) == 0)
-       {
-               theFlags |= kDequeueWriteScheduled;
-               [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
-       }
-}
-
-/**
- * Conditionally starts a new write.
- * 
- * IF there is not another write in process
- * AND there is a write queued
- * AND we have a write stream available
- * 
- * This method also handles auto-disconnect post read/write completion.
-**/
-- (void)maybeDequeueWrite
-{
-       // Unset the flag indicating a call to this method is scheduled
-       theFlags &= ~kDequeueWriteScheduled;
-       
-       // If we're not currently processing a write AND we have an available write stream
-       if((theCurrentWrite == nil) && (theWriteStream != NULL))
-       {
-               if([theWriteQueue count] > 0)
-               {
-                       // Dequeue the next object in the write queue
-                       theCurrentWrite = [[theWriteQueue objectAtIndex:0] retain];
-                       [theWriteQueue removeObjectAtIndex:0];
-                       
-                       if([theCurrentWrite isKindOfClass:[AsyncSpecialPacket class]])
-                       {
-                               // Attempt to start TLS
-                               theFlags |= kStartingWriteTLS;
-                               
-                               // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set
-                               [self maybeStartTLS];
-                       }
-                       else
-                       {
-                               // Start time-out timer
-                               if(theCurrentWrite->timeout >= 0.0)
-                               {
-                                       theWriteTimer = [NSTimer timerWithTimeInterval:theCurrentWrite->timeout
-                                                                                                                       target:self
-                                                                                                                 selector:@selector(doWriteTimeout:)
-                                                                                                                 userInfo:nil
-                                                                                                                  repeats:NO];
-                                       [self runLoopAddTimer:theWriteTimer];
-                               }
-                               
-                               // Immediately write, if possible
-                               [self doSendBytes];
-                       }
-               }
-               else if(theFlags & kDisconnectAfterWrites)
-               {
-                       if(theFlags & kDisconnectAfterReads)
-                       {
-                               if(([theReadQueue count] == 0) && (theCurrentRead == nil))
-                               {
-                                       [self disconnect];
-                               }
-                       }
-                       else
-                       {
-                               [self disconnect];
-                       }
-               }
-       }
-}
-
-/**
- * Call this method in doSendBytes instead of CFWriteStreamCanAcceptBytes().
- * This method supports the kSocketCanAcceptBytes flag.
-**/
-- (BOOL)canAcceptBytes
-{
-       if (theFlags & kSocketCanAcceptBytes)
-       {
-               return YES;
-       }
-       else
-       {
-               return CFWriteStreamCanAcceptBytes(theWriteStream);
-       }
-}
-
-- (void)doSendBytes
-{
-       if((theCurrentWrite != nil) && (theWriteStream != NULL))
-       {
-               // Note: This method is not called if theCurrentWrite is an AsyncSpecialPacket (startTLS packet)
-               
-               CFIndex totalBytesWritten = 0;
-               
-               BOOL done = NO;
-               BOOL error = NO;
-               
-               while (!done && !error && [self canAcceptBytes])
-               {
-                       // Figure out what to write.
-                       CFIndex bytesRemaining = [theCurrentWrite->buffer length] - theCurrentWrite->bytesDone;
-                       CFIndex bytesToWrite = (bytesRemaining < WRITE_CHUNKSIZE) ? bytesRemaining : WRITE_CHUNKSIZE;
-                       UInt8 *writestart = (UInt8 *)([theCurrentWrite->buffer bytes] + theCurrentWrite->bytesDone);
-
-                       // Write.
-                       CFIndex bytesWritten = CFWriteStreamWrite(theWriteStream, writestart, bytesToWrite);
-
-                       // Unset the "can accept bytes" flag
-                       theFlags &= ~kSocketCanAcceptBytes;
-                       
-                       // Check results
-                       if (bytesWritten < 0)
-                       {
-                               error = YES;
-                       }
-                       else
-                       {
-                               // Update total amount read for the current write
-                               theCurrentWrite->bytesDone += bytesWritten;
-                               
-                               // Update total amount written in this method invocation
-                               totalBytesWritten += bytesWritten;
-                               
-                               // Is packet done?
-                               done = ([theCurrentWrite->buffer length] == theCurrentWrite->bytesDone);
-                       }
-               }
-
-               if(done)
-               {
-                       [self completeCurrentWrite];
-                       [self scheduleDequeueWrite];
-               }
-               else if(error)
-               {
-                       CFStreamError err = CFWriteStreamGetError(theWriteStream);
-                       [self closeWithError:[self errorFromCFStreamError:err]];
-                       return;
-               }
-               else
-               {
-                       // We're not done with the entire write, but we have written some bytes
-                       if ([theDelegate respondsToSelector:@selector(onSocket:didWritePartialDataOfLength:tag:)])
-                       {
-                               [theDelegate onSocket:self didWritePartialDataOfLength:totalBytesWritten tag:theCurrentWrite->tag];
-                       }
-               }
-       }
-}
-
-// Ends current write and calls delegate.
-- (void)completeCurrentWrite
-{
-       NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write.");
-       
-       if ([theDelegate respondsToSelector:@selector(onSocket:didWriteDataWithTag:)])
-       {
-               [theDelegate onSocket:self didWriteDataWithTag:theCurrentWrite->tag];
-       }
-       
-       if (theCurrentWrite != nil) [self endCurrentWrite]; // Caller may have disconnected.
-}
-
-// Ends current write.
-- (void)endCurrentWrite
-{
-       NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write.");
-       
-       [theWriteTimer invalidate];
-       theWriteTimer = nil;
-       
-       [theCurrentWrite release];
-       theCurrentWrite = nil;
-}
-
-- (void)doWriteTimeout:(NSTimer *)timer
-{
-       NSTimeInterval timeoutExtension = 0.0;
-       
-       if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutWriteWithTag:elapsed:bytesDone:)])
-       {
-               timeoutExtension = [theDelegate onSocket:self shouldTimeoutWriteWithTag:theCurrentWrite->tag
-                                                                               elapsed:theCurrentWrite->timeout
-                                                                             bytesDone:theCurrentWrite->bytesDone];
-       }
-       
-       if(timeoutExtension > 0.0)
-       {
-               theCurrentWrite->timeout += timeoutExtension;
-               
-               theWriteTimer = [NSTimer timerWithTimeInterval:timeoutExtension
-                                                       target:self 
-                                                     selector:@selector(doWriteTimeout:)
-                                                     userInfo:nil
-                                                      repeats:NO];
-               [self runLoopAddTimer:theWriteTimer];
-       }
-       else
-       {
-               [self closeWithError:[self getWriteTimeoutError]];
-       }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Security
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (void)startTLS:(NSDictionary *)tlsSettings
-{
-       if(tlsSettings == nil)
-    {
-        // Passing nil/NULL to CFReadStreamSetProperty will appear to work the same as passing an empty dictionary,
-        // but causes problems if we later try to fetch the remote host's certificate.
-        // 
-        // To be exact, it causes the following to return NULL instead of the normal result:
-        // CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates)
-        // 
-        // So we use an empty dictionary instead, which works perfectly.
-        
-        tlsSettings = [NSDictionary dictionary];
-    }
-       
-       AsyncSpecialPacket *packet = [[AsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings];
-       
-       [theReadQueue addObject:packet];
-       [self scheduleDequeueRead];
-       
-       [theWriteQueue addObject:packet];
-       [self scheduleDequeueWrite];
-       
-       [packet release];
-}
-
-- (void)maybeStartTLS
-{
-       // We can't start TLS until:
-       // - All queued reads prior to the user calling StartTLS are complete
-       // - All queued writes prior to the user calling StartTLS are complete
-       // 
-       // We'll know these conditions are met when both kStartingReadTLS and kStartingWriteTLS are set
-       
-       if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS))
-       {
-               AsyncSpecialPacket *tlsPacket = (AsyncSpecialPacket *)theCurrentRead;
-               
-               BOOL didStartOnReadStream = CFReadStreamSetProperty(theReadStream, kCFStreamPropertySSLSettings,
-                                                                                                                  (CFDictionaryRef)tlsPacket->tlsSettings);
-               BOOL didStartOnWriteStream = CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertySSLSettings,
-                                                                                                                        (CFDictionaryRef)tlsPacket->tlsSettings);
-               
-               if(!didStartOnReadStream || !didStartOnWriteStream)
-               {
-            [self closeWithError:[self getSocketError]];
-               }
-       }
-}
-
-- (void)onTLSHandshakeSuccessful
-{
-       if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS))
-       {
-               theFlags &= ~kStartingReadTLS;
-               theFlags &= ~kStartingWriteTLS;
-               
-               if([theDelegate respondsToSelector:@selector(onSocketDidSecure:)])
-               {
-                       [theDelegate onSocketDidSecure:self];
-               }
-               
-               [self endCurrentRead];
-               [self endCurrentWrite];
-               
-               [self scheduleDequeueRead];
-               [self scheduleDequeueWrite];
-       }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark CF Callbacks
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (void)doCFSocketCallback:(CFSocketCallBackType)type
-                                forSocket:(CFSocketRef)sock
-                          withAddress:(NSData *)address
-                                 withData:(const void *)pData
-{
-       NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
-       
-       switch (type)
-       {
-               case kCFSocketConnectCallBack:
-                       // The data argument is either NULL or a pointer to an SInt32 error code, if the connect failed.
-                       if(pData)
-                               [self doSocketOpen:sock withCFSocketError:kCFSocketError];
-                       else
-                               [self doSocketOpen:sock withCFSocketError:kCFSocketSuccess];
-                       break;
-               case kCFSocketAcceptCallBack:
-                       [self doAcceptWithSocket: *((CFSocketNativeHandle *)pData)];
-                       break;
-               default:
-                       NSLog (@"AsyncSocket %p received unexpected CFSocketCallBackType %d.", self, type);
-                       break;
-       }
-}
-
-- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream
-{
-       NSParameterAssert(theReadStream != NULL);
-       
-       CFStreamError err;
-       switch (type)
-       {
-               case kCFStreamEventOpenCompleted:
-                       theFlags |= kDidCompleteOpenForRead;
-                       [self doStreamOpen];
-                       break;
-               case kCFStreamEventHasBytesAvailable:
-                       if(theFlags & kStartingReadTLS) {
-                               [self onTLSHandshakeSuccessful];
-                       }
-                       else {
-                               theFlags |= kSocketHasBytesAvailable;
-                               [self doBytesAvailable];
-                       }
-                       break;
-               case kCFStreamEventErrorOccurred:
-               case kCFStreamEventEndEncountered:
-                       err = CFReadStreamGetError (theReadStream);
-                       [self closeWithError: [self errorFromCFStreamError:err]];
-                       break;
-               default:
-                       NSLog (@"AsyncSocket %p received unexpected CFReadStream callback, CFStreamEventType %d.", self, type);
-       }
-}
-
-- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream
-{
-       NSParameterAssert(theWriteStream != NULL);
-       
-       CFStreamError err;
-       switch (type)
-       {
-               case kCFStreamEventOpenCompleted:
-                       theFlags |= kDidCompleteOpenForWrite;
-                       [self doStreamOpen];
-                       break;
-               case kCFStreamEventCanAcceptBytes:
-                       if(theFlags & kStartingWriteTLS) {
-                               [self onTLSHandshakeSuccessful];
-                       }
-                       else {
-                               theFlags |= kSocketCanAcceptBytes;
-                               [self doSendBytes];
-                       }
-                       break;
-               case kCFStreamEventErrorOccurred:
-               case kCFStreamEventEndEncountered:
-                       err = CFWriteStreamGetError (theWriteStream);
-                       [self closeWithError: [self errorFromCFStreamError:err]];
-                       break;
-               default:
-                       NSLog (@"AsyncSocket %p received unexpected CFWriteStream callback, CFStreamEventType %d.", self, type);
-       }
-}
-
-/**
- * This is the callback we setup for CFSocket.
- * This method does nothing but forward the call to it's Objective-C counterpart
-**/
-static void MyCFSocketCallback (CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo)
-{
-       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-       
-       AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
-       [theSocket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData];
-       
-       [pool release];
-}
-
-/**
- * This is the callback we setup for CFReadStream.
- * This method does nothing but forward the call to it's Objective-C counterpart
-**/
-static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo)
-{
-       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-       
-       AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
-       [theSocket doCFReadStreamCallback:type forStream:stream];
-       
-       [pool release];
-}
-
-/**
- * This is the callback we setup for CFWriteStream.
- * This method does nothing but forward the call to it's Objective-C counterpart
-**/
-static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo)
-{
-       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-       
-       AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
-       [theSocket doCFWriteStreamCallback:type forStream:stream];
-       
-       [pool release];
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark Class Methods
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Return line separators.
-+ (NSData *)CRLFData
-{
-       return [NSData dataWithBytes:"\x0D\x0A" length:2];
-}
-
-+ (NSData *)CRData
-{
-       return [NSData dataWithBytes:"\x0D" length:1];
-}
-
-+ (NSData *)LFData
-{
-       return [NSData dataWithBytes:"\x0A" length:1];
-}
-
-+ (NSData *)ZeroData
-{
-       return [NSData dataWithBytes:"" length:1];
-}
-
-@end