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