diff --git a/ChangeLog b/ChangeLog index 23e271ca2..fa856dc1a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2006-02-16 Derek Zhou + + * Source/GSStream.h: + * Source/GSStream.m: + * Source/unix/NSStream.m: + * Source/win32/NSStreamWin32.m: + * Headers/Foundation/NSStream.h: + Add server side and pipe stream extensions along with a few fixes. + 2006-02-15 Andrew Ruder * Headers/Foundation/NSStream.h: NSRunLoop was not defined in diff --git a/Headers/Foundation/NSStream.h b/Headers/Foundation/NSStream.h index b1ffd90f1..23420752b 100644 --- a/Headers/Foundation/NSStream.h +++ b/Headers/Foundation/NSStream.h @@ -233,6 +233,63 @@ typedef enum { @end +/** + * the additional interface defined for gnustep + */ +@interface NSStream (GNUstepExtensions) + +/** + * Creates and returns by reference an NSInputStream object and + * NSOutputStream object for a local socket connection with the + * specified path. To use them you need to open them and wait + * on the NSStreamEventOpenCompleted event on one of them + */ ++ (void) getLocalStreamsToPath: (NSString *)path + inputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream; +/** + * Creates and returns by reference an NSInputStream object and NSOutputStream + * object for a anonymous local socket. Although you still need to open them, + * the open will be instantanious, and no NSStreamEventOpenCompleted event + * will be delivered. + */ ++ (void) pipeWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream; +@end + +/** + * GSServerStream is a subclass of NSStream that encapsulate a "server" stream; + * that is a stream that binds to a socket and accepts incoming connections + */ +@interface GSServerStream : NSStream +/** + * Createe a ip (ipv6) server stream + */ ++ (id) serverStreamToAddr: (NSString*)addr port: (int)port; +/** + * Create a local (unix domain) server stream + */ ++ (id) serverStreamToAddr: (NSString*)addr; +/** + * This is the method that accepts a connection and generates two streams + * as the server side inputStream and OutputStream. + * Although you still need to open them, the open will be + * instantanious, and no NSStreamEventOpenCompleted event will be delivered. + */ +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream; + +/** + * the designated initializer for a ip (ipv6) server stream + */ +- (id) initToAddr: (NSString*)addr port: (int)port; +/** + * the designated initializer for a local (unix domain) server stream + */ +- (id) initToAddr: (NSString*)addr; + +@end + @protocol GSStreamListener /** * The delegate receives this message when streamEvent diff --git a/Source/GSStream.h b/Source/GSStream.h index 46af2b088..a41d31549 100644 --- a/Source/GSStream.h +++ b/Source/GSStream.h @@ -34,15 +34,21 @@ | |-- GSInetInputStream | |-- GSLocalInputStream | `-- GSInet6InputStream - `-- NSOutputStream - `--GSOutputStream - |-- GSMemoryOutputStream - |-- GSFileOutputStream - `-- GSSocketOutputStream - |-- GSInetOutputStream - |-- GSLocalOutputStream - `-- GSInet6InputStream - */ + |-- NSOutputStream + | `--GSOutputStream + | |-- GSMemoryOutputStream + | |-- GSFileOutputStream + | `-- GSSocketOutputStream + | |-- GSInetOutputStream + | |-- GSLocalOutputStream + | `-- GSInet6InputStream + `-- GSServerStream + `-- GSAbstractServerStream + `-- GSSocketServerStream + |-- GSInetServerStream + |-- GSInet6ServerStream + `-- GSLocalServerStream +*/ #include @@ -55,6 +61,7 @@ NSStreamStatus _currentStatus;/* current status */\ NSMutableArray *_modes; /* currently scheduled modes. */\ NSRunLoop *_runloop; /* currently scheduled loop. */\ + void *_fd; /* the file descriptor (if any) */\ } /** @@ -82,6 +89,11 @@ IVARS */ - (void) _setStatus: (NSStreamStatus)newStatus; +/** + * setter for fd + */ +- (void) _setFd: (void *)fd; + /** * record an error based on errno */ @@ -95,6 +107,7 @@ IVARS - (BOOL) _isOpened; - (void) _sendEvent: (NSStreamEvent)event; - (void) _setStatus: (NSStreamStatus)newStatus; +- (void) _setFd: (void*)fd; - (void) _recordError; @end @@ -105,9 +118,20 @@ IVARS - (BOOL) _isOpened; - (void) _sendEvent: (NSStreamEvent)event; - (void) _setStatus: (NSStreamStatus)newStatus; +- (void) _setFd: (void*)fd; - (void) _recordError; @end +@interface GSAbstractServerStream : GSServerStream +IVARS +@end +@interface GSAbstractServerStream (private) +- (BOOL) _isOpened; +- (void) _sendEvent: (NSStreamEvent)event; +- (void) _setStatus: (NSStreamStatus)newStatus; +- (void) _setFd: (void*)fd; +- (void) _recordError; +@end /** * The concrete subclass of NSInputStream that reads from the memory @@ -122,7 +146,7 @@ IVARS /** * this is the bridge method for asynchronized operation. Do not call. */ -- (void) dispatch; +- (void) _dispatch; @end /** @@ -139,7 +163,7 @@ IVARS /** * this is the bridge method for asynchronized operation. Do not call. */ -- (void) dispatch; +- (void) _dispatch; @end #endif diff --git a/Source/GSStream.m b/Source/GSStream.m index 70edd0e0a..134026f05 100644 --- a/Source/GSStream.m +++ b/Source/GSStream.m @@ -128,6 +128,7 @@ NSString * const NSStreamSOCKSProxyVersionKey _lastError = nil; _modes = [NSMutableArray new]; _currentStatus = NSStreamStatusNotOpen; + _fd = (void*)-1; // any operation will fail } return self; } @@ -179,7 +180,7 @@ NSString * const NSStreamSOCKSProxyVersionKey NSError *theError = [NSError errorWithDomain: NSPOSIXErrorDomain code: errno userInfo: nil]; - perror(""); + NSLog(@"stream error: - %s", GSLastErrorStr(errno)); ASSIGN(_lastError, theError); _currentStatus = NSStreamStatusError; } @@ -202,6 +203,11 @@ NSString * const NSStreamSOCKSProxyVersionKey } } +- (void) _setFd: (void *)fd +{ + _fd = fd; +} + @end @implementation GSInputStream @@ -223,6 +229,15 @@ NSString * const NSStreamSOCKSProxyVersionKey } } @end +@implementation GSAbstractServerStream ++ (void) initialize +{ + if (self == [GSAbstractServerStream class]) + { + GSObjCAddClassBehavior(self, [GSStream class]); + } +} +@end @implementation GSMemoryInputStream @@ -299,8 +314,11 @@ NSString * const NSStreamSOCKSProxyVersionKey if (![_modes containsObject: mode]) [_modes addObject: mode]; if ([self _isOpened]) - [_runloop performSelector:@selector(dispatch:) target: self - argument: nil order: 0 modes: _modes]; + [_runloop performSelector: @selector(_dispatch:) + target: self + argument: nil + order: 0 + modes: _modes]; } - (void) removeFromRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode @@ -311,8 +329,9 @@ NSString * const NSStreamSOCKSProxyVersionKey { [_modes removeObject: mode]; if ([self _isOpened]) - [_runloop cancelPerformSelector:@selector(dispatch:) - target: self argument: nil]; + [_runloop cancelPerformSelector: @selector(_dispatch:) + target: self + argument: nil]; if ([_modes count] == 0) DESTROY(_runloop); } @@ -329,7 +348,7 @@ NSString * const NSStreamSOCKSProxyVersionKey { [super open]; if (_runloop) - [_runloop performSelector: @selector(dispatch:) + [_runloop performSelector: @selector(_dispatch:) target: self argument: nil order: 0 @@ -343,7 +362,7 @@ NSString * const NSStreamSOCKSProxyVersionKey [super close]; } -- (void) dispatch +- (void) _dispatch { BOOL av = [self hasBytesAvailable]; NSStreamEvent myEvent = av ? NSStreamEventHasBytesAvailable : @@ -356,7 +375,7 @@ NSString * const NSStreamSOCKSProxyVersionKey // dispatch again iff still opened, and last event is not eos if (av && [self _isOpened]) { - [_runloop performSelector: @selector(dispatch:) + [_runloop performSelector: @selector(_dispatch:) target: self argument: nil order: 0 @@ -428,7 +447,7 @@ NSString * const NSStreamSOCKSProxyVersionKey if (![_modes containsObject: mode]) [_modes addObject: mode]; if ([self _isOpened]) - [_runloop performSelector: @selector(dispatch:) + [_runloop performSelector: @selector(_dispatch:) target: self argument: nil order: 0 @@ -443,7 +462,7 @@ NSString * const NSStreamSOCKSProxyVersionKey { [_modes removeObject: mode]; if ([self _isOpened]) - [_runloop cancelPerformSelector: @selector(dispatch:) + [_runloop cancelPerformSelector: @selector(_dispatch:) target: self argument: nil]; if ([_modes count] == 0) @@ -470,7 +489,7 @@ NSString * const NSStreamSOCKSProxyVersionKey [super open]; if (_runloop) { - [_runloop performSelector: @selector(dispatch:) + [_runloop performSelector: @selector(_dispatch:) target: self argument: nil order: 0 @@ -485,7 +504,7 @@ NSString * const NSStreamSOCKSProxyVersionKey [super close]; } -- (void) dispatch +- (void) _dispatch { BOOL av = [self hasSpaceAvailable]; NSStreamEvent myEvent = av ? NSStreamEventHasSpaceAvailable : @@ -494,7 +513,7 @@ NSString * const NSStreamSOCKSProxyVersionKey [self _sendEvent: myEvent]; // dispatch again iff still opened, and last event is not eos if (av && [self _isOpened]) - [_runloop performSelector: @selector(dispatch:) + [_runloop performSelector: @selector(_dispatch:) target: self argument: nil order: 0 diff --git a/Source/unix/NSStream.m b/Source/unix/NSStream.m index 0e62f002b..d5a15b1e1 100644 --- a/Source/unix/NSStream.m +++ b/Source/unix/NSStream.m @@ -51,7 +51,6 @@ { @private NSString *_path; - int _fd; } @end @@ -61,9 +60,8 @@ @interface GSSocketInputStream : GSInputStream { @protected - int _fd; GSOutputStream *_sibling; - BOOL _passive; /* YES means it is the server side */ + BOOL _passive; /* YES means already connected */ } /** @@ -82,9 +80,9 @@ - (void) setSibling: (GSOutputStream*)sibling; /** - * setter for fd + * setter for passive */ -- (void) setFd: (int)fd; +- (void)setPassive: (BOOL)passive; @end @@ -97,7 +95,7 @@ /** * the designated initializer */ -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive; +- (id) initToAddr: (NSString*)addr port: (int)port; @end @@ -110,7 +108,7 @@ /** * the designated initializer */ -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive; +- (id) initToAddr: (NSString*)addr port: (int)port; @end @@ -123,11 +121,10 @@ /** * the designated initializer */ -- (id) initToAddr: (NSString*)addr passive: (BOOL)passive; +- (id) initToAddr: (NSString*)addr; @end - /** * The concrete subclass of NSOutputStream that writes to a file */ @@ -135,7 +132,6 @@ { @private NSString *_path; - int _fd; BOOL _shouldAppend; } @end @@ -146,9 +142,8 @@ @interface GSSocketOutputStream : GSOutputStream { @protected - int _fd; GSInputStream *_sibling; - BOOL _passive; /* YES means it is the server side */ + BOOL _passive; /* YES means already connected */ } /** @@ -167,9 +162,9 @@ - (void) setSibling: (GSInputStream*)sibling; /** - * setter for fd + * setter for passive */ -- (void) setFd: (int)fd; +- (void)setPassive: (BOOL)passive; @end @@ -182,7 +177,7 @@ /** * the designated initializer */ -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive; +- (id) initToAddr: (NSString*)addr port: (int)port; @end @@ -195,7 +190,7 @@ /** * the designated initializer */ -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive; +- (id) initToAddr: (NSString*)addr port: (int)port; @end @@ -208,10 +203,62 @@ /** * the designated initializer */ -- (id) initToAddr: (NSString*)addr passive: (BOOL)passive; +- (id) initToAddr: (NSString*)addr; @end +/** + * The concrete subclass of NSServerStream that accept connection from a socket + */ +@interface GSSocketServerStream : GSAbstractServerStream +/** + * return the class of the inputStream associated with this type of serverStream. + */ +- (Class)_inputStreamClass; +/** + * return the class of the outputStream associated with this type of serverStream. + */ +- (Class)_outputStreamClass; +/** + * get the length of the socket addr + */ +- (socklen_t) sockLen; +/** + * get the sockaddr + */ +- (struct sockaddr*) serverAddr; + +@end + +@interface GSInetServerStream : GSSocketServerStream +{ + @private + struct sockaddr_in _serverAddr; +} +@end + +@interface GSInet6ServerStream : GSSocketServerStream +{ + @private + struct sockaddr_in6 _serverAddr; +} +@end + +@interface GSLocalServerStream : GSSocketServerStream +{ + @private + struct sockaddr_un _serverAddr; +} +@end + +/** + * set the file descriptor to non-blocking + */ +static void setNonblocking(int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} @implementation GSFileInputStream @@ -220,8 +267,6 @@ if ((self = [super init]) != nil) { ASSIGN(_path, path); - // so that unopened access will fail - _fd = -1; } return self; } @@ -238,8 +283,8 @@ { int readLen; - readLen = read(_fd, buffer, len); - if (readLen < 0) + readLen = read((int)_fd, buffer, len); + if (readLen < 0 && errno != EAGAIN && errno != EINTR) [self _recordError]; else if (readLen == 0) [self _setStatus: NSStreamStatusAtEnd]; @@ -265,7 +310,7 @@ off_t offset = 0; if ([self _isOpened]) - offset = lseek(_fd, 0, SEEK_CUR); + offset = lseek((int)_fd, 0, SEEK_CUR); return [NSNumber numberWithLong: offset]; } return [super propertyForKey: key]; @@ -282,7 +327,7 @@ return; } [super open]; - _fd = fd; + _fd = (void*)fd; // put it self to the runloop if we havn't do so. if (_runloop) { @@ -299,7 +344,7 @@ - (void) close { - int closeReturn = close(_fd); + int closeReturn = close((int)_fd); if (closeReturn < 0) [self _recordError]; @@ -315,7 +360,7 @@ [self removeFromRunLoop: _runloop forMode: thisMode]; } } - _fd = -1; + _fd = (void*)-1; [super close]; } @@ -328,11 +373,11 @@ [_modes addObject: mode]; if ([self _isOpened]) { - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_RDESC watcher: self forMode: mode]; - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_EDESC watcher: self forMode: mode]; @@ -347,11 +392,11 @@ { if ([self _isOpened]) { - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_RDESC forMode: mode all: YES]; - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_EDESC forMode: mode all: YES]; @@ -379,7 +424,7 @@ int desc = (int)(uintptr_t)extra; NSStreamEvent myEvent; - NSAssert(desc == _fd, @"Wrong file descriptor received."); + NSAssert(desc == (int)_fd, @"Wrong file descriptor received."); if (type == ET_RDESC) { [self _setStatus: NSStreamStatusReading]; @@ -415,9 +460,9 @@ ASSIGN(_sibling, sibling); } -- (void) setFd: (int)fd +-(void)setPassive: (BOOL)passive { - _fd = fd; + _passive = passive; } - (id) init @@ -425,7 +470,6 @@ if ((self = [super init]) != nil) { // so that unopened access will fail - _fd = -1; _sibling = nil; _passive = NO; } @@ -455,7 +499,7 @@ } else { - int connectReturn = connect(_fd, [self peerAddr], [self sockLen]); + int connectReturn = connect((int)_fd, [self peerAddr], [self sockLen]); if (connectReturn < 0 && errno != EINPROGRESS) {// make an error @@ -471,7 +515,7 @@ { NSString *thisMode = [_modes objectAtIndex: i]; - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_WDESC watcher: self forMode: thisMode]; @@ -484,6 +528,7 @@ open_ok: // put itself to the runloop [super open]; + setNonblocking((int)_fd); if (_runloop) { int i; @@ -500,7 +545,10 @@ - (void)close { // read shutdown is ignored, because the other side may shutdown first. - shutdown(_fd, SHUT_RD); + if (!_sibling || [_sibling streamStatus]==NSStreamStatusClosed) + close((int)_fd); + else + shutdown((int)_fd, SHUT_RD); // remove itself from the runloop, if any if (_runloop) { @@ -511,7 +559,7 @@ NSString *thisMode = [_modes objectAtIndex: i]; if ([self streamStatus] == NSStreamStatusOpening) - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_WDESC forMode: thisMode all: YES]; @@ -519,12 +567,8 @@ [self removeFromRunLoop: _runloop forMode: thisMode]; } } - // clean up - if ([_sibling streamStatus] == NSStreamStatusClosed) - { - close(_fd); - _fd = -1; - } + // safety against double close + _fd = (void*)-1; [super close]; } @@ -532,8 +576,8 @@ { int readLen; - readLen = read(_fd, buffer, len); - if (readLen < 0) + readLen = read((int)_fd, buffer, len); + if (readLen < 0 && errno != EAGAIN && errno != EINTR) [self _recordError]; else if (readLen == 0) [self _setStatus: NSStreamStatusAtEnd]; @@ -561,11 +605,11 @@ [_modes addObject: mode]; if ([self _isOpened]) { - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_RDESC watcher: self forMode: mode]; - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_EDESC watcher: self forMode: mode]; @@ -581,11 +625,11 @@ [_modes removeObject: mode]; if ([self _isOpened]) { - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_RDESC forMode: mode all: YES]; - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_EDESC forMode: mode all: YES]; @@ -609,14 +653,24 @@ { int desc = (int)(uintptr_t)extra; NSStreamEvent myEvent; + int error, getReturn; + socklen_t len = sizeof(error); - NSAssert(desc == _fd, @"Wrong file descriptor received."); + NSAssert(desc == (int)_fd, @"Wrong file descriptor received."); if ([self streamStatus] == NSStreamStatusOpening) { - int i, error; - socklen_t len = sizeof(error);; - int getReturn = getsockopt(_fd, SOL_SOCKET, SO_ERROR, &error, &len); + int i; + getReturn = getsockopt((int)_fd, SOL_SOCKET, SO_ERROR, &error, &len); + // clean up the event listener + for (i = 0; i < [_modes count]; i++) + { + NSString* thisMode = [_modes objectAtIndex: i]; + [_runloop removeEvent: _fd + type: ET_WDESC + forMode: thisMode + all: YES]; + } if (getReturn >= 0 && !error && type == ET_WDESC) { // finish up the opening myEvent = NSStreamEventOpenCompleted; @@ -633,16 +687,6 @@ [self _recordError]; myEvent = NSStreamEventErrorOccurred; } - // clean up the event listener - for (i = 0; i < [_modes count]; i++) - { - NSString *thisMode = [_modes objectAtIndex: i]; - - [_runloop removeEvent: (void*)(uintptr_t)_fd - type: ET_WDESC - forMode: thisMode - all: YES]; - } } else { @@ -652,9 +696,10 @@ myEvent = NSStreamEventHasBytesAvailable; } else - { // must be an error then - [self _recordError]; - myEvent = NSStreamEventErrorOccurred; + { + // the only possible thing that can happened is writer hang up + // which is just fine. + return; } } [self _sendEvent: myEvent]; @@ -674,7 +719,7 @@ return (struct sockaddr*)&_peerAddr; } -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive +- (id) initToAddr: (NSString*)addr port: (int)port { int ptonReturn; @@ -684,7 +729,6 @@ { _peerAddr.sin_family = AF_INET; _peerAddr.sin_port = htons(port); - _passive = passive; ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); if (ptonReturn == 0) // error { @@ -708,7 +752,7 @@ return (struct sockaddr*)&_peerAddr; } -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive +- (id) initToAddr: (NSString*)addr port: (int)port { int ptonReturn; const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; @@ -717,7 +761,6 @@ { _peerAddr.sin6_family = AF_INET6; _peerAddr.sin6_port = htons(port); - _passive = passive; ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); if (ptonReturn == 0) // error { @@ -741,14 +784,13 @@ return (struct sockaddr*)&_peerAddr; } -- (id) initToAddr: (NSString*)addr passive: (BOOL)passive +- (id) initToAddr: (NSString*)addr { const char* real_addr = [addr fileSystemRepresentation]; if ((self = [super init]) != nil) { _peerAddr.sun_family = AF_LOCAL; - _passive = passive; if (strlen(real_addr)>sizeof(_peerAddr.sun_path)-1) // too long { DESTROY(self); @@ -771,7 +813,6 @@ { ASSIGN(_path, path); // so that unopened access will fail - _fd = -1; _shouldAppend = shouldAppend; } return self; @@ -788,8 +829,8 @@ - (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len { int writeLen; - writeLen = write(_fd, buffer, len); - if (writeLen < 0) + writeLen = write((int)_fd, buffer, len); + if (writeLen < 0 && errno != EAGAIN && errno != EINTR) [self _recordError]; return writeLen; } @@ -818,11 +859,12 @@ return; } [super open]; - _fd = fd; + _fd = (void*)fd; // put it self to the runloop if we haven't do so. if (_runloop) { int i; + for (i = 0; i < [_modes count]; i++) { NSString* thisMode = [_modes objectAtIndex: i]; @@ -833,7 +875,7 @@ - (void) close { - int closeReturn = close(_fd); + int closeReturn = close((int)_fd); if (closeReturn < 0) [self _recordError]; // remove itself from the runloop, if any @@ -848,7 +890,7 @@ [self removeFromRunLoop: _runloop forMode: thisMode]; } } - _fd = -1; + _fd = (void*)-1; [super close]; } @@ -861,11 +903,11 @@ [_modes addObject: mode]; if ([self _isOpened]) { - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_WDESC watcher: self forMode: mode]; - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_EDESC watcher: self forMode: mode]; @@ -881,11 +923,11 @@ [_modes removeObject: mode]; if ([self _isOpened]) { - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_WDESC forMode: mode all: YES]; - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_EDESC forMode: mode all: YES]; @@ -902,7 +944,7 @@ off_t offset = 0; if ([self _isOpened]) - offset = lseek(_fd, 0, SEEK_CUR); + offset = lseek((int)_fd, 0, SEEK_CUR); return [NSNumber numberWithLong: offset]; } return [super propertyForKey: key]; @@ -923,7 +965,7 @@ int desc = (int)(uintptr_t)extra; NSStreamEvent myEvent; - NSAssert(desc == _fd, @"Wrong file descriptor received."); + NSAssert(desc == (int)_fd, @"Wrong file descriptor received."); if (type == ET_WDESC) { [self _setStatus: NSStreamStatusWriting]; @@ -959,17 +1001,15 @@ ASSIGN(_sibling, sibling); } -- (void) setFd: (int)fd +-(void)setPassive: (BOOL)passive { - _fd = fd; + _passive = passive; } - (id) init { if ((self = [super init]) != nil) { - // so that unopened access will fail - _fd = -1; _sibling = nil; _passive = NO; } @@ -987,8 +1027,8 @@ - (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len { int writeLen; - writeLen = write(_fd, buffer, len); - if (writeLen < 0) + writeLen = write((int)_fd, buffer, len); + if (writeLen < 0 && errno != EAGAIN && errno != EINTR) [self _recordError]; return writeLen; } @@ -1015,7 +1055,7 @@ } else { - int connectReturn = connect(_fd, [self peerAddr], [self sockLen]); + int connectReturn = connect((int)_fd, [self peerAddr], [self sockLen]); if (connectReturn < 0 && errno != EINPROGRESS) {// make an error @@ -1026,10 +1066,12 @@ if (_runloop) { int i; + for (i = 0; i < [_modes count]; i++) { - NSString* thisMode = [_modes objectAtIndex: i]; - [_runloop addEvent: (void*)(uintptr_t)_fd type: ET_WDESC + NSString *thisMode = [_modes objectAtIndex: i]; + + [_runloop addEvent: _fd type: ET_WDESC watcher: self forMode: thisMode]; } } @@ -1040,12 +1082,14 @@ open_ok: // put itself to the runloop [super open]; + setNonblocking((int)_fd); if (_runloop) { int i; + for (i = 0; i < [_modes count]; i++) { - NSString* thisMode = [_modes objectAtIndex: i]; + NSString * thisMode = [_modes objectAtIndex: i]; [self scheduleInRunLoop: _runloop forMode: thisMode]; } } @@ -1054,7 +1098,11 @@ - (void) close { // shutdown may fail (broken pipe). Record it. - int closeReturn = shutdown(_fd, SHUT_WR); + int closeReturn; + if (!_sibling || [_sibling streamStatus]==NSStreamStatusClosed) + closeReturn = close((int)_fd); + else + closeReturn = shutdown((int)_fd, SHUT_WR); if (closeReturn < 0) [self _recordError]; // remove itself from the runloop, if any @@ -1066,7 +1114,7 @@ { NSString *thisMode = [_modes objectAtIndex: i]; if ([self streamStatus] == NSStreamStatusOpening) - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_WDESC forMode: thisMode all: YES]; @@ -1074,12 +1122,8 @@ [self removeFromRunLoop: _runloop forMode: thisMode]; } } - // clean up - if ([_sibling streamStatus] == NSStreamStatusClosed) - { - close(_fd); - _fd = -1; - } + // safety against double close + _fd = (void*)-1; [super close]; } @@ -1092,11 +1136,11 @@ [_modes addObject: mode]; if ([self _isOpened]) { - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_WDESC watcher: self forMode: mode]; - [_runloop addEvent: (void*)(uintptr_t)_fd + [_runloop addEvent: _fd type: ET_EDESC watcher: self forMode: mode]; @@ -1112,11 +1156,11 @@ [_modes removeObject: mode]; if ([self _isOpened]) { - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_WDESC forMode: mode all: YES]; - [_runloop removeEvent: (void*)(uintptr_t)_fd + [_runloop removeEvent: _fd type: ET_EDESC forMode: mode all: YES]; @@ -1140,14 +1184,26 @@ { int desc = (int)(uintptr_t)extra; NSStreamEvent myEvent; + int error, getReturn; + socklen_t len = sizeof(error); - NSAssert(desc == _fd, @"Wrong file descriptor received."); + NSAssert(desc == (int)_fd, @"Wrong file descriptor received."); if ([self streamStatus] == NSStreamStatusOpening) { - int i, error; - socklen_t len = sizeof(error);; - int getReturn = getsockopt(_fd, SOL_SOCKET, SO_ERROR, &error, &len); + int i; + + getReturn = getsockopt((int)_fd, SOL_SOCKET, SO_ERROR, &error, &len); + // clean up the event listener + for (i = 0; i < [_modes count]; i++) + { + NSString *thisMode = [_modes objectAtIndex: i]; + + [_runloop removeEvent: _fd + type: ET_WDESC + forMode: thisMode + all: YES]; + } if (getReturn >= 0 && !error && type == ET_WDESC) { // finish up the opening myEvent = NSStreamEventOpenCompleted; @@ -1164,16 +1220,6 @@ [self _recordError]; myEvent = NSStreamEventErrorOccurred; } - // clean up the event listener - for (i = 0; i < [_modes count]; i++) - { - NSString *thisMode = [_modes objectAtIndex: i]; - - [_runloop removeEvent: (void*)(uintptr_t)_fd - type: ET_WDESC - forMode: thisMode - all: YES]; - } } else { @@ -1183,9 +1229,18 @@ myEvent = NSStreamEventHasSpaceAvailable; } else - { // must be an error then - [self _recordError]; - myEvent = NSStreamEventErrorOccurred; + { + // check if there is an real error + getReturn = getsockopt((int)_fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (getReturn >= 0 && !error) + return; + else + { + if (error) + errno = error; + [self _recordError]; + myEvent = NSStreamEventErrorOccurred; + } } } @@ -1206,7 +1261,7 @@ return (struct sockaddr*)&_peerAddr; } -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive +- (id) initToAddr: (NSString*)addr port: (int)port { int ptonReturn; const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; @@ -1215,7 +1270,6 @@ { _peerAddr.sin_family = AF_INET; _peerAddr.sin_port = htons(port); - _passive = passive; ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); if (ptonReturn == 0) // error { @@ -1239,7 +1293,7 @@ return (struct sockaddr*)&_peerAddr; } -- (id) initToAddr: (NSString*)addr port: (int)port passive: (BOOL)passive +- (id) initToAddr: (NSString*)addr port: (int)port { int ptonReturn; const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; @@ -1248,7 +1302,6 @@ { _peerAddr.sin6_family = AF_INET6; _peerAddr.sin6_port = htons(port); - _passive = passive; ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); if (ptonReturn == 0) // error { @@ -1272,14 +1325,13 @@ return (struct sockaddr*)&_peerAddr; } -- (id) initToAddr: (NSString*)addr passive: (BOOL)passive +- (id) initToAddr: (NSString*)addr { const char* real_addr = [addr fileSystemRepresentation]; if ((self = [super init]) != nil) { _peerAddr.sun_family = AF_LOCAL; - _passive = passive; if (strlen(real_addr) > sizeof(_peerAddr.sun_path)-1) // too long { DESTROY(self); @@ -1294,8 +1346,6 @@ @end - - @implementation NSStream + (void) getStreamsToHost: (NSHost *)host @@ -1307,21 +1357,18 @@ GSSocketInputStream *ins = nil; GSSocketOutputStream *outs = nil; int sock; - int flags; // try ipv4 first ins = AUTORELEASE([[GSInetInputStream alloc] - initToAddr: address port: port passive: NO]); + initToAddr: address port: port]); outs = AUTORELEASE([[GSInetOutputStream alloc] - initToAddr: address port: port passive: NO]); + initToAddr: address port: port]); if (!ins) { ins = [[GSInet6InputStream alloc] initToAddr: address - port: port - passive: NO]; + port: port]; outs = [[GSInet6OutputStream alloc] initToAddr: address - port: port - passive: NO]; + port: port]; sock = socket(PF_INET6, SOCK_STREAM, 0); } else @@ -1329,12 +1376,9 @@ sock = socket(PF_INET, SOCK_STREAM, 0); } - // set nonblocking - flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); - - [ins setFd: sock]; - [outs setFd: sock]; + NSAssert(sock >= 0, @"Cannot open socket"); + [ins _setFd: (void*)(intptr_t)sock]; + [outs _setFd: (void*)(intptr_t)sock]; if (inputStream) { [ins setSibling: outs]; @@ -1348,6 +1392,59 @@ return; } ++ (void) getLocalStreamsToPath: (NSString *)path + inputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + GSSocketInputStream *ins = nil; + GSSocketOutputStream *outs = nil; + int sock; + + ins = AUTORELEASE([[GSLocalInputStream alloc] initToAddr: path]); + outs = AUTORELEASE([[GSLocalOutputStream alloc] initToAddr: path]); + sock = socket(PF_LOCAL, SOCK_STREAM, 0); + + NSAssert(sock >= 0, @"Cannot open socket"); + [ins _setFd: (void*)(intptr_t)sock]; + [outs _setFd: (void*)(intptr_t)sock]; + if (inputStream) + { + [ins setSibling: outs]; + *inputStream = ins; + } + if (outputStream) + { + [outs setSibling: ins]; + *outputStream = outs; + } + return; +} + ++ (void) pipeWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + GSSocketInputStream *ins = nil; + GSSocketOutputStream *outs = nil; + int fds[2]; + int pipeReturn; + + // the type of the stream does not matter, since we are only using the fd + ins = AUTORELEASE([GSLocalInputStream new]); + outs = AUTORELEASE([GSLocalOutputStream new]); + pipeReturn = pipe(fds); + + NSAssert(pipeReturn >= 0, @"Cannot open pipe"); + [ins _setFd: (void*)(intptr_t)fds[0]]; + [outs _setFd: (void*)(intptr_t)fds[1]]; + // no need to connect + [ins setPassive: YES]; + [outs setPassive: YES]; + if (inputStream) + *inputStream = ins; + if (outputStream) + *outputStream = outs; + return; +} - (void) close { @@ -1503,3 +1600,363 @@ @end +@implementation GSServerStream + ++ (id) serverStreamToAddr: (NSString*)addr port: (int)port +{ + GSServerStream *s; + + // try inet first, then inet6 + s = [[GSInetServerStream alloc] initToAddr: addr port: port]; + if (!s) + s = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; + return AUTORELEASE(s); +} + ++ (id) serverStreamToAddr: (NSString*)addr +{ + return AUTORELEASE([[GSLocalServerStream alloc] initToAddr: addr]); +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + // try inet first, then inet6 + self = [[GSInetServerStream alloc] initToAddr: addr port: port]; + if (!self) + self = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; + return self; +} + +- (id) initToAddr: (NSString*)addr +{ + RELEASE(self); + return [[GSLocalServerStream alloc] initToAddr: addr]; +} + +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + [self subclassResponsibility: _cmd]; +} + +@end + +@implementation GSSocketServerStream + +- (Class) _inputStreamClass +{ + [self subclassResponsibility: _cmd]; + return Nil; +} + +- (Class) _outputStreamClass +{ + [self subclassResponsibility: _cmd]; + return Nil; +} + +- (void) dealloc +{ + if ([self _isOpened]) + [self close]; + [super dealloc]; +} + +- (socklen_t) sockLen +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +- (struct sockaddr*) serverAddr +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +#define SOCKET_BACKLOG 5 + +- (void) open +{ + int bindReturn = bind((int)_fd, [self serverAddr], [self sockLen]); + int listenReturn = listen((int)_fd, SOCKET_BACKLOG); + + if (bindReturn < 0 || listenReturn) + { + [self _recordError]; + return; + } + setNonblocking((int)_fd); + // put itself to the runloop + [super open]; + if (_runloop) + { + int i; + + for (i = 0; i < [_modes count]; i++) + { + NSString* thisMode = [_modes objectAtIndex: i]; + [self scheduleInRunLoop: _runloop forMode: thisMode]; + } + } +} + +- (void) close +{ + // close a server socket is safe + close((int)_fd); + // remove itself from the runloop, if any + if (_runloop) + { + int i; + + for (i = 0; i < [_modes count]; i++) + { + NSString* thisMode = [_modes objectAtIndex: i]; + [self removeFromRunLoop: _runloop forMode: thisMode]; + } + } + _fd = (void*)-1; + [super close]; +} + +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + GSSocketInputStream *ins = AUTORELEASE([[self _inputStreamClass] new]); + GSSocketOutputStream *outs = AUTORELEASE([[self _outputStreamClass] new]); + socklen_t len = [ins sockLen]; + int acceptReturn = accept((int)_fd, [ins peerAddr], &len); + + if (acceptReturn < 0) + { // test for real error + if (errno != EWOULDBLOCK && errno != ECONNABORTED + && errno != EPROTO && errno != EINTR) + { + [self _recordError]; + } + ins = nil; + outs = nil; + } + else + { + // no need to connect again + [ins setPassive: YES]; + [outs setPassive: YES]; + // copy the addr to outs + memcpy([outs peerAddr], [ins peerAddr], len); + [ins _setFd: (void*)(intptr_t)acceptReturn]; + [outs _setFd: (void*)(intptr_t)acceptReturn]; + } + if (inputStream) + { + [ins setSibling: outs]; + *inputStream = ins; + } + if (outputStream) + { + [outs setSibling: ins]; + *outputStream = outs; + } +} + +- (void) scheduleInRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode +{ + NSAssert(!_runloop || _runloop == aRunLoop, + @"Attempt to schedule in more than one runloop."); + ASSIGN(_runloop, aRunLoop); + if (![_modes containsObject: mode]) + [_modes addObject: mode]; + if ([self _isOpened]) + { + [_runloop addEvent: _fd + type: ET_RDESC + watcher: self + forMode: mode]; + [_runloop addEvent: _fd + type: ET_EDESC + watcher: self + forMode: mode]; + } +} + +- (void) removeFromRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode +{ + NSAssert(_runloop == aRunLoop, + @"Attempt to remove unscheduled runloop"); + if ([_modes containsObject: mode]) + { + [_modes removeObject: mode]; + if ([self _isOpened]) + { + [_runloop removeEvent: _fd + type: ET_RDESC + forMode: mode + all: YES]; + [_runloop removeEvent: _fd + type: ET_EDESC + forMode: mode + all: YES]; + } + if ([_modes count] == 0) + DESTROY(_runloop); + } +} + +- (NSDate*) timedOutEvent: (void*)data + type: (RunLoopEventType)type + forMode: (NSString*)mode +{ + return nil; +} + +- (void) receivedEvent: (void*)data + type: (RunLoopEventType)type + extra: (void*)extra + forMode: (NSString*)mode +{ + int desc = (int)(uintptr_t)extra; + NSStreamEvent myEvent; + + NSAssert(desc == (int)_fd, @"Wrong file descriptor received."); + if (type == ET_RDESC) + { + [self _setStatus: NSStreamStatusReading]; + myEvent = NSStreamEventHasBytesAvailable; + } + else + { // must be an error then + [self _recordError]; + myEvent = NSStreamEventErrorOccurred; + } + [self _sendEvent: myEvent]; +} + +@end + +@implementation GSInetServerStream + +- (Class) _inputStreamClass +{ + return [GSInetInputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSInetOutputStream class]; +} + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in); +} + +- (struct sockaddr*) serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + [super init]; + _serverAddr.sin_family = AF_INET; + _serverAddr.sin_port = htons(port); + ptonReturn = inet_pton(AF_INET, addr_c, &(_serverAddr.sin_addr)); + _fd = (void*)socket(AF_INET, SOCK_STREAM, 0); + if (ptonReturn == 0 || _fd < 0) // error + { + RELEASE(self); + return nil; + } + NSAssert(_fd >= 0, @"cannot open socket"); + return self; +} + +@end + +@implementation GSInet6ServerStream + +- (Class) _inputStreamClass +{ + return [GSInet6InputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSInet6OutputStream class]; +} + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in6); +} + +- (struct sockaddr*) serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + [super init]; + _serverAddr.sin6_family = AF_INET6; + _serverAddr.sin6_port = htons(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &(_serverAddr.sin6_addr)); + _fd = (void*)socket(AF_INET6, SOCK_STREAM, 0); + if (ptonReturn == 0 || _fd < 0) // error + { + RELEASE(self); + return nil; + } + NSAssert(_fd >= 0, @"cannot open socket"); + return self; +} + +@end + +@implementation GSLocalServerStream + +- (Class) _inputStreamClass +{ + return [GSLocalInputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSLocalOutputStream class]; +} + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_un); +} + +- (struct sockaddr*) serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr +{ + const char* real_addr = [addr fileSystemRepresentation]; + [super init]; + _serverAddr.sun_family = AF_LOCAL; + _fd = (void *)socket(AF_LOCAL, SOCK_STREAM, 0); + if (strlen(real_addr)>sizeof(_serverAddr.sun_path)-1 || _fd < 0) // too long + { + RELEASE(self); + return nil; + } + strncpy(_serverAddr.sun_path, real_addr, sizeof(_serverAddr.sun_path)-1); + return self; +} + +@end + diff --git a/Source/win32/NSStreamWin32.m b/Source/win32/NSStreamWin32.m index e47ceaa52..968ac5eaa 100644 --- a/Source/win32/NSStreamWin32.m +++ b/Source/win32/NSStreamWin32.m @@ -39,7 +39,7 @@ inputStream: (NSInputStream **)inputStream outputStream: (NSOutputStream **)outputStream { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; } - (void) close @@ -103,25 +103,25 @@ + (id) inputStreamWithData: (NSData *)data { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } + (id) inputStreamWithFileAtPath: (NSString *)path { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } - (id) initWithData: (NSData *)data { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } - (id) initWithFileAtPath: (NSString *)path { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } @@ -149,37 +149,37 @@ + (id) outputStreamToMemory { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } + (id) outputStreamToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } + (id) outputStreamToFileAtPath: (NSString *)path append: (BOOL)shouldAppend { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } - (id) initToMemory { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } - (id) initToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } - (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend { - [self notImplemented:_cmd]; + [self notImplemented: _cmd]; return nil; } @@ -197,3 +197,35 @@ @end +@implementation GSServerStream + ++ (id) serverStreamToAddr: (NSString*)addr port: (int)port +{ + [self notImplemented: _cmd]; +} + ++ (id) serverStreamToAddr: (NSString*)addr +{ + [self notImplemented: _cmd]; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + [self notImplemented: _cmd]; + return nil; +} + +- (id) initToAddr: (NSString*)addr +{ + [self notImplemented: _cmd]; + return nil; +} + +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + [self subclassResponsibility: _cmd]; +} + +@end +