BLIP/BLIPTest.m
author Dan Preston <danpreston@codechemistry.com>
Tue May 05 14:46:42 2009 -0700 (2009-05-05)
changeset 36 5165944a89b3
parent 29 59689fbdcf77
child 37 7c7d5a0cb4d6
permissions -rw-r--r--
Fixed a double release in a garbage collected environment.
     1 //
     2 //  BLIPTest.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 5/13/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #ifndef NDEBUG
    10 
    11 
    12 #import "BLIPRequest.h"
    13 #import "BLIPProperties.h"
    14 #import "BLIPConnection.h"
    15 
    16 #import "IPAddress.h"
    17 #import "Target.h"
    18 #import "CollectionUtils.h"
    19 #import "Logging.h"
    20 #import "Test.h"
    21 
    22 #import <Security/Security.h>
    23 #import <SecurityInterface/SFChooseIdentityPanel.h>
    24 
    25 @interface TCPEndpoint ()
    26 + (NSString*) describeCert: (SecCertificateRef)cert;
    27 + (NSString*) describeIdentity: (SecIdentityRef)identity;
    28 @end
    29 
    30 
    31 #define kListenerHost               @"localhost"
    32 #define kListenerPort               46353
    33 #define kSendInterval               0.5
    34 #define kNBatchedMessages           20
    35 #define kUseCompression             YES
    36 #define kUrgentEvery                4
    37 #define kListenerCloseAfter         50
    38 #define kClientAcceptCloseRequest   YES
    39 
    40 #define kListenerUsesSSL            YES     // Does the listener (server) use an SSL connection?
    41 #define kListenerRequiresClientCert YES     // Does the listener require clients to have an SSL cert?
    42 #define kClientRequiresSSL          YES     // Does the client require the listener to use SSL?
    43 #define kClientUsesSSLCert          YES     // Does the client use an SSL cert?
    44 
    45 
    46 static SecIdentityRef ChooseIdentity( NSString *prompt ) {
    47     NSMutableArray *identities = [NSMutableArray array];
    48     SecKeychainRef kc;
    49     SecKeychainCopyDefault(&kc);
    50     SecIdentitySearchRef search;
    51     SecIdentitySearchCreate(kc, CSSM_KEYUSE_ANY, &search);
    52     SecIdentityRef identity;
    53     while (SecIdentitySearchCopyNext(search, &identity) == noErr)
    54         [identities addObject: (id)identity];
    55     CFRelease(search);
    56     Log(@"Found %u identities -- prompting '%@'", identities.count, prompt);
    57     if (identities.count > 0) {
    58         SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
    59         if ([panel runModalForIdentities: identities message: prompt] == NSOKButton) {
    60             Log(@"Using SSL identity: %@", panel.identity);
    61             return panel.identity;
    62         }
    63     }
    64     return NULL;
    65 }
    66 
    67 static SecIdentityRef GetClientIdentity(void) {
    68     return ChooseIdentity(@"Choose an identity for the BLIP Client Test:");
    69 }
    70 
    71 static SecIdentityRef GetListenerIdentity(void) {
    72     return ChooseIdentity(@"Choose an identity for the BLIP Listener Test:");
    73 }
    74 
    75 
    76 #pragma mark -
    77 #pragma mark CLIENT TEST:
    78 
    79 
    80 @interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
    81 {
    82     BLIPConnection *_conn;
    83     NSMutableDictionary *_pending;
    84 }
    85 
    86 @end
    87 
    88 
    89 @implementation BLIPConnectionTester
    90 
    91 - (id) init
    92 {
    93     self = [super init];
    94     if (self != nil) {
    95         Log(@"** INIT %@",self);
    96         _pending = [[NSMutableDictionary alloc] init];
    97         IPAddress *addr = [[[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort] autorelease];
    98         _conn = [[BLIPConnection alloc] initToAddress: addr];
    99         if( ! _conn ) {
   100             [self release];
   101             return nil;
   102         }
   103         if( kClientUsesSSLCert ) {
   104             [_conn setPeerToPeerIdentity: GetClientIdentity()];
   105         } else if( kClientRequiresSSL ) {
   106             _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true},
   107                                         {(id)kCFStreamSSLPeerName, [NSNull null]});
   108         }
   109         _conn.delegate = self;
   110         Log(@"** Opening connection...");
   111         [_conn open];
   112     }
   113     return self;
   114 }
   115 
   116 - (void) dealloc
   117 {
   118     Log(@"** %@ closing",self);
   119     [_conn close];
   120     [_conn release];
   121     [super dealloc];
   122 }
   123 
   124 - (void) sendAMessage
   125 {
   126     if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) {
   127         if(_pending.count<100) {
   128             Log(@"** Sending another %i messages...", kNBatchedMessages);
   129             for( int i=0; i<kNBatchedMessages; i++ ) {
   130                 size_t size = random() % 32768;
   131                 NSMutableData *body = [NSMutableData dataWithLength: size];
   132                 UInt8 *bytes = body.mutableBytes;
   133                 for( size_t i=0; i<size; i++ )
   134                     bytes[i] = i % 256;
   135                 
   136                 BLIPRequest *q = [_conn requestWithBody: body
   137                                              properties: $dict({@"Content-Type", @"application/octet-stream"},
   138                                                                {@"User-Agent", @"BLIPConnectionTester"},
   139                                                                {@"Date", [[NSDate date] description]},
   140                                                                {@"Size",$sprintf(@"%u",size)})];
   141                 Assert(q);
   142                 if( kUseCompression && (random()%2==1) )
   143                     q.compressed = YES;
   144                 if( random()%16 > 12 )
   145                     q.urgent = YES;
   146                 BLIPResponse *response = [q send];
   147                 Assert(response);
   148                 Assert(q.number>0);
   149                 Assert(response.number==q.number);
   150                 [_pending setObject: $object(size) forKey: $object(q.number)];
   151                 response.onComplete = $target(self,responseArrived:);
   152             }
   153         } else {
   154             Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
   155         }
   156         [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
   157     }
   158 }
   159 
   160 - (void) responseArrived: (BLIPResponse*)response
   161 {
   162     Log(@"********** called responseArrived: %@",response);
   163 }
   164 
   165 - (void) connectionDidOpen: (TCPConnection*)connection
   166 {
   167     Log(@"** %@ didOpen",connection);
   168     [self sendAMessage];
   169 }
   170 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
   171 {
   172     Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
   173     return peerCert != nil;
   174 }
   175 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
   176 {
   177     Warn(@"** %@ failedToOpen: %@",connection,error);
   178     CFRunLoopStop(CFRunLoopGetCurrent());
   179 }
   180 - (void) connectionDidClose: (TCPConnection*)connection
   181 {
   182     if (connection.error)
   183         Warn(@"** %@ didClose: %@", connection,connection.error);
   184     else
   185         Log(@"** %@ didClose", connection);
   186     setObj(&_conn,nil);
   187     [NSObject cancelPreviousPerformRequestsWithTarget: self];
   188     CFRunLoopStop(CFRunLoopGetCurrent());
   189 }
   190 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
   191 {
   192     Log(@"***** %@ received %@",connection,request);
   193     [request respondWithData: request.body contentType: request.contentType];
   194 }
   195 
   196 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
   197 {
   198     Log(@"********** %@ received %@",connection,response);
   199     NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
   200 
   201     if( response.error )
   202         Warn(@"Got error response: %@",response.error);
   203     else {
   204         NSData *body = response.body;
   205         size_t size = body.length;
   206         Assert(size<32768);
   207         const UInt8 *bytes = body.bytes;
   208         for( size_t i=0; i<size; i++ )
   209             AssertEq(bytes[i],i % 256);
   210         AssertEq(size,sizeObj.unsignedIntValue);
   211     }
   212     Assert(sizeObj);
   213     [_pending removeObjectForKey: $object(response.number)];
   214     Log(@"Now %u replies pending", _pending.count);
   215 }
   216 
   217 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection
   218 {
   219     BOOL response = kClientAcceptCloseRequest;
   220     Log(@"***** %@ received a close request; returning %i",connection,response);
   221     return response;
   222 }
   223 
   224 
   225 @end
   226 
   227 
   228 TestCase(BLIPConnection) {
   229     SecKeychainSetUserInteractionAllowed(true);
   230     BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
   231     CAssert(tester);
   232     
   233     [[NSRunLoop currentRunLoop] run];
   234     
   235     Log(@"** Runloop stopped");
   236     [tester release];
   237 }
   238 
   239 
   240 
   241 
   242 #pragma mark LISTENER TEST:
   243 
   244 
   245 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
   246 {
   247     BLIPListener *_listener;
   248     int _nReceived;
   249 }
   250 
   251 @end
   252 
   253 
   254 @implementation BLIPTestListener
   255 
   256 - (id) init
   257 {
   258     self = [super init];
   259     if (self != nil) {
   260         _listener = [[BLIPListener alloc] initWithPort: kListenerPort];
   261         _listener.delegate = self;
   262         _listener.pickAvailablePort = YES;
   263         _listener.bonjourServiceType = @"_bliptest._tcp";
   264         if( kListenerUsesSSL ) {
   265             [_listener setPeerToPeerIdentity: GetListenerIdentity()];
   266             if (!kListenerRequiresClientCert)
   267                 [_listener setSSLProperty: $object(kTCPTryAuthenticate) 
   268                                    forKey: kTCPPropertySSLClientSideAuthentication];
   269         }
   270         Assert( [_listener open] );
   271         Log(@"%@ is listening...",self);
   272     }
   273     return self;
   274 }
   275 
   276 - (void) dealloc
   277 {
   278     Log(@"%@ closing",self);
   279     [_listener close];
   280     [_listener release];
   281     [super dealloc];
   282 }
   283 
   284 - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
   285 {
   286     Log(@"** %@ accepted %@",self,connection);
   287     connection.delegate = self;
   288 }
   289 
   290 - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
   291 {
   292     Log(@"** BLIPTestListener failed to open: %@",error);
   293 }
   294 
   295 - (void) listenerDidOpen: (TCPListener*)listener   {Log(@"** BLIPTestListener did open");}
   296 - (void) listenerDidClose: (TCPListener*)listener   {Log(@"** BLIPTestListener did close");}
   297 
   298 - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
   299 {
   300     Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
   301     return YES;
   302 }
   303 
   304 
   305 - (void) connectionDidOpen: (TCPConnection*)connection
   306 {
   307     Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
   308     _nReceived = 0;
   309 }
   310 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
   311 {
   312     Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
   313     return peerCert != nil || ! kListenerRequiresClientCert;
   314 }
   315 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
   316 {
   317     Log(@"** %@ failedToOpen: %@",connection,error);
   318 }
   319 - (void) connectionDidClose: (TCPConnection*)connection
   320 {
   321     if (connection.error)
   322         Warn(@"** %@ didClose: %@", connection,connection.error);
   323     else
   324         Log(@"** %@ didClose", connection);
   325     [connection release];
   326 }
   327 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
   328 {
   329     Log(@"***** %@ received %@",connection,request);
   330     NSData *body = request.body;
   331     size_t size = body.length;
   332     Assert(size<32768);
   333     const UInt8 *bytes = body.bytes;
   334     for( size_t i=0; i<size; i++ )
   335         AssertEq(bytes[i],i % 256);
   336     
   337     AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
   338     Assert([request valueOfProperty: @"User-Agent"] != nil);
   339     AssertEq((size_t)[[request valueOfProperty: @"Size"] intValue], size);
   340 
   341     [request respondWithData: body contentType: request.contentType];
   342     
   343     if( ++ _nReceived == kListenerCloseAfter ) {
   344         Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
   345         [connection close];
   346     }
   347 }
   348 
   349 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
   350 {
   351     Log(@"***** %@ received a close request",connection);
   352     return YES;
   353 }
   354 
   355 - (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error
   356 {
   357     Log(@"***** %@'s close request failed: %@",connection,error);
   358 }
   359 
   360 
   361 @end
   362 
   363 
   364 TestCase(BLIPListener) {
   365     EnableLogTo(BLIP,YES);
   366     EnableLogTo(PortMapper,YES);
   367     EnableLogTo(Bonjour,YES);
   368     SecKeychainSetUserInteractionAllowed(true);
   369     BLIPTestListener *listener = [[BLIPTestListener alloc] init];
   370     
   371     [[NSRunLoop currentRunLoop] run];
   372     
   373     [listener release];
   374 }
   375 
   376 
   377 #endif
   378 
   379 
   380 /*
   381  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   382  
   383  Redistribution and use in source and binary forms, with or without modification, are permitted
   384  provided that the following conditions are met:
   385  
   386  * Redistributions of source code must retain the above copyright notice, this list of conditions
   387  and the following disclaimer.
   388  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   389  and the following disclaimer in the documentation and/or other materials provided with the
   390  distribution.
   391  
   392  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   393  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   394  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   395  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   396  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   397   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   398  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   399  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   400  */