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