* 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.
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
40 static SecIdentityRef GetClientIdentity(void) {
41 return NULL; // Make this return a valid identity to test client-side certs
44 static SecIdentityRef GetListenerIdentity(void) {
45 return NULL; // Make this return a valid identity to test client-side certs
50 #pragma mark CLIENT TEST:
53 @interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
55 BLIPConnection *_conn;
56 NSMutableDictionary *_pending;
62 @implementation BLIPConnectionTester
68 Log(@"** INIT %@",self);
69 _pending = [[NSMutableDictionary alloc] init];
70 IPAddress *addr = [[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort];
71 _conn = [[BLIPConnection alloc] initToAddress: addr];
76 if( kClientRequiresSSL ) {
77 _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true});
78 if( kClientUsesSSLCert ) {
79 SecIdentityRef clientIdentity = GetClientIdentity();
80 if( clientIdentity ) {
81 [_conn setSSLProperty: $array((id)clientIdentity)
82 forKey: kTCPPropertySSLCertificates];
86 _conn.delegate = self;
87 Log(@"** Opening connection...");
95 Log(@"** %@ closing",self);
101 - (void) sendAMessage
103 if(_pending.count<100) {
104 Log(@"** Sending another %i messages...", kNBatchedMessages);
105 for( int i=0; i<kNBatchedMessages; i++ ) {
106 size_t size = random() % 32768;
107 NSMutableData *body = [NSMutableData dataWithLength: size];
108 UInt8 *bytes = body.mutableBytes;
109 for( size_t i=0; i<size; i++ )
112 BLIPRequest *q = [_conn requestWithBody: body
113 properties: $dict({@"Content-Type", @"application/octet-stream"},
114 {@"User-Agent", @"BLIPConnectionTester"},
115 {@"Date", [[NSDate date] description]},
116 {@"Size",$sprintf(@"%u",size)})];
118 if( kUseCompression && (random()%2==1) )
120 if( random()%16 > 12 )
122 BLIPResponse *response = [q send];
125 Assert(response.number==q.number);
126 [_pending setObject: $object(size) forKey: $object(q.number)];
127 response.onComplete = $target(self,responseArrived:);
130 Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
132 [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
135 - (void) responseArrived: (BLIPResponse*)response
137 Log(@"********** called responseArrived: %@",response);
140 - (void) connectionDidOpen: (TCPConnection*)connection
142 Log(@"** %@ didOpen",connection);
145 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
147 #if HAVE_KEYCHAIN_FRAMEWORK
148 Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
149 Log(@"** %@ authorizeSSLPeer: %@",self,cert);
151 Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
153 return peerCert != nil;
155 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
157 Log(@"** %@ failedToOpen: %@",connection,error);
158 CFRunLoopStop(CFRunLoopGetCurrent());
160 - (void) connectionDidClose: (TCPConnection*)connection
162 Log(@"** %@ didClose",connection);
164 [NSObject cancelPreviousPerformRequestsWithTarget: self];
165 CFRunLoopStop(CFRunLoopGetCurrent());
167 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
169 Log(@"***** %@ received %@",connection,request);
170 [request respondWithData: request.body contentType: request.contentType];
173 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
175 Log(@"********** %@ received %@",connection,response);
176 NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
179 Warn(@"Got error response: %@",response.error);
181 NSData *body = response.body;
182 size_t size = body.length;
184 const UInt8 *bytes = body.bytes;
185 for( size_t i=0; i<size; i++ )
186 AssertEq(bytes[i],i % 256);
187 AssertEq(size,sizeObj.intValue);
190 [_pending removeObjectForKey: $object(response.number)];
191 Log(@"Now %u replies pending", _pending.count);
198 TestCase(BLIPConnection) {
199 #if HAVE_KEYCHAIN_FRAMEWORK
200 [Keychain setUserInteractionAllowed: YES];
202 BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
205 [[NSRunLoop currentRunLoop] run];
207 Log(@"** Runloop stopped");
214 #pragma mark LISTENER TEST:
217 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
219 BLIPListener *_listener;
225 @implementation BLIPTestListener
231 _listener = [[BLIPListener alloc] initWithPort: kListenerPort];
232 _listener.delegate = self;
233 _listener.pickAvailablePort = YES;
234 _listener.bonjourServiceType = @"_bliptest._tcp";
235 if( kListenerRequiresSSL ) {
236 SecIdentityRef listenerIdentity = GetListenerIdentity();
237 Assert(listenerIdentity);
238 _listener.SSLProperties = $mdict({kTCPPropertySSLCertificates, $array((id)listenerIdentity)},
239 {kTCPPropertySSLAllowsAnyRoot,$true},
240 {kTCPPropertySSLClientSideAuthentication, $object(kTCPTryAuthenticate)});
242 Assert( [_listener open] );
243 Log(@"%@ is listening...",self);
250 Log(@"%@ closing",self);
256 - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
258 Log(@"** %@ accepted %@",self,connection);
259 connection.delegate = self;
262 - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
264 Log(@"** BLIPTestListener failed to open: %@",error);
267 - (void) listenerDidOpen: (TCPListener*)listener {Log(@"** BLIPTestListener did open");}
268 - (void) listenerDidClose: (TCPListener*)listener {Log(@"** BLIPTestListener did close");}
270 - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
272 Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
277 - (void) connectionDidOpen: (TCPConnection*)connection
279 Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
281 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
283 #if HAVE_KEYCHAIN_FRAMEWORK
284 Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
285 Log(@"** %@ authorizeSSLPeer: %@",connection,cert);
287 Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
289 return peerCert != nil || ! kListenerRequiresClientCert;
291 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
293 Log(@"** %@ failedToOpen: %@",connection,error);
295 - (void) connectionDidClose: (TCPConnection*)connection
297 Log(@"** %@ didClose",connection);
298 [connection release];
300 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
302 Log(@"***** %@ received %@",connection,request);
303 NSData *body = request.body;
304 size_t size = body.length;
306 const UInt8 *bytes = body.bytes;
307 for( size_t i=0; i<size; i++ )
308 AssertEq(bytes[i],i % 256);
310 AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
311 Assert([request valueOfProperty: @"User-Agent"] != nil);
312 AssertEq([[request valueOfProperty: @"Size"] intValue], size);
314 [request respondWithData: body contentType: request.contentType];
321 TestCase(BLIPListener) {
322 EnableLogTo(BLIP,YES);
323 EnableLogTo(PortMapper,YES);
324 EnableLogTo(Bonjour,YES);
325 #if HAVE_KEYCHAIN_FRAMEWORK
326 [Keychain setUserInteractionAllowed: YES];
328 BLIPTestListener *listener = [[BLIPTestListener alloc] init];
330 [[NSRunLoop currentRunLoop] run];
340 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
342 Redistribution and use in source and binary forms, with or without modification, are permitted
343 provided that the following conditions are met:
345 * Redistributions of source code must retain the above copyright notice, this list of conditions
346 and the following disclaimer.
347 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
348 and the following disclaimer in the documentation and/or other materials provided with the
351 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
352 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
353 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
354 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
355 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
356 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
357 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
358 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.