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