# HG changeset patch # User morrowa@betelgeuse.local # Date 1245782670 25200 # Node ID de59ce19f42e27f844a78508468797a17b3078f6 # Parent 20cccc7c26eed98989eb4281cbf4f033c5a9d494 BROKEN COMMIT. Majority of code to handle closing has been added. Listeners do not close correctly. diff -r 20cccc7c26ee -r de59ce19f42e 1_0_to_1_1_diffs.diff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1_0_to_1_1_diffs.diff Tue Jun 23 11:44:30 2009 -0700 @@ -0,0 +1,425 @@ +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPConnection.h +--- a/BLIP/BLIPConnection.h Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIPConnection.h Mon Jun 23 14:02:31 2008 -0700 +@@ -20,6 +20,7 @@ + @interface BLIPConnection : TCPConnection + { + BLIPDispatcher *_dispatcher; ++ BOOL _blipClosing; + } + + /** The delegate object that will be called when the connection opens, closes or receives messages. */ +@@ -73,6 +74,13 @@ + /** Called when a BLIPResponse (to one of your requests) is received from the peer. + This is called after the response object's onComplete target, if any, is invoked.*/ + - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response; ++ ++/** Called when the peer wants to close the connection. Return YES to allow, NO to prevent. */ ++- (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection; ++ ++/** Called if the peer refuses a close request. ++ The typical error is kBLIPError_Forbidden. */ ++- (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error; + @end + + +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPConnection.m +--- a/BLIP/BLIPConnection.m Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIPConnection.m Mon Jun 23 14:02:31 2008 -0700 +@@ -8,6 +8,7 @@ + + #import "BLIPConnection.h" + #import "BLIP_Internal.h" ++#import "TCP_Internal.h" + #import "BLIPReader.h" + #import "BLIPWriter.h" + #import "BLIPDispatcher.h" +@@ -15,6 +16,7 @@ + #import "Logging.h" + #import "Test.h" + #import "ExceptionUtils.h" ++#import "Target.h" + + + NSString* const BLIPErrorDomain = @"BLIP"; +@@ -33,10 +35,14 @@ + } + + ++@interface BLIPConnection () ++- (void) _handleCloseRequest: (BLIPRequest*)request; ++@end + + + @implementation BLIPConnection + ++ + - (void) dealloc + { + [_dispatcher release]; +@@ -48,6 +54,11 @@ + - (id) delegate {return (id)_delegate;} + - (void) setDelegate: (id)delegate {_delegate = delegate;} + ++ ++#pragma mark - ++#pragma mark RECEIVING: ++ ++ + - (BLIPDispatcher*) dispatcher + { + if( ! _dispatcher ) { +@@ -58,11 +69,23 @@ + } + + ++- (void) _dispatchMetaRequest: (BLIPRequest*)request ++{ ++ NSString* profile = request.profile; ++ if( [profile isEqualToString: kBLIPProfile_Bye] ) ++ [self _handleCloseRequest: request]; ++ else ++ [request respondWithErrorCode: kBLIPError_NotFound message: @"Unknown meta profile"]; ++} ++ ++ + - (void) _dispatchRequest: (BLIPRequest*)request + { + LogTo(BLIP,@"Received all of %@",request.descriptionWithProperties); + @try{ +- if( ! [self.dispatcher dispatchMessage: request] ) ++ if( request._flags & kBLIP_Meta ) ++ [self _dispatchMetaRequest: request]; ++ else if( ! [self.dispatcher dispatchMessage: request] ) + [self tellDelegate: @selector(connection:receivedRequest:) withObject: request]; + if( ! request.noReply && ! request.repliedTo ) { + LogTo(BLIP,@"Returning default empty response to %@",request); +@@ -81,6 +104,10 @@ + } + + ++#pragma mark - ++#pragma mark SENDING: ++ ++ + - (BLIPRequest*) request + { + return [[[BLIPRequest alloc] _initWithConnection: self body: nil properties: nil] autorelease]; +@@ -103,11 +130,61 @@ + } + + ++#pragma mark - ++#pragma mark CLOSING: ++ ++ ++- (void) _beginClose ++{ ++ // Override of TCPConnection method. Instead of closing the socket, send a 'bye' request: ++ if( ! _blipClosing ) { ++ LogTo(BLIPVerbose,@"Sending close request..."); ++ BLIPRequest *r = [self request]; ++ [r _setFlag: kBLIP_Meta value: YES]; ++ r.profile = kBLIPProfile_Bye; ++ BLIPResponse *response = [r send]; ++ response.onComplete = $target(self,_receivedCloseResponse:); ++ } ++ // Put the writer in close mode, to prevent client from sending any more requests: ++ [self.writer close]; ++} ++ ++- (void) _receivedCloseResponse: (BLIPResponse*)response ++{ ++ NSError *error = response.error; ++ LogTo(BLIPVerbose,@"Received close response: error=%@",error); ++ if( error ) { ++ [self _unclose]; ++ [self tellDelegate: @selector(connection:closeRequestFailedWithError:) withObject: error]; ++ } else { ++ // Now finally close the socket: ++ [super _beginClose]; ++ } ++} ++ ++ ++- (void) _handleCloseRequest: (BLIPRequest*)request ++{ ++ LogTo(BLIPVerbose,@"Received a close request"); ++ if( [_delegate respondsToSelector: @selector(connectionReceivedCloseRequest:)] ) ++ if( ! [_delegate connectionReceivedCloseRequest: self] ) { ++ LogTo(BLIPVerbose,@"Responding with denial of close request"); ++ [request respondWithErrorCode: kBLIPError_Forbidden message: @"Close request denied"]; ++ return; ++ } ++ ++ LogTo(BLIPVerbose,@"Close request accepted"); ++ _blipClosing = YES; // this prevents _beginClose from sending a close request back ++ [self close]; ++} ++ ++ + @end + + + + ++#pragma mark - + @implementation BLIPListener + + - (id) initWithPort: (UInt16)port +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPMessage.m +--- a/BLIP/BLIPMessage.m Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIPMessage.m Mon Jun 23 14:02:31 2008 -0700 +@@ -74,6 +74,8 @@ + [desc appendString: @", urgent"]; + if( _flags & kBLIP_NoReply ) + [desc appendString: @", noreply"]; ++ if( _flags & kBLIP_Meta ) ++ [desc appendString: @", META"]; + [desc appendString: @"]"]; + return desc; + } +@@ -103,6 +105,8 @@ + _flags &= ~flag; + } + ++- (BLIPMessageFlags) _flags {return _flags;} ++ + - (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;} + - (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;} + - (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];} +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPReader.m +--- a/BLIP/BLIPReader.m Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIPReader.m Mon Jun 23 14:02:31 2008 -0700 +@@ -93,7 +93,7 @@ + + - (BOOL) isBusy + { +- return _curBytesRead > 0; ++ return _curBytesRead > 0 || _pendingRequests.count > 0 || _pendingResponses.count > 0; + } + + +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPRequest.m +--- a/BLIP/BLIPRequest.m Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIPRequest.m Mon Jun 23 14:02:31 2008 -0700 +@@ -199,6 +199,8 @@ + setObj(&_mutableBody,nil); + + BLIPMutableProperties *errorProps = [self.properties mutableCopy]; ++ if( ! errorProps ) ++ errorProps = [[BLIPMutableProperties alloc] init]; + NSDictionary *userInfo = error.userInfo; + for( NSString *key in userInfo ) { + id value = $castIf(NSString,[userInfo objectForKey: key]); +@@ -227,8 +229,12 @@ + { + Assert(_connection,@"%@ has no connection to send over",self); + Assert(!_sent,@"%@ was already sent",self); ++ BLIPWriter *writer = (BLIPWriter*)_connection.writer; ++ Assert(writer,@"%@'s connection has no writer (already closed?)",self); + [self _encode]; +- return (self.sent = [(BLIPWriter*)_connection.writer sendMessage: self]); ++ BOOL sent = self.sent = [writer sendMessage: self]; ++ Assert(sent); ++ return sent; + } + + +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPTest.m +--- a/BLIP/BLIPTest.m Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIPTest.m Mon Jun 23 14:02:31 2008 -0700 +@@ -35,6 +35,7 @@ + #define kClientUsesSSLCert NO + #define kListenerRequiresSSL NO + #define kListenerRequiresClientCert NO ++#define kListenerCloseAfter 50 + + + static SecIdentityRef GetClientIdentity(void) { +@@ -100,36 +101,38 @@ + + - (void) sendAMessage + { +- if(_pending.count<100) { +- Log(@"** Sending another %i messages...", kNBatchedMessages); +- for( int i=0; i 12 ) +- q.urgent = YES; +- BLIPResponse *response = [q send]; +- Assert(response); +- Assert(q.number>0); +- Assert(response.number==q.number); +- [_pending setObject: $object(size) forKey: $object(q.number)]; +- response.onComplete = $target(self,responseArrived:); ++ if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) { ++ if(_pending.count<100) { ++ Log(@"** Sending another %i messages...", kNBatchedMessages); ++ for( int i=0; i 12 ) ++ q.urgent = YES; ++ BLIPResponse *response = [q send]; ++ Assert(response); ++ Assert(q.number>0); ++ Assert(response.number==q.number); ++ [_pending setObject: $object(size) forKey: $object(q.number)]; ++ response.onComplete = $target(self,responseArrived:); ++ } ++ } else { ++ Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count); + } +- } else { +- Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count); ++ [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval]; + } +- [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval]; + } + + - (void) responseArrived: (BLIPResponse*)response +@@ -191,6 +194,13 @@ + Log(@"Now %u replies pending", _pending.count); + } + ++- (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection ++{ ++ BOOL response = NO; ++ Log(@"***** %@ received a close request; returning %i",connection,response); ++ return response; ++} ++ + + @end + +@@ -217,6 +227,7 @@ + @interface BLIPTestListener : NSObject + { + BLIPListener *_listener; ++ int _nReceived; + } + + @end +@@ -277,6 +288,7 @@ + - (void) connectionDidOpen: (TCPConnection*)connection + { + Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel); ++ _nReceived = 0; + } + - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert + { +@@ -312,6 +324,22 @@ + AssertEq([[request valueOfProperty: @"Size"] intValue], size); + + [request respondWithData: body contentType: request.contentType]; ++ ++ if( ++ _nReceived == kListenerCloseAfter ) { ++ Log(@"********** Closing BLIPTestListener after %i requests",_nReceived); ++ [connection close]; ++ } ++} ++ ++- (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection; ++{ ++ Log(@"***** %@ received a close request",connection); ++ return YES; ++} ++ ++- (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error ++{ ++ Log(@"***** %@'s close request failed: %@",connection,error); + } + + +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIPWriter.m +--- a/BLIP/BLIPWriter.m Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIPWriter.m Mon Jun 23 14:02:31 2008 -0700 +@@ -79,10 +79,6 @@ + + - (BOOL) sendMessage: (BLIPMessage*)message + { +- if( _shouldClose ) { +- Warn(@"%@: Attempt to send a message after the connection has started closing",self); +- return NO; +- } + Assert(!message.sent,@"message has already been sent"); + [self _queueMessage: message isNew: YES]; + return YES; +@@ -91,12 +87,14 @@ + + - (BOOL) sendRequest: (BLIPRequest*)q response: (BLIPResponse*)response + { +- if( !_shouldClose ) { +- [q _assignedNumber: ++_numRequestsSent]; +- if( response ) { +- [response _assignedNumber: _numRequestsSent]; +- [(BLIPReader*)self.reader _addPendingResponse: response]; +- } ++ if( _shouldClose ) { ++ Warn(@"%@: Attempt to send a request after the connection has started closing: %@",self,q); ++ return NO; ++ } ++ [q _assignedNumber: ++_numRequestsSent]; ++ if( response ) { ++ [response _assignedNumber: _numRequestsSent]; ++ [(BLIPReader*)self.reader _addPendingResponse: response]; + } + return [self sendMessage: q]; + } +diff -r 70590cc555aa -r 16454d63d4c2 BLIP/BLIP_Internal.h +--- a/BLIP/BLIP_Internal.h Thu Jun 19 10:22:19 2008 -0700 ++++ b/BLIP/BLIP_Internal.h Mon Jun 23 14:02:31 2008 -0700 +@@ -29,6 +29,7 @@ + kBLIP_Urgent = 0x0020, // please send sooner/faster + kBLIP_NoReply = 0x0040, // no RPY needed + kBLIP_MoreComing= 0x0080, // More frames coming (Applies only to individual frame) ++ kBLIP_Meta = 0x0100, // Special message type, handled internally (hello, bye, ...) + }; + typedef UInt16 BLIPMessageFlags; + +@@ -41,7 +42,10 @@ + UInt16 size; // total size of frame, _including_ this header + } BLIPFrameHeader; + +-#define kBLIPFrameHeaderMagicNumber 0x9B34F205 ++#define kBLIPFrameHeaderMagicNumber 0x9B34F206 ++ ++#define kBLIPProfile_Hi @"Hi" // Used for Profile header in meta greeting message ++#define kBLIPProfile_Bye @"Bye" // Used for Profile header in meta close-request message + + + @interface BLIPConnection () +@@ -52,6 +56,7 @@ + + @interface BLIPMessage () + @property BOOL sent, propertiesAvailable, complete; ++- (BLIPMessageFlags) _flags; + - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value; + - (void) _encode; + @end diff -r 20cccc7c26ee -r de59ce19f42e Python/BLIP.py --- a/Python/BLIP.py Sun May 24 15:03:39 2009 -0700 +++ b/Python/BLIP.py Tue Jun 23 11:44:30 2009 -0700 @@ -27,7 +27,7 @@ # INTERNAL CONSTANTS -- NO TOUCHIES! -kFrameMagicNumber = 0x9B34F205 +kFrameMagicNumber = 0x9B34F206 kFrameHeaderFormat = '!LLHH' kFrameHeaderSize = 12 @@ -36,11 +36,15 @@ kMsgFlag_Urgent = 0x0020 kMsgFlag_NoReply = 0x0040 kMsgFlag_MoreComing = 0x0080 +kMsgFlag_Meta = 0x0100 kMsgType_Request = 0 kMsgType_Response = 1 kMsgType_Error = 2 +kMsgProfile_Hi = "Hi" +kMsgProfile_Bye = "Bye" + log = logging.getLogger('BLIP') log.propagate = True @@ -104,7 +108,7 @@ self.connect(address) self.address = address self.listener = listener - self.onRequest = None + self.onRequest = self.onCloseRequest = self.onCloseRefused = None self.pendingRequests = {} self.pendingResponses = {} self.outBox = [] @@ -112,12 +116,7 @@ self.inNumRequests = self.outNumRequests = 0 self.sending = False self._endOfFrame() - - def close(self): - if self.status > kClosed: - self.status = kClosing - log.info("Connection closing...") - asynchat.async_chat.close(self) + self._closeWhenPossible = False def handle_connect(self): log.info("Connection open!") @@ -130,25 +129,19 @@ self.status = kDisconnected self.close() - def handle_close(self): - log.info("Connection closed!") - self.pendingRequests = self.pendingResponses = None - self.outBox = None - if self.status == kClosing: - self.status = kClosed - else: - self.status = kDisconnected - asynchat.async_chat.handle_close(self) - ### SENDING: @property - def canSend(self): + def isOpen(self): return self.status==kOpening or self.status==kOpen + @property + def canSend(self): + return self.isOpen and not self._closeWhenPossible + def _sendMessage(self, msg): - if self.canSend: + if self.isOpen: self._outQueueMessage(msg,True) if not self.sending: log.debug("Waking up the output stream") @@ -208,6 +201,7 @@ else: log.debug("Nothing more to send") self.sending = False + self._closeIfReady() return None ### RECEIVING: @@ -255,6 +249,7 @@ self.inNumRequests += 1 elif msgType==kMsgType_Response or msgType==kMsgType_Error: message = self.pendingResponses.get(requestNo) + message._updateFlags(flags) if message != None: message._beginFrame(flags) @@ -284,10 +279,88 @@ try: msg._finished() if not msg.isResponse: - self.onRequest(msg) + if msg._meta: + self._dispatchMetaRequest(msg) + else: + self.onRequest(msg) except Exception, x: log.error("Exception handling incoming message: %s", traceback.format_exc()) #FIX: Send an error reply + # Check to see if we're done and ready to close: + self._closeIfReady() + + def _dispatchMetaRequest(self, request): + """Handles dispatching internal meta requests.""" + if request['Profile'] == kMsgProfile_Bye: + shouldClose = True + if self.onCloseRequest: + shouldClose = self.onCloseRequest() + if not shouldClose: + log.debug("Sending resfusal to close...") + response = request.response + response.isError = True + response['Error-Domain'] = "BLIP" + response['Error-Code'] = 403 + response.body = "Close request denied" + response.send() + else: + log.debug("Sending permission to close...") + response = request.response + response.send() + else: + response = request.response + response.isError = True + response['Error-Domain'] = "BLIP" + response['Error-Code'] = 404 + response.body = "Unknown meta profile" + response.send() + + ### CLOSING: + + def close(self): + """Publicly callable close method. Sends close request to peer.""" + if self.status != kOpen: + return False + log.info("Sending close request...") + req = OutgoingRequest(self, None, {'Profile': kMsgProfile_Bye}) + req._meta = True + req.response.onComplete = self._handleCloseResponse + if not req.send(): + log.error("Error sending close request.") + return False + else: + self.status = kClosing + return True + + def _handleCloseResponse(self, response): + """Called when we receive a response to a close request.""" + log.info("Received close response.") + if response.isError: + # remote refused to close + if self.onCloseRefused: + self.onCloseRefused(response) + self.status = kOpen + else: + # now wait until everything has finished sending, then actually close + log.info("No refusal, actually closing...") + self._closeWhenPossible = True + + def _closeIfReady(self): + """Checks if all transmissions are complete and then closes the actual socket.""" + if self._closeWhenPossible and len(self.outBox) == 0 and len(self.pendingRequests) == 0 and len(self.pendingResponses) == 0: + # self._closeWhenPossible = False + log.debug("_closeIfReady closing.") + asynchat.async_chat.close(self) + + def handle_close(self): + """Called when the socket actually closes.""" + log.info("Connection closed!") + self.pendingRequests = self.pendingResponses = None + self.outBox = None + if self.status == kClosing: + self.status = kClosed + else: + self.status = kDisconnected ### MESSAGE CLASSES: @@ -305,13 +378,17 @@ @property def flags(self): if self.isResponse: - flags = kMsgType_Response + if self.isError: + flags = kMsgType_Error + else: + flags = kMsgType_Response else: flags = kMsgType_Request if self.urgent: flags |= kMsgFlag_Urgent if self.compressed: flags |= kMsgFlag_Compressed if self.noReply: flags |= kMsgFlag_NoReply if self._moreComing:flags |= kMsgFlag_MoreComing + if self._meta: flags |= kMsgFlag_Meta return flags def __str__(self): @@ -322,6 +399,7 @@ if self.compressed: s += " CMP" if self.noReply: s += " NOR" if self._moreComing:s += " MOR" + if self._meta: s += " MET" if self.body: s += " %i bytes" %len(self.body) return s+"]" @@ -352,11 +430,16 @@ def __init__(self, connection, requestNo, flags): super(IncomingMessage,self).__init__(connection) self.requestNo = requestNo + self._updateFlags(flags) + self.frames = [] + + def _updateFlags(self, flags): self.urgent = (flags & kMsgFlag_Urgent) != 0 self.compressed = (flags & kMsgFlag_Compressed) != 0 self.noReply = (flags & kMsgFlag_NoReply) != 0 self._moreComing= (flags & kMsgFlag_MoreComing) != 0 - self.frames = [] + self._meta = (flags & kMsgFlag_Meta) != 0 + self.isError = (flags & kMsgType_Error) != 0 def _beginFrame(self, flags): """Received a frame header.""" @@ -377,16 +460,18 @@ if propSize>len(encoded): raise MessageException, "properties too long to fit" if propSize>2 and encoded[propSize-1] != '\000': raise MessageException, "properties are not nul-terminated" - proplist = encoded[2:propSize-1].split('\000') + if propSize > 2: + proplist = encoded[2:propSize-1].split('\000') + + if len(proplist) & 1: raise MessageException, "odd number of property strings" + for i in xrange(0,len(proplist),2): + def expand(str): + if len(str)==1: + str = IncomingMessage.__expandDict.get(str,str) + return str + self.properties[ expand(proplist[i])] = expand(proplist[i+1]) + encoded = encoded[propSize:] - if len(proplist) & 1: raise MessageException, "odd number of property strings" - for i in xrange(0,len(proplist),2): - def expand(str): - if len(str)==1: - str = IncomingMessage.__expandDict.get(str,str) - return str - self.properties[ expand(proplist[i])] = expand(proplist[i+1]) - # Decode the body: if self.compressed and len(encoded)>0: try: @@ -411,7 +496,7 @@ def __init__(self, connection, body=None, properties=None): Message.__init__(self,connection,body,properties) - self.urgent = self.compressed = self.noReply = False + self.urgent = self.compressed = self.noReply = self._meta = self.isError = False self._moreComing = True def __setitem__(self, key,val): @@ -435,7 +520,7 @@ propertiesSize = out.tell() assert propertiesSize<65536 #FIX: Return an error instead - body = self.body + body = self.body or "" if self.compressed: z = zlib.compressobj(6,zlib.DEFLATED,31) # window size of 31 needed for gzip format out.write(z.compress(body)) diff -r 20cccc7c26ee -r de59ce19f42e Python/BLIPConnectionTest.py --- a/Python/BLIPConnectionTest.py Sun May 24 15:03:39 2009 -0700 +++ b/Python/BLIPConnectionTest.py Tue Jun 23 11:44:30 2009 -0700 @@ -72,7 +72,8 @@ def tearDown(self): self.connection.close() + asyncore.loop() # got to give it time to negotiate close; this call should exit eventually if __name__ == '__main__': - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.DEBUG) unittest.main() diff -r 20cccc7c26ee -r de59ce19f42e Python/BLIPListenerTest.py --- a/Python/BLIPListenerTest.py Sun May 24 15:03:39 2009 -0700 +++ b/Python/BLIPListenerTest.py Tue Jun 23 11:44:30 2009 -0700 @@ -42,5 +42,5 @@ logging.info("KeyboardInterrupt") if __name__ == '__main__': - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.DEBUG) unittest.main() diff -r 20cccc7c26ee -r de59ce19f42e Python/CloseTestPing.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Python/CloseTestPing.py Tue Jun 23 11:44:30 2009 -0700 @@ -0,0 +1,36 @@ +# CloseTestPing.py +# Tests the closing negotiation facilities of the BLIP 1.1 protocol + +from BLIP import Connection, OutgoingRequest + +import unittest +import asyncore +import logging + +class CloseTestPing(unittest.TestCase): + + def handleCloseRefusal(self, resp): + logging.info("Close request was refused!") + + def setUp(self): + self.connection = Connection( ('localhost', 1337) ) + self.connection.onCloseRefused = self.handleCloseRefusal + + def handleResponse(self, resp): + logging.info("Got response...") + + def testClose(self): + req = OutgoingRequest(self.connection, "Ping") + req.response.onComplete = self.handleResponse + req.send() + + asyncore.loop(timeout=1, count=5) + + self.connection.close() + + asyncore.loop() + + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + unittest.main() diff -r 20cccc7c26ee -r de59ce19f42e Python/CloseTestPong.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Python/CloseTestPong.py Tue Jun 23 11:44:30 2009 -0700 @@ -0,0 +1,38 @@ +# CloseTestPong.py +# Tests the closing negotiation facilities of the BLIP 1.1 protocol + +from BLIP import Listener + +import logging +import asyncore +import unittest + +class CloseTestPong(unittest.TestCase): + + def shouldClose(self): + logging.info("Allowed to close.") + return True + + def handleConnection(self, conn): + logging.info("Accepted connection.") + conn.onCloseRequest = self.shouldClose + + def handleRequest(self, req): + resp = req.response + resp.body = "Pong" + resp.send() + + def testClose(self): + listen = Listener(1337) + listen.onConnected = self.handleConnection + listen.onRequest = self.handleRequest + + try: + asyncore.loop() + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + unittest.main() diff -r 20cccc7c26ee -r de59ce19f42e Python/asynchatPing.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Python/asynchatPing.py Tue Jun 23 11:44:30 2009 -0700 @@ -0,0 +1,60 @@ +# asynchatPing +# Uses asynchat +# Not related to BLIP - just to aid in my understanding of what's going on +# Sends "Ping", waits for "Pong" + +import socket +import asyncore +import asynchat + +kNumPings = 10 + +class asynchatPing(asynchat.async_chat): + def __init__(self, address): + asynchat.async_chat.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.connect(address) + self.set_terminator("Pong") + self.pingsSent = self.pongsGot = 0 + self.donePing = self.donePong = False + + def handle_connect(self): + print "Connected" + + def handle_close(self): + print "Closed" + asynchat.async_chat.handle_close(self) + + def collect_incoming_data(self, data): + """discard data""" + pass + + def found_terminator(self): + """when we get a Pong""" + print "Received 'Pong'" + self.pongsGot += 1 + if self.pongsGot == kNumPings: + print "Done ponging" + self.donePong = True + self.close_when_done() + + def ping(self): + if not self.donePing: + self.push("Ping") + print "Sent 'Ping'" + self.pingsSent += 1 + if self.pingsSent == kNumPings: + print "Done pinging" + self.donePing = True + + def run(self): + timeout = 0 + while not self.donePing: + self.ping() + asyncore.loop(timeout=timeout, count=1) + asyncore.loop() + print "Done!" + +if __name__ == '__main__': + ping = asynchatPing( ('localhost', 1337) ) + ping.run() diff -r 20cccc7c26ee -r de59ce19f42e Python/asynchatPong.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Python/asynchatPong.py Tue Jun 23 11:44:30 2009 -0700 @@ -0,0 +1,59 @@ +# asynchatPong +# Listener using asynchat +# Not related to BLIP - just to aid in my understanding of what's going on +# Sends "Pong" when it gets "Ping" + +import sys +import traceback +import socket +import asyncore +import asynchat + +class asynchatPongListener(asyncore.dispatcher): + def __init__(self, port): + asyncore.dispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.bind( ('', port) ) + self.listen(2) + self.shouldAccept = True + + def handle_accept(self): + if self.shouldAccept: + sock, addr = self.accept() + self.conn = asynchatPong(sock, self) + self.shouldAccept = False + + def handle_error(self): + (typ,val,trace) = sys.exc_info() + print "Listener caught: %s %s\n%s" % (typ,val,traceback.format_exc()) + self.close() + + def handle_close(self): + print "Listener got close" + asyncore.dispatcher.handle_close(self) + +class asynchatPong(asynchat.async_chat): + def __init__(self, socket, listener): + asynchat.async_chat.__init__(self, socket) + self._listener = listener + self.set_terminator("Ping") + + def collect_incoming_data(self, data): + """called when arbitrary amount of data arrives. we just eat it""" + pass + + def found_terminator(self): + """called when the terminator we set is found""" + print "Found 'Ping'" + self.push("Pong") + print "Sent 'Pong'" + + def handle_close(self): + print "Closed; closing listener" + self._listener.close() + asynchat.async_chat.handle_close(self) + + +if __name__ == '__main__': + pong = asynchatPongListener(1337) + asyncore.loop()