* The BLIPConnection receivedRequest: delegate method now returns BOOL. If the method returns NO (or if the method isn't implemented in the delegate), that means it didn't handle the message at all; an error will be returned to the sender.
* If the connection closes unexpectedly due to an error, then the auto-generated responses to pending requests will contain that error. This makes it easier to display a meaningful error message in the handler for the request.
5 // Created by Jens Alfke on 5/10/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
10 #import "TCP_Internal.h"
17 extern const CFStringRef _kCFStreamPropertySSLClientSideAuthentication; // in CFNetwork
19 static NSError* fixStreamError( NSError *error );
22 @implementation TCPStream
25 - (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream
29 _conn = [conn retain];
30 _stream = [stream retain];
31 _stream.delegate = self;
32 [_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
33 LogTo(TCPVerbose,@"%@ initialized; status=%i", self,_stream.streamStatus);
41 LogTo(TCP,@"DEALLOC %@",self);
48 - (id) propertyForKey: (CFStringRef)cfStreamProperty
50 return [_stream propertyForKey: (NSString*)cfStreamProperty];
53 - (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
55 if( ! [_stream setProperty: value forKey: (NSString*)cfStreamProperty] )
56 Warn(@"Failed to set property %@ on %@",cfStreamProperty,self);
60 - (IPAddress*) peerAddress
62 const CFSocketNativeHandle *socketPtr = [[self propertyForKey: kCFStreamPropertySocketNativeHandle] bytes];
63 return socketPtr ?[IPAddress addressOfSocket: *socketPtr] :nil;
71 - (NSString*) securityLevel {return [_stream propertyForKey: NSStreamSocketSecurityLevelKey];}
73 - (NSDictionary*) SSLProperties {return [self propertyForKey: kCFStreamPropertySSLSettings];}
75 - (void) setSSLProperties: (NSDictionary*)p
77 LogTo(TCPVerbose,@"%@ SSL settings := %@",self,p);
78 [self setProperty: p forKey: kCFStreamPropertySSLSettings];
80 id clientAuth = [p objectForKey: kTCPPropertySSLClientSideAuthentication];
82 [self setProperty: clientAuth forKey: _kCFStreamPropertySSLClientSideAuthentication];
85 - (NSArray*) peerSSLCerts
88 return [self propertyForKey: kCFStreamPropertySSLPeerCertificates];
93 #pragma mark OPENING/CLOSING:
99 AssertEq(_stream.streamStatus,(NSStreamStatus)NSStreamStatusNotOpen);
100 LogTo(TCP,@"Opening %@",self);
108 LogTo(TCP,@"Disconnect %@",self);
109 _stream.delegate = nil;
111 setObj(&_stream,nil);
115 [_conn _streamDisconnected: self];
124 if( ! _shouldClose ) {
126 LogTo(TCP,@"Request to close %@",self);
131 [[self retain] autorelease]; // don't let myself be dealloced in the midst of this
132 [_conn _streamCanClose: self];
145 NSStreamStatus status = _stream.streamStatus;
146 return status >= NSStreamStatusOpen && status < NSStreamStatusAtEnd;
151 return NO; // abstract
156 return !_shouldClose || self.isBusy;
162 [_conn _streamOpened: self];
177 [_conn _streamGotEOF: self];
180 - (BOOL) _gotError: (NSError*)error
182 [_conn _stream: self gotError: fixStreamError(error)];
188 NSError *error = _stream.streamError;
190 error = [NSError errorWithDomain: NSPOSIXErrorDomain code: EIO userInfo: nil]; //fallback
191 return [self _gotError: error];
195 - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)streamEvent
197 [[self retain] autorelease];
198 switch(streamEvent) {
199 case NSStreamEventOpenCompleted:
200 LogTo(TCPVerbose,@"%@ opened",self);
203 case NSStreamEventHasBytesAvailable:
204 if( ! [_conn _streamPeerCertAvailable: self] )
206 LogTo(TCPVerbose,@"%@ can read",self);
209 case NSStreamEventHasSpaceAvailable:
210 if( ! [_conn _streamPeerCertAvailable: self] )
212 LogTo(TCPVerbose,@"%@ can write",self);
215 case NSStreamEventErrorOccurred:
216 LogTo(TCPVerbose,@"%@ got error",self);
219 case NSStreamEventEndEncountered:
220 LogTo(TCPVerbose,@"%@ got EOF",self);
224 Warn(@"%@: unknown NSStreamEvent %i",self,streamEvent);
228 // If I was previously asked to close, try again in case I'm no longer busy
239 @implementation TCPReader
242 - (TCPWriter*) writer
247 - (NSInteger) read: (void*)dst maxLength: (NSUInteger)maxLength
249 NSInteger bytesRead = [(NSInputStream*)_stream read:dst maxLength: maxLength];
261 static NSError* fixStreamError( NSError *error )
263 // NSStream incorrectly returns SSL errors without the correct error domain:
264 if( $equal(error.domain,@"NSUnknownErrorDomain") ) {
265 int code = error.code;
266 if( -9899 <= code && code <= -9800 ) {
267 NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
268 if( ! [userInfo objectForKey: NSLocalizedFailureReasonErrorKey] ) {
269 // look up error message:
270 NSBundle *secBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Security.framework"];
271 NSString *message = [secBundle localizedStringForKey: $sprintf(@"%i",code)
273 table: @"SecErrorMessages"];
275 if( ! userInfo ) userInfo = $mdict();
276 [userInfo setObject: message forKey: NSLocalizedFailureReasonErrorKey];
279 error = [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
280 code: code userInfo: userInfo];
282 Warn(@"NSStream returned error with unknown domain: %@",error);
288 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
290 Redistribution and use in source and binary forms, with or without modification, are permitted
291 provided that the following conditions are met:
293 * Redistributions of source code must retain the above copyright notice, this list of conditions
294 and the following disclaimer.
295 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
296 and the following disclaimer in the documentation and/or other materials provided with the
299 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
300 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
301 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
302 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
303 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
304 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
305 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
306 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.