morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPConnection.h
morrowa@51: --- a/BLIP/BLIPConnection.h Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIPConnection.h Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -20,6 +20,7 @@
morrowa@51: @interface BLIPConnection : TCPConnection
morrowa@51: {
morrowa@51: BLIPDispatcher *_dispatcher;
morrowa@51: + BOOL _blipClosing;
morrowa@51: }
morrowa@51:
morrowa@51: /** The delegate object that will be called when the connection opens, closes or receives messages. */
morrowa@51: @@ -73,6 +74,13 @@
morrowa@51: /** Called when a BLIPResponse (to one of your requests) is received from the peer.
morrowa@51: This is called after the response object's onComplete target, if any, is invoked.*/
morrowa@51: - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response;
morrowa@51: +
morrowa@51: +/** Called when the peer wants to close the connection. Return YES to allow, NO to prevent. */
morrowa@51: +- (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
morrowa@51: +
morrowa@51: +/** Called if the peer refuses a close request.
morrowa@51: + The typical error is kBLIPError_Forbidden. */
morrowa@51: +- (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error;
morrowa@51: @end
morrowa@51:
morrowa@51:
morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPConnection.m
morrowa@51: --- a/BLIP/BLIPConnection.m Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIPConnection.m Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -8,6 +8,7 @@
morrowa@51:
morrowa@51: #import "BLIPConnection.h"
morrowa@51: #import "BLIP_Internal.h"
morrowa@51: +#import "TCP_Internal.h"
morrowa@51: #import "BLIPReader.h"
morrowa@51: #import "BLIPWriter.h"
morrowa@51: #import "BLIPDispatcher.h"
morrowa@51: @@ -15,6 +16,7 @@
morrowa@51: #import "Logging.h"
morrowa@51: #import "Test.h"
morrowa@51: #import "ExceptionUtils.h"
morrowa@51: +#import "Target.h"
morrowa@51:
morrowa@51:
morrowa@51: NSString* const BLIPErrorDomain = @"BLIP";
morrowa@51: @@ -33,10 +35,14 @@
morrowa@51: }
morrowa@51:
morrowa@51:
morrowa@51: +@interface BLIPConnection ()
morrowa@51: +- (void) _handleCloseRequest: (BLIPRequest*)request;
morrowa@51: +@end
morrowa@51:
morrowa@51:
morrowa@51: @implementation BLIPConnection
morrowa@51:
morrowa@51: +
morrowa@51: - (void) dealloc
morrowa@51: {
morrowa@51: [_dispatcher release];
morrowa@51: @@ -48,6 +54,11 @@
morrowa@51: - (id) delegate {return (id)_delegate;}
morrowa@51: - (void) setDelegate: (id)delegate {_delegate = delegate;}
morrowa@51:
morrowa@51: +
morrowa@51: +#pragma mark -
morrowa@51: +#pragma mark RECEIVING:
morrowa@51: +
morrowa@51: +
morrowa@51: - (BLIPDispatcher*) dispatcher
morrowa@51: {
morrowa@51: if( ! _dispatcher ) {
morrowa@51: @@ -58,11 +69,23 @@
morrowa@51: }
morrowa@51:
morrowa@51:
morrowa@51: +- (void) _dispatchMetaRequest: (BLIPRequest*)request
morrowa@51: +{
morrowa@51: + NSString* profile = request.profile;
morrowa@51: + if( [profile isEqualToString: kBLIPProfile_Bye] )
morrowa@51: + [self _handleCloseRequest: request];
morrowa@51: + else
morrowa@51: + [request respondWithErrorCode: kBLIPError_NotFound message: @"Unknown meta profile"];
morrowa@51: +}
morrowa@51: +
morrowa@51: +
morrowa@51: - (void) _dispatchRequest: (BLIPRequest*)request
morrowa@51: {
morrowa@51: LogTo(BLIP,@"Received all of %@",request.descriptionWithProperties);
morrowa@51: @try{
morrowa@51: - if( ! [self.dispatcher dispatchMessage: request] )
morrowa@51: + if( request._flags & kBLIP_Meta )
morrowa@51: + [self _dispatchMetaRequest: request];
morrowa@51: + else if( ! [self.dispatcher dispatchMessage: request] )
morrowa@51: [self tellDelegate: @selector(connection:receivedRequest:) withObject: request];
morrowa@51: if( ! request.noReply && ! request.repliedTo ) {
morrowa@51: LogTo(BLIP,@"Returning default empty response to %@",request);
morrowa@51: @@ -81,6 +104,10 @@
morrowa@51: }
morrowa@51:
morrowa@51:
morrowa@51: +#pragma mark -
morrowa@51: +#pragma mark SENDING:
morrowa@51: +
morrowa@51: +
morrowa@51: - (BLIPRequest*) request
morrowa@51: {
morrowa@51: return [[[BLIPRequest alloc] _initWithConnection: self body: nil properties: nil] autorelease];
morrowa@51: @@ -103,11 +130,61 @@
morrowa@51: }
morrowa@51:
morrowa@51:
morrowa@51: +#pragma mark -
morrowa@51: +#pragma mark CLOSING:
morrowa@51: +
morrowa@51: +
morrowa@51: +- (void) _beginClose
morrowa@51: +{
morrowa@51: + // Override of TCPConnection method. Instead of closing the socket, send a 'bye' request:
morrowa@51: + if( ! _blipClosing ) {
morrowa@51: + LogTo(BLIPVerbose,@"Sending close request...");
morrowa@51: + BLIPRequest *r = [self request];
morrowa@51: + [r _setFlag: kBLIP_Meta value: YES];
morrowa@51: + r.profile = kBLIPProfile_Bye;
morrowa@51: + BLIPResponse *response = [r send];
morrowa@51: + response.onComplete = $target(self,_receivedCloseResponse:);
morrowa@51: + }
morrowa@51: + // Put the writer in close mode, to prevent client from sending any more requests:
morrowa@51: + [self.writer close];
morrowa@51: +}
morrowa@51: +
morrowa@51: +- (void) _receivedCloseResponse: (BLIPResponse*)response
morrowa@51: +{
morrowa@51: + NSError *error = response.error;
morrowa@51: + LogTo(BLIPVerbose,@"Received close response: error=%@",error);
morrowa@51: + if( error ) {
morrowa@51: + [self _unclose];
morrowa@51: + [self tellDelegate: @selector(connection:closeRequestFailedWithError:) withObject: error];
morrowa@51: + } else {
morrowa@51: + // Now finally close the socket:
morrowa@51: + [super _beginClose];
morrowa@51: + }
morrowa@51: +}
morrowa@51: +
morrowa@51: +
morrowa@51: +- (void) _handleCloseRequest: (BLIPRequest*)request
morrowa@51: +{
morrowa@51: + LogTo(BLIPVerbose,@"Received a close request");
morrowa@51: + if( [_delegate respondsToSelector: @selector(connectionReceivedCloseRequest:)] )
morrowa@51: + if( ! [_delegate connectionReceivedCloseRequest: self] ) {
morrowa@51: + LogTo(BLIPVerbose,@"Responding with denial of close request");
morrowa@51: + [request respondWithErrorCode: kBLIPError_Forbidden message: @"Close request denied"];
morrowa@51: + return;
morrowa@51: + }
morrowa@51: +
morrowa@51: + LogTo(BLIPVerbose,@"Close request accepted");
morrowa@51: + _blipClosing = YES; // this prevents _beginClose from sending a close request back
morrowa@51: + [self close];
morrowa@51: +}
morrowa@51: +
morrowa@51: +
morrowa@51: @end
morrowa@51:
morrowa@51:
morrowa@51:
morrowa@51:
morrowa@51: +#pragma mark -
morrowa@51: @implementation BLIPListener
morrowa@51:
morrowa@51: - (id) initWithPort: (UInt16)port
morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPMessage.m
morrowa@51: --- a/BLIP/BLIPMessage.m Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIPMessage.m Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -74,6 +74,8 @@
morrowa@51: [desc appendString: @", urgent"];
morrowa@51: if( _flags & kBLIP_NoReply )
morrowa@51: [desc appendString: @", noreply"];
morrowa@51: + if( _flags & kBLIP_Meta )
morrowa@51: + [desc appendString: @", META"];
morrowa@51: [desc appendString: @"]"];
morrowa@51: return desc;
morrowa@51: }
morrowa@51: @@ -103,6 +105,8 @@
morrowa@51: _flags &= ~flag;
morrowa@51: }
morrowa@51:
morrowa@51: +- (BLIPMessageFlags) _flags {return _flags;}
morrowa@51: +
morrowa@51: - (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;}
morrowa@51: - (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;}
morrowa@51: - (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];}
morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPReader.m
morrowa@51: --- a/BLIP/BLIPReader.m Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIPReader.m Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -93,7 +93,7 @@
morrowa@51:
morrowa@51: - (BOOL) isBusy
morrowa@51: {
morrowa@51: - return _curBytesRead > 0;
morrowa@51: + return _curBytesRead > 0 || _pendingRequests.count > 0 || _pendingResponses.count > 0;
morrowa@51: }
morrowa@51:
morrowa@51:
morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPRequest.m
morrowa@51: --- a/BLIP/BLIPRequest.m Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIPRequest.m Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -199,6 +199,8 @@
morrowa@51: setObj(&_mutableBody,nil);
morrowa@51:
morrowa@51: BLIPMutableProperties *errorProps = [self.properties mutableCopy];
morrowa@51: + if( ! errorProps )
morrowa@51: + errorProps = [[BLIPMutableProperties alloc] init];
morrowa@51: NSDictionary *userInfo = error.userInfo;
morrowa@51: for( NSString *key in userInfo ) {
morrowa@51: id value = $castIf(NSString,[userInfo objectForKey: key]);
morrowa@51: @@ -227,8 +229,12 @@
morrowa@51: {
morrowa@51: Assert(_connection,@"%@ has no connection to send over",self);
morrowa@51: Assert(!_sent,@"%@ was already sent",self);
morrowa@51: + BLIPWriter *writer = (BLIPWriter*)_connection.writer;
morrowa@51: + Assert(writer,@"%@'s connection has no writer (already closed?)",self);
morrowa@51: [self _encode];
morrowa@51: - return (self.sent = [(BLIPWriter*)_connection.writer sendMessage: self]);
morrowa@51: + BOOL sent = self.sent = [writer sendMessage: self];
morrowa@51: + Assert(sent);
morrowa@51: + return sent;
morrowa@51: }
morrowa@51:
morrowa@51:
morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPTest.m
morrowa@51: --- a/BLIP/BLIPTest.m Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIPTest.m Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -35,6 +35,7 @@
morrowa@51: #define kClientUsesSSLCert NO
morrowa@51: #define kListenerRequiresSSL NO
morrowa@51: #define kListenerRequiresClientCert NO
morrowa@51: +#define kListenerCloseAfter 50
morrowa@51:
morrowa@51:
morrowa@51: static SecIdentityRef GetClientIdentity(void) {
morrowa@51: @@ -100,36 +101,38 @@
morrowa@51:
morrowa@51: - (void) sendAMessage
morrowa@51: {
morrowa@51: - if(_pending.count<100) {
morrowa@51: - Log(@"** Sending another %i messages...", kNBatchedMessages);
morrowa@51: - for( int i=0; i 12 )
morrowa@51: - q.urgent = YES;
morrowa@51: - BLIPResponse *response = [q send];
morrowa@51: - Assert(response);
morrowa@51: - Assert(q.number>0);
morrowa@51: - Assert(response.number==q.number);
morrowa@51: - [_pending setObject: $object(size) forKey: $object(q.number)];
morrowa@51: - response.onComplete = $target(self,responseArrived:);
morrowa@51: + if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) {
morrowa@51: + if(_pending.count<100) {
morrowa@51: + Log(@"** Sending another %i messages...", kNBatchedMessages);
morrowa@51: + for( int i=0; i 12 )
morrowa@51: + q.urgent = YES;
morrowa@51: + BLIPResponse *response = [q send];
morrowa@51: + Assert(response);
morrowa@51: + Assert(q.number>0);
morrowa@51: + Assert(response.number==q.number);
morrowa@51: + [_pending setObject: $object(size) forKey: $object(q.number)];
morrowa@51: + response.onComplete = $target(self,responseArrived:);
morrowa@51: + }
morrowa@51: + } else {
morrowa@51: + Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
morrowa@51: }
morrowa@51: - } else {
morrowa@51: - Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
morrowa@51: + [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
morrowa@51: }
morrowa@51: - [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
morrowa@51: }
morrowa@51:
morrowa@51: - (void) responseArrived: (BLIPResponse*)response
morrowa@51: @@ -191,6 +194,13 @@
morrowa@51: Log(@"Now %u replies pending", _pending.count);
morrowa@51: }
morrowa@51:
morrowa@51: +- (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection
morrowa@51: +{
morrowa@51: + BOOL response = NO;
morrowa@51: + Log(@"***** %@ received a close request; returning %i",connection,response);
morrowa@51: + return response;
morrowa@51: +}
morrowa@51: +
morrowa@51:
morrowa@51: @end
morrowa@51:
morrowa@51: @@ -217,6 +227,7 @@
morrowa@51: @interface BLIPTestListener : NSObject
morrowa@51: {
morrowa@51: BLIPListener *_listener;
morrowa@51: + int _nReceived;
morrowa@51: }
morrowa@51:
morrowa@51: @end
morrowa@51: @@ -277,6 +288,7 @@
morrowa@51: - (void) connectionDidOpen: (TCPConnection*)connection
morrowa@51: {
morrowa@51: Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
morrowa@51: + _nReceived = 0;
morrowa@51: }
morrowa@51: - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
morrowa@51: {
morrowa@51: @@ -312,6 +324,22 @@
morrowa@51: AssertEq([[request valueOfProperty: @"Size"] intValue], size);
morrowa@51:
morrowa@51: [request respondWithData: body contentType: request.contentType];
morrowa@51: +
morrowa@51: + if( ++ _nReceived == kListenerCloseAfter ) {
morrowa@51: + Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
morrowa@51: + [connection close];
morrowa@51: + }
morrowa@51: +}
morrowa@51: +
morrowa@51: +- (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
morrowa@51: +{
morrowa@51: + Log(@"***** %@ received a close request",connection);
morrowa@51: + return YES;
morrowa@51: +}
morrowa@51: +
morrowa@51: +- (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error
morrowa@51: +{
morrowa@51: + Log(@"***** %@'s close request failed: %@",connection,error);
morrowa@51: }
morrowa@51:
morrowa@51:
morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPWriter.m
morrowa@51: --- a/BLIP/BLIPWriter.m Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIPWriter.m Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -79,10 +79,6 @@
morrowa@51:
morrowa@51: - (BOOL) sendMessage: (BLIPMessage*)message
morrowa@51: {
morrowa@51: - if( _shouldClose ) {
morrowa@51: - Warn(@"%@: Attempt to send a message after the connection has started closing",self);
morrowa@51: - return NO;
morrowa@51: - }
morrowa@51: Assert(!message.sent,@"message has already been sent");
morrowa@51: [self _queueMessage: message isNew: YES];
morrowa@51: return YES;
morrowa@51: @@ -91,12 +87,14 @@
morrowa@51:
morrowa@51: - (BOOL) sendRequest: (BLIPRequest*)q response: (BLIPResponse*)response
morrowa@51: {
morrowa@51: - if( !_shouldClose ) {
morrowa@51: - [q _assignedNumber: ++_numRequestsSent];
morrowa@51: - if( response ) {
morrowa@51: - [response _assignedNumber: _numRequestsSent];
morrowa@51: - [(BLIPReader*)self.reader _addPendingResponse: response];
morrowa@51: - }
morrowa@51: + if( _shouldClose ) {
morrowa@51: + Warn(@"%@: Attempt to send a request after the connection has started closing: %@",self,q);
morrowa@51: + return NO;
morrowa@51: + }
morrowa@51: + [q _assignedNumber: ++_numRequestsSent];
morrowa@51: + if( response ) {
morrowa@51: + [response _assignedNumber: _numRequestsSent];
morrowa@51: + [(BLIPReader*)self.reader _addPendingResponse: response];
morrowa@51: }
morrowa@51: return [self sendMessage: q];
morrowa@51: }
morrowa@51: diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIP_Internal.h
morrowa@51: --- a/BLIP/BLIP_Internal.h Thu Jun 19 10:22:19 2008 -0700
morrowa@51: +++ b/BLIP/BLIP_Internal.h Mon Jun 23 14:02:31 2008 -0700
morrowa@51: @@ -29,6 +29,7 @@
morrowa@51: kBLIP_Urgent = 0x0020, // please send sooner/faster
morrowa@51: kBLIP_NoReply = 0x0040, // no RPY needed
morrowa@51: kBLIP_MoreComing= 0x0080, // More frames coming (Applies only to individual frame)
morrowa@51: + kBLIP_Meta = 0x0100, // Special message type, handled internally (hello, bye, ...)
morrowa@51: };
morrowa@51: typedef UInt16 BLIPMessageFlags;
morrowa@51:
morrowa@51: @@ -41,7 +42,10 @@
morrowa@51: UInt16 size; // total size of frame, _including_ this header
morrowa@51: } BLIPFrameHeader;
morrowa@51:
morrowa@51: -#define kBLIPFrameHeaderMagicNumber 0x9B34F205
morrowa@51: +#define kBLIPFrameHeaderMagicNumber 0x9B34F206
morrowa@51: +
morrowa@51: +#define kBLIPProfile_Hi @"Hi" // Used for Profile header in meta greeting message
morrowa@51: +#define kBLIPProfile_Bye @"Bye" // Used for Profile header in meta close-request message
morrowa@51:
morrowa@51:
morrowa@51: @interface BLIPConnection ()
morrowa@51: @@ -52,6 +56,7 @@
morrowa@51:
morrowa@51: @interface BLIPMessage ()
morrowa@51: @property BOOL sent, propertiesAvailable, complete;
morrowa@51: +- (BLIPMessageFlags) _flags;
morrowa@51: - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value;
morrowa@51: - (void) _encode;
morrowa@51: @end