5 // Created by Jens Alfke on 5/13/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
12 #import "BLIPRequest.h"
13 #import "BLIPProperties.h"
14 #import "BLIPConnection.h"
18 #import "CollectionUtils.h"
22 #define HAVE_KEYCHAIN_FRAMEWORK 0
23 #if HAVE_KEYCHAIN_FRAMEWORK
24 #import <Keychain/Keychain.h>
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
42 static SecIdentityRef GetClientIdentity(void) {
43 return NULL; // Make this return a valid identity to test client-side certs
46 static SecIdentityRef GetListenerIdentity(void) {
47 return NULL; // Make this return a valid identity to test client-side certs
52 #pragma mark CLIENT TEST:
55 @interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
57 BLIPConnection *_conn;
58 NSMutableDictionary *_pending;
64 @implementation BLIPConnectionTester
70 Log(@"** INIT %@",self);
71 _pending = [[NSMutableDictionary alloc] init];
72 IPAddress *addr = [[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort];
73 _conn = [[BLIPConnection alloc] initToAddress: addr];
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];
88 _conn.delegate = self;
89 Log(@"** Opening connection...");
97 Log(@"** %@ closing",self);
103 - (void) sendAMessage
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++ )
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)})];
121 if( kUseCompression && (random()%2==1) )
123 if( random()%16 > 12 )
125 BLIPResponse *response = [q send];
128 Assert(response.number==q.number);
129 [_pending setObject: $object(size) forKey: $object(q.number)];
130 response.onComplete = $target(self,responseArrived:);
133 Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
135 [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
139 - (void) responseArrived: (BLIPResponse*)response
141 Log(@"********** called responseArrived: %@",response);
144 - (void) connectionDidOpen: (TCPConnection*)connection
146 Log(@"** %@ didOpen",connection);
149 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
151 #if HAVE_KEYCHAIN_FRAMEWORK
152 Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
153 Log(@"** %@ authorizeSSLPeer: %@",self,cert);
155 Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
157 return peerCert != nil;
159 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
161 Log(@"** %@ failedToOpen: %@",connection,error);
162 CFRunLoopStop(CFRunLoopGetCurrent());
164 - (void) connectionDidClose: (TCPConnection*)connection
166 Log(@"** %@ didClose",connection);
168 [NSObject cancelPreviousPerformRequestsWithTarget: self];
169 CFRunLoopStop(CFRunLoopGetCurrent());
171 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
173 Log(@"***** %@ received %@",connection,request);
174 [request respondWithData: request.body contentType: request.contentType];
177 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
179 Log(@"********** %@ received %@",connection,response);
180 NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
183 Warn(@"Got error response: %@",response.error);
185 NSData *body = response.body;
186 size_t size = body.length;
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);
194 [_pending removeObjectForKey: $object(response.number)];
195 Log(@"Now %u replies pending", _pending.count);
198 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection
200 BOOL response = kClientAcceptCloseRequest;
201 Log(@"***** %@ received a close request; returning %i",connection,response);
209 TestCase(BLIPConnection) {
210 #if HAVE_KEYCHAIN_FRAMEWORK
211 [Keychain setUserInteractionAllowed: YES];
213 BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
216 [[NSRunLoop currentRunLoop] run];
218 Log(@"** Runloop stopped");
225 #pragma mark LISTENER TEST:
228 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
230 BLIPListener *_listener;
237 @implementation BLIPTestListener
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)});
254 Assert( [_listener open] );
255 Log(@"%@ is listening...",self);
262 Log(@"%@ closing",self);
268 - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
270 Log(@"** %@ accepted %@",self,connection);
271 connection.delegate = self;
274 - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
276 Log(@"** BLIPTestListener failed to open: %@",error);
279 - (void) listenerDidOpen: (TCPListener*)listener {Log(@"** BLIPTestListener did open");}
280 - (void) listenerDidClose: (TCPListener*)listener {Log(@"** BLIPTestListener did close");}
282 - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
284 Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
289 - (void) connectionDidOpen: (TCPConnection*)connection
291 Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
294 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
296 #if HAVE_KEYCHAIN_FRAMEWORK
297 Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
298 Log(@"** %@ authorizeSSLPeer: %@",connection,cert);
300 Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
302 return peerCert != nil || ! kListenerRequiresClientCert;
304 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
306 Log(@"** %@ failedToOpen: %@",connection,error);
308 - (void) connectionDidClose: (TCPConnection*)connection
310 Log(@"** %@ didClose",connection);
311 [connection release];
313 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
315 Log(@"***** %@ received %@",connection,request);
316 NSData *body = request.body;
317 size_t size = body.length;
319 const UInt8 *bytes = body.bytes;
320 for( size_t i=0; i<size; i++ )
321 AssertEq(bytes[i],i % 256);
323 AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
324 Assert([request valueOfProperty: @"User-Agent"] != nil);
325 AssertEq([[request valueOfProperty: @"Size"] intValue], size);
327 [request respondWithData: body contentType: request.contentType];
329 if( ++ _nReceived == kListenerCloseAfter ) {
330 Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
335 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
337 Log(@"***** %@ received a close request",connection);
341 - (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error
343 Log(@"***** %@'s close request failed: %@",connection,error);
350 TestCase(BLIPListener) {
351 EnableLogTo(BLIP,YES);
352 EnableLogTo(PortMapper,YES);
353 EnableLogTo(Bonjour,YES);
354 #if HAVE_KEYCHAIN_FRAMEWORK
355 [Keychain setUserInteractionAllowed: YES];
357 BLIPTestListener *listener = [[BLIPTestListener alloc] init];
359 [[NSRunLoop currentRunLoop] run];
369 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
371 Redistribution and use in source and binary forms, with or without modification, are permitted
372 provided that the following conditions are met:
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
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.