Implemented new close protocol with 'bye' meta-message.
1.1 --- a/BLIP/BLIPConnection.h Thu Jun 19 10:22:19 2008 -0700
1.2 +++ b/BLIP/BLIPConnection.h Thu Jun 19 16:22:05 2008 -0700
1.3 @@ -20,6 +20,7 @@
1.4 @interface BLIPConnection : TCPConnection
1.5 {
1.6 BLIPDispatcher *_dispatcher;
1.7 + BOOL _blipClosing;
1.8 }
1.9
1.10 /** The delegate object that will be called when the connection opens, closes or receives messages. */
1.11 @@ -73,6 +74,13 @@
1.12 /** Called when a BLIPResponse (to one of your requests) is received from the peer.
1.13 This is called <i>after</i> the response object's onComplete target, if any, is invoked.*/
1.14 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response;
1.15 +
1.16 +/** Called when the peer wants to close the connection. Return YES to allow, NO to prevent. */
1.17 +- (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
1.18 +
1.19 +/** Called if the peer refuses a close request.
1.20 + The typical error is BLIP error kBLIPError_Forbidden. */
1.21 +- (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error;
1.22 @end
1.23
1.24
2.1 --- a/BLIP/BLIPConnection.m Thu Jun 19 10:22:19 2008 -0700
2.2 +++ b/BLIP/BLIPConnection.m Thu Jun 19 16:22:05 2008 -0700
2.3 @@ -15,6 +15,7 @@
2.4 #import "Logging.h"
2.5 #import "Test.h"
2.6 #import "ExceptionUtils.h"
2.7 +#import "Target.h"
2.8
2.9
2.10 NSString* const BLIPErrorDomain = @"BLIP";
2.11 @@ -33,10 +34,14 @@
2.12 }
2.13
2.14
2.15 +@interface BLIPConnection ()
2.16 +- (void) _handleCloseRequest: (BLIPRequest*)request;
2.17 +@end
2.18
2.19
2.20 @implementation BLIPConnection
2.21
2.22 +
2.23 - (void) dealloc
2.24 {
2.25 [_dispatcher release];
2.26 @@ -48,6 +53,11 @@
2.27 - (id<BLIPConnectionDelegate>) delegate {return (id)_delegate;}
2.28 - (void) setDelegate: (id<BLIPConnectionDelegate>)delegate {_delegate = delegate;}
2.29
2.30 +
2.31 +#pragma mark -
2.32 +#pragma mark RECEIVING:
2.33 +
2.34 +
2.35 - (BLIPDispatcher*) dispatcher
2.36 {
2.37 if( ! _dispatcher ) {
2.38 @@ -58,11 +68,23 @@
2.39 }
2.40
2.41
2.42 +- (void) _dispatchMetaRequest: (BLIPRequest*)request
2.43 +{
2.44 + NSString* profile = request.profile;
2.45 + if( [profile isEqualToString: kBLIPProfile_Bye] )
2.46 + [self _handleCloseRequest: request];
2.47 + else
2.48 + [request respondWithErrorCode: kBLIPError_NotFound message: @"Unknown meta profile"];
2.49 +}
2.50 +
2.51 +
2.52 - (void) _dispatchRequest: (BLIPRequest*)request
2.53 {
2.54 LogTo(BLIP,@"Received all of %@",request.descriptionWithProperties);
2.55 @try{
2.56 - if( ! [self.dispatcher dispatchMessage: request] )
2.57 + if( request._flags & kBLIP_Meta )
2.58 + [self _dispatchMetaRequest: request];
2.59 + else if( ! [self.dispatcher dispatchMessage: request] )
2.60 [self tellDelegate: @selector(connection:receivedRequest:) withObject: request];
2.61 if( ! request.noReply && ! request.repliedTo ) {
2.62 LogTo(BLIP,@"Returning default empty response to %@",request);
2.63 @@ -81,6 +103,10 @@
2.64 }
2.65
2.66
2.67 +#pragma mark -
2.68 +#pragma mark SENDING:
2.69 +
2.70 +
2.71 - (BLIPRequest*) request
2.72 {
2.73 return [[[BLIPRequest alloc] _initWithConnection: self body: nil properties: nil] autorelease];
2.74 @@ -103,11 +129,61 @@
2.75 }
2.76
2.77
2.78 +#pragma mark -
2.79 +#pragma mark CLOSING:
2.80 +
2.81 +
2.82 +- (void) _beginClose
2.83 +{
2.84 + // Override of TCPConnection method. Instead of closing the socket, send a 'bye' request:
2.85 + if( ! _blipClosing ) {
2.86 + LogTo(BLIPVerbose,@"Sending close request...");
2.87 + BLIPRequest *r = [self request];
2.88 + [r _setFlag: kBLIP_Meta value: YES];
2.89 + r.profile = kBLIPProfile_Bye;
2.90 + BLIPResponse *response = [r send];
2.91 + response.onComplete = $target(self,_receivedCloseResponse:);
2.92 + }
2.93 + // Put the writer in close mode, to prevent client from sending any more requests:
2.94 + [self.writer close];
2.95 +}
2.96 +
2.97 +- (void) _receivedCloseResponse: (BLIPResponse*)response
2.98 +{
2.99 + NSError *error = response.error;
2.100 + LogTo(BLIPVerbose,@"Received close response: error=%@",error);
2.101 + if( error ) {
2.102 + if( [_delegate respondsToSelector: @selector(connection:closeRequestFailedWithError:)] )
2.103 + [_delegate connection: self closeRequestFailedWithError: error];
2.104 + } else {
2.105 + // Now finally close the socket:
2.106 + [super _beginClose];
2.107 + }
2.108 +}
2.109 +
2.110 +
2.111 +- (void) _handleCloseRequest: (BLIPRequest*)request
2.112 +{
2.113 + LogTo(BLIPVerbose,@"Received a close request");
2.114 + if( [_delegate respondsToSelector: @selector(connectionReceivedCloseRequest:)] )
2.115 + if( ! [_delegate connectionReceivedCloseRequest: self] ) {
2.116 + LogTo(BLIPVerbose,@"Responding with denial of close request");
2.117 + [request respondWithErrorCode: kBLIPError_Forbidden message: @"Close request denied"];
2.118 + return;
2.119 + }
2.120 +
2.121 + LogTo(BLIPVerbose,@"Close request accepted");
2.122 + _blipClosing = YES; // this prevents _beginClose from sending a close request back
2.123 + [self close];
2.124 +}
2.125 +
2.126 +
2.127 @end
2.128
2.129
2.130
2.131
2.132 +#pragma mark -
2.133 @implementation BLIPListener
2.134
2.135 - (id) initWithPort: (UInt16)port
3.1 --- a/BLIP/BLIPMessage.m Thu Jun 19 10:22:19 2008 -0700
3.2 +++ b/BLIP/BLIPMessage.m Thu Jun 19 16:22:05 2008 -0700
3.3 @@ -74,6 +74,8 @@
3.4 [desc appendString: @", urgent"];
3.5 if( _flags & kBLIP_NoReply )
3.6 [desc appendString: @", noreply"];
3.7 + if( _flags & kBLIP_Meta )
3.8 + [desc appendString: @", META"];
3.9 [desc appendString: @"]"];
3.10 return desc;
3.11 }
3.12 @@ -103,6 +105,8 @@
3.13 _flags &= ~flag;
3.14 }
3.15
3.16 +- (BLIPMessageFlags) _flags {return _flags;}
3.17 +
3.18 - (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;}
3.19 - (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;}
3.20 - (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];}
4.1 --- a/BLIP/BLIPReader.m Thu Jun 19 10:22:19 2008 -0700
4.2 +++ b/BLIP/BLIPReader.m Thu Jun 19 16:22:05 2008 -0700
4.3 @@ -93,7 +93,7 @@
4.4
4.5 - (BOOL) isBusy
4.6 {
4.7 - return _curBytesRead > 0;
4.8 + return _curBytesRead > 0 || _pendingRequests.count > 0 || _pendingResponses.count > 0;
4.9 }
4.10
4.11
5.1 --- a/BLIP/BLIPRequest.m Thu Jun 19 10:22:19 2008 -0700
5.2 +++ b/BLIP/BLIPRequest.m Thu Jun 19 16:22:05 2008 -0700
5.3 @@ -199,6 +199,8 @@
5.4 setObj(&_mutableBody,nil);
5.5
5.6 BLIPMutableProperties *errorProps = [self.properties mutableCopy];
5.7 + if( ! errorProps )
5.8 + errorProps = [[BLIPMutableProperties alloc] init];
5.9 NSDictionary *userInfo = error.userInfo;
5.10 for( NSString *key in userInfo ) {
5.11 id value = $castIf(NSString,[userInfo objectForKey: key]);
5.12 @@ -227,8 +229,12 @@
5.13 {
5.14 Assert(_connection,@"%@ has no connection to send over",self);
5.15 Assert(!_sent,@"%@ was already sent",self);
5.16 + BLIPWriter *writer = (BLIPWriter*)_connection.writer;
5.17 + Assert(writer,@"%@'s connection has no writer (already closed?)",self);
5.18 [self _encode];
5.19 - return (self.sent = [(BLIPWriter*)_connection.writer sendMessage: self]);
5.20 + BOOL sent = self.sent = [writer sendMessage: self];
5.21 + Assert(sent);
5.22 + return sent;
5.23 }
5.24
5.25
6.1 --- a/BLIP/BLIPTest.m Thu Jun 19 10:22:19 2008 -0700
6.2 +++ b/BLIP/BLIPTest.m Thu Jun 19 16:22:05 2008 -0700
6.3 @@ -35,6 +35,7 @@
6.4 #define kClientUsesSSLCert NO
6.5 #define kListenerRequiresSSL NO
6.6 #define kListenerRequiresClientCert NO
6.7 +#define kListenerCloseAfter 50
6.8
6.9
6.10 static SecIdentityRef GetClientIdentity(void) {
6.11 @@ -100,36 +101,38 @@
6.12
6.13 - (void) sendAMessage
6.14 {
6.15 - if(_pending.count<100) {
6.16 - Log(@"** Sending another %i messages...", kNBatchedMessages);
6.17 - for( int i=0; i<kNBatchedMessages; i++ ) {
6.18 - size_t size = random() % 32768;
6.19 - NSMutableData *body = [NSMutableData dataWithLength: size];
6.20 - UInt8 *bytes = body.mutableBytes;
6.21 - for( size_t i=0; i<size; i++ )
6.22 - bytes[i] = i % 256;
6.23 -
6.24 - BLIPRequest *q = [_conn requestWithBody: body
6.25 - properties: $dict({@"Content-Type", @"application/octet-stream"},
6.26 - {@"User-Agent", @"BLIPConnectionTester"},
6.27 - {@"Date", [[NSDate date] description]},
6.28 - {@"Size",$sprintf(@"%u",size)})];
6.29 - Assert(q);
6.30 - if( kUseCompression && (random()%2==1) )
6.31 - q.compressed = YES;
6.32 - if( random()%16 > 12 )
6.33 - q.urgent = YES;
6.34 - BLIPResponse *response = [q send];
6.35 - Assert(response);
6.36 - Assert(q.number>0);
6.37 - Assert(response.number==q.number);
6.38 - [_pending setObject: $object(size) forKey: $object(q.number)];
6.39 - response.onComplete = $target(self,responseArrived:);
6.40 + if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) {
6.41 + if(_pending.count<100) {
6.42 + Log(@"** Sending another %i messages...", kNBatchedMessages);
6.43 + for( int i=0; i<kNBatchedMessages; i++ ) {
6.44 + size_t size = random() % 32768;
6.45 + NSMutableData *body = [NSMutableData dataWithLength: size];
6.46 + UInt8 *bytes = body.mutableBytes;
6.47 + for( size_t i=0; i<size; i++ )
6.48 + bytes[i] = i % 256;
6.49 +
6.50 + BLIPRequest *q = [_conn requestWithBody: body
6.51 + properties: $dict({@"Content-Type", @"application/octet-stream"},
6.52 + {@"User-Agent", @"BLIPConnectionTester"},
6.53 + {@"Date", [[NSDate date] description]},
6.54 + {@"Size",$sprintf(@"%u",size)})];
6.55 + Assert(q);
6.56 + if( kUseCompression && (random()%2==1) )
6.57 + q.compressed = YES;
6.58 + if( random()%16 > 12 )
6.59 + q.urgent = YES;
6.60 + BLIPResponse *response = [q send];
6.61 + Assert(response);
6.62 + Assert(q.number>0);
6.63 + Assert(response.number==q.number);
6.64 + [_pending setObject: $object(size) forKey: $object(q.number)];
6.65 + response.onComplete = $target(self,responseArrived:);
6.66 + }
6.67 + } else {
6.68 + Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
6.69 }
6.70 - } else {
6.71 - Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
6.72 + [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
6.73 }
6.74 - [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
6.75 }
6.76
6.77 - (void) responseArrived: (BLIPResponse*)response
6.78 @@ -217,6 +220,7 @@
6.79 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
6.80 {
6.81 BLIPListener *_listener;
6.82 + int _nReceived;
6.83 }
6.84
6.85 @end
6.86 @@ -277,6 +281,7 @@
6.87 - (void) connectionDidOpen: (TCPConnection*)connection
6.88 {
6.89 Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
6.90 + _nReceived = 0;
6.91 }
6.92 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
6.93 {
6.94 @@ -312,6 +317,11 @@
6.95 AssertEq([[request valueOfProperty: @"Size"] intValue], size);
6.96
6.97 [request respondWithData: body contentType: request.contentType];
6.98 +
6.99 + if( ++ _nReceived == kListenerCloseAfter ) {
6.100 + Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
6.101 + [connection close];
6.102 + }
6.103 }
6.104
6.105
7.1 --- a/BLIP/BLIPWriter.m Thu Jun 19 10:22:19 2008 -0700
7.2 +++ b/BLIP/BLIPWriter.m Thu Jun 19 16:22:05 2008 -0700
7.3 @@ -79,10 +79,6 @@
7.4
7.5 - (BOOL) sendMessage: (BLIPMessage*)message
7.6 {
7.7 - if( _shouldClose ) {
7.8 - Warn(@"%@: Attempt to send a message after the connection has started closing",self);
7.9 - return NO;
7.10 - }
7.11 Assert(!message.sent,@"message has already been sent");
7.12 [self _queueMessage: message isNew: YES];
7.13 return YES;
7.14 @@ -91,12 +87,14 @@
7.15
7.16 - (BOOL) sendRequest: (BLIPRequest*)q response: (BLIPResponse*)response
7.17 {
7.18 - if( !_shouldClose ) {
7.19 - [q _assignedNumber: ++_numRequestsSent];
7.20 - if( response ) {
7.21 - [response _assignedNumber: _numRequestsSent];
7.22 - [(BLIPReader*)self.reader _addPendingResponse: response];
7.23 - }
7.24 + if( _shouldClose ) {
7.25 + Warn(@"%@: Attempt to send a request after the connection has started closing: %@",self,q);
7.26 + return NO;
7.27 + }
7.28 + [q _assignedNumber: ++_numRequestsSent];
7.29 + if( response ) {
7.30 + [response _assignedNumber: _numRequestsSent];
7.31 + [(BLIPReader*)self.reader _addPendingResponse: response];
7.32 }
7.33 return [self sendMessage: q];
7.34 }
8.1 --- a/BLIP/BLIP_Internal.h Thu Jun 19 10:22:19 2008 -0700
8.2 +++ b/BLIP/BLIP_Internal.h Thu Jun 19 16:22:05 2008 -0700
8.3 @@ -29,6 +29,7 @@
8.4 kBLIP_Urgent = 0x0020, // please send sooner/faster
8.5 kBLIP_NoReply = 0x0040, // no RPY needed
8.6 kBLIP_MoreComing= 0x0080, // More frames coming (Applies only to individual frame)
8.7 + kBLIP_Meta = 0x0100, // Special message type, handled internally (hello, bye, ...)
8.8 };
8.9 typedef UInt16 BLIPMessageFlags;
8.10
8.11 @@ -43,6 +44,9 @@
8.12
8.13 #define kBLIPFrameHeaderMagicNumber 0x9B34F205
8.14
8.15 +#define kBLIPProfile_Hi @"Hi" // Used for Profile header in meta greeting message
8.16 +#define kBLIPProfile_Bye @"Bye" // Used for Profile header in meta close-request message
8.17 +
8.18
8.19 @interface BLIPConnection ()
8.20 - (void) _dispatchRequest: (BLIPRequest*)request;
8.21 @@ -52,6 +56,7 @@
8.22
8.23 @interface BLIPMessage ()
8.24 @property BOOL sent, propertiesAvailable, complete;
8.25 +- (BLIPMessageFlags) _flags;
8.26 - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value;
8.27 - (void) _encode;
8.28 @end
9.1 --- a/TCP/TCPConnection.h Thu Jun 19 10:22:19 2008 -0700
9.2 +++ b/TCP/TCPConnection.h Thu Jun 19 16:22:05 2008 -0700
9.3 @@ -108,6 +108,7 @@
9.4 // protected:
9.5 - (Class) readerClass;
9.6 - (Class) writerClass;
9.7 +- (void) _beginClose;
9.8
9.9 @end
9.10
10.1 --- a/TCP/TCPConnection.m Thu Jun 19 10:22:19 2008 -0700
10.2 +++ b/TCP/TCPConnection.m Thu Jun 19 16:22:05 2008 -0700
10.3 @@ -207,9 +207,7 @@
10.4 if( _status > kTCP_Closed ) {
10.5 LogTo(TCP,@"%@ disconnecting",self);
10.6 [_writer disconnect];
10.7 - setObj(&_writer,nil);
10.8 [_reader disconnect];
10.9 - setObj(&_reader,nil);
10.10 self.status = kTCP_Disconnected;
10.11 }
10.12 [self _stopOpenTimer];
10.13 @@ -231,8 +229,7 @@
10.14 LogTo(TCP,@"%@ closing",self);
10.15 self.status = kTCP_Closing;
10.16 [self retain];
10.17 - [_reader close];
10.18 - [_writer close];
10.19 + [self _beginClose];
10.20 if( ! [self _checkIfClosed] ) {
10.21 if( timeout <= 0.0 )
10.22 [self disconnect];
10.23 @@ -251,6 +248,13 @@
10.24 }
10.25
10.26
10.27 +- (void) _beginClose
10.28 +{
10.29 + [_reader close];
10.30 + [_writer close];
10.31 +}
10.32 +
10.33 +
10.34 - (BOOL) _checkIfClosed
10.35 {
10.36 if( _status == kTCP_Closing && _writer==nil && _reader==nil ) {
10.37 @@ -356,27 +360,16 @@
10.38 [[self retain] autorelease];
10.39 setObj(&_error,error);
10.40 [_reader disconnect];
10.41 - setObj(&_reader,nil);
10.42 [_writer disconnect];
10.43 - setObj(&_writer,nil);
10.44 [self _closed];
10.45 }
10.46
10.47 - (void) _streamGotEOF: (TCPStream*)stream
10.48 {
10.49 LogTo(TCP,@"%@ got EOF on %@",self,stream);
10.50 - if( stream == _reader ) {
10.51 - setObj(&_reader,nil);
10.52 - // This is the expected way for he peer to initiate closing the connection.
10.53 - if( _status==kTCP_Open ) {
10.54 - [self closeWithTimeout: INFINITY];
10.55 - return;
10.56 - }
10.57 - } else if( stream == _writer ) {
10.58 - setObj(&_writer,nil);
10.59 - }
10.60 -
10.61 + [stream disconnect];
10.62 if( _status == kTCP_Closing ) {
10.63 + [self _streamCanClose: stream];
10.64 [self _checkIfClosed];
10.65 } else {
10.66 [self _stream: stream
10.67 @@ -385,14 +378,27 @@
10.68 }
10.69
10.70
10.71 +// Called as soon as a stream is ready to close, after its -close method has been called.
10.72 +- (void) _streamCanClose: (TCPStream*)stream
10.73 +{
10.74 + if( ! _reader.isActive && !_writer.isActive ) {
10.75 + LogTo(TCPVerbose,@"Both streams are ready to close now!");
10.76 + [_reader disconnect];
10.77 + [_writer disconnect];
10.78 + }
10.79 +}
10.80 +
10.81 +
10.82 // Called after I called -close on a stream and it finished closing:
10.83 -- (void) _streamClosed: (TCPStream*)stream
10.84 +- (void) _streamDisconnected: (TCPStream*)stream
10.85 {
10.86 - LogTo(TCP,@"%@ finished closing %@",self,stream);
10.87 + LogTo(TCP,@"%@: disconnected %@",self,stream);
10.88 if( stream == _reader )
10.89 setObj(&_reader,nil);
10.90 else if( stream == _writer )
10.91 setObj(&_writer,nil);
10.92 + else
10.93 + return;
10.94 if( !_reader.isOpen && !_writer.isOpen )
10.95 [self _closed];
10.96 }
11.1 --- a/TCP/TCPStream.h Thu Jun 19 10:22:19 2008 -0700
11.2 +++ b/TCP/TCPStream.h Thu Jun 19 16:22:05 2008 -0700
11.3 @@ -47,6 +47,9 @@
11.4 /** Does the stream have pending data to read or write, that prevents it from closing? */
11.5 @property (readonly) BOOL isBusy;
11.6
11.7 +/** Returns NO if the stream is ready to close (-close has been called and -isBusy is NO.) */
11.8 +@property (readonly) BOOL isActive;
11.9 +
11.10 /** Generic accessor for CFStream/NSStream properties. */
11.11 - (id) propertyForKey: (CFStringRef)cfStreamProperty;
11.12
12.1 --- a/TCP/TCPStream.m Thu Jun 19 10:22:19 2008 -0700
12.2 +++ b/TCP/TCPStream.m Thu Jun 19 16:22:05 2008 -0700
12.3 @@ -110,20 +110,24 @@
12.4 [_stream close];
12.5 setObj(&_stream,nil);
12.6 }
12.7 - setObj(&_conn,nil);
12.8 + if( _conn ) {
12.9 + [self retain];
12.10 + [_conn _streamDisconnected: self];
12.11 + setObj(&_conn,nil);
12.12 + [self release];
12.13 + }
12.14 }
12.15
12.16
12.17 - (BOOL) close
12.18 {
12.19 + _shouldClose = YES;
12.20 if( self.isBusy ) {
12.21 - _shouldClose = YES;
12.22 return NO;
12.23 } else {
12.24 - LogTo(TCP,@"Closing %@",self);
12.25 - [[self retain] autorelease]; // don't let myself be dealloced in the midst of this
12.26 - [_conn _streamClosed: self]; // have to do this before disconnect
12.27 - [self disconnect];
12.28 + LogTo(TCP,@"Request to close %@",self);
12.29 + [[self retain] autorelease]; // don't let myself be dealloced in the midst of this
12.30 + [_conn _streamCanClose: self];
12.31 return YES;
12.32 }
12.33 }
12.34 @@ -140,6 +144,11 @@
12.35 return NO; // abstract
12.36 }
12.37
12.38 +- (BOOL) isActive
12.39 +{
12.40 + return !_shouldClose || self.isBusy;
12.41 +}
12.42 +
12.43
12.44 - (void) _opened
12.45 {
12.46 @@ -158,14 +167,7 @@
12.47
12.48 - (void) _gotEOF
12.49 {
12.50 - if( self.isBusy )
12.51 - [self _gotError: [NSError errorWithDomain: NSPOSIXErrorDomain code: ECONNRESET userInfo: nil]];
12.52 - else {
12.53 - [self retain];
12.54 - [_conn _streamGotEOF: self];
12.55 - [self disconnect];
12.56 - [self release];
12.57 - }
12.58 + [_conn _streamGotEOF: self];
12.59 }
12.60
12.61 - (BOOL) _gotError: (NSError*)error
13.1 --- a/TCP/TCP_Internal.h Thu Jun 19 10:22:19 2008 -0700
13.2 +++ b/TCP/TCP_Internal.h Thu Jun 19 16:22:05 2008 -0700
13.3 @@ -20,6 +20,7 @@
13.4 - (void) _streamOpened: (TCPStream*)stream;
13.5 - (BOOL) _streamPeerCertAvailable: (TCPStream*)stream;
13.6 - (void) _stream: (TCPStream*)stream gotError: (NSError*)error;
13.7 +- (void) _streamCanClose: (TCPStream*)stream;
13.8 - (void) _streamGotEOF: (TCPStream*)stream;
13.9 -- (void) _streamClosed: (TCPStream*)stream;
13.10 +- (void) _streamDisconnected: (TCPStream*)stream;
13.11 @end