TCP/TCPStream.m
author Jens Alfke <jens@mooseyard.com>
Fri May 23 17:37:36 2008 -0700 (2008-05-23)
changeset 0 9d67172bb323
child 1 8267d5c429c4
permissions -rw-r--r--
First checkin after breaking out of Cloudy
     1 //
     2 //  TCPStream.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 5/10/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "TCPStream.h"
    10 #import "TCP_Internal.h"
    11 
    12 
    13 extern const CFStringRef _kCFStreamPropertySSLClientSideAuthentication; // in CFNetwork
    14 
    15 static NSError* fixStreamError( NSError *error );
    16 
    17 
    18 @implementation TCPStream
    19 
    20 
    21 - (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream
    22 {
    23     self = [super init];
    24     if (self != nil) {
    25         _conn = [conn retain];
    26         _stream = [stream retain];
    27         _stream.delegate = self;
    28         [_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
    29         LogTo(TCPVerbose,@"%@ initialized; status=%i", self,_stream.streamStatus);
    30     }
    31     return self;
    32 }
    33 
    34 
    35 - (void) dealloc
    36 {
    37     LogTo(TCP,@"DEALLOC %@",self);
    38     if( _stream )
    39         [self disconnect];
    40     [super dealloc];
    41 }
    42 
    43 
    44 - (id) propertyForKey: (CFStringRef)cfStreamProperty
    45 {
    46     return nil; // abstract -- overridden by TCPReader and TCPWriter
    47 }
    48 
    49 - (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
    50 { // abstract -- overridden by TCPReader and TCPWriter
    51 }
    52 
    53 
    54 #pragma mark -
    55 #pragma mark SSL:
    56 
    57 
    58 - (NSString*) securityLevel                 {return [_stream propertyForKey: NSStreamSocketSecurityLevelKey];}
    59 
    60 - (NSDictionary*) SSLProperties             {return [self propertyForKey: kCFStreamPropertySSLSettings];}
    61 
    62 - (void) setSSLProperties: (NSDictionary*)p
    63 {
    64     LogTo(TCPVerbose,@"%@ SSL settings := %@",self,p);
    65     [self setProperty: p forKey: kCFStreamPropertySSLSettings];
    66     
    67     id clientAuth = [p objectForKey: kTCPPropertySSLClientSideAuthentication];
    68     if( clientAuth )
    69         [self setProperty: clientAuth forKey: _kCFStreamPropertySSLClientSideAuthentication];
    70 }
    71 
    72 - (NSArray*) peerSSLCerts
    73 {
    74     Assert(self.isOpen);
    75     return [self propertyForKey: kCFStreamPropertySSLPeerCertificates];
    76 }
    77 
    78 
    79 #pragma mark -
    80 #pragma mark OPENING/CLOSING:
    81 
    82 
    83 - (void) open
    84 {
    85     Assert(_stream);
    86     AssertEq(_stream.streamStatus,NSStreamStatusNotOpen);
    87     LogTo(TCP,@"Opening %@",self);
    88     [_stream open];
    89 }
    90 
    91 
    92 - (void) disconnect
    93 {
    94     if( _stream ) {
    95         LogTo(TCP,@"Disconnect %@",self);
    96         _stream.delegate = nil;
    97         [_stream close];
    98         setObj(&_stream,nil);
    99     }
   100     setObj(&_conn,nil);
   101 }
   102 
   103 
   104 - (BOOL) close
   105 {
   106     if( self.isBusy ) {
   107         _shouldClose = YES;
   108         return NO;
   109     } else {
   110         LogTo(TCP,@"Closing %@",self);
   111         [[self retain] autorelease];    // don't let myself be dealloced in the midst of this
   112         [_conn _streamClosed: self];    // have to do this before disconnect
   113         [self disconnect];
   114         return YES;
   115     }
   116 }
   117 
   118 
   119 - (BOOL) isOpen
   120 {
   121     NSStreamStatus status = _stream.streamStatus;
   122     return status >= NSStreamStatusOpen && status < NSStreamStatusAtEnd;
   123 }
   124 
   125 - (BOOL) isBusy
   126 {
   127     return NO;  // abstract
   128 }
   129 
   130 
   131 - (void) _opened
   132 {
   133     [_conn _streamOpened: self];
   134 }
   135 
   136 - (void) _canRead
   137 {
   138     // abstract
   139 }
   140 
   141 - (void) _canWrite
   142 {
   143     // abstract
   144 }
   145 
   146 - (void) _gotEOF
   147 {
   148     if( self.isBusy )
   149         [self _gotError: [NSError errorWithDomain: NSPOSIXErrorDomain code: ECONNRESET userInfo: nil]];
   150     else {
   151         [self retain];
   152         [_conn _streamGotEOF: self];
   153         [self disconnect];
   154         [self release];
   155     }
   156 }
   157 
   158 - (BOOL) _gotError: (NSError*)error
   159 {
   160     [_conn _stream: self gotError: fixStreamError(error)];
   161     return NO;
   162 }
   163 
   164 - (BOOL) _gotError
   165 {
   166     NSError *error = _stream.streamError;
   167     if( ! error )
   168         error = [NSError errorWithDomain: NSPOSIXErrorDomain code: EIO userInfo: nil]; //fallback
   169     return [self _gotError: error];
   170 }
   171 
   172 
   173 - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)streamEvent 
   174 {
   175     [[self retain] autorelease];
   176     switch(streamEvent) {
   177         case NSStreamEventOpenCompleted:
   178             LogTo(TCPVerbose,@"%@ opened",self);
   179             [self _opened];
   180             break;
   181         case NSStreamEventHasBytesAvailable:
   182             if( ! [_conn _streamPeerCertAvailable: self] )
   183                 return;
   184             LogTo(TCPVerbose,@"%@ can read",self);
   185             [self _canRead];
   186             break;
   187         case NSStreamEventHasSpaceAvailable:
   188             if( ! [_conn _streamPeerCertAvailable: self] )
   189                 return;
   190             LogTo(TCPVerbose,@"%@ can write",self);
   191             [self _canWrite];
   192             break;
   193         case NSStreamEventErrorOccurred:
   194             LogTo(TCPVerbose,@"%@ got error",self);
   195             [self _gotError];
   196             break;
   197         case NSStreamEventEndEncountered:
   198             LogTo(TCPVerbose,@"%@ got EOF",self);
   199             [self _gotEOF];
   200             break;
   201         default:
   202             Warn(@"%@: unknown NSStreamEvent %i",self,streamEvent);
   203             break;
   204     }
   205     
   206     // If I was previously asked to close, try again in case I'm no longer busy
   207     if( _shouldClose )
   208         [self close];
   209 }
   210 
   211 
   212 @end
   213 
   214 
   215 
   216 
   217 @implementation TCPReader
   218 
   219 
   220 - (TCPWriter*) writer
   221 {
   222     return _conn.writer;
   223 }
   224 
   225 
   226 - (id) propertyForKey: (CFStringRef)cfStreamProperty
   227 {
   228     CFTypeRef value = CFReadStreamCopyProperty((CFReadStreamRef)_stream,cfStreamProperty);
   229     return [(id)CFMakeCollectable(value) autorelease];
   230 }
   231 
   232 - (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
   233 {
   234     if( ! CFReadStreamSetProperty((CFReadStreamRef)_stream,cfStreamProperty,(CFTypeRef)value) )
   235         Warn(@"%@ didn't accept property '%@'", self,cfStreamProperty);
   236 }
   237 
   238 
   239 @end
   240 
   241 
   242 
   243 
   244 static NSError* fixStreamError( NSError *error )
   245 {
   246     // NSStream incorrectly returns SSL errors without the correct error domain:
   247     if( $equal(error.domain,@"NSUnknownErrorDomain") ) {
   248         int code = error.code;
   249         if( -9899 <= code && code <= -9800 ) {
   250             NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
   251             if( ! [userInfo objectForKey: NSLocalizedFailureReasonErrorKey] ) {
   252                 // look up error message:
   253                 NSBundle *secBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Security.framework"];
   254                 NSString *message = [secBundle localizedStringForKey: $sprintf(@"%i",code)
   255                                                                value: nil
   256                                                                table: @"SecErrorMessages"];
   257                 if( message ) {
   258                     if( ! userInfo ) userInfo = $mdict();
   259                     [userInfo setObject: message forKey: NSLocalizedFailureReasonErrorKey];
   260                 }
   261             }
   262             error = [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
   263                                         code: code userInfo: userInfo];
   264         } else
   265             Warn(@"NSStream returned error with unknown domain: %@",error);
   266     }
   267     return error;
   268 }
   269 
   270 /*
   271  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   272  
   273  Redistribution and use in source and binary forms, with or without modification, are permitted
   274  provided that the following conditions are met:
   275  
   276  * Redistributions of source code must retain the above copyright notice, this list of conditions
   277  and the following disclaimer.
   278  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   279  and the following disclaimer in the documentation and/or other materials provided with the
   280  distribution.
   281  
   282  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   283  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   284  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   285  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   286  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   287   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   288  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   289  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   290  */