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