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