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