Merged Jens' latest changes.
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 #import <Security/Security.h>
23 #import <SecurityInterface/SFChooseIdentityPanel.h>
25 @interface TCPEndpoint ()
26 + (NSString*) describeCert: (SecCertificateRef)cert;
27 + (NSString*) describeIdentity: (SecIdentityRef)identity;
31 #define kListenerHost @"localhost"
32 #define kListenerPort 46353
33 #define kSendInterval 0.5
34 #define kNBatchedMessages 20
35 #define kUseCompression YES
36 #define kUrgentEvery 4
37 #define kListenerCloseAfter 50
38 #define kClientAcceptCloseRequest YES
40 #define kListenerUsesSSL YES // Does the listener (server) use an SSL connection?
41 #define kListenerRequiresClientCert YES // Does the listener require clients to have an SSL cert?
42 #define kClientRequiresSSL YES // Does the client require the listener to use SSL?
43 #define kClientUsesSSLCert YES // Does the client use an SSL cert?
46 static SecIdentityRef ChooseIdentity( NSString *prompt ) {
47 NSMutableArray *identities = [NSMutableArray array];
49 SecKeychainCopyDefault(&kc);
50 SecIdentitySearchRef search;
51 SecIdentitySearchCreate(kc, CSSM_KEYUSE_ANY, &search);
52 SecIdentityRef identity;
53 while (SecIdentitySearchCopyNext(search, &identity) == noErr) {
54 [identities addObject: (id)identity];
55 CFRelease( identity );
58 Log(@"Found %u identities -- prompting '%@'", identities.count, prompt);
59 if (identities.count > 0) {
60 SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
61 if ([panel runModalForIdentities: identities message: prompt] == NSOKButton) {
62 Log(@"Using SSL identity: %@", panel.identity);
63 return panel.identity;
69 static SecIdentityRef GetClientIdentity(void) {
70 return ChooseIdentity(@"Choose an identity for the BLIP Client Test:");
73 static SecIdentityRef GetListenerIdentity(void) {
74 return ChooseIdentity(@"Choose an identity for the BLIP Listener Test:");
79 #pragma mark CLIENT TEST:
82 @interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
84 BLIPConnection *_conn;
85 NSMutableDictionary *_pending;
91 @implementation BLIPConnectionTester
97 Log(@"** INIT %@",self);
98 _pending = [[NSMutableDictionary alloc] init];
99 IPAddress *addr = [[[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort] autorelease];
100 _conn = [[BLIPConnection alloc] initToAddress: addr];
105 if( kClientUsesSSLCert ) {
106 [_conn setPeerToPeerIdentity: GetClientIdentity()];
107 } else if( kClientRequiresSSL ) {
108 _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true},
109 {(id)kCFStreamSSLPeerName, [NSNull null]});
111 _conn.delegate = self;
112 Log(@"** Opening connection...");
120 Log(@"** %@ closing",self);
126 - (void) sendAMessage
128 if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) {
129 if(_pending.count<100) {
130 Log(@"** Sending another %i messages...", kNBatchedMessages);
131 for( int i=0; i<kNBatchedMessages; i++ ) {
132 size_t size = random() % 32768;
133 NSMutableData *body = [NSMutableData dataWithLength: size];
134 UInt8 *bytes = body.mutableBytes;
135 for( size_t i=0; i<size; i++ )
138 BLIPRequest *q = [_conn requestWithBody: body
139 properties: $dict({@"Content-Type", @"application/octet-stream"},
140 {@"User-Agent", @"BLIPConnectionTester"},
141 {@"Date", [[NSDate date] description]},
142 {@"Size",$sprintf(@"%u",size)})];
144 if( kUseCompression && (random()%2==1) )
146 if( random()%16 > 12 )
148 BLIPResponse *response = [q send];
151 Assert(response.number==q.number);
152 [_pending setObject: $object(size) forKey: $object(q.number)];
153 response.onComplete = $target(self,responseArrived:);
156 Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
158 [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
162 - (void) responseArrived: (BLIPResponse*)response
164 Log(@"********** called responseArrived: %@",response);
167 - (void) connectionDidOpen: (TCPConnection*)connection
169 Log(@"** %@ didOpen",connection);
172 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
174 Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
175 return peerCert != nil;
177 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
179 Warn(@"** %@ failedToOpen: %@",connection,error);
180 CFRunLoopStop(CFRunLoopGetCurrent());
182 - (void) connectionDidClose: (TCPConnection*)connection
184 if (connection.error)
185 Warn(@"** %@ didClose: %@", connection,connection.error);
187 Log(@"** %@ didClose", connection);
189 [NSObject cancelPreviousPerformRequestsWithTarget: self];
190 CFRunLoopStop(CFRunLoopGetCurrent());
192 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
194 Log(@"***** %@ received %@",connection,request);
195 [request respondWithData: request.body contentType: request.contentType];
198 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
200 Log(@"********** %@ received %@",connection,response);
201 NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
204 Warn(@"Got error response: %@",response.error);
206 NSData *body = response.body;
207 size_t size = body.length;
209 const UInt8 *bytes = body.bytes;
210 for( size_t i=0; i<size; i++ )
211 AssertEq(bytes[i],i % 256);
212 AssertEq(size,sizeObj.unsignedIntValue);
215 [_pending removeObjectForKey: $object(response.number)];
216 Log(@"Now %u replies pending", _pending.count);
219 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection
221 BOOL response = kClientAcceptCloseRequest;
222 Log(@"***** %@ received a close request; returning %i",connection,response);
230 TestCase(BLIPConnection) {
231 SecKeychainSetUserInteractionAllowed(true);
232 BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
235 [[NSRunLoop currentRunLoop] run];
237 Log(@"** Runloop stopped");
244 #pragma mark LISTENER TEST:
247 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
249 BLIPListener *_listener;
256 @implementation BLIPTestListener
262 _listener = [[BLIPListener alloc] initWithPort: kListenerPort];
263 _listener.delegate = self;
264 _listener.pickAvailablePort = YES;
265 _listener.bonjourServiceType = @"_bliptest._tcp";
266 if( kListenerUsesSSL ) {
267 [_listener setPeerToPeerIdentity: GetListenerIdentity()];
268 if (!kListenerRequiresClientCert)
269 [_listener setSSLProperty: $object(kTCPTryAuthenticate)
270 forKey: kTCPPropertySSLClientSideAuthentication];
272 Assert( [_listener open] );
273 Log(@"%@ is listening...",self);
280 Log(@"%@ closing",self);
286 - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
288 Log(@"** %@ accepted %@",self,connection);
289 connection.delegate = self;
292 - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
294 Log(@"** BLIPTestListener failed to open: %@",error);
297 - (void) listenerDidOpen: (TCPListener*)listener {Log(@"** BLIPTestListener did open");}
298 - (void) listenerDidClose: (TCPListener*)listener {Log(@"** BLIPTestListener did close");}
300 - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
302 Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
307 - (void) connectionDidOpen: (TCPConnection*)connection
309 Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
312 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
314 Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
315 return peerCert != nil || ! kListenerRequiresClientCert;
317 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
319 Log(@"** %@ failedToOpen: %@",connection,error);
321 - (void) connectionDidClose: (TCPConnection*)connection
323 if (connection.error)
324 Warn(@"** %@ didClose: %@", connection,connection.error);
326 Log(@"** %@ didClose", connection);
327 [connection release];
329 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
331 Log(@"***** %@ received %@",connection,request);
332 NSData *body = request.body;
333 size_t size = body.length;
335 const UInt8 *bytes = body.bytes;
336 for( size_t i=0; i<size; i++ )
337 AssertEq(bytes[i],i % 256);
339 AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
340 Assert([request valueOfProperty: @"User-Agent"] != nil);
341 AssertEq((size_t)[[request valueOfProperty: @"Size"] intValue], size);
343 [request respondWithData: body contentType: request.contentType];
345 if( ++ _nReceived == kListenerCloseAfter ) {
346 Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
351 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
353 Log(@"***** %@ received a close request",connection);
357 - (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error
359 Log(@"***** %@'s close request failed: %@",connection,error);
366 TestCase(BLIPListener) {
367 EnableLogTo(BLIP,YES);
368 EnableLogTo(PortMapper,YES);
369 EnableLogTo(Bonjour,YES);
370 SecKeychainSetUserInteractionAllowed(true);
371 BLIPTestListener *listener = [[BLIPTestListener alloc] init];
373 [[NSRunLoop currentRunLoop] run];
383 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
385 Redistribution and use in source and binary forms, with or without modification, are permitted
386 provided that the following conditions are met:
388 * Redistributions of source code must retain the above copyright notice, this list of conditions
389 and the following disclaimer.
390 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
391 and the following disclaimer in the documentation and/or other materials provided with the
394 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
395 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
396 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
397 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
398 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
399 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
400 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
401 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.