Implemented new close protocol with 'bye' meta-message.
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
41 static SecIdentityRef GetClientIdentity(void) {
42 return NULL; // Make this return a valid identity to test client-side certs
45 static SecIdentityRef GetListenerIdentity(void) {
46 return NULL; // Make this return a valid identity to test client-side certs
51 #pragma mark CLIENT TEST:
54 @interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
56 BLIPConnection *_conn;
57 NSMutableDictionary *_pending;
63 @implementation BLIPConnectionTester
69 Log(@"** INIT %@",self);
70 _pending = [[NSMutableDictionary alloc] init];
71 IPAddress *addr = [[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort];
72 _conn = [[BLIPConnection alloc] initToAddress: addr];
77 if( kClientRequiresSSL ) {
78 _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true});
79 if( kClientUsesSSLCert ) {
80 SecIdentityRef clientIdentity = GetClientIdentity();
81 if( clientIdentity ) {
82 [_conn setSSLProperty: $array((id)clientIdentity)
83 forKey: kTCPPropertySSLCertificates];
87 _conn.delegate = self;
88 Log(@"** Opening connection...");
96 Log(@"** %@ closing",self);
102 - (void) sendAMessage
104 if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) {
105 if(_pending.count<100) {
106 Log(@"** Sending another %i messages...", kNBatchedMessages);
107 for( int i=0; i<kNBatchedMessages; i++ ) {
108 size_t size = random() % 32768;
109 NSMutableData *body = [NSMutableData dataWithLength: size];
110 UInt8 *bytes = body.mutableBytes;
111 for( size_t i=0; i<size; i++ )
114 BLIPRequest *q = [_conn requestWithBody: body
115 properties: $dict({@"Content-Type", @"application/octet-stream"},
116 {@"User-Agent", @"BLIPConnectionTester"},
117 {@"Date", [[NSDate date] description]},
118 {@"Size",$sprintf(@"%u",size)})];
120 if( kUseCompression && (random()%2==1) )
122 if( random()%16 > 12 )
124 BLIPResponse *response = [q send];
127 Assert(response.number==q.number);
128 [_pending setObject: $object(size) forKey: $object(q.number)];
129 response.onComplete = $target(self,responseArrived:);
132 Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
134 [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
138 - (void) responseArrived: (BLIPResponse*)response
140 Log(@"********** called responseArrived: %@",response);
143 - (void) connectionDidOpen: (TCPConnection*)connection
145 Log(@"** %@ didOpen",connection);
148 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
150 #if HAVE_KEYCHAIN_FRAMEWORK
151 Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
152 Log(@"** %@ authorizeSSLPeer: %@",self,cert);
154 Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
156 return peerCert != nil;
158 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
160 Log(@"** %@ failedToOpen: %@",connection,error);
161 CFRunLoopStop(CFRunLoopGetCurrent());
163 - (void) connectionDidClose: (TCPConnection*)connection
165 Log(@"** %@ didClose",connection);
167 [NSObject cancelPreviousPerformRequestsWithTarget: self];
168 CFRunLoopStop(CFRunLoopGetCurrent());
170 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
172 Log(@"***** %@ received %@",connection,request);
173 [request respondWithData: request.body contentType: request.contentType];
176 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
178 Log(@"********** %@ received %@",connection,response);
179 NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
182 Warn(@"Got error response: %@",response.error);
184 NSData *body = response.body;
185 size_t size = body.length;
187 const UInt8 *bytes = body.bytes;
188 for( size_t i=0; i<size; i++ )
189 AssertEq(bytes[i],i % 256);
190 AssertEq(size,sizeObj.intValue);
193 [_pending removeObjectForKey: $object(response.number)];
194 Log(@"Now %u replies pending", _pending.count);
201 TestCase(BLIPConnection) {
202 #if HAVE_KEYCHAIN_FRAMEWORK
203 [Keychain setUserInteractionAllowed: YES];
205 BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
208 [[NSRunLoop currentRunLoop] run];
210 Log(@"** Runloop stopped");
217 #pragma mark LISTENER TEST:
220 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
222 BLIPListener *_listener;
229 @implementation BLIPTestListener
235 _listener = [[BLIPListener alloc] initWithPort: kListenerPort];
236 _listener.delegate = self;
237 _listener.pickAvailablePort = YES;
238 _listener.bonjourServiceType = @"_bliptest._tcp";
239 if( kListenerRequiresSSL ) {
240 SecIdentityRef listenerIdentity = GetListenerIdentity();
241 Assert(listenerIdentity);
242 _listener.SSLProperties = $mdict({kTCPPropertySSLCertificates, $array((id)listenerIdentity)},
243 {kTCPPropertySSLAllowsAnyRoot,$true},
244 {kTCPPropertySSLClientSideAuthentication, $object(kTCPTryAuthenticate)});
246 Assert( [_listener open] );
247 Log(@"%@ is listening...",self);
254 Log(@"%@ closing",self);
260 - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
262 Log(@"** %@ accepted %@",self,connection);
263 connection.delegate = self;
266 - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
268 Log(@"** BLIPTestListener failed to open: %@",error);
271 - (void) listenerDidOpen: (TCPListener*)listener {Log(@"** BLIPTestListener did open");}
272 - (void) listenerDidClose: (TCPListener*)listener {Log(@"** BLIPTestListener did close");}
274 - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
276 Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
281 - (void) connectionDidOpen: (TCPConnection*)connection
283 Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
286 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
288 #if HAVE_KEYCHAIN_FRAMEWORK
289 Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
290 Log(@"** %@ authorizeSSLPeer: %@",connection,cert);
292 Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
294 return peerCert != nil || ! kListenerRequiresClientCert;
296 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
298 Log(@"** %@ failedToOpen: %@",connection,error);
300 - (void) connectionDidClose: (TCPConnection*)connection
302 Log(@"** %@ didClose",connection);
303 [connection release];
305 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
307 Log(@"***** %@ received %@",connection,request);
308 NSData *body = request.body;
309 size_t size = body.length;
311 const UInt8 *bytes = body.bytes;
312 for( size_t i=0; i<size; i++ )
313 AssertEq(bytes[i],i % 256);
315 AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
316 Assert([request valueOfProperty: @"User-Agent"] != nil);
317 AssertEq([[request valueOfProperty: @"Size"] intValue], size);
319 [request respondWithData: body contentType: request.contentType];
321 if( ++ _nReceived == kListenerCloseAfter ) {
322 Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
331 TestCase(BLIPListener) {
332 EnableLogTo(BLIP,YES);
333 EnableLogTo(PortMapper,YES);
334 EnableLogTo(Bonjour,YES);
335 #if HAVE_KEYCHAIN_FRAMEWORK
336 [Keychain setUserInteractionAllowed: YES];
338 BLIPTestListener *listener = [[BLIPTestListener alloc] init];
340 [[NSRunLoop currentRunLoop] run];
350 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
352 Redistribution and use in source and binary forms, with or without modification, are permitted
353 provided that the following conditions are met:
355 * Redistributions of source code must retain the above copyright notice, this list of conditions
356 and the following disclaimer.
357 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
358 and the following disclaimer in the documentation and/or other materials provided with the
361 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
362 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
363 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
364 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
365 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
366 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
367 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
368 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.